aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buildconfig/BuildConfig.h3
-rw-r--r--launcher/Application.cpp1
-rw-r--r--launcher/CMakeLists.txt16
-rw-r--r--launcher/InstanceImportTask.cpp249
-rw-r--r--launcher/InstanceImportTask.h51
-rw-r--r--launcher/MMCZip.cpp28
-rw-r--r--launcher/modplatform/modrinth/ModrinthAPI.h40
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackIndex.cpp17
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackIndex.h17
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackManifest.cpp156
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackManifest.h107
-rw-r--r--launcher/resources/multimc/128x128/instances/modrinth.pngbin10575 -> 0 bytes
-rw-r--r--launcher/resources/multimc/32x32/instances/modrinth.pngbin1913 -> 0 bytes
-rw-r--r--launcher/resources/multimc/multimc.qrc6
-rw-r--r--launcher/resources/multimc/scalable/instances/modrinth.svg4
-rw-r--r--launcher/ui/dialogs/ModDownloadDialog.cpp4
-rw-r--r--launcher/ui/dialogs/ModDownloadDialog.h4
-rw-r--r--launcher/ui/dialogs/NewInstanceDialog.cpp2
-rw-r--r--launcher/ui/pages/modplatform/ImportPage.cpp8
-rw-r--r--launcher/ui/pages/modplatform/ImportPage.ui78
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModModel.cpp43
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModModel.h43
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp82
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h61
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp291
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModel.h123
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp241
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthPage.h60
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui112
29 files changed, 1699 insertions, 148 deletions
diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h
index a920a3d4..8594e46d 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/Application.cpp b/launcher/Application.cpp
index 11109857..afb33a50 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/CMakeLists.txt b/launcher/CMakeLists.txt
index b79f03c8..8e75be20 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
@@ -774,6 +776,11 @@ 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/modrinth/ModrinthModel.cpp
+ ui/pages/modplatform/modrinth/ModrinthModel.h
+
ui/pages/modplatform/technic/TechnicModel.cpp
ui/pages/modplatform/technic/TechnicModel.h
ui/pages/modplatform/technic/TechnicPage.cpp
@@ -782,10 +789,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
@@ -908,6 +915,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 1a13c997..8f68b95f 100644
--- a/launcher/InstanceImportTask.cpp
+++ b/launcher/InstanceImportTask.cpp
@@ -1,43 +1,72 @@
-/* Copyright 2013-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
*
- * 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 "InstanceImportTask.h"
+#include <QtConcurrentRun>
+#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 <QtConcurrentRun>
+#include "settings/INISettingsObject.h"
// FIXME: this does not belong here, it's Minecraft/Flame specific
+#include <quazip/quazipdir.h>
+#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 <quazip/quazipdir.h>
+#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"
-InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
+#include "ui/dialogs/CustomMessageBox.h"
+
+#include <algorithm>
+#include <iterator>
+
+InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
{
m_sourceUrl = sourceUrl;
+ m_parent = parent;
}
bool InstanceImportTask::abort()
@@ -109,6 +138,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 +162,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 +225,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;
@@ -439,25 +479,174 @@ 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()
+{
+ std::vector<Modrinth::File> files;
+ QString minecraftVersion, fabricVersion, quiltVersion, 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<QJsonObject>(obj, "files", "modrinth.index.json");
+ 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::validateDownloadUrl(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) {
+ QString name = it.key();
+ if (name == "minecraft") {
+ minecraftVersion = Json::requireString(*it, "Minecraft version");
+ }
+ else if (name == "fabric-loader") {
+ fabricVersion = Json::requireString(*it, "Fabric Loader version");
+ }
+ else if (name == "quilt-loader") {
+ quiltVersion = Json::requireString(*it, "Quilt Loader version");
+ }
+ else if (name == "forge") {
+ 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<INISettingsObject>(configPath);
+ 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 (!quiltVersion.isEmpty())
+ components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion, true);
+ if (!forgeVersion.isEmpty())
+ components->setComponentVersion("net.minecraftforge", forgeVersion, true);
+ if (m_instIcon != "default")
+ {
+ instance.setIconKey(m_instIcon);
+ }
+ else
+ {
+ instance.setIconKey("modrinth");
+ }
+ 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 365c3dc4..5e4d3235 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 <contact@scrumplex.net>
*
- * 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
@@ -35,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;
@@ -47,8 +67,9 @@ protected:
private:
void processZipPack();
void processMultiMC();
- void processFlame();
void processTechnic();
+ void processFlame();
+ void processModrinth();
private slots:
void downloadSucceeded();
@@ -69,7 +90,11 @@ private: /* data */
enum class ModpackType{
Unknown,
MultiMC,
+ Technic,
Flame,
- Technic
+ Modrinth,
} m_modpackType = ModpackType::Unknown;
+
+ //FIXME: nuke
+ QWidget* m_parent;
};
diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp
index b92f1781..8591fcc0 100644
--- a/launcher/MMCZip.cpp
+++ b/launcher/MMCZip.cpp
@@ -297,20 +297,40 @@ nonstd::optional<QStringList> 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;
}
diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h
index 86852c94..6d642b5e 100644
--- a/launcher/modplatform/modrinth/ModrinthAPI.h
+++ b/launcher/modplatform/modrinth/ModrinthAPI.h
@@ -1,5 +1,24 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ *
+ * 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 "BuildConfig.h"
#include "modplatform/ModAPI.h"
#include "modplatform/helpers/NetworkModAPI.h"
@@ -47,13 +66,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 +82,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/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
index a3c2f166..f7fa9864 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 <https://www.gnu.org/licenses/>.
+ */
+
#include "ModrinthPackIndex.h"
#include "ModrinthAPI.h"
diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.h b/launcher/modplatform/modrinth/ModrinthPackIndex.h
index fd17847a..7f306f25 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 <https://www.gnu.org/licenses/>.
+ */
+
#pragma once
#include "modplatform/ModIndex.h"
diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp
new file mode 100644
index 00000000..f1ad39ce
--- /dev/null
+++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
+ *
+ * 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/>.
+ *
+ * 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"
+#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)
+{
+ pack.id = Json::ensureString(obj, "project_id");
+
+ pack.name = Json::ensureString(obj, "title");
+ pack.description = Json::ensureString(obj, "description");
+ 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");
+}
+
+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");
+
+ pack.extraInfoLoaded = true;
+}
+
+void loadIndexedVersions(Modpack& pack, QJsonDocument& doc)
+{
+ QVector<ModpackVersion> 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