diff options
Diffstat (limited to 'launcher/modplatform')
-rw-r--r-- | launcher/modplatform/ModAPI.h | 37 | ||||
-rw-r--r-- | launcher/modplatform/flame/FlameAPI.h | 17 | ||||
-rw-r--r-- | launcher/modplatform/flame/FlameModIndex.cpp | 7 | ||||
-rw-r--r-- | launcher/modplatform/modrinth/ModrinthAPI.h | 35 | ||||
-rw-r--r-- | launcher/modplatform/modrinth/ModrinthPackIndex.cpp | 8 | ||||
-rw-r--r-- | launcher/modplatform/technic/SolderPackInstallTask.cpp | 137 | ||||
-rw-r--r-- | launcher/modplatform/technic/SolderPackInstallTask.h | 47 | ||||
-rw-r--r-- | launcher/modplatform/technic/SolderPackManifest.cpp | 58 | ||||
-rw-r--r-- | launcher/modplatform/technic/SolderPackManifest.h | 49 |
9 files changed, 291 insertions, 104 deletions
diff --git a/launcher/modplatform/ModAPI.h b/launcher/modplatform/ModAPI.h index ae6ac80f..8e6cd45c 100644 --- a/launcher/modplatform/ModAPI.h +++ b/launcher/modplatform/ModAPI.h @@ -3,6 +3,8 @@ #include <QString> #include <QList> +#include "Version.h" + namespace ModPlatform { class ListModel; } @@ -15,14 +17,14 @@ class ModAPI { virtual ~ModAPI() = default; // https://docs.curseforge.com/?http#tocS_ModLoaderType - enum ModLoaderType { Any = 0, Forge = 1, Cauldron = 2, LiteLoader = 3, Fabric = 4 }; + enum ModLoaderType { Unspecified = 0, Forge = 1, Cauldron = 2, LiteLoader = 3, Fabric = 4, Quilt = 5 }; struct SearchArgs { int offset; QString search; QString sorting; ModLoaderType mod_loader; - QString version; + std::list<Version> versions; }; virtual void searchMods(CallerType* caller, SearchArgs&& args) const = 0; @@ -30,9 +32,38 @@ class ModAPI { struct VersionSearchArgs { QString addonId; - QList<QString> mcVersions; + std::list<Version> mcVersions; ModLoaderType loader; }; virtual void getVersions(CallerType* caller, VersionSearchArgs&& args) const = 0; + + static auto getModLoaderString(ModLoaderType type) -> const QString { + switch (type) { + case Unspecified: + break; + case Forge: + return "forge"; + case Cauldron: + return "cauldron"; + case LiteLoader: + return "liteloader"; + case Fabric: + return "fabric"; + case Quilt: + return "quilt"; + } + return ""; + } + + protected: + inline auto getGameVersionsString(std::list<Version> mcVersions) const -> QString + { + QString s; + for(auto& ver : mcVersions){ + s += QString("%1,").arg(ver.toString()); + } + s.remove(s.length() - 1, 1); //remove last comma + return s; + } }; diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 8654a693..ce02df65 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -6,6 +6,8 @@ class FlameAPI : public NetworkModAPI { private: inline auto getModSearchURL(SearchArgs& args) const -> QString override { + auto gameVersionStr = args.versions.size() != 0 ? QString("gameVersion=%1").arg(args.versions.front().toString()) : QString(); + return QString( "https://addons-ecs.forgesvc.net/api/v2/addon/search?" "gameId=432&" @@ -17,16 +19,25 @@ class FlameAPI : public NetworkModAPI { "searchFilter=%2&" "sort=%3&" "modLoaderType=%4&" - "gameVersion=%5") + "%5") .arg(args.offset) .arg(args.search) .arg(args.sorting) - .arg(args.mod_loader) - .arg(args.version); + .arg(getMappedModLoader(args.mod_loader)) + .arg(gameVersionStr); }; inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override { return QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(args.addonId); }; + + public: + static auto getMappedModLoader(const ModLoaderType type) -> const ModLoaderType + { + // TODO: remove this once Quilt drops official Fabric support + if (type == Quilt) // NOTE: Most if not all Fabric mods should work *currently* + return Fabric; + return type; + } }; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 2c3adee4..c7b86b5c 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -3,6 +3,7 @@ #include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" +#include "modplatform/flame/FlameAPI.h" #include "net/NetJob.h" void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) @@ -43,8 +44,9 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, BaseInstance* inst) { QVector<ModPlatform::IndexedVersion> unsortedVersions; - bool hasFabric = !(dynamic_cast<MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty(); - QString mcVersion = (dynamic_cast<MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.minecraft"); + auto profile = (dynamic_cast<MinecraftInstance*>(inst))->getPackProfile(); + bool hasFabric = FlameAPI::getMappedModLoader(profile->getModLoader()) == ModAPI::Fabric; + QString mcVersion = profile->getComponentVersion("net.minecraft"); for (auto versionIter : arr) { auto obj = versionIter.toObject(); @@ -69,6 +71,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, for (auto m : modules) { auto fname = Json::requireString(m.toObject(), "foldername"); // FIXME: This does not work properly when a mod supports more than one mod loader, since + // FIXME: This also doesn't deal with Quilt mods at the moment // they bundle the meta files for all of them in the same arquive, even when that version // doesn't support the given mod loader. if (hasFabric) { diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 30952e99..84dd7d03 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -22,12 +22,12 @@ class ModrinthAPI : public NetworkModAPI { "limit=25&" "query=%2&" "index=%3&" - "facets=[[\"categories:%4\"],[\"versions:%5\"],[\"project_type:mod\"]]") + "facets=[[\"categories:%4\"],%5[\"project_type:mod\"]]") .arg(args.offset) .arg(args.search) .arg(args.sorting) .arg(getModLoaderString(args.mod_loader)) - .arg(args.version); + .arg(getGameVersionsArray(args.versions)); }; inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override @@ -40,34 +40,29 @@ class ModrinthAPI : public NetworkModAPI { .arg(getModLoaderString(args.loader)); }; - inline auto getGameVersionsString(QList<QString> mcVersions) const -> QString + auto getGameVersionsArray(std::list<Version> mcVersions) const -> QString { QString s; - for(int i = 0; i < mcVersions.count(); i++){ - s += mcVersions.at(i); - if(i < mcVersions.count() - 1) - s += ","; + for(auto& ver : mcVersions){ + s += QString("\"versions:%1\",").arg(ver.toString()); } - return s; + s.remove(s.length() - 1, 1); //remove last comma + return s.isEmpty() ? QString() : QString("[%1],").arg(s); } - inline auto getModLoaderString(ModLoaderType modLoader) const -> QString + static auto getModLoaderString(ModLoaderType type) -> const QString { - switch (modLoader) { - case Any: - return "fabric, forge"; - case Forge: - return "forge"; - case Fabric: - return "fabric"; - default: - return ""; - } + if (type == Unspecified) + return "fabric, forge, quilt"; + // TODO: remove this once Quilt drops official Fabric support + if (type == Quilt) // NOTE: Most if not all Fabric mods should work *currently* + return "fabric, quilt"; + return ModAPI::getModLoaderString(type); } inline auto validateModLoader(ModLoaderType modLoader) const -> bool { - return modLoader == Any || modLoader == Forge || modLoader == Fabric; + return modLoader == Unspecified || modLoader == Forge || modLoader == Fabric || modLoader == Quilt; } }; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 5b75f034..a3c2f166 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -12,7 +12,13 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.addonId = Json::requireString(obj, "project_id"); pack.name = Json::requireString(obj, "title"); - pack.websiteUrl = "https://modrinth.com/mod/" + Json::ensureString(obj, "slug", ""); + + QString slug = Json::ensureString(obj, "slug", ""); + if (!slug.isEmpty()) + pack.websiteUrl = "https://modrinth.com/mod/" + Json::ensureString(obj, "slug", ""); + else + pack.websiteUrl = ""; + pack.description = Json::ensureString(obj, "description", ""); pack.logoUrl = Json::requireString(obj, "icon_url"); diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp index b5c91582..89dbf4ca 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.cpp +++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org> * - * 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 <https://www.gnu.org/licenses/>. + * + * 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 "SolderPackInstallTask.h" @@ -19,16 +39,23 @@ #include <Json.h> #include <QtConcurrentRun> #include <MMCZip.h> + #include "TechnicPackProcessor.h" +#include "SolderPackManifest.h" +#include "net/ChecksumValidator.h" Technic::SolderPackInstallTask::SolderPackInstallTask( shared_qobject_ptr<QNetworkAccessManager> network, - const QUrl &sourceUrl, + const QUrl &solderUrl, + const QString &pack, + const QString &version, const QString &minecraftVersion ) { - m_sourceUrl = sourceUrl; - m_minecraftVersion = minecraftVersion; + m_solderUrl = solderUrl; + m_pack = pack; + m_version = version; m_network = network; + m_minecraftVersion = minecraftVersion; } bool Technic::SolderPackInstallTask::abort() { @@ -41,34 +68,12 @@ bool Technic::SolderPackInstallTask::abort() { void Technic::SolderPackInstallTask::executeTask() { - setStatus(tr("Finding recommended version:\n%1").arg(m_sourceUrl.toString())); - m_filesNetJob = new NetJob(tr("Finding recommended version"), m_network); - m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response)); - auto job = m_filesNetJob.get(); - connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::versionSucceeded); - connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed); - m_filesNetJob->start(); -} - -void Technic::SolderPackInstallTask::versionSucceeded() -{ - try - { - QJsonDocument doc = Json::requireDocument(m_response); - QJsonObject obj = Json::requireObject(doc); - QString version = Json::requireString(obj, "recommended", "__placeholder__"); - m_sourceUrl = m_sourceUrl.toString() + '/' + version; - } - catch (const JSONValidationError &e) - { - emitFailed(e.cause()); - m_filesNetJob.reset(); - return; - } + setStatus(tr("Resolving modpack files")); - setStatus(tr("Resolving modpack files:\n%1").arg(m_sourceUrl.toString())); m_filesNetJob = new NetJob(tr("Resolving modpack files"), m_network); - m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response)); + auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version); + m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, &m_response)); + auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded); connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed); @@ -77,38 +82,47 @@ void Technic::SolderPackInstallTask::versionSucceeded() void Technic::SolderPackInstallTask::fileListSucceeded() { - setStatus(tr("Downloading modpack:")); - QStringList modUrls; - try - { - QJsonDocument doc = Json::requireDocument(m_response); - QJsonObject obj = Json::requireObject(doc); - QString minecraftVersion = Json::ensureString(obj, "minecraft", QString(), "__placeholder__"); - if (!minecraftVersion.isEmpty()) - m_minecraftVersion = minecraftVersion; - QJsonArray mods = Json::requireArray(obj, "mods", "'mods'"); - for (auto mod: mods) - { - QJsonObject modObject = Json::requireObject(mod); - modUrls.append(Json::requireString(modObject, "url", "'url'")); - } + setStatus(tr("Downloading modpack")); + + QJsonParseError parse_error {}; + QJsonDocument doc = QJsonDocument::fromJson(m_response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString(); + qWarning() << m_response; + return; } - catch (const JSONValidationError &e) - { - emitFailed(e.cause()); + auto obj = doc.object(); + + TechnicSolder::PackBuild build; + try { + TechnicSolder::loadPackBuild(build, obj); + } + catch (const JSONValidationError& e) { + emitFailed(tr("Could not understand pack manifest:\n") + e.cause()); m_filesNetJob.reset(); return; } + + if (!build.minecraft.isEmpty()) + m_minecraftVersion = build.minecraft; + m_filesNetJob = new NetJob(tr("Downloading modpack"), m_network); + int i = 0; - for (auto &modUrl: modUrls) - { + for (const auto &mod : build.mods) { auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); - m_filesNetJob->addNetAction(Net::Download::makeFile(modUrl, path)); + + auto dl = Net::Download::makeFile(mod.url, path); + if (!mod.md5.isEmpty()) { + auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); + } + m_filesNetJob->addNetAction(dl); + i++; } - m_modCount = modUrls.size(); + m_modCount = build.mods.size(); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged); @@ -206,6 +220,5 @@ void Technic::SolderPackInstallTask::extractFinished() void Technic::SolderPackInstallTask::extractAborted() { emitFailed(tr("Instance import has been aborted.")); - return; } diff --git a/launcher/modplatform/technic/SolderPackInstallTask.h b/launcher/modplatform/technic/SolderPackInstallTask.h index 9b2058d8..117a7bd6 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.h +++ b/launcher/modplatform/technic/SolderPackInstallTask.h @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org> * - * 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 <https://www.gnu.org/licenses/>. + * + * 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 @@ -27,7 +47,7 @@ namespace Technic { Q_OBJECT public: - explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &sourceUrl, const QString &minecraftVersion); + explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &solderUrl, const QString& pack, const QString& version, const QString &minecraftVersion); bool canAbort() const override { return true; } bool abort() override; @@ -37,7 +57,6 @@ namespace Technic virtual void executeTask() override; private slots: - void versionSucceeded(); void fileListSucceeded(); void downloadSucceeded(); void downloadFailed(QString reason); @@ -51,7 +70,9 @@ namespace Technic shared_qobject_ptr<QNetworkAccessManager> m_network; NetJob::Ptr m_filesNetJob; - QUrl m_sourceUrl; + QUrl m_solderUrl; + QString m_pack; + QString m_version; QString m_minecraftVersion; QByteArray m_response; QTemporaryDir m_outputDir; diff --git a/launcher/modplatform/technic/SolderPackManifest.cpp b/launcher/modplatform/technic/SolderPackManifest.cpp new file mode 100644 index 00000000..16fe0b0e --- /dev/null +++ b/launcher/modplatform/technic/SolderPackManifest.cpp @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "SolderPackManifest.h" + +#include "Json.h" + +namespace TechnicSolder { + +void loadPack(Pack& v, QJsonObject& obj) +{ + v.recommended = Json::requireString(obj, "recommended"); + v.latest = Json::requireString(obj, "latest"); + + auto builds = Json::requireArray(obj, "builds"); + for (const auto buildRaw : builds) { + auto build = Json::requireString(buildRaw); + v.builds.append(build); + } +} + +static void loadPackBuildMod(PackBuildMod& b, QJsonObject& obj) +{ + b.name = Json::requireString(obj, "name"); + b.version = Json::requireString(obj, "version"); + b.md5 = Json::requireString(obj, "md5"); + b.url = Json::requireString(obj, "url"); +} + +void loadPackBuild(PackBuild& v, QJsonObject& obj) +{ + v.minecraft = Json::requireString(obj, "minecraft"); + + auto mods = Json::requireArray(obj, "mods"); + for (const auto modRaw : mods) { + auto modObj = Json::requireObject(modRaw); + PackBuildMod mod; + loadPackBuildMod(mod, modObj); + v.mods.append(mod); + } +} + +} diff --git a/launcher/modplatform/technic/SolderPackManifest.h b/launcher/modplatform/technic/SolderPackManifest.h new file mode 100644 index 00000000..09f18df0 --- /dev/null +++ b/launcher/modplatform/technic/SolderPackManifest.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <QString> +#include <QVector> +#include <QJsonObject> + +namespace TechnicSolder { + +struct Pack { + QString recommended; + QString latest; + QVector<QString> builds; +}; + +void loadPack(Pack& v, QJsonObject& obj); + +struct PackBuildMod { + QString name; + QString version; + QString md5; + QString url; +}; + +struct PackBuild { + QString minecraft; + QVector<PackBuildMod> mods; +}; + +void loadPackBuild(PackBuild& v, QJsonObject& obj); + +} |