From c6b3eccbdf2785b59ab33ed99fabf6f3f5d81d2a Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 14 May 2022 19:46:52 +0200 Subject: [PATCH 01/33] refactor: rename Modrinth classes to ModrinthMod --- launcher/CMakeLists.txt | 8 ++++---- launcher/ui/dialogs/ModDownloadDialog.cpp | 4 ++-- launcher/ui/dialogs/ModDownloadDialog.h | 4 ++-- .../{ModrinthModel.cpp => ModrinthModModel.cpp} | 2 +- .../{ModrinthModel.h => ModrinthModModel.h} | 4 ++-- .../{ModrinthPage.cpp => ModrinthModPage.cpp} | 16 ++++++++-------- .../{ModrinthPage.h => ModrinthModPage.h} | 6 +++--- 7 files changed, 22 insertions(+), 22 deletions(-) rename launcher/ui/pages/modplatform/modrinth/{ModrinthModel.cpp => ModrinthModModel.cpp} (97%) rename launcher/ui/pages/modplatform/modrinth/{ModrinthModel.h => ModrinthModModel.h} (85%) rename launcher/ui/pages/modplatform/modrinth/{ModrinthPage.cpp => ModrinthModPage.cpp} (85%) rename launcher/ui/pages/modplatform/modrinth/{ModrinthPage.h => ModrinthModPage.h} (93%) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index b79f03c86..16ec4c047 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -782,10 +782,10 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/ImportPage.cpp ui/pages/modplatform/ImportPage.h - ui/pages/modplatform/modrinth/ModrinthModel.cpp - ui/pages/modplatform/modrinth/ModrinthModel.h - ui/pages/modplatform/modrinth/ModrinthPage.cpp - ui/pages/modplatform/modrinth/ModrinthPage.h + ui/pages/modplatform/modrinth/ModrinthModModel.cpp + ui/pages/modplatform/modrinth/ModrinthModModel.h + ui/pages/modplatform/modrinth/ModrinthModPage.cpp + ui/pages/modplatform/modrinth/ModrinthModPage.h # GUI - dialogs ui/dialogs/AboutDialog.cpp diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp index d02ea4769..305e85c06 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.cpp +++ b/launcher/ui/dialogs/ModDownloadDialog.cpp @@ -13,7 +13,7 @@ #include #include "ui/widgets/PageContainer.h" -#include "ui/pages/modplatform/modrinth/ModrinthPage.h" +#include "ui/pages/modplatform/modrinth/ModrinthModPage.h" #include "ModDownloadTask.h" @@ -98,7 +98,7 @@ void ModDownloadDialog::accept() QList ModDownloadDialog::getPages() { - modrinthPage = new ModrinthPage(this, m_instance); + modrinthPage = new ModrinthModPage(this, m_instance); flameModPage = new FlameModPage(this, m_instance); return { diff --git a/launcher/ui/dialogs/ModDownloadDialog.h b/launcher/ui/dialogs/ModDownloadDialog.h index 309d89d06..782dc3619 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.h +++ b/launcher/ui/dialogs/ModDownloadDialog.h @@ -16,7 +16,7 @@ class ModDownloadDialog; class PageContainer; class QDialogButtonBox; -class ModrinthPage; +class ModrinthModPage; class ModDownloadDialog : public QDialog, public BasePageProvider { @@ -50,7 +50,7 @@ private: QVBoxLayout *m_verticalLayout = nullptr; - ModrinthPage *modrinthPage = nullptr; + ModrinthModPage *modrinthPage = nullptr; FlameModPage *flameModPage = nullptr; QHash modTask; BaseInstance *m_instance; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.cpp similarity index 97% rename from launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp rename to launcher/ui/pages/modplatform/modrinth/ModrinthModModel.cpp index b788860a3..1d9f4d60b 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#include "ModrinthModel.h" +#include "ModrinthModModel.h" #include "modplatform/modrinth/ModrinthPackIndex.h" diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h similarity index 85% rename from launcher/ui/pages/modplatform/modrinth/ModrinthModel.h rename to launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h index 45a6090a7..63c23bbeb 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h @@ -1,6 +1,6 @@ #pragma once -#include "ModrinthPage.h" +#include "ModrinthModPage.h" namespace Modrinth { @@ -8,7 +8,7 @@ class ListModel : public ModPlatform::ListModel { Q_OBJECT public: - ListModel(ModrinthPage* parent) : ModPlatform::ListModel(parent){}; + ListModel(ModrinthModPage* parent) : ModPlatform::ListModel(parent){}; ~ListModel() override = default; private: diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp similarity index 85% rename from launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp rename to launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp index 98bde0ae3..d3a1f8594 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp @@ -33,14 +33,14 @@ * limitations under the License. */ -#include "ModrinthPage.h" +#include "ModrinthModPage.h" #include "modplatform/modrinth/ModrinthAPI.h" #include "ui_ModPage.h" -#include "ModrinthModel.h" +#include "ModrinthModModel.h" #include "ui/dialogs/ModDownloadDialog.h" -ModrinthPage::ModrinthPage(ModDownloadDialog* dialog, BaseInstance* instance) +ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance* instance) : ModPage(dialog, instance, new ModrinthAPI()) { listModel = new Modrinth::ListModel(this); @@ -56,12 +56,12 @@ ModrinthPage::ModrinthPage(ModDownloadDialog* dialog, BaseInstance* instance) // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, // so it's best not to connect them in the parent's constructor... connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); - connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthPage::onSelectionChanged); - connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthPage::onVersionSelectionChanged); - connect(ui->modSelectionButton, &QPushButton::clicked, this, &ModrinthPage::onModSelected); + connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthModPage::onSelectionChanged); + connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthModPage::onVersionSelectionChanged); + connect(ui->modSelectionButton, &QPushButton::clicked, this, &ModrinthModPage::onModSelected); } -auto ModrinthPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderType loader) const -> bool +auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderType loader) const -> bool { auto loaderStrings = ModrinthAPI::getModLoaderStrings(loader); @@ -79,4 +79,4 @@ auto ModrinthPage::validateVersion(ModPlatform::IndexedVersion& ver, QString min // I don't know why, but doing this on the parent class makes it so that // other mod providers start loading before being selected, at least with // my Qt, so we need to implement this in every derived class... -auto ModrinthPage::shouldDisplay() const -> bool { return true; } +auto ModrinthModPage::shouldDisplay() const -> bool { return true; } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h similarity index 93% rename from launcher/ui/pages/modplatform/modrinth/ModrinthPage.h rename to launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h index e3a0e1f00..b1e72bfea 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h @@ -40,12 +40,12 @@ #include "modplatform/modrinth/ModrinthAPI.h" -class ModrinthPage : public ModPage { +class ModrinthModPage : public ModPage { Q_OBJECT public: - explicit ModrinthPage(ModDownloadDialog* dialog, BaseInstance* instance); - ~ModrinthPage() override = default; + explicit ModrinthModPage(ModDownloadDialog* dialog, BaseInstance* instance); + ~ModrinthModPage() override = default; inline auto displayName() const -> QString override { return "Modrinth"; } inline auto icon() const -> QIcon override { return APPLICATION->getThemedIcon("modrinth"); } From db038463581400005f045a277a249ab07175ab2b Mon Sep 17 00:00:00 2001 From: kb1000 Date: Mon, 31 Jan 2022 15:25:36 +0100 Subject: [PATCH 02/33] Add support for importing Modrinth packs from files --- launcher/CMakeLists.txt | 10 ++ launcher/InstanceImportTask.cpp | 170 +++++++++++++++++- launcher/InstanceImportTask.h | 6 +- .../modrinth/ModrinthPackManifest.cpp | 16 ++ .../modrinth/ModrinthPackManifest.h | 32 ++++ launcher/resources/multimc/multimc.qrc | 3 + .../resources/multimc/scalable/modrinth.svg | 4 + launcher/ui/dialogs/NewInstanceDialog.cpp | 2 + launcher/ui/pages/modplatform/ImportPage.cpp | 4 +- .../modplatform/modrinth/ModrinthPage.cpp | 55 ++++++ .../pages/modplatform/modrinth/ModrinthPage.h | 62 +++++++ .../modplatform/modrinth/ModrinthPage.ui | 94 ++++++++++ 12 files changed, 452 insertions(+), 6 deletions(-) create mode 100644 launcher/modplatform/modrinth/ModrinthPackManifest.cpp create mode 100644 launcher/modplatform/modrinth/ModrinthPackManifest.h create mode 100644 launcher/resources/multimc/scalable/modrinth.svg create mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp create mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthPage.h create mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 16ec4c047..cbe135e2b 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -563,6 +563,11 @@ set(ATLAUNCHER_SOURCES modplatform/atlauncher/ATLShareCode.h ) +set(MODRINTH_SOURCES + modplatform/modrinth/ModrinthPackManifest.cpp + modplatform/modrinth/ModrinthPackManifest.h +) + add_unit_test(Index SOURCES meta/Index_test.cpp LIBS Launcher_logic @@ -596,6 +601,7 @@ set(LOGIC_SOURCES ${MODPACKSCH_SOURCES} ${TECHNIC_SOURCES} ${ATLAUNCHER_SOURCES} + ${MODRINTH_SOURCES} ) SET(LAUNCHER_SOURCES @@ -774,6 +780,9 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/flame/FlameModPage.cpp ui/pages/modplatform/flame/FlameModPage.h + ui/pages/modplatform/modrinth/ModrinthPage.cpp + ui/pages/modplatform/modrinth/ModrinthPage.h + ui/pages/modplatform/technic/TechnicModel.cpp ui/pages/modplatform/technic/TechnicModel.h ui/pages/modplatform/technic/TechnicPage.cpp @@ -908,6 +917,7 @@ qt5_wrap_ui(LAUNCHER_UI ui/pages/modplatform/legacy_ftb/Page.ui ui/pages/modplatform/ImportPage.ui ui/pages/modplatform/ftb/FtbPage.ui + ui/pages/modplatform/modrinth/ModrinthPage.ui ui/pages/modplatform/technic/TechnicPage.ui ui/widgets/InstanceCardWidget.ui ui/widgets/CustomCommands.ui diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 1a13c9973..517155811 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -30,10 +30,15 @@ #include "modplatform/flame/PackManifest.h" #include "Json.h" #include +#include "modplatform/modrinth/ModrinthPackManifest.h" #include "modplatform/technic/TechnicPackProcessor.h" #include "icons/IconList.h" #include "Application.h" +#include "net/ChecksumValidator.h" + +#include +#include InstanceImportTask::InstanceImportTask(const QUrl sourceUrl) { @@ -109,6 +114,7 @@ void InstanceImportTask::processZipPack() QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg"); bool technicFound = QuaZipDir(m_packZip.get()).exists("/bin/modpack.jar") || QuaZipDir(m_packZip.get()).exists("/bin/version.json"); QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json"); + QString modrinthFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "modrinth.index.json"); QString root; if(!mmcFound.isNull()) { @@ -132,6 +138,13 @@ void InstanceImportTask::processZipPack() root = flameFound; m_modpackType = ModpackType::Flame; } + else if(!modrinthFound.isNull()) + { + // process as Modrinth pack + qDebug() << "Modrinth:" << modrinthFound; + root = modrinthFound; + m_modpackType = ModpackType::Modrinth; + } if(m_modpackType == ModpackType::Unknown) { emitFailed(tr("Archive does not contain a recognized modpack type.")); @@ -188,15 +201,18 @@ void InstanceImportTask::extractFinished() switch(m_modpackType) { - case ModpackType::Flame: - processFlame(); - return; case ModpackType::MultiMC: processMultiMC(); return; case ModpackType::Technic: processTechnic(); return; + case ModpackType::Flame: + processFlame(); + return; + case ModpackType::Modrinth: + processModrinth(); + return; case ModpackType::Unknown: emitFailed(tr("Archive does not contain a recognized modpack type.")); return; @@ -461,3 +477,151 @@ void InstanceImportTask::processMultiMC() } emitSucceeded(); } + +void InstanceImportTask::processModrinth() { + std::vector files; + QString minecraftVersion, fabricVersion, forgeVersion; + try + { + QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json"); + auto doc = Json::requireDocument(indexPath); + auto obj = Json::requireObject(doc, "modrinth.index.json"); + int formatVersion = Json::requireInteger(obj, "formatVersion", "modrinth.index.json"); + if (formatVersion == 1) + { + auto game = Json::requireString(obj, "game", "modrinth.index.json"); + if (game != "minecraft") + { + throw JSONValidationError("Unknown game: " + game); + } + + auto jsonFiles = Json::requireIsArrayOf(obj, "files", "modrinth.index.json"); + std::transform(jsonFiles.begin(), jsonFiles.end(), std::back_inserter(files), [](const QJsonObject& obj) + { + Modrinth::File file; + file.path = Json::requireString(obj, "path"); + QString supported = Json::ensureString(Json::ensureObject(obj, "env")); + QJsonObject hashes = Json::requireObject(obj, "hashes"); + QString hash; + QCryptographicHash::Algorithm hashAlgorithm; + hash = Json::ensureString(hashes, "sha256"); + hashAlgorithm = QCryptographicHash::Sha256; + if (hash.isEmpty()) + { + hash = Json::ensureString(hashes, "sha512"); + hashAlgorithm = QCryptographicHash::Sha512; + if (hash.isEmpty()) + { + hash = Json::ensureString(hashes, "sha1"); + hashAlgorithm = QCryptographicHash::Sha1; + if (hash.isEmpty()) + { + throw JSONValidationError("No hash found for: " + file.path); + } + } + } + file.hash = QByteArray::fromHex(hash.toLatin1()); + file.hashAlgorithm = hashAlgorithm; + // Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode (as Modrinth seems to incorrectly handle spaces) + file.download = Json::requireString(Json::ensureArray(obj, "downloads").first(), "Download URL for " + file.path); + if (!file.download.isValid()) + { + throw JSONValidationError("Download URL for " + file.path + " is not a correctly formatted URL"); + } + return file; + }); + + auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json"); + for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) + { + QString name = it.key(); + if (name == "minecraft") + { + if (!minecraftVersion.isEmpty()) + throw JSONValidationError("Duplicate Minecraft version"); + minecraftVersion = Json::requireString(*it, "Minecraft version"); + } + else if (name == "fabric-loader") + { + if (!fabricVersion.isEmpty()) + throw JSONValidationError("Duplicate Fabric Loader version"); + fabricVersion = Json::requireString(*it, "Fabric Loader version"); + } + else if (name == "forge") + { + if (!forgeVersion.isEmpty()) + throw JSONValidationError("Duplicate Forge version"); + forgeVersion = Json::requireString(*it, "Forge version"); + } + else + { + throw JSONValidationError("Unknown dependency type: " + name); + } + } + } + else + { + throw JSONValidationError(QStringLiteral("Unknown format version: %s").arg(formatVersion)); + } + QFile::remove(indexPath); + } + catch (const JSONValidationError &e) + { + emitFailed(tr("Could not understand pack index:\n") + e.cause()); + return; + } + QString overridePath = FS::PathCombine(m_stagingPath, "overrides"); + if (QFile::exists(overridePath)) { + QString mcPath = FS::PathCombine(m_stagingPath, ".minecraft"); + if (!QFile::rename(overridePath, mcPath)) { + emitFailed(tr("Could not rename the overrides folder:\n") + "overrides"); + return; + } + } + + QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); + auto instanceSettings = std::make_shared(configPath); + instanceSettings->registerSetting("InstanceType", "Legacy"); + instanceSettings->set("InstanceType", "OneSix"); + MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); + auto components = instance.getPackProfile(); + components->buildingFromScratch(); + components->setComponentVersion("net.minecraft", minecraftVersion, true); + if (!fabricVersion.isEmpty()) + components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion, true); + if (!forgeVersion.isEmpty()) + components->setComponentVersion("net.minecraftforge", forgeVersion, true); + if (m_instIcon != "default") + { + instance.setIconKey(m_instIcon); + } + instance.setName(m_instName); + instance.saveNow(); + + m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network()); + for (auto &file : files) + { + auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path); + qDebug() << "Will download" << file.download << "to" << path; + auto dl = Net::Download::makeFile(file.download, path); + dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); + m_filesNetJob->addNetAction(dl); + } + connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() + { + m_filesNetJob.reset(); + emitSucceeded(); + } + ); + connect(m_filesNetJob.get(), &NetJob::failed, [&](const QString &reason) + { + m_filesNetJob.reset(); + emitFailed(reason); + }); + connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) + { + setProgress(current, total); + }); + setStatus(tr("Downloading mods...")); + m_filesNetJob->start(); +} diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h index 365c3dc47..317562d91 100644 --- a/launcher/InstanceImportTask.h +++ b/launcher/InstanceImportTask.h @@ -47,8 +47,9 @@ protected: private: void processZipPack(); void processMultiMC(); - void processFlame(); void processTechnic(); + void processFlame(); + void processModrinth(); private slots: void downloadSucceeded(); @@ -69,7 +70,8 @@ private: /* data */ enum class ModpackType{ Unknown, MultiMC, + Technic, Flame, - Technic + Modrinth, } m_modpackType = ModpackType::Unknown; }; diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp new file mode 100644 index 000000000..2100aaf91 --- /dev/null +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -0,0 +1,16 @@ +/* Copyright 2022 kb1000 + * + * 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 "ModrinthPackManifest.h" diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h new file mode 100644 index 000000000..9742aeb21 --- /dev/null +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -0,0 +1,32 @@ +/* Copyright 2022 kb1000 + * + * 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 +#include +#include +#include + +namespace Modrinth { +struct File +{ + QString path; + QCryptographicHash::Algorithm hashAlgorithm; + QByteArray hash; + // TODO: should this support multiple download URLs, like the JSON does? + QUrl download; +}; +} diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 0fe673ff5..1671093d7 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -20,6 +20,9 @@ scalable/atlauncher.svg scalable/atlauncher-placeholder.png + + scalable/modrinth.svg + scalable/proxy.svg diff --git a/launcher/resources/multimc/scalable/modrinth.svg b/launcher/resources/multimc/scalable/modrinth.svg new file mode 100644 index 000000000..32715f5ce --- /dev/null +++ b/launcher/resources/multimc/scalable/modrinth.svg @@ -0,0 +1,4 @@ + + + + diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index b402839cf..05ea091de 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -39,6 +39,7 @@ #include "ui/pages/modplatform/legacy_ftb/Page.h" #include "ui/pages/modplatform/flame/FlamePage.h" #include "ui/pages/modplatform/ImportPage.h" +#include "ui/pages/modplatform/modrinth/ModrinthPage.h" #include "ui/pages/modplatform/technic/TechnicPage.h" @@ -134,6 +135,7 @@ QList NewInstanceDialog::getPages() flamePage, new FtbPage(this), new LegacyFTB::Page(this), + new ModrinthPage(this), technicPage }; } diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 1b53dd402..8ae38f8dd 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -109,7 +109,8 @@ void ImportPage::updateState() { // FIXME: actually do some validation of what's inside here... this is fake AF QFileInfo fi(input); - if(fi.exists() && fi.suffix() == "zip") + // mrpack is a modrinth pack + if(fi.exists() && (fi.suffix() == "zip" || fi.suffix() == "mrpack")) { QFileInfo fi(url.fileName()); dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url)); @@ -143,6 +144,7 @@ void ImportPage::setUrl(const QString& url) void ImportPage::on_modpackBtn_clicked() { + // TODO: Add .mrpack filter auto filter = QMimeDatabase().mimeTypeForName("application/zip").filterString(); const QUrl url = QFileDialog::getOpenFileUrl(this, tr("Choose modpack"), modpackUrl(), filter); if (url.isValid()) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp new file mode 100644 index 000000000..93b1ca027 --- /dev/null +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2021 MultiMC Contributors + * Copyright 2021-2022 kb1000 + * + * 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 "ModrinthPage.h" + +#include "ui_ModrinthPage.h" + +#include + +ModrinthPage::ModrinthPage(NewInstanceDialog *dialog, QWidget *parent) : QWidget(parent), ui(new Ui::ModrinthPage), dialog(dialog) +{ + ui->setupUi(this); +} + +ModrinthPage::~ModrinthPage() +{ + delete ui; +} + +void ModrinthPage::openedImpl() +{ + BasePage::openedImpl(); + triggerSearch(); +} + +bool ModrinthPage::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) { + auto *keyEvent = reinterpret_cast(event); + if (keyEvent->key() == Qt::Key_Return) { + this->triggerSearch(); + keyEvent->accept(); + return true; + } + } + return QObject::eventFilter(watched, event); +} + +void ModrinthPage::triggerSearch() { + +} diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h new file mode 100644 index 000000000..6c75b60dd --- /dev/null +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h @@ -0,0 +1,62 @@ +/* + * Copyright 2013-2021 MultiMC Contributors + * Copyright 2021-2022 kb1000 + * + * 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 "Application.h" +#include "ui/dialogs/NewInstanceDialog.h" +#include "ui/pages/BasePage.h" + +#include + +namespace Ui +{ + class ModrinthPage; +} + +class ModrinthPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit ModrinthPage(NewInstanceDialog *dialog, QWidget *parent = nullptr); + ~ModrinthPage() override; + + QString displayName() const override + { + return tr("Modrinth"); + } + QIcon icon() const override + { + return APPLICATION->getThemedIcon("modrinth"); + } + QString id() const override + { + return "modrinth"; + } + + void openedImpl() override; + + bool eventFilter(QObject *watched, QEvent *event) override; + +private slots: + void triggerSearch(); + +private: + Ui::ModrinthPage *ui; + NewInstanceDialog *dialog; +}; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui new file mode 100644 index 000000000..7ef099d34 --- /dev/null +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui @@ -0,0 +1,94 @@ + + + ModrinthPage + + + + 0 + 0 + 837 + 685 + + + + + + + + + Search and filter ... + + + + + + + Search + + + + + + + + + + + Qt::ScrollBarAlwaysOff + + + true + + + + 48 + 48 + + + + + + + + true + + + true + + + + + + + + + + + + + + Version selected: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + searchEdit + searchButton + packView + packDescription + sortByBox + versionSelectionBox + + + + From 31988f0529f6c316d6a9ba3e66cf981a807ed710 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 14 May 2022 19:56:38 +0200 Subject: [PATCH 03/33] fix: adapt upstream Modrinth code to our codebase --- launcher/CMakeLists.txt | 8 +-- launcher/InstanceImportTask.cpp | 2 - .../multimc/128x128/instances/modrinth.png | Bin 10575 -> 0 bytes .../multimc/32x32/instances/modrinth.png | Bin 1913 -> 0 bytes launcher/resources/multimc/multimc.qrc | 3 -- launcher/ui/pages/modplatform/ImportPage.cpp | 2 +- .../modplatform/modrinth/ModrinthPage.cpp | 45 ++++++++++++----- .../pages/modplatform/modrinth/ModrinthPage.h | 46 +++++++++++++----- 8 files changed, 72 insertions(+), 34 deletions(-) delete mode 100644 launcher/resources/multimc/128x128/instances/modrinth.png delete mode 100644 launcher/resources/multimc/32x32/instances/modrinth.png diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index cbe135e2b..7984d3c98 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -532,6 +532,8 @@ set(FLAME_SOURCES set(MODRINTH_SOURCES modplatform/modrinth/ModrinthPackIndex.cpp modplatform/modrinth/ModrinthPackIndex.h + modplatform/modrinth/ModrinthPackManifest.cpp + modplatform/modrinth/ModrinthPackManifest.h ) set(MODPACKSCH_SOURCES @@ -563,11 +565,6 @@ set(ATLAUNCHER_SOURCES modplatform/atlauncher/ATLShareCode.h ) -set(MODRINTH_SOURCES - modplatform/modrinth/ModrinthPackManifest.cpp - modplatform/modrinth/ModrinthPackManifest.h -) - add_unit_test(Index SOURCES meta/Index_test.cpp LIBS Launcher_logic @@ -601,7 +598,6 @@ set(LOGIC_SOURCES ${MODPACKSCH_SOURCES} ${TECHNIC_SOURCES} ${ATLAUNCHER_SOURCES} - ${MODRINTH_SOURCES} ) SET(LAUNCHER_SOURCES diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 517155811..ec0f58e08 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -581,8 +581,6 @@ void InstanceImportTask::processModrinth() { QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_shared(configPath); - instanceSettings->registerSetting("InstanceType", "Legacy"); - instanceSettings->set("InstanceType", "OneSix"); MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); auto components = instance.getPackProfile(); components->buildingFromScratch(); diff --git a/launcher/resources/multimc/128x128/instances/modrinth.png b/launcher/resources/multimc/128x128/instances/modrinth.png deleted file mode 100644 index 740bc8f02469f108db79d92d05484aff17bf8801..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10575 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 diff --git a/launcher/resources/multimc/32x32/instances/modrinth.png b/launcher/resources/multimc/32x32/instances/modrinth.png deleted file mode 100644 index 025ed06534234458f6de2456868d0a66b4324f3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1913 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}EX7WqAsieW95oy%9SjWY z2A(dCArY-_!!vV2u1nh9-~0aThWEZ61x*{DFJw5|q_AYEruR}6uCuq&4y488G>cDT z*}5Vt^wz5235&u)J-ZxMmdiY5RcmZpv2=&G!`fSIWz6;|F(X=zUTM3^7EDVzu&cf&h-Daa~_M#9qzUEJaQ_xew^1<&VBE{!#_ELrO|$7x3@d*$tyyC!kW} zfjgGzJ@ql4CUA%~EitUPsd8s|^yz9pe&)7?0&I*1)m2aZKg*a#UGF-f#IWGFa_z@| zsq4e`nSZ#*#G#p06epBWN9y;^MQqP^C!H2^-d3HrY13L0G3JH~>kjaxuH;q8wAs9E z>5|n})^Vx!BG=CBh|J7kWRTjDdV>4)r+4AgzE-u(X|pw{I9Kc&xn$FC7KQ*HIR=f1 z?7F%CD^uT|VP&`=-CZ}8tzJMdFv?bX&DohHzKlt$m%FCeoaFb7F=IN=^=i+|Tg&4` z54udM&1+h%-}j=VZ2hYnSAvAro<8#J*sfE)lU{qw6ITtM(r>SyapJ74Oxx?DzO0_% zE^5DZ{_k2nEg|lzP4%@CpVeIwSAG1@{OnO*KBL2mla`BI#3ueZ;9J4JW$nv<-ph`e z--mfe7TDD&(ir; zD>7|Xxn z$883MC|!1jsy8KmC8mrHfz6*AHhsCLSX6Yuw_+iWCBqlF_iI`odNKrTTi0^v5|1Ur z4ZFyKRDO-G{!Xhr{zy!^cfG6o^3+@6e1}<9e|p!_`d7(l){@ti&u;$}_sNTn{GPw* zb(ICrbpd;CZXb#M-#s6-dJ=V}mYqA5u=Y&&D=t<>w(v7HbohL`eMv5>WY$()c9)aB zE8lh>emOOLd4Wx*xBFLJh6OI?QYzZL7#0{`)tJNYq@d6v)9A#|m94XMp5VklhgGi? zcX+zI6YQ;KXjqaxDduXF;NRnu40Kd0luY}TR|-AtQ!sF1=y>o}+lWEDo{8aNQFh|n z9_wJ2W!itY2D_w)oU}7PnI92nBtB*Or6ZhD{|o z#4Y*b#pcJKUd%dvxADnBp0`{17O8Q$XSV7yDrG83U9VoXbW6271IzZOJzso3*dMFd zWP7_JMc$D2iu-hL+ogy#3-t2$ZTng$@LQPq)8P}l`tE6aGaOJk%Qva^ zrT({`)A@Z;GUeN+e~?rCxUZupeA}we$LF3~B%YqfZTe)o_!aB#j0}FPYMHIFl0}mz z=x>{PEQvp#|A%gLxVEb0qJRuzyEDQ(!WW|M72n$NBTmbFQOk$$@O=|=VlV%#j`<=c z!M~odL1U&OSA6icb=!1qvN25Ty=bg(?*mW$)2frz(Wk8XnkIA#2k3e|V^WCS{Ih6& zMX0k-)kVt%TWpvn9D5q3F7~%>S>5T^pQiuH5mnj1;lS6CqV$ecY3kkDgL8KVD@!LW zXL#`NE#v-spfInwfmnstFoMS zaha^b6aT84e@e_*{sl66_oy>@_0;g n-#M$BpSMfzob|W32x32/instances/flame.png 128x128/instances/flame.png - 32x32/instances/modrinth.png - 128x128/instances/modrinth.png - 32x32/instances/gear.png 128x128/instances/gear.png diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 8ae38f8dd..3b65de9d4 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -144,8 +144,8 @@ void ImportPage::setUrl(const QString& url) void ImportPage::on_modpackBtn_clicked() { - // TODO: Add .mrpack filter auto filter = QMimeDatabase().mimeTypeForName("application/zip").filterString(); + filter += ";;" + tr("Modrinth pack (*.mrpack)"); const QUrl url = QFileDialog::getOpenFileUrl(this, tr("Choose modpack"), modpackUrl(), filter); if (url.isValid()) { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 93b1ca027..0d65ef166 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -1,18 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2013-2021 MultiMC Contributors - * Copyright 2021-2022 kb1000 + * PolyMC - Minecraft Launcher * - * 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 + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * 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. + * 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 + * Copyright 2021-2022 kb1000 + * + * 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 "ModrinthPage.h" @@ -31,6 +49,11 @@ ModrinthPage::~ModrinthPage() delete ui; } +void ModrinthPage::retranslate() +{ + ui->retranslateUi(this); +} + void ModrinthPage::openedImpl() { BasePage::openedImpl(); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h index 6c75b60dd..562049b48 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h @@ -1,18 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2013-2021 MultiMC Contributors - * Copyright 2021-2022 kb1000 + * PolyMC - Minecraft Launcher * - * 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 + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * 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. + * 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 + * Copyright 2021-2022 kb1000 + * + * 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 @@ -49,6 +67,12 @@ public: return "modrinth"; } + virtual QString helpPage() const override + { + return "Modrinth-platform"; + } + void retranslate() override; + void openedImpl() override; bool eventFilter(QObject *watched, QEvent *event) override; From 4fda35b466e4e3f242955cf8cb692a10e8820f0b Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 14 May 2022 20:17:05 -0300 Subject: [PATCH 04/33] feat: add modrinth pack downloading Things that don't work / work poorly (there's more for sure but those are the evident ones): - Icons are broken in the import dialog - No way to search for private packs - Icons are not downloaded when downloading a mod - No support for multiple download URLs - Probably a lot more... --- launcher/CMakeLists.txt | 2 + .../modrinth/ModrinthPackManifest.cpp | 84 ++++++ .../modrinth/ModrinthPackManifest.h | 50 ++++ .../modplatform/modrinth/ModrinthModel.cpp | 274 ++++++++++++++++++ .../modplatform/modrinth/ModrinthModel.h | 81 ++++++ .../modplatform/modrinth/ModrinthPage.cpp | 209 ++++++++++++- .../pages/modplatform/modrinth/ModrinthPage.h | 65 +++-- 7 files changed, 729 insertions(+), 36 deletions(-) create mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp create mode 100644 launcher/ui/pages/modplatform/modrinth/ModrinthModel.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 7984d3c98..8e75be204 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -778,6 +778,8 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/modrinth/ModrinthPage.cpp ui/pages/modplatform/modrinth/ModrinthPage.h + ui/pages/modplatform/modrinth/ModrinthModel.cpp + ui/pages/modplatform/modrinth/ModrinthModel.h ui/pages/modplatform/technic/TechnicModel.cpp ui/pages/modplatform/technic/TechnicModel.h diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 2100aaf91..4dcd2fd49 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -14,3 +14,87 @@ */ #include "ModrinthPackManifest.h" +#include "Json.h" + +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" + +namespace Modrinth { + +void loadIndexedPack(Modpack& pack, QJsonObject& obj) +{ + pack.id = Json::ensureString(obj, "project_id"); + + pack.name = Json::ensureString(obj, "title"); + pack.description = Json::ensureString(obj, "description"); + pack.authors << Json::ensureString(obj, "author"); + pack.iconName = QString("modrinth_%1").arg(Json::ensureString(obj, "slug")); + pack.iconUrl = Json::ensureString(obj, "icon_url"); +} + +void loadIndexedInfo(Modpack& pack, QJsonObject& obj) +{ + pack.extra.body = Json::ensureString(obj, "body"); + pack.extra.sourceUrl = Json::ensureString(obj, "source_url"); + pack.extra.wikiUrl = Json::ensureString(obj, "wiki_url"); + + pack.extraInfoLoaded = true; +} + +void loadIndexedVersions(Modpack& pack, QJsonDocument& doc) +{ + QVector unsortedVersions; + + auto arr = Json::requireArray(doc); + + for (auto versionIter : arr) { + auto obj = Json::requireObject(versionIter); + auto file = loadIndexedVersion(obj); + + if(!file.id.isEmpty()) // Heuristic to check if the returned value is valid + unsortedVersions.append(file); + } + auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool { + // dates are in RFC 3339 format + return a.date > b.date; + }; + + std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); + + pack.versions.swap(unsortedVersions); + + pack.versionsLoaded = true; +} + +auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion +{ + ModpackVersion file; + + file.name = Json::requireString(obj, "name"); + file.version = Json::requireString(obj, "version_number"); + + file.id = Json::requireString(obj, "id"); + file.project_id = Json::requireString(obj, "project_id"); + + file.date = Json::requireString(obj, "date_published"); + + auto files = Json::requireArray(obj, "files"); + + for (auto file_iter : files) { + File indexed_file; + auto parent = Json::requireObject(file_iter); + if (!Json::ensureBoolean(parent, "primary", false)) { + continue; + } + + file.download_url = Json::requireString(parent, "url"); + break; + } + + if(file.download_url.isEmpty()) + return {}; + + return file; +} + +} // namespace Modrinth diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 9742aeb21..7dab893ca 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -15,18 +15,68 @@ #pragma once +#include + #include #include #include #include +class MinecraftInstance; + namespace Modrinth { + struct File { QString path; + QCryptographicHash::Algorithm hashAlgorithm; QByteArray hash; // TODO: should this support multiple download URLs, like the JSON does? QUrl download; }; + +struct ModpackExtra { + QString body; + + QString sourceUrl; + QString wikiUrl; +}; + +struct ModpackVersion { + QString name; + QString version; + + QString id; + QString project_id; + + QString date; + + QString download_url; +}; + +struct Modpack { + QString id; + + QString name; + QString description; + QStringList authors; + QString iconName; + QUrl iconUrl; + + bool versionsLoaded = false; + bool extraInfoLoaded = false; + + ModpackExtra extra; + QVector versions; +}; + +void loadIndexedPack(Modpack&, QJsonObject&); +void loadIndexedInfo(Modpack&, QJsonObject&); +void loadIndexedVersions(Modpack&, QJsonDocument&); +auto loadIndexedVersion(QJsonObject&) -> ModpackVersion; + } + +Q_DECLARE_METATYPE(Modrinth::Modpack); +Q_DECLARE_METATYPE(Modrinth::ModpackVersion); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp new file mode 100644 index 000000000..2890e27d5 --- /dev/null +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -0,0 +1,274 @@ +#include "ModrinthModel.h" + +#include "BuildConfig.h" +#include "Json.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" +#include "ui/dialogs/ModDownloadDialog.h" + +#include + +namespace Modrinth { + +ModpackListModel::ModpackListModel(ModrinthPage* parent) : QAbstractListModel(parent), m_parent(parent) {} + +auto ModpackListModel::debugName() const -> QString +{ + return m_parent->debugName(); +} + +/******** Make data requests ********/ + +void ModpackListModel::fetchMore(const QModelIndex& parent) +{ + if (parent.isValid()) + return; + if (nextSearchOffset == 0) { + qWarning() << "fetchMore with 0 offset is wrong..."; + return; + } + performPaginatedSearch(); +} + +auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVariant +{ + int pos = index.row(); + if (pos >= modpacks.size() || pos < 0 || !index.isValid()) { + return QString("INVALID INDEX %1").arg(pos); + } + + Modrinth::Modpack pack = modpacks.at(pos); + if (role == Qt::DisplayRole) { + return pack.name; + } else if (role == Qt::ToolTipRole) { + if (pack.description.length() > 100) { + // some magic to prevent to long tooltips and replace html linebreaks + QString edit = pack.description.left(97); + edit = edit.left(edit.lastIndexOf("
")).left(edit.lastIndexOf(" ")).append("..."); + return edit; + } + return pack.description; + } else if (role == Qt::DecorationRole) { + // FIXME: help the icons dont have the same size ;-; + if (m_logoMap.contains(pack.iconName)) { + return (m_logoMap.value(pack.iconName)); + } + QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); + ((ModpackListModel*)this)->requestLogo(pack.iconName, pack.iconUrl.toString()); + return icon; + } else if (role == Qt::UserRole) { + QVariant v; + v.setValue(pack); + return v; + } + + return {}; +} + +/* +void ModpackListModel::requestModVersions(ModPlatform::IndexedPack const& current) +{ + auto profile = (dynamic_cast((dynamic_cast(parent()))->m_instance))->getPackProfile(); + + m_parent->apiProvider()->getVersions(this, { current.addonId.toString(), getMineVersions(), profile->getModLoader() }); +}*/ + +void ModpackListModel::performPaginatedSearch() +{ + // TODO: Move to standalone API + NetJob* netJob = new NetJob("Modrinth::SearchModpack", APPLICATION->network()); + auto searchAllUrl = QString( + "https://staging-api.modrinth.com/v2/search?" + "query=%1&" + "facets=[[\"project_type:modpack\"]]") + .arg(currentSearchTerm); + + netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), &m_all_response)); + + QObject::connect(netJob, &NetJob::succeeded, this, [this] { + QJsonParseError parse_error_all {}; + + QJsonDocument doc_all = QJsonDocument::fromJson(m_all_response, &parse_error_all); + if (parse_error_all.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from " << debugName() << " at " << parse_error_all.offset + << " reason: " << parse_error_all.errorString(); + qWarning() << m_all_response; + return; + } + + searchRequestFinished(doc_all); + }); + QObject::connect(netJob, &NetJob::failed, this, &ModpackListModel::searchRequestFailed); + + jobPtr = netJob; + jobPtr->start(); +} + +void ModpackListModel::refresh() +{ + if (jobPtr) { + jobPtr->abort(); + searchState = ResetRequested; + return; + } else { + beginResetModel(); + modpacks.clear(); + endResetModel(); + searchState = None; + } + nextSearchOffset = 0; + performPaginatedSearch(); +} + +void ModpackListModel::searchWithTerm(const QString& term, const int sort) +{ + if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort) { + return; + } + + currentSearchTerm = term; + currentSort = sort; + + refresh(); +} + +void ModpackListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback) +{ + if (m_logoMap.contains(logo)) { + callback(APPLICATION->metacache() + ->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))) + ->getFullPath()); + } else { + requestLogo(logo, logoUrl); + } +} + +void ModpackListModel::requestLogo(QString logo, QString url) +{ + if (m_loadingLogos.contains(logo) || m_failedLogos.contains(logo)) { + return; + } + + MetaEntryPtr entry = + APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))); + auto job = new NetJob(QString("%1 Icon Download %2").arg(m_parent->debugName()).arg(logo), APPLICATION->network()); + job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + + auto fullPath = entry->getFullPath(); + QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { + job->deleteLater(); + emit logoLoaded(logo, QIcon(fullPath)); + if (waitingCallbacks.contains(logo)) { + waitingCallbacks.value(logo)(fullPath); + } + }); + + QObject::connect(job, &NetJob::failed, this, [this, logo, job] { + job->deleteLater(); + emit logoFailed(logo); + }); + + job->start(); + m_loadingLogos.append(logo); +} + +/******** Request callbacks ********/ + +void ModpackListModel::logoLoaded(QString logo, QIcon out) +{ + m_loadingLogos.removeAll(logo); + m_logoMap.insert(logo, out); + for (int i = 0; i < modpacks.size(); i++) { + if (modpacks[i].iconName == logo) { + emit dataChanged(createIndex(i, 0), createIndex(i, 0), { Qt::DecorationRole }); + } + } +} + +void ModpackListModel::logoFailed(QString logo) +{ + m_failedLogos.append(logo); + m_loadingLogos.removeAll(logo); +} + +void ModpackListModel::searchRequestFinished(QJsonDocument& doc_all) +{ + jobPtr.reset(); + + QList newList; + + auto packs_all = doc_all.object().value("hits").toArray(); + for (auto packRaw : packs_all) { + auto packObj = packRaw.toObject(); + + Modrinth::Modpack pack; + try { + Modrinth::loadIndexedPack(pack, packObj); + newList.append(pack); + } catch (const JSONValidationError& e) { + qWarning() << "Error while loading mod from " << m_parent->debugName() << ": " << e.cause(); + continue; + } + } + + if (packs_all.size() < 25) { + searchState = Finished; + } else { + nextSearchOffset += 25; + searchState = CanPossiblyFetchMore; + } + + beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1); + modpacks.append(newList); + endInsertRows(); +} + +void ModpackListModel::searchRequestFailed(QString reason) +{ + if (!jobPtr->first()->m_reply) { + // Network error + QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load mods.")); + } else if (jobPtr->first()->m_reply && jobPtr->first()->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 409) { + // 409 Gone, notify user to update + QMessageBox::critical(nullptr, tr("Error"), + //: %1 refers to the launcher itself + QString("%1 %2") + .arg(m_parent->displayName()) + .arg(tr("API version too old!\nPlease update %1!").arg(BuildConfig.LAUNCHER_NAME))); + } + jobPtr.reset(); + + if (searchState == ResetRequested) { + beginResetModel(); + modpacks.clear(); + endResetModel(); + + nextSearchOffset = 0; + performPaginatedSearch(); + } else { + searchState = Finished; + } +} + +void ModpackListModel::versionRequestSucceeded(QJsonDocument doc, QString id) +{ + auto& current = m_parent->getCurrent(); + if (id != current.id) { + return; + } + + auto arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array(); + + try { + // loadIndexedPackVersions(current, arr); + } catch (const JSONValidationError& e) { + qDebug() << doc; + qWarning() << "Error while reading " << debugName() << " mod version: " << e.cause(); + } + + // m_parent->updateModVersions(); +} + +} // namespace Modrinth + +/******** Helpers ********/ diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h new file mode 100644 index 000000000..1fdbe278b --- /dev/null +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include "modplatform/modrinth/ModrinthPackManifest.h" +#include "ui/pages/modplatform/modrinth/ModrinthPage.h" + +class ModPage; +class Version; + +namespace Modrinth { + +using LogoMap = QMap; +using LogoCallback = std::function; + +class ModpackListModel : public QAbstractListModel { + Q_OBJECT + + public: + ModpackListModel(ModrinthPage* parent); + ~ModpackListModel() override = default; + + inline auto rowCount(const QModelIndex& parent) const -> int override { return modpacks.size(); }; + inline auto columnCount(const QModelIndex& parent) const -> int override { return 1; }; + inline auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); }; + + auto debugName() const -> QString; + + /* Retrieve information from the model at a given index with the given role */ + auto data(const QModelIndex& index, int role) const -> QVariant override; + + inline void setActiveJob(NetJob::Ptr ptr) { jobPtr = ptr; } + + /* Ask the API for more information */ + void fetchMore(const QModelIndex& parent) override; + void refresh(); + void searchWithTerm(const QString& term, const int sort); + + void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); + + inline auto canFetchMore(const QModelIndex& parent) const -> bool override { return searchState == CanPossiblyFetchMore; }; + + public slots: + void searchRequestFinished(QJsonDocument& doc_all); + void searchRequestFailed(QString reason); + + void versionRequestSucceeded(QJsonDocument doc, QString addonId); + + protected slots: + + void logoFailed(QString logo); + void logoLoaded(QString logo, QIcon out); + + void performPaginatedSearch(); + + protected: + void requestLogo(QString file, QString url); + + inline auto getMineVersions() const -> std::list; + + protected: + ModrinthPage* m_parent; + + QList modpacks; + + LogoMap m_logoMap; + QMap waitingCallbacks; + QStringList m_failedLogos; + QStringList m_loadingLogos; + + QString currentSearchTerm; + int currentSort = 0; + int nextSearchOffset = 0; + enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None; + + NetJob::Ptr jobPtr; + + QByteArray m_all_response; + QByteArray m_specific_response; +}; +} // namespace ModPlatform diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 0d65ef166..688053166 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -34,14 +34,41 @@ */ #include "ModrinthPage.h" - #include "ui_ModrinthPage.h" -#include +#include "ModrinthModel.h" -ModrinthPage::ModrinthPage(NewInstanceDialog *dialog, QWidget *parent) : QWidget(parent), ui(new Ui::ModrinthPage), dialog(dialog) +#include "InstanceImportTask.h" +#include "Json.h" + +#include + +#include +#include +#include + +ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::ModrinthPage), dialog(dialog) { ui->setupUi(this); + + connect(ui->searchButton, &QPushButton::clicked, this, &ModrinthPage::triggerSearch); + ui->searchEdit->installEventFilter(this); + m_model = new Modrinth::ModpackListModel(this); + ui->packView->setModel(m_model); + + ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); + + ui->sortByBox->addItem(tr("Sort by Featured")); + ui->sortByBox->addItem(tr("Sort by Popularity")); + ui->sortByBox->addItem(tr("Sort by Last Updated")); + ui->sortByBox->addItem(tr("Sort by Name")); + ui->sortByBox->addItem(tr("Sort by Author")); + ui->sortByBox->addItem(tr("Sort by Total Downloads")); + + connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); + connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthPage::onSelectionChanged); + connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthPage::onVersionSelectionChanged); } ModrinthPage::~ModrinthPage() @@ -60,10 +87,10 @@ void ModrinthPage::openedImpl() triggerSearch(); } -bool ModrinthPage::eventFilter(QObject *watched, QEvent *event) +bool ModrinthPage::eventFilter(QObject* watched, QEvent* event) { if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) { - auto *keyEvent = reinterpret_cast(event); + auto* keyEvent = reinterpret_cast(event); if (keyEvent->key() == Qt::Key_Return) { this->triggerSearch(); keyEvent->accept(); @@ -73,6 +100,176 @@ bool ModrinthPage::eventFilter(QObject *watched, QEvent *event) return QObject::eventFilter(watched, event); } -void ModrinthPage::triggerSearch() { +void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second) +{ + ui->versionSelectionBox->clear(); + if (!first.isValid()) { + if (isOpened) { + dialog->setSuggestedPack(); + } + return; + } + + current = m_model->data(first, Qt::UserRole).value(); + auto name = current.name; + + if (!current.extraInfoLoaded) { + qDebug() << "Loading modrinth modpack information"; + + auto netJob = new NetJob(QString("Modrinth::PackInformation(%1)").arg(current.name), APPLICATION->network()); + auto response = new QByteArray(); + + QString id = current.id; + + netJob->addNetAction(Net::Download::makeByteArray(QString("https://staging-api.modrinth.com/v2/project/%1").arg(id), response)); + + QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id] { + if (id != current.id) { + return; // wrong request? + } + + QJsonParseError parse_error; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return; + } + + auto obj = Json::requireObject(doc); + + try { + Modrinth::loadIndexedInfo(current, obj); + } catch (const JSONValidationError& e) { + qDebug() << *response; + qWarning() << "Error while reading modrinth modpack version: " << e.cause(); + } + + updateUI(); + suggestCurrent(); + }); + QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { + netJob->deleteLater(); + delete response; + }); + netJob->start(); + } else + updateUI(); + + if (!current.versionsLoaded) { + qDebug() << "Loading modrinth modpack versions"; + + auto netJob = new NetJob(QString("Modrinth::PackVersions(%1)").arg(current.name), APPLICATION->network()); + auto response = new QByteArray(); + + QString id = current.id; + + netJob->addNetAction( + Net::Download::makeByteArray(QString("https://staging-api.modrinth.com/v2/project/%1/version").arg(id), response)); + + QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id] { + if (id != current.id) { + return; // wrong request? + } + + QJsonParseError parse_error; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return; + } + + try { + Modrinth::loadIndexedVersions(current, doc); + } catch (const JSONValidationError& e) { + qDebug() << *response; + qWarning() << "Error while reading modrinth modpack version: " << e.cause(); + } + + for (auto version : current.versions) { + ui->versionSelectionBox->addItem(version.version, QVariant(version.id)); + } + + updateVersionsUI(); + suggestCurrent(); + }); + QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { + netJob->deleteLater(); + delete response; + }); + netJob->start(); + + } else { + for (auto version : current.versions) { + ui->versionSelectionBox->addItem(QString("%1 - %2").arg(version.name, version.version), QVariant(version.id)); + } + + suggestCurrent(); + } +} + +void ModrinthPage::updateUI() +{ + QString text = ""; + + if (current.extra.sourceUrl.isEmpty()) + text = current.name; + else + text = "" + current.name + ""; + + if (!current.authors.empty()) { + // TODO: Implement multiple authors with links + text += "
" + tr(" by ") + current.authors.at(0); + } + + text += "
"; + + HoeDown h; + text += h.process(current.extra.body.toUtf8()); + + ui->packDescription->setHtml(text + current.description); +} + +void ModrinthPage::updateVersionsUI() +{ + // idk +} + +void ModrinthPage::suggestCurrent() +{ + if (!isOpened) { + return; + } + + if (selectedVersion.isEmpty()) { + dialog->setSuggestedPack(); + return; + } + + for (auto& ver : current.versions) { + if (ver.id == selectedVersion) { + dialog->setSuggestedPack(current.name, new InstanceImportTask(ver.download_url)); + + break; + } + } +} + +void ModrinthPage::triggerSearch() +{ + m_model->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex()); +} + +void ModrinthPage::onVersionSelectionChanged(QString data) +{ + if (data.isNull() || data.isEmpty()) { + selectedVersion = ""; + return; + } + selectedVersion = ui->versionSelectionBox->currentData().toString(); + suggestCurrent(); } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h index 562049b48..f72a5071f 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h @@ -39,48 +39,53 @@ #include "ui/dialogs/NewInstanceDialog.h" #include "ui/pages/BasePage.h" +#include "modplatform/modrinth/ModrinthPackManifest.h" + #include -namespace Ui -{ - class ModrinthPage; +namespace Ui { +class ModrinthPage; } -class ModrinthPage : public QWidget, public BasePage -{ +namespace Modrinth { +class ModpackListModel; +} + +class ModrinthPage : public QWidget, public BasePage { Q_OBJECT -public: - explicit ModrinthPage(NewInstanceDialog *dialog, QWidget *parent = nullptr); + public: + explicit ModrinthPage(NewInstanceDialog* dialog, QWidget* parent = nullptr); ~ModrinthPage() override; - QString displayName() const override - { - return tr("Modrinth"); - } - QIcon icon() const override - { - return APPLICATION->getThemedIcon("modrinth"); - } - QString id() const override - { - return "modrinth"; - } + QString displayName() const override { return tr("Modrinth"); } + QIcon icon() const override { return APPLICATION->getThemedIcon("modrinth"); } + QString id() const override { return "modrinth"; } + QString helpPage() const override { return "Modrinth-platform"; } + + inline auto debugName() const -> QString { return "Modrinth"; } + inline auto metaEntryBase() const -> QString { return "ModrinthModpacks"; }; + + auto getCurrent() -> Modrinth::Modpack& { return current; } + void suggestCurrent(); + + void updateUI(); + void updateVersionsUI(); - virtual QString helpPage() const override - { - return "Modrinth-platform"; - } void retranslate() override; - void openedImpl() override; + bool eventFilter(QObject* watched, QEvent* event) override; - bool eventFilter(QObject *watched, QEvent *event) override; - -private slots: + private slots: + void onSelectionChanged(QModelIndex first, QModelIndex second); + void onVersionSelectionChanged(QString data); void triggerSearch(); -private: - Ui::ModrinthPage *ui; - NewInstanceDialog *dialog; + private: + Ui::ModrinthPage* ui; + NewInstanceDialog* dialog; + Modrinth::ModpackListModel* m_model; + + Modrinth::Modpack current; + QString selectedVersion; }; From 9dd70ca9ae6fdab913a77467e803bf90ddd949ed Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 14 May 2022 20:26:20 -0300 Subject: [PATCH 05/33] fix: download icon as well when importing modrinth modpacks --- launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 3 +++ launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui | 3 +++ 2 files changed, 6 insertions(+) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 688053166..b21fdf4a5 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -253,6 +253,9 @@ void ModrinthPage::suggestCurrent() for (auto& ver : current.versions) { if (ver.id == selectedVersion) { dialog->setSuggestedPack(current.name, new InstanceImportTask(ver.download_url)); + auto iconName = current.iconName; + m_model->getLogo(iconName, current.iconUrl.toString(), + [this, iconName](QString logo) { dialog->setSuggestedIconFromFile(logo, iconName); }); break; } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui index 7ef099d34..8de53a693 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui @@ -45,6 +45,9 @@ 48 + + true + From 5ea8cec16f6dfbaeaca56ccf7f9151039a1dd145 Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 14 May 2022 21:29:48 -0300 Subject: [PATCH 06/33] fix: make all modrinth modpacks have the same icon size --- .../modplatform/modrinth/ModrinthModel.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 2890e27d5..121f5d4e4 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -49,9 +49,10 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian } return pack.description; } else if (role == Qt::DecorationRole) { - // FIXME: help the icons dont have the same size ;-; if (m_logoMap.contains(pack.iconName)) { - return (m_logoMap.value(pack.iconName)); + return (m_logoMap.value(pack.iconName) + .pixmap(48, 48) + .scaled(48, 48, Qt::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation)); } QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); ((ModpackListModel*)this)->requestLogo(pack.iconName, pack.iconUrl.toString()); @@ -65,14 +66,6 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian return {}; } -/* -void ModpackListModel::requestModVersions(ModPlatform::IndexedPack const& current) -{ - auto profile = (dynamic_cast((dynamic_cast(parent()))->m_instance))->getPackProfile(); - - m_parent->apiProvider()->getVersions(this, { current.addonId.toString(), getMineVersions(), profile->getModLoader() }); -}*/ - void ModpackListModel::performPaginatedSearch() { // TODO: Move to standalone API @@ -86,7 +79,7 @@ void ModpackListModel::performPaginatedSearch() netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), &m_all_response)); QObject::connect(netJob, &NetJob::succeeded, this, [this] { - QJsonParseError parse_error_all {}; + QJsonParseError parse_error_all{}; QJsonDocument doc_all = QJsonDocument::fromJson(m_all_response, &parse_error_all); if (parse_error_all.error != QJsonParseError::NoError) { @@ -210,7 +203,7 @@ void ModpackListModel::searchRequestFinished(QJsonDocument& doc_all) continue; } } - + if (packs_all.size() < 25) { searchState = Finished; } else { From 9899a0e098e5cfb76a754fa9da2f73be46cc880a Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 14 May 2022 21:47:35 -0300 Subject: [PATCH 07/33] fix: Have the URL be the project URL itself (I think, doesn't seem to work for the waffle though, probably because of the staging API :/) --- launcher/modplatform/modrinth/ModrinthPackManifest.cpp | 1 + launcher/modplatform/modrinth/ModrinthPackManifest.h | 1 + launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 4dcd2fd49..4b8a9a9b5 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -35,6 +35,7 @@ void loadIndexedPack(Modpack& pack, QJsonObject& obj) void loadIndexedInfo(Modpack& pack, QJsonObject& obj) { pack.extra.body = Json::ensureString(obj, "body"); + pack.extra.projectUrl = QString("https://modrinth.com/modpack/%1").arg(Json::ensureString(obj, "slug")); pack.extra.sourceUrl = Json::ensureString(obj, "source_url"); pack.extra.wikiUrl = Json::ensureString(obj, "wiki_url"); diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 7dab893ca..aaaacf2cd 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -39,6 +39,7 @@ struct File struct ModpackExtra { QString body; + QString projectUrl; QString sourceUrl; QString wikiUrl; }; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index b21fdf4a5..cf519b8c7 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -219,7 +219,7 @@ void ModrinthPage::updateUI() if (current.extra.sourceUrl.isEmpty()) text = current.name; else - text = "" + current.name + ""; + text = "" + current.name + ""; if (!current.authors.empty()) { // TODO: Implement multiple authors with links From 365cc198ba1e4e8129c95291e60e2c3c7ffbbf7a Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 14 May 2022 21:50:54 -0300 Subject: [PATCH 08/33] refactor: some random improvements --- launcher/InstanceImportTask.cpp | 8 ++++---- launcher/ui/pages/modplatform/ImportPage.cpp | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index ec0f58e08..29e3a26cd 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -504,16 +504,16 @@ void InstanceImportTask::processModrinth() { QJsonObject hashes = Json::requireObject(obj, "hashes"); QString hash; QCryptographicHash::Algorithm hashAlgorithm; - hash = Json::ensureString(hashes, "sha256"); - hashAlgorithm = QCryptographicHash::Sha256; + hash = Json::ensureString(hashes, "sha1"); + hashAlgorithm = QCryptographicHash::Sha1; if (hash.isEmpty()) { hash = Json::ensureString(hashes, "sha512"); hashAlgorithm = QCryptographicHash::Sha512; if (hash.isEmpty()) { - hash = Json::ensureString(hashes, "sha1"); - hashAlgorithm = QCryptographicHash::Sha1; + hash = Json::ensureString(hashes, "sha256"); + hashAlgorithm = QCryptographicHash::Sha256; if (hash.isEmpty()) { throw JSONValidationError("No hash found for: " + file.path); diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 3b65de9d4..c86d02cae 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -110,7 +110,10 @@ void ImportPage::updateState() // FIXME: actually do some validation of what's inside here... this is fake AF QFileInfo fi(input); // mrpack is a modrinth pack - if(fi.exists() && (fi.suffix() == "zip" || fi.suffix() == "mrpack")) + + // Allow non-latin people to use ZIP files! + auto zip = QMimeDatabase().mimeTypeForUrl(url).suffixes().contains("zip"); + if(fi.exists() && (zip || fi.suffix() == "mrpack")) { QFileInfo fi(url.fileName()); dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url)); From 49de5d9b07c8e05681ef9d485ccfd3d8e4bca784 Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 14 May 2022 22:04:40 -0300 Subject: [PATCH 09/33] change: list what file types can be entered in the importer --- launcher/ui/pages/modplatform/ImportPage.ui | 78 +++++++++++++++++---- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/launcher/ui/pages/modplatform/ImportPage.ui b/launcher/ui/pages/modplatform/ImportPage.ui index eb63cbe90..77bc5da5b 100644 --- a/launcher/ui/pages/modplatform/ImportPage.ui +++ b/launcher/ui/pages/modplatform/ImportPage.ui @@ -11,28 +11,75 @@ - - - - Browse - - - - + http:// - - + + - Local file or link to a direct download: + Browse - + + + + + + The following file types are implemented (both for local files and URLs): + + + Qt::AlignCenter + + + + + + + - Curseforge modpacks (ZIP) + + + Qt::AlignCenter + + + + + + + - Modrinth modpacks (ZIP and mrpack) + + + Qt::AlignCenter + + + + + + + - PolyMC / MultiMC exported instances (ZIP) + + + Qt::AlignCenter + + + + + + + - Technic modpacks (ZIP) + + + Qt::AlignCenter + + + + + + Qt::Vertical @@ -45,6 +92,13 @@ + + + + Local file or link to a direct download: + + + From 4745ed28186f46de60de155826c8f2bfb54f45cb Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 14 May 2022 22:12:51 -0300 Subject: [PATCH 10/33] fix: choose valid download url even if it's not the primary one It seems to be possible to have modpack versions that have to primary file. In those cases, we pick a valid one "at random". --- .../modplatform/modrinth/ModrinthPackManifest.cpp | 14 +++++++++++--- .../modplatform/modrinth/ModrinthPackManifest.h | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 4b8a9a9b5..88ca808af 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -81,15 +81,23 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion auto files = Json::requireArray(obj, "files"); + qWarning() << files; + for (auto file_iter : files) { File indexed_file; auto parent = Json::requireObject(file_iter); - if (!Json::ensureBoolean(parent, "primary", false)) { - continue; + auto is_primary = Json::ensureBoolean(parent, "primary", false); + if (!is_primary) { + auto filename = Json::ensureString(parent, "filename"); + // Checking suffix here is fine because it's the response from Modrinth, + // so one would assume it will always be in English. + if(!filename.endsWith("mrpack") && !filename.endsWith("zip")) + continue; } file.download_url = Json::requireString(parent, "url"); - break; + if(is_primary) + break; } if(file.download_url.isEmpty()) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index aaaacf2cd..585f692aa 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -79,5 +79,5 @@ auto loadIndexedVersion(QJsonObject&) -> ModpackVersion; } -Q_DECLARE_METATYPE(Modrinth::Modpack); -Q_DECLARE_METATYPE(Modrinth::ModpackVersion); +Q_DECLARE_METATYPE(Modrinth::Modpack) +Q_DECLARE_METATYPE(Modrinth::ModpackVersion) From 9731e06728ab1bdf11f6891b563d9f7123c1a0d8 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 15 May 2022 11:49:27 +0200 Subject: [PATCH 11/33] fix: fix build on Qt 5.12 --- launcher/modplatform/modrinth/ModrinthPackManifest.h | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 585f692aa..33c3fc5ea 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -21,6 +21,7 @@ #include #include #include +#include class MinecraftInstance; From a43f882d482061b86a339c1338e26246f6fc5f70 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 15 May 2022 12:06:01 +0200 Subject: [PATCH 12/33] feat: add support for Quilt Loader in Modrinth packs --- launcher/InstanceImportTask.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 29e3a26cd..293105380 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -480,7 +480,7 @@ void InstanceImportTask::processMultiMC() void InstanceImportTask::processModrinth() { std::vector files; - QString minecraftVersion, fabricVersion, forgeVersion; + QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion; try { QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json"); @@ -547,6 +547,12 @@ void InstanceImportTask::processModrinth() { throw JSONValidationError("Duplicate Fabric Loader version"); fabricVersion = Json::requireString(*it, "Fabric Loader version"); } + else if (name == "quilt-loader") + { + if (!quiltVersion.isEmpty()) + throw JSONValidationError("Duplicate Quilt Loader version"); + quiltVersion = Json::requireString(*it, "Quilt Loader version"); + } else if (name == "forge") { if (!forgeVersion.isEmpty()) @@ -587,6 +593,8 @@ void InstanceImportTask::processModrinth() { components->setComponentVersion("net.minecraft", minecraftVersion, true); if (!fabricVersion.isEmpty()) components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion, true); + if (!quiltVersion.isEmpty()) + components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion, true); if (!forgeVersion.isEmpty()) components->setComponentVersion("net.minecraftforge", forgeVersion, true); if (m_instIcon != "default") From 4a0e4fdb85ae6782406919c4b4df9554a81356aa Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 07:12:31 -0300 Subject: [PATCH 13/33] fix: add author page url --- launcher/modplatform/modrinth/ModrinthPackManifest.cpp | 7 ++++++- launcher/modplatform/modrinth/ModrinthPackManifest.h | 2 +- launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 8 +++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 88ca808af..f690984b8 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -16,9 +16,13 @@ #include "ModrinthPackManifest.h" #include "Json.h" +#include "modplatform/modrinth/ModrinthAPI.h" + #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" +static ModrinthAPI api; + namespace Modrinth { void loadIndexedPack(Modpack& pack, QJsonObject& obj) @@ -27,7 +31,8 @@ void loadIndexedPack(Modpack& pack, QJsonObject& obj) pack.name = Json::ensureString(obj, "title"); pack.description = Json::ensureString(obj, "description"); - pack.authors << Json::ensureString(obj, "author"); + auto temp_author_name = Json::ensureString(obj, "author"); + pack.author = std::make_tuple(temp_author_name, api.getAuthorURL(temp_author_name)); pack.iconName = QString("modrinth_%1").arg(Json::ensureString(obj, "slug")); pack.iconUrl = Json::ensureString(obj, "icon_url"); } diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 33c3fc5ea..47817bad1 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -62,7 +62,7 @@ struct Modpack { QString name; QString description; - QStringList authors; + std::tuple author; QString iconName; QUrl iconUrl; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index cf519b8c7..acfd14b5e 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -221,10 +221,8 @@ void ModrinthPage::updateUI() else text = "" + current.name + ""; - if (!current.authors.empty()) { - // TODO: Implement multiple authors with links - text += "
" + tr(" by ") + current.authors.at(0); - } + // TODO: Implement multiple authors with links + text += "
" + tr(" by ") + QString("%2").arg(std::get<1>(current.author).toString(), std::get<0>(current.author)); text += "
"; @@ -255,7 +253,7 @@ void ModrinthPage::suggestCurrent() dialog->setSuggestedPack(current.name, new InstanceImportTask(ver.download_url)); auto iconName = current.iconName; m_model->getLogo(iconName, current.iconUrl.toString(), - [this, iconName](QString logo) { dialog->setSuggestedIconFromFile(logo, iconName); }); + [this, iconName](QString logo) { dialog->setSuggestedIconFromFile(logo, iconName); }); break; } From 4bb429a0fbe698d0f4dbdbf02719e76730b5b6bd Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 07:43:02 -0300 Subject: [PATCH 14/33] change: use build variables for the modrinth API URLs Make it more consistent with the others --- buildconfig/BuildConfig.h | 3 +++ launcher/modplatform/modrinth/ModrinthAPI.h | 22 ++++++++++--------- .../modplatform/modrinth/ModrinthModel.cpp | 6 ++--- .../modplatform/modrinth/ModrinthPage.cpp | 5 +++-- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index a920a3d41..8594e46dc 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -151,6 +151,9 @@ class Config { */ QString TECHNIC_API_BUILD = "multimc"; + QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2"; + QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2"; + /** * \brief Converts the Version to a string. * \return The version number in string format (major.minor.revision.build). diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 86852c946..874383757 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -1,5 +1,6 @@ #pragma once +#include "BuildConfig.h" #include "modplatform/ModAPI.h" #include "modplatform/helpers/NetworkModAPI.h" @@ -47,13 +48,13 @@ class ModrinthAPI : public NetworkModAPI { return ""; } - return QString( - "https://api.modrinth.com/v2/search?" - "offset=%1&" - "limit=25&" - "query=%2&" - "index=%3&" - "facets=[[%4],%5[\"project_type:mod\"]]") + return QString(BuildConfig.MODRINTH_PROD_URL + + "/search?" + "offset=%1&" + "limit=25&" + "query=%2&" + "index=%3&" + "facets=[[%4],%5[\"project_type:mod\"]]") .arg(args.offset) .arg(args.search) .arg(args.sorting) @@ -63,9 +64,10 @@ class ModrinthAPI : public NetworkModAPI { inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override { - return QString("https://api.modrinth.com/v2/project/%1/version?" - "game_versions=[%2]" - "loaders=[\"%3\"]") + return QString(BuildConfig.MODRINTH_PROD_URL + + "/project/%1/version?" + "game_versions=[%2]" + "loaders=[\"%3\"]") .arg(args.addonId) .arg(getGameVersionsString(args.mcVersions)) .arg(getModLoaderStrings(args.loader).join("\",\"")); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 121f5d4e4..1d1b4c8e7 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -71,10 +71,10 @@ void ModpackListModel::performPaginatedSearch() // TODO: Move to standalone API NetJob* netJob = new NetJob("Modrinth::SearchModpack", APPLICATION->network()); auto searchAllUrl = QString( - "https://staging-api.modrinth.com/v2/search?" - "query=%1&" + "%1/search?" + "query=%2&" "facets=[[\"project_type:modpack\"]]") - .arg(currentSearchTerm); + .arg(BuildConfig.MODRINTH_STAGING_URL, currentSearchTerm); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), &m_all_response)); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index acfd14b5e..5dc66e56a 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -38,6 +38,7 @@ #include "ModrinthModel.h" +#include "BuildConfig.h" #include "InstanceImportTask.h" #include "Json.h" @@ -122,7 +123,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second) QString id = current.id; - netJob->addNetAction(Net::Download::makeByteArray(QString("https://staging-api.modrinth.com/v2/project/%1").arg(id), response)); + netJob->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_STAGING_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id] { if (id != current.id) { @@ -167,7 +168,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second) QString id = current.id; netJob->addNetAction( - Net::Download::makeByteArray(QString("https://staging-api.modrinth.com/v2/project/%1/version").arg(id), response)); + Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_STAGING_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id] { if (id != current.id) { From 3abf466632588f9285579a9822b5da2c9fea7bec Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 15 May 2022 13:20:05 +0200 Subject: [PATCH 15/33] chore: add/update license headers --- launcher/InstanceImportTask.cpp | 40 ++++++++++++++----- launcher/InstanceImportTask.h | 40 ++++++++++++++----- launcher/modplatform/modrinth/ModrinthAPI.h | 17 ++++++++ .../modrinth/ModrinthPackIndex.cpp | 17 ++++++++ .../modplatform/modrinth/ModrinthPackIndex.h | 17 ++++++++ .../modrinth/ModrinthPackManifest.cpp | 40 ++++++++++++++----- .../modrinth/ModrinthPackManifest.h | 40 ++++++++++++++----- launcher/ui/pages/modplatform/ImportPage.cpp | 1 + .../modplatform/modrinth/ModrinthModModel.h | 18 +++++++++ .../modplatform/modrinth/ModrinthModel.cpp | 34 ++++++++++++++++ .../modplatform/modrinth/ModrinthModel.h | 34 ++++++++++++++++ 11 files changed, 258 insertions(+), 40 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 293105380..8a0432c9f 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu * - * 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 + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * 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. + * 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 "InstanceImportTask.h" diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h index 317562d91..0dc6ba88b 100644 --- a/launcher/InstanceImportTask.h +++ b/launcher/InstanceImportTask.h @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu * - * 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 + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * 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. + * 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/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 874383757..09eefcd1a 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -1,3 +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 . + */ + #pragma once #include "BuildConfig.h" diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index a3c2f166f..f7fa98641 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -1,3 +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 . + */ + #include "ModrinthPackIndex.h" #include "ModrinthAPI.h" diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.h b/launcher/modplatform/modrinth/ModrinthPackIndex.h index fd17847af..7f306f25f 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.h +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.h @@ -1,3 +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 . + */ + #pragma once #include "modplatform/ModIndex.h" diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index f690984b8..f77baa6ae 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -1,16 +1,36 @@ -/* Copyright 2022 kb1000 +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher * - * 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 + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * 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. + * 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 + * Copyright 2022 kb1000 + * + * 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 "ModrinthPackManifest.h" diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 47817bad1..d350477b5 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -1,16 +1,36 @@ -/* Copyright 2022 kb1000 +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher * - * 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 + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * 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. + * 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 + * Copyright 2022 kb1000 + * + * 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/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index c86d02cae..c7bc13d88 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -2,6 +2,7 @@ /* * PolyMC - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield + * Copyright (c) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h index 63c23bbeb..ae7b0bddc 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h @@ -1,3 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * 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 "ModrinthModPage.h" diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 1d1b4c8e7..b0dfb1b75 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -1,3 +1,37 @@ +// 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 . + * + * 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 "ModrinthModel.h" #include "BuildConfig.h" diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h index 1fdbe278b..6ec3bb976 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -1,3 +1,37 @@ +// 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 . + * + * 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 From 5f2398fe59b0053d94c0600f54bddc642751bf74 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 08:25:58 -0300 Subject: [PATCH 16/33] chore: license headers 2 --- launcher/InstanceImportTask.cpp | 1 + launcher/modplatform/modrinth/ModrinthAPI.h | 1 + launcher/modplatform/modrinth/ModrinthPackManifest.cpp | 1 + launcher/modplatform/modrinth/ModrinthPackManifest.h | 1 + launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 1 + launcher/ui/pages/modplatform/modrinth/ModrinthModel.h | 1 + launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 1 + launcher/ui/pages/modplatform/modrinth/ModrinthPage.h | 1 + 8 files changed, 8 insertions(+) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 8a0432c9f..26d46be03 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -2,6 +2,7 @@ /* * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * 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 diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 09eefcd1a..6d642b5e8 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -1,6 +1,7 @@ // 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 diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index f77baa6ae..facf5ddbd 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -1,6 +1,7 @@ // 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 diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index d350477b5..55ad40d94 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -1,6 +1,7 @@ // 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 diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index b0dfb1b75..50974e13f 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -1,6 +1,7 @@ // 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 diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h index 6ec3bb976..e61eae7cf 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -1,6 +1,7 @@ // 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 diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 5dc66e56a..f69983ee5 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -1,6 +1,7 @@ // 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 diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h index f72a5071f..9aa702f92 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h @@ -1,6 +1,7 @@ // 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 From 682a7fb6bad2c2d07ae5ddf67c139ac3f15672bb Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 15 May 2022 13:36:55 +0200 Subject: [PATCH 17/33] feat: add version of Modrinth modpack to instance name --- launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index f69983ee5..fd9adc247 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -252,7 +252,7 @@ void ModrinthPage::suggestCurrent() for (auto& ver : current.versions) { if (ver.id == selectedVersion) { - dialog->setSuggestedPack(current.name, new InstanceImportTask(ver.download_url)); + dialog->setSuggestedPack(current.name + " " + ver.version, new InstanceImportTask(ver.download_url)); auto iconName = current.iconName; m_model->getLogo(iconName, current.iconUrl.toString(), [this, iconName](QString logo) { dialog->setSuggestedIconFromFile(logo, iconName); }); From 93e0041d0e6c3d7859f7d8b058a0fd014329bec6 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 11:09:45 -0300 Subject: [PATCH 18/33] change: use modrinth icon as default on modrinth packs --- launcher/InstanceImportTask.cpp | 4 ++++ launcher/resources/multimc/multimc.qrc | 2 +- .../resources/multimc/scalable/{ => instances}/modrinth.svg | 0 3 files changed, 5 insertions(+), 1 deletion(-) rename launcher/resources/multimc/scalable/{ => instances}/modrinth.svg (100%) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 26d46be03..f02aed910 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -622,6 +622,10 @@ void InstanceImportTask::processModrinth() { { instance.setIconKey(m_instIcon); } + else + { + instance.setIconKey("modrinth"); + } instance.setName(m_instName); instance.saveNow(); diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 86ebf753c..e22fe7eef 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -21,7 +21,7 @@ scalable/atlauncher-placeholder.png - scalable/modrinth.svg + scalable/instances/modrinth.svg scalable/proxy.svg diff --git a/launcher/resources/multimc/scalable/modrinth.svg b/launcher/resources/multimc/scalable/instances/modrinth.svg similarity index 100% rename from launcher/resources/multimc/scalable/modrinth.svg rename to launcher/resources/multimc/scalable/instances/modrinth.svg From 4adc61bda91bb01e603fb975b05651df7decaf52 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 11:26:15 -0300 Subject: [PATCH 19/33] change: update modrinth icon Updates to the version at https://github.com/modrinth/docs/blob/master/static/img/logo.svg --- launcher/resources/multimc/scalable/instances/modrinth.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/resources/multimc/scalable/instances/modrinth.svg b/launcher/resources/multimc/scalable/instances/modrinth.svg index 32715f5ce..a40f0e72b 100644 --- a/launcher/resources/multimc/scalable/instances/modrinth.svg +++ b/launcher/resources/multimc/scalable/instances/modrinth.svg @@ -1,4 +1,4 @@ - - + + From 78cf0c73c89f0d1207bb079bf4670cc032607c4d Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 15 May 2022 20:38:27 +0200 Subject: [PATCH 20/33] fix: always show project url, if available --- launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index fd9adc247..a2e18d19a 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -218,7 +218,7 @@ void ModrinthPage::updateUI() { QString text = ""; - if (current.extra.sourceUrl.isEmpty()) + if (current.extra.projectUrl.isEmpty()) text = current.name; else text = "" + current.name + ""; From 7194bb1b8114a2ec96d3cb30a4fe3338f3962d4c Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 15:58:23 -0300 Subject: [PATCH 21/33] fix: validate whitelisted download urls --- launcher/InstanceImportTask.cpp | 2 +- .../modrinth/ModrinthPackManifest.cpp | 25 +++++++++++++++++-- .../modrinth/ModrinthPackManifest.h | 2 ++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index f02aed910..3ca82923e 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -545,7 +545,7 @@ void InstanceImportTask::processModrinth() { file.hashAlgorithm = hashAlgorithm; // Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode (as Modrinth seems to incorrectly handle spaces) file.download = Json::requireString(Json::ensureArray(obj, "downloads").first(), "Download URL for " + file.path); - if (!file.download.isValid()) + if (!file.download.isValid() || !Modrinth::validadeDownloadUrl(file.download)) { throw JSONValidationError("Download URL for " + file.path + " is not a correctly formatted URL"); } diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index facf5ddbd..947ac1823 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -93,6 +93,23 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc) pack.versionsLoaded = true; } +auto validadeDownloadUrl(QUrl url) -> bool +{ + auto domain = url.host(); + if(domain == "cdn.modrinth.com") + return true; + if(domain == "edge.forgecdn.net") + return true; + if(domain == "media.forgecdn.net") + return true; + if(domain == "github.com") + return true; + if(domain == "raw.githubusercontent.com") + return true; + + return false; +} + auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion { ModpackVersion file; @@ -107,7 +124,6 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion auto files = Json::requireArray(obj, "files"); - qWarning() << files; for (auto file_iter : files) { File indexed_file; @@ -121,7 +137,12 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion continue; } - file.download_url = Json::requireString(parent, "url"); + auto url = Json::requireString(parent, "url"); + + if(!validadeDownloadUrl(url)) + continue; + + file.download_url = url; if(is_primary) break; } diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 55ad40d94..4db4a75d8 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -99,6 +99,8 @@ void loadIndexedInfo(Modpack&, QJsonObject&); void loadIndexedVersions(Modpack&, QJsonDocument&); auto loadIndexedVersion(QJsonObject&) -> ModpackVersion; +auto validadeDownloadUrl(QUrl) -> bool; + } Q_DECLARE_METATYPE(Modrinth::Modpack) From 66ce5a4a2d38803bf667d7326f2ffb1bc06bff99 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 20:45:27 -0300 Subject: [PATCH 22/33] fix: pack sorting and other search parameters --- .../modplatform/modrinth/ModrinthModel.cpp | 24 ++++++++++++++----- .../modplatform/modrinth/ModrinthModel.h | 2 +- .../modplatform/modrinth/ModrinthPage.cpp | 9 ++++--- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 50974e13f..6786b0dad 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -105,11 +105,16 @@ void ModpackListModel::performPaginatedSearch() { // TODO: Move to standalone API NetJob* netJob = new NetJob("Modrinth::SearchModpack", APPLICATION->network()); - auto searchAllUrl = QString( - "%1/search?" + auto searchAllUrl = QString(BuildConfig.MODRINTH_STAGING_URL + + "/search?" + "offset=%1&" + "limit=20&" "query=%2&" + "index=%3&" "facets=[[\"project_type:modpack\"]]") - .arg(BuildConfig.MODRINTH_STAGING_URL, currentSearchTerm); + .arg(nextSearchOffset) + .arg(currentSearchTerm) + .arg(currentSort); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), &m_all_response)); @@ -148,14 +153,21 @@ void ModpackListModel::refresh() performPaginatedSearch(); } +static std::array sorts {"relevance", "downloads", "follows", "newest", "updated"}; + void ModpackListModel::searchWithTerm(const QString& term, const int sort) { - if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort) { + if(sort > 5 || sort < 0) + return; + + auto sort_str = sorts.at(sort); + + if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort_str) { return; } currentSearchTerm = term; - currentSort = sort; + currentSort = sort_str; refresh(); } @@ -255,7 +267,7 @@ void ModpackListModel::searchRequestFailed(QString reason) { if (!jobPtr->first()->m_reply) { // Network error - QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load mods.")); + QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load modpacks.")); } else if (jobPtr->first()->m_reply && jobPtr->first()->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 409) { // 409 Gone, notify user to update QMessageBox::critical(nullptr, tr("Error"), diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h index e61eae7cf..bffea54da 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -104,7 +104,7 @@ class ModpackListModel : public QAbstractListModel { QStringList m_loadingLogos; QString currentSearchTerm; - int currentSort = 0; + QString currentSort; int nextSearchOffset = 0; enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index a2e18d19a..ceddcfb51 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -61,12 +61,11 @@ ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); - ui->sortByBox->addItem(tr("Sort by Featured")); - ui->sortByBox->addItem(tr("Sort by Popularity")); - ui->sortByBox->addItem(tr("Sort by Last Updated")); - ui->sortByBox->addItem(tr("Sort by Name")); - ui->sortByBox->addItem(tr("Sort by Author")); + ui->sortByBox->addItem(tr("Sort by Relevance")); ui->sortByBox->addItem(tr("Sort by Total Downloads")); + ui->sortByBox->addItem(tr("Sort by Follows")); + ui->sortByBox->addItem(tr("Sort by Newest")); + ui->sortByBox->addItem(tr("Sort by Last Updated")); connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthPage::onSelectionChanged); From ec3c882a44624f18b088322b28efe7153e7db083 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 20:52:57 -0300 Subject: [PATCH 23/33] change: add alpha note to modrinth page --- .../ui/pages/modplatform/modrinth/ModrinthPage.ui | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui index 8de53a693..90e8dba3c 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui @@ -11,6 +11,21 @@ + + + + + true + + + + Note: Modrinth modpacks is still in alpha phase. Some things may be rough on the edges, or not working at all! Use it with caution. + + + Qt::AlignCenter + + + From e7bb3b277647a21b85cb01ee90bf640e22d01552 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 20:59:07 -0300 Subject: [PATCH 24/33] fix: macos compilation i forgor macos is cringe with static arrays :skull: edit: WHY DONT MAC LET ME USE STD::ARRAY ;----; --- .../modplatform/modrinth/ModrinthModel.cpp | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 6786b0dad..2504b294b 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -153,14 +153,31 @@ void ModpackListModel::refresh() performPaginatedSearch(); } -static std::array sorts {"relevance", "downloads", "follows", "newest", "updated"}; +static auto sortFromIndex(int index) -> QString +{ + switch(index){ + default: + case 1: + return "relevance"; + case 2: + return "downloads"; + case 3: + return "follows"; + case 4: + return "newest"; + case 5: + return "updated"; + } + + return {}; +} void ModpackListModel::searchWithTerm(const QString& term, const int sort) { if(sort > 5 || sort < 0) return; - auto sort_str = sorts.at(sort); + auto sort_str = sortFromIndex(sort); if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort_str) { return; From e92b7bd25e9eccf293ff97652364def63a674df2 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 21:50:42 -0300 Subject: [PATCH 25/33] change: switch to modrinth production servers --- launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 2 +- launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 2504b294b..0cf53659b 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -105,7 +105,7 @@ void ModpackListModel::performPaginatedSearch() { // TODO: Move to standalone API NetJob* netJob = new NetJob("Modrinth::SearchModpack", APPLICATION->network()); - auto searchAllUrl = QString(BuildConfig.MODRINTH_STAGING_URL + + auto searchAllUrl = QString(BuildConfig.MODRINTH_PROD_URL + "/search?" "offset=%1&" "limit=20&" diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index ceddcfb51..fadad9df7 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -123,7 +123,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second) QString id = current.id; - netJob->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_STAGING_URL, id), response)); + netJob->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id] { if (id != current.id) { @@ -168,7 +168,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second) QString id = current.id; netJob->addNetAction( - Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_STAGING_URL, id), response)); + Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id] { if (id != current.id) { From 62e099ace5db8e04bba684e6c2517291dcce3578 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 15 May 2022 22:16:52 -0300 Subject: [PATCH 26/33] feat: better handling of optional mods This disables the optional mods by default and tell the user about it. Pretty hackish, but a better solution would involve the modrinth metadata to have the mod names... Also sorry for the diffs, my clangd went rogue x.x --- launcher/InstanceImportTask.cpp | 153 +++++++++--------- launcher/InstanceImportTask.h | 5 +- .../modplatform/modrinth/ModrinthPage.cpp | 2 +- 3 files changed, 80 insertions(+), 80 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 3ca82923e..64f2dd021 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -35,35 +35,38 @@ */ #include "InstanceImportTask.h" +#include +#include "Application.h" #include "BaseInstance.h" #include "FileSystem.h" -#include "Application.h" #include "MMCZip.h" #include "NullInstance.h" -#include "settings/INISettingsObject.h" #include "icons/IconUtils.h" -#include +#include "settings/INISettingsObject.h" // FIXME: this does not belong here, it's Minecraft/Flame specific +#include +#include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "modplatform/flame/FileResolvingTask.h" #include "modplatform/flame/PackManifest.h" -#include "Json.h" -#include #include "modplatform/modrinth/ModrinthPackManifest.h" #include "modplatform/technic/TechnicPackProcessor.h" -#include "icons/IconList.h" #include "Application.h" +#include "icons/IconList.h" #include "net/ChecksumValidator.h" +#include "ui/dialogs/CustomMessageBox.h" + #include #include -InstanceImportTask::InstanceImportTask(const QUrl sourceUrl) +InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent) { m_sourceUrl = sourceUrl; + m_parent = parent; } bool InstanceImportTask::abort() @@ -476,124 +479,118 @@ void InstanceImportTask::processMultiMC() instance.setName(m_instName); // if the icon was specified by user, use that. otherwise pull icon from the pack - if (m_instIcon != "default") - { + if (m_instIcon != "default") { instance.setIconKey(m_instIcon); - } - else - { + } else { m_instIcon = instance.iconKey(); auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon); - if (!importIconPath.isNull() && QFile::exists(importIconPath)) - { + if (!importIconPath.isNull() && QFile::exists(importIconPath)) { // import icon auto iconList = APPLICATION->icons(); - if (iconList->iconFileExists(m_instIcon)) - { + if (iconList->iconFileExists(m_instIcon)) { iconList->deleteIcon(m_instIcon); } - iconList->installIcons({importIconPath}); + iconList->installIcons({ importIconPath }); } } emitSucceeded(); } -void InstanceImportTask::processModrinth() { +void InstanceImportTask::processModrinth() +{ std::vector files; QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion; - try - { + try { QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json"); auto doc = Json::requireDocument(indexPath); auto obj = Json::requireObject(doc, "modrinth.index.json"); int formatVersion = Json::requireInteger(obj, "formatVersion", "modrinth.index.json"); - if (formatVersion == 1) - { + if (formatVersion == 1) { auto game = Json::requireString(obj, "game", "modrinth.index.json"); - if (game != "minecraft") - { + if (game != "minecraft") { throw JSONValidationError("Unknown game: " + game); } auto jsonFiles = Json::requireIsArrayOf(obj, "files", "modrinth.index.json"); - std::transform(jsonFiles.begin(), jsonFiles.end(), std::back_inserter(files), [](const QJsonObject& obj) - { - Modrinth::File file; - file.path = Json::requireString(obj, "path"); - QString supported = Json::ensureString(Json::ensureObject(obj, "env")); - QJsonObject hashes = Json::requireObject(obj, "hashes"); - QString hash; - QCryptographicHash::Algorithm hashAlgorithm; - hash = Json::ensureString(hashes, "sha1"); - hashAlgorithm = QCryptographicHash::Sha1; - if (hash.isEmpty()) - { - hash = Json::ensureString(hashes, "sha512"); - hashAlgorithm = QCryptographicHash::Sha512; - if (hash.isEmpty()) - { - hash = Json::ensureString(hashes, "sha256"); - hashAlgorithm = QCryptographicHash::Sha256; - if (hash.isEmpty()) - { - throw JSONValidationError("No hash found for: " + file.path); - } + bool had_optional = false; + for (auto& obj : jsonFiles) { + Modrinth::File file; + file.path = Json::requireString(obj, "path"); + + auto env = Json::ensureObject(obj, "env"); + QString support = Json::ensureString(env, "client", "unsupported"); + if (support == "unsupported") { + continue; + } else if (support == "optional") { + // TODO: Make a review dialog for choosing which ones the user wants! + if (!had_optional) { + had_optional = true; + auto info = CustomMessageBox::selectable( + m_parent, tr("Optional mod detected!"), + tr("One or more mods from this modpack are optional. They will be downloaded, but disabled by default!"), QMessageBox::Information); + info->exec(); + } + + if (file.path.endsWith(".jar")) + file.path += ".disabled"; + } + + QJsonObject hashes = Json::requireObject(obj, "hashes"); + QString hash; + QCryptographicHash::Algorithm hashAlgorithm; + hash = Json::ensureString(hashes, "sha1"); + hashAlgorithm = QCryptographicHash::Sha1; + if (hash.isEmpty()) { + hash = Json::ensureString(hashes, "sha512"); + hashAlgorithm = QCryptographicHash::Sha512; + if (hash.isEmpty()) { + hash = Json::ensureString(hashes, "sha256"); + hashAlgorithm = QCryptographicHash::Sha256; + if (hash.isEmpty()) { + throw JSONValidationError("No hash found for: " + file.path); } } - file.hash = QByteArray::fromHex(hash.toLatin1()); - file.hashAlgorithm = hashAlgorithm; - // Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode (as Modrinth seems to incorrectly handle spaces) - file.download = Json::requireString(Json::ensureArray(obj, "downloads").first(), "Download URL for " + file.path); - if (!file.download.isValid() || !Modrinth::validadeDownloadUrl(file.download)) - { - throw JSONValidationError("Download URL for " + file.path + " is not a correctly formatted URL"); - } - return file; - }); + } + file.hash = QByteArray::fromHex(hash.toLatin1()); + file.hashAlgorithm = hashAlgorithm; + // Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode (as Modrinth seems to incorrectly + // handle spaces) + file.download = Json::requireString(Json::ensureArray(obj, "downloads").first(), "Download URL for " + file.path); + if (!file.download.isValid() || !Modrinth::validadeDownloadUrl(file.download)) { + throw JSONValidationError("Download URL for " + file.path + " is not a correctly formatted URL"); + } + files.push_back(file); + } auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json"); - for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) - { + for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) { QString name = it.key(); - if (name == "minecraft") - { + if (name == "minecraft") { if (!minecraftVersion.isEmpty()) throw JSONValidationError("Duplicate Minecraft version"); minecraftVersion = Json::requireString(*it, "Minecraft version"); - } - else if (name == "fabric-loader") - { + } else if (name == "fabric-loader") { if (!fabricVersion.isEmpty()) throw JSONValidationError("Duplicate Fabric Loader version"); fabricVersion = Json::requireString(*it, "Fabric Loader version"); - } - else if (name == "quilt-loader") - { + } else if (name == "quilt-loader") { if (!quiltVersion.isEmpty()) throw JSONValidationError("Duplicate Quilt Loader version"); quiltVersion = Json::requireString(*it, "Quilt Loader version"); - } - else if (name == "forge") - { + } else if (name == "forge") { if (!forgeVersion.isEmpty()) throw JSONValidationError("Duplicate Forge version"); forgeVersion = Json::requireString(*it, "Forge version"); - } - else - { + } else { throw JSONValidationError("Unknown dependency type: " + name); } } - } - else - { + } else { throw JSONValidationError(QStringLiteral("Unknown format version: %s").arg(formatVersion)); } QFile::remove(indexPath); - } - catch (const JSONValidationError &e) - { + } catch (const JSONValidationError& e) { emitFailed(tr("Could not understand pack index:\n") + e.cause()); return; } diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h index 0dc6ba88b..5e4d32351 100644 --- a/launcher/InstanceImportTask.h +++ b/launcher/InstanceImportTask.h @@ -55,7 +55,7 @@ class InstanceImportTask : public InstanceTask { Q_OBJECT public: - explicit InstanceImportTask(const QUrl sourceUrl); + explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr); bool canAbort() const override { return true; } bool abort() override; @@ -94,4 +94,7 @@ private: /* data */ Flame, Modrinth, } m_modpackType = ModpackType::Unknown; + + //FIXME: nuke + QWidget* m_parent; }; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index fadad9df7..f24d36518 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -251,7 +251,7 @@ void ModrinthPage::suggestCurrent() for (auto& ver : current.versions) { if (ver.id == selectedVersion) { - dialog->setSuggestedPack(current.name + " " + ver.version, new InstanceImportTask(ver.download_url)); + dialog->setSuggestedPack(current.name + " " + ver.version, new InstanceImportTask(ver.download_url, this)); auto iconName = current.iconName; m_model->getLogo(iconName, current.iconUrl.toString(), [this, iconName](QString logo) { dialog->setSuggestedIconFromFile(logo, iconName); }); From 82760f4b916ef122eabb644e8679f9ae76587e44 Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 16 May 2022 10:50:46 -0300 Subject: [PATCH 27/33] fix: import modrinth packs with weird overrides structure Probably because of Packwiz limitations, or an space optimizer that did this :) --- launcher/MMCZip.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index b92f17817..8591fcc06 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -297,20 +297,40 @@ nonstd::optional MMCZip::extractSubDir(QuaZip *zip, const QString & { continue; } + name.remove(0, subdir.size()); - QString absFilePath = directory.absoluteFilePath(name); + auto original_name = name; + + // Fix weird "folders with a single file get squashed" thing + QString path; + if(name.contains('/') && !name.endsWith('/')){ + path = name.section('/', 0, -2) + "/"; + FS::ensureFolderPathExists(path); + + name = name.split('/').last(); + } + + QString absFilePath; if(name.isEmpty()) { - absFilePath += "/"; + absFilePath = directory.absoluteFilePath(name) + "/"; } + else + { + absFilePath = directory.absoluteFilePath(path + name); + } + if (!JlCompress::extractFile(zip, "", absFilePath)) { - qWarning() << "Failed to extract file" << name << "to" << absFilePath; + qWarning() << "Failed to extract file" << original_name << "to" << absFilePath; JlCompress::removeFile(extracted); return nonstd::nullopt; } + extracted.append(absFilePath); - qDebug() << "Extracted file" << name; + QFile::setPermissions(absFilePath, QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); + + qDebug() << "Extracted file" << name << "to" << absFilePath; } while (zip->goToNextFile()); return extracted; } From a6d2c5e18131ab155ed482aeab548dabc2741d62 Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 16 May 2022 12:59:32 -0300 Subject: [PATCH 28/33] fix: better hack for icons that cant be natively scaled to 48x48 --- launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 0cf53659b..bb54bc20f 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -85,9 +85,10 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian return pack.description; } else if (role == Qt::DecorationRole) { if (m_logoMap.contains(pack.iconName)) { - return (m_logoMap.value(pack.iconName) - .pixmap(48, 48) - .scaled(48, 48, Qt::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation)); + auto icon = m_logoMap.value(pack.iconName); + auto icon_scaled = QIcon(icon.pixmap(48, 48).scaledToWidth(48)); + + return icon_scaled; } QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); ((ModpackListModel*)this)->requestLogo(pack.iconName, pack.iconUrl.toString()); From cd9e0e0cc0228ffa24466814a649abef43045745 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 16 May 2022 20:17:19 +0200 Subject: [PATCH 29/33] fix: use own metacache base for modrinth icons --- launcher/Application.cpp | 1 + launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 11109857f..afb33a502 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -819,6 +819,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath()); m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath()); m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").absolutePath()); + m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath()); m_metacache->addBase("root", QDir::currentPath()); m_metacache->addBase("translations", QDir("translations").absolutePath()); m_metacache->addBase("icons", QDir("cache/icons").absolutePath()); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index bb54bc20f..bc1046ad5 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -208,7 +208,7 @@ void ModpackListModel::requestLogo(QString logo, QString url) } MetaEntryPtr entry = - APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))); + APPLICATION->metacache()->resolveEntry("ModrinthPacks", QString("logos/%1").arg(logo.section(".", 0, 0))); auto job = new NetJob(QString("%1 Icon Download %2").arg(m_parent->debugName()).arg(logo), APPLICATION->network()); job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); From 887246a66b5391b16d5b0d275ba77fe3a8bf540b Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 16 May 2022 17:05:54 -0300 Subject: [PATCH 30/33] fix: typo and useless code --- launcher/InstanceImportTask.cpp | 27 +++++++++---------- .../modrinth/ModrinthPackManifest.cpp | 4 +-- .../modrinth/ModrinthPackManifest.h | 2 +- .../modplatform/modrinth/ModrinthPage.cpp | 6 ----- .../pages/modplatform/modrinth/ModrinthPage.h | 1 - 5 files changed, 15 insertions(+), 25 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 64f2dd021..8f68b95f0 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -554,10 +554,10 @@ void InstanceImportTask::processModrinth() } file.hash = QByteArray::fromHex(hash.toLatin1()); file.hashAlgorithm = hashAlgorithm; - // Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode (as Modrinth seems to incorrectly - // handle spaces) + // Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode + // (as Modrinth seems to incorrectly handle spaces) file.download = Json::requireString(Json::ensureArray(obj, "downloads").first(), "Download URL for " + file.path); - if (!file.download.isValid() || !Modrinth::validadeDownloadUrl(file.download)) { + if (!file.download.isValid() || !Modrinth::validateDownloadUrl(file.download)) { throw JSONValidationError("Download URL for " + file.path + " is not a correctly formatted URL"); } files.push_back(file); @@ -567,22 +567,18 @@ void InstanceImportTask::processModrinth() for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) { QString name = it.key(); if (name == "minecraft") { - if (!minecraftVersion.isEmpty()) - throw JSONValidationError("Duplicate Minecraft version"); minecraftVersion = Json::requireString(*it, "Minecraft version"); - } else if (name == "fabric-loader") { - if (!fabricVersion.isEmpty()) - throw JSONValidationError("Duplicate Fabric Loader version"); + } + else if (name == "fabric-loader") { fabricVersion = Json::requireString(*it, "Fabric Loader version"); - } else if (name == "quilt-loader") { - if (!quiltVersion.isEmpty()) - throw JSONValidationError("Duplicate Quilt Loader version"); + } + else if (name == "quilt-loader") { quiltVersion = Json::requireString(*it, "Quilt Loader version"); - } else if (name == "forge") { - if (!forgeVersion.isEmpty()) - throw JSONValidationError("Duplicate Forge version"); + } + else if (name == "forge") { forgeVersion = Json::requireString(*it, "Forge version"); - } else { + } + else { throw JSONValidationError("Unknown dependency type: " + name); } } @@ -594,6 +590,7 @@ void InstanceImportTask::processModrinth() emitFailed(tr("Could not understand pack index:\n") + e.cause()); return; } + QString overridePath = FS::PathCombine(m_stagingPath, "overrides"); if (QFile::exists(overridePath)) { QString mcPath = FS::PathCombine(m_stagingPath, ".minecraft"); diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 947ac1823..f1ad39cea 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -93,7 +93,7 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc) pack.versionsLoaded = true; } -auto validadeDownloadUrl(QUrl url) -> bool +auto validateDownloadUrl(QUrl url) -> bool { auto domain = url.host(); if(domain == "cdn.modrinth.com") @@ -139,7 +139,7 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion auto url = Json::requireString(parent, "url"); - if(!validadeDownloadUrl(url)) + if(!validateDownloadUrl(url)) continue; file.download_url = url; diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 4db4a75d8..e5fc9a700 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -99,7 +99,7 @@ void loadIndexedInfo(Modpack&, QJsonObject&); void loadIndexedVersions(Modpack&, QJsonDocument&); auto loadIndexedVersion(QJsonObject&) -> ModpackVersion; -auto validadeDownloadUrl(QUrl) -> bool; +auto validateDownloadUrl(QUrl) -> bool; } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index f24d36518..9bd24b578 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -195,7 +195,6 @@ void ModrinthPage::onSelectionChanged(QModelIndex first, QModelIndex second) ui->versionSelectionBox->addItem(version.version, QVariant(version.id)); } - updateVersionsUI(); suggestCurrent(); }); QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { @@ -233,11 +232,6 @@ void ModrinthPage::updateUI() ui->packDescription->setHtml(text + current.description); } -void ModrinthPage::updateVersionsUI() -{ - // idk -} - void ModrinthPage::suggestCurrent() { if (!isOpened) { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h index 9aa702f92..db5e1a3d6 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h @@ -71,7 +71,6 @@ class ModrinthPage : public QWidget, public BasePage { void suggestCurrent(); void updateUI(); - void updateVersionsUI(); void retranslate() override; void openedImpl() override; From 696a711e397440275a55f4dbe02947a78ab0b208 Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 16 May 2022 19:10:31 -0300 Subject: [PATCH 31/33] fix: missed change to metacache entry lookup --- launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index bc1046ad5..701a20324 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -194,7 +194,7 @@ void ModpackListModel::getLogo(const QString& logo, const QString& logoUrl, Logo { if (m_logoMap.contains(logo)) { callback(APPLICATION->metacache() - ->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))) + ->resolveEntry("ModrinthPacks", QString("logos/%1").arg(logo.section(".", 0, 0))) ->getFullPath()); } else { requestLogo(logo, logoUrl); From 2e9d7f5c3d3cbc33ad95d830af4fdcab6eab6a06 Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 16 May 2022 19:17:37 -0300 Subject: [PATCH 32/33] fix: mod skipping between pages and remove dead code --- .../modplatform/modrinth/ModrinthModel.cpp | 30 ++++--------------- .../modplatform/modrinth/ModrinthModel.h | 4 +-- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 701a20324..7cacf37ad 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -109,11 +109,12 @@ void ModpackListModel::performPaginatedSearch() auto searchAllUrl = QString(BuildConfig.MODRINTH_PROD_URL + "/search?" "offset=%1&" - "limit=20&" - "query=%2&" - "index=%3&" + "limit=%2&" + "query=%3&" + "index=%4&" "facets=[[\"project_type:modpack\"]]") .arg(nextSearchOffset) + .arg(m_modpacks_per_page) .arg(currentSearchTerm) .arg(currentSort); @@ -269,10 +270,10 @@ void ModpackListModel::searchRequestFinished(QJsonDocument& doc_all) } } - if (packs_all.size() < 25) { + if (packs_all.size() < m_modpacks_per_page) { searchState = Finished; } else { - nextSearchOffset += 25; + nextSearchOffset += m_modpacks_per_page; searchState = CanPossiblyFetchMore; } @@ -308,25 +309,6 @@ void ModpackListModel::searchRequestFailed(QString reason) } } -void ModpackListModel::versionRequestSucceeded(QJsonDocument doc, QString id) -{ - auto& current = m_parent->getCurrent(); - if (id != current.id) { - return; - } - - auto arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array(); - - try { - // loadIndexedPackVersions(current, arr); - } catch (const JSONValidationError& e) { - qDebug() << doc; - qWarning() << "Error while reading " << debugName() << " mod version: " << e.cause(); - } - - // m_parent->updateModVersions(); -} - } // namespace Modrinth /******** Helpers ********/ diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h index bffea54da..14aa67473 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -79,8 +79,6 @@ class ModpackListModel : public QAbstractListModel { void searchRequestFinished(QJsonDocument& doc_all); void searchRequestFailed(QString reason); - void versionRequestSucceeded(QJsonDocument doc, QString addonId); - protected slots: void logoFailed(QString logo); @@ -112,5 +110,7 @@ class ModpackListModel : public QAbstractListModel { QByteArray m_all_response; QByteArray m_specific_response; + + int m_modpacks_per_page = 20; }; } // namespace ModPlatform From ddc3b5eb0bbd17edd60d96f5094d65dbdb922765 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Tue, 17 May 2022 15:14:53 +0200 Subject: [PATCH 33/33] Update launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui Co-authored-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui index 90e8dba3c..4fb59cdf0 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui @@ -19,7 +19,7 @@ - Note: Modrinth modpacks is still in alpha phase. Some things may be rough on the edges, or not working at all! Use it with caution. + Note: Modrinth modpacks are still in alpha phase. Some things may be rough on the edges, or not working at all! Use it with caution. Qt::AlignCenter