aboutsummaryrefslogtreecommitdiff
path: root/launcher/modplatform
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/modplatform')
-rw-r--r--launcher/modplatform/ModAPI.h17
-rw-r--r--launcher/modplatform/atlauncher/ATLPackInstallTask.cpp184
-rw-r--r--launcher/modplatform/atlauncher/ATLPackInstallTask.h47
-rw-r--r--launcher/modplatform/atlauncher/ATLPackManifest.cpp78
-rw-r--r--launcher/modplatform/atlauncher/ATLPackManifest.h70
-rw-r--r--launcher/modplatform/flame/FileResolvingTask.cpp138
-rw-r--r--launcher/modplatform/flame/FileResolvingTask.h8
-rw-r--r--launcher/modplatform/flame/FlameAPI.h17
-rw-r--r--launcher/modplatform/flame/FlameModIndex.cpp9
-rw-r--r--launcher/modplatform/flame/FlamePackIndex.cpp12
-rw-r--r--launcher/modplatform/flame/FlamePackIndex.h1
-rw-r--r--launcher/modplatform/flame/PackManifest.cpp32
-rw-r--r--launcher/modplatform/flame/PackManifest.h10
-rw-r--r--launcher/modplatform/modrinth/ModrinthAPI.h41
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackManifest.cpp22
15 files changed, 491 insertions, 195 deletions
diff --git a/launcher/modplatform/ModAPI.h b/launcher/modplatform/ModAPI.h
index 8e6cd45c..eb0de3f0 100644
--- a/launcher/modplatform/ModAPI.h
+++ b/launcher/modplatform/ModAPI.h
@@ -16,14 +16,21 @@ class ModAPI {
public:
virtual ~ModAPI() = default;
- // https://docs.curseforge.com/?http#tocS_ModLoaderType
- enum ModLoaderType { Unspecified = 0, Forge = 1, Cauldron = 2, LiteLoader = 3, Fabric = 4, Quilt = 5 };
+ enum ModLoaderType {
+ Unspecified = 0,
+ Forge = 1 << 0,
+ Cauldron = 1 << 1,
+ LiteLoader = 1 << 2,
+ Fabric = 1 << 3,
+ Quilt = 1 << 4
+ };
+ Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType)
struct SearchArgs {
int offset;
QString search;
QString sorting;
- ModLoaderType mod_loader;
+ ModLoaderTypes loaders;
std::list<Version> versions;
};
@@ -33,7 +40,7 @@ class ModAPI {
struct VersionSearchArgs {
QString addonId;
std::list<Version> mcVersions;
- ModLoaderType loader;
+ ModLoaderTypes loaders;
};
virtual void getVersions(CallerType* caller, VersionSearchArgs&& args) const = 0;
@@ -61,7 +68,7 @@ class ModAPI {
{
QString s;
for(auto& ver : mcVersions){
- s += QString("%1,").arg(ver.toString());
+ s += QString("\"%1\",").arg(ver.toString());
}
s.remove(s.length() - 1, 1); //remove last comma
return s;
diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
index 9dcb3504..62c7bf6d 100644
--- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
+++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
@@ -1,18 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-only
/*
- * Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
- * Copyright 2021 Petr Mrazek <peterix@gmail.com>
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 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 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright 2021 Petr Mrazek <peterix@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
+ *
+ * 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 "ATLPackInstallTask.h"
@@ -39,6 +58,8 @@
namespace ATLauncher {
+static Meta::VersionPtr getComponentVersion(const QString& uid, const QString& version);
+
PackInstallTask::PackInstallTask(UserInteractionSupport *support, QString pack, QString version)
{
m_support = support;
@@ -74,14 +95,13 @@ void PackInstallTask::onDownloadSucceeded()
qDebug() << "PackInstallTask::onDownloadSucceeded: " << QThread::currentThreadId();
jobPtr.reset();
- QJsonParseError parse_error;
+ QJsonParseError parse_error {};
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
if(parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response from FTB at " << parse_error.offset << " reason: " << parse_error.errorString();
qWarning() << response;
return;
}
-
auto obj = doc.object();
ATLauncher::PackVersion version;
@@ -96,19 +116,15 @@ void PackInstallTask::onDownloadSucceeded()
}
m_version = version;
- auto vlist = APPLICATION->metadataIndex()->get("net.minecraft");
- if(!vlist)
- {
- emitFailed(tr("Failed to get local metadata index for %1").arg("net.minecraft"));
- return;
- }
+ // Display install message if one exists
+ if (!m_version.messages.install.isEmpty())
+ m_support->displayMessage(m_version.messages.install);
- auto ver = vlist->getVersion(m_version.minecraft);
+ auto ver = getComponentVersion("net.minecraft", m_version.minecraft);
if (!ver) {
- emitFailed(tr("Failed to get local metadata index for '%1' v%2").arg("net.minecraft").arg(m_version.minecraft));
+ emitFailed(tr("Failed to get local metadata index for '%1' v%2").arg("net.minecraft", m_version.minecraft));
return;
}
- ver->load(Net::Mode::Online);
minecraftVersion = ver;
if(m_version.noConfigs) {
@@ -305,7 +321,48 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared
auto f = std::make_shared<VersionFile>();
f->name = m_pack + " " + m_version_name + " (libraries)";
+ const static QMap<QString, QString> liteLoaderMap = {
+ { "61179803bcd5fb7790789b790908663d", "1.12-SNAPSHOT" },
+ { "1420785ecbfed5aff4a586c5c9dd97eb", "1.12.2-SNAPSHOT" },
+ { "073f68e2fcb518b91fd0d99462441714", "1.6.2_03" },
+ { "10a15b52fc59b1bfb9c05b56de1097d6", "1.6.2_02" },
+ { "b52f90f08303edd3d4c374e268a5acf1", "1.6.2_04" },
+ { "ea747e24e03e24b7cad5bc8a246e0319", "1.6.2_01" },
+ { "55785ccc82c07ff0ba038fe24be63ea2", "1.7.10_01" },
+ { "63ada46e033d0cb6782bada09ad5ca4e", "1.7.10_04" },
+ { "7983e4b28217c9ae8569074388409c86", "1.7.10_03" },
+ { "c09882458d74fe0697c7681b8993097e", "1.7.10_02" },
+ { "db7235aefd407ac1fde09a7baba50839", "1.7.10_00" },
+ { "6e9028816027f53957bd8fcdfabae064", "1.8" },
+ { "5e732dc446f9fe2abe5f9decaec40cde", "1.10-SNAPSHOT" },
+ { "3a98b5ed95810bf164e71c1a53be568d", "1.11.2-SNAPSHOT" },
+ { "ba8e6285966d7d988a96496f48cbddaa", "1.8.9-SNAPSHOT" },
+ { "8524af3ac3325a82444cc75ae6e9112f", "1.11-SNAPSHOT" },
+ { "53639d52340479ccf206a04f5e16606f", "1.5.2_01" },
+ { "1fcdcf66ce0a0806b7ad8686afdce3f7", "1.6.4_00" },
+ { "531c116f71ae2b11033f9a11a0f8e668", "1.6.4_01" },
+ { "4009eeb99c9068f608d3483a6439af88", "1.7.2_03" },
+ { "66f343354b8417abce1a10d557d2c6e9", "1.7.2_04" },
+ { "ab554c21f28fbc4ae9b098bcb5f4cceb", "1.7.2_05" },
+ { "e1d76a05a3723920e2f80a5e66c45f16", "1.7.2_02" },
+ { "00318cb0c787934d523f63cdfe8ddde4", "1.9-SNAPSHOT" },
+ { "986fd1ee9525cb0dcab7609401cef754", "1.9.4-SNAPSHOT" },
+ { "571ad5e6edd5ff40259570c9be588bb5", "1.9.4" },
+ { "1cdd72f7232e45551f16cc8ffd27ccf3", "1.10.2-SNAPSHOT" },
+ { "8a7c21f32d77ee08b393dd3921ced8eb", "1.10.2" },
+ { "b9bef8abc8dc309069aeba6fbbe58980", "1.12.1-SNAPSHOT" }
+ };
+
for(const auto & lib : m_version.libraries) {
+ // If the library is LiteLoader, we need to ignore it and handle it separately.
+ if (liteLoaderMap.contains(lib.md5)) {
+ auto ver = getComponentVersion("com.mumfrey.liteloader", liteLoaderMap.value(lib.md5));
+ if (ver) {
+ componentsToInstall.insert("com.mumfrey.liteloader", ver);
+ continue;
+ }
+ }
+
auto libName = detectLibrary(lib);
GradleSpecifier libSpecifier(libName);
@@ -357,7 +414,31 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared
bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr<PackProfile> profile)
{
- if(m_version.mainClass == QString() && m_version.extraArguments == QString()) {
+ if (m_version.mainClass.mainClass.isEmpty() && m_version.extraArguments.arguments.isEmpty()) {
+ return true;
+ }
+
+ auto mainClass = m_version.mainClass.mainClass;
+ auto extraArguments = m_version.extraArguments.arguments;
+
+ auto hasMainClassDepends = !m_version.mainClass.depends.isEmpty();
+ auto hasExtraArgumentsDepends = !m_version.extraArguments.depends.isEmpty();
+ if (hasMainClassDepends || hasExtraArgumentsDepends) {
+ QSet<QString> mods;
+ for (const auto& item : m_version.mods) {
+ mods.insert(item.name);
+ }
+
+ if (hasMainClassDepends && !mods.contains(m_version.mainClass.depends)) {
+ mainClass = "";
+ }
+
+ if (hasExtraArgumentsDepends && !mods.contains(m_version.extraArguments.depends)) {
+ extraArguments = "";
+ }
+ }
+
+ if (mainClass.isEmpty() && extraArguments.isEmpty()) {
return true;
}
@@ -385,12 +466,12 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr<
auto f = std::make_shared<VersionFile>();
f->name = m_pack + " " + m_version_name;
- if(m_version.mainClass != QString() && !mainClasses.contains(m_version.mainClass)) {
- f->mainClass = m_version.mainClass;
+ if (!mainClass.isEmpty() && !mainClasses.contains(mainClass)) {
+ f->mainClass = mainClass;
}
// Parse out tweakers
- auto args = m_version.extraArguments.split(" ");
+ auto args = extraArguments.split(" ");
QString previous;
for(auto arg : args) {
if(arg.startsWith("--tweakClass=") || previous == "--tweakClass") {
@@ -502,7 +583,7 @@ void PackInstallTask::downloadMods()
QVector<QString> selectedMods;
if (!optionalMods.isEmpty()) {
setStatus(tr("Selecting optional mods..."));
- selectedMods = m_support->chooseOptionalMods(optionalMods);
+ selectedMods = m_support->chooseOptionalMods(m_version, optionalMods);
}
setStatus(tr("Downloading mods..."));
@@ -574,19 +655,12 @@ void PackInstallTask::downloadMods()
jobPtr->addNetAction(dl);
auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file);
- qDebug() << "Will download" << url << "to" << path;
- modsToCopy[entry->getFullPath()] = path;
if(mod.type == ModType::Forge) {
- auto vlist = APPLICATION->metadataIndex()->get("net.minecraftforge");
- if(vlist)
- {
- auto ver = vlist->getVersion(mod.version);
- if(ver) {
- ver->load(Net::Mode::Online);
- componentsToInstall.insert("net.minecraftforge", ver);
- continue;
- }
+ auto ver = getComponentVersion("net.minecraftforge", mod.version);
+ if (ver) {
+ componentsToInstall.insert("net.minecraftforge", ver);
+ continue;
}
qDebug() << "Jarmod: " + path;
@@ -597,6 +671,10 @@ void PackInstallTask::downloadMods()
qDebug() << "Jarmod: " + path;
jarmods.push_back(path);
}
+
+ // Download after Forge handling, to avoid downloading Forge twice.
+ qDebug() << "Will download" << url << "to" << path;
+ modsToCopy[entry->getFullPath()] = path;
}
}
@@ -703,6 +781,17 @@ bool PackInstallTask::extractMods(
for (auto iter = toCopy.begin(); iter != toCopy.end(); iter++) {
auto &from = iter.key();
auto &to = iter.value();
+
+ // If the file already exists, assume the mod is the correct copy - and remove
+ // the copy from the Configs.zip
+ QFileInfo fileInfo(to);
+ if (fileInfo.exists()) {
+ if (!QFile::remove(to)) {
+ qWarning() << "Failed to delete" << to;
+ return false;
+ }
+ }
+
FS::copy fileCopyOperation(from, to);
if(!fileCopyOperation()) {
qWarning() << "Failed to copy" << from << "to" << to;
@@ -779,4 +868,23 @@ void PackInstallTask::install()
emitSucceeded();
}
+static Meta::VersionPtr getComponentVersion(const QString& uid, const QString& version)
+{
+ auto vlist = APPLICATION->metadataIndex()->get(uid);
+ if (!vlist)
+ return {};
+
+ if (!vlist->isLoaded())
+ vlist->load(Net::Mode::Online);
+
+ auto ver = vlist->getVersion(version);
+ if (!ver)
+ return {};
+
+ if (!ver->isLoaded())
+ ver->load(Net::Mode::Online);
+
+ return ver;
+}
+
}
diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.h b/launcher/modplatform/atlauncher/ATLPackInstallTask.h
index 783ec19b..f0af4e3a 100644
--- a/launcher/modplatform/atlauncher/ATLPackInstallTask.h
+++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.h
@@ -1,18 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-only
/*
- * Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
- * Copyright 2021 Petr Mrazek <peterix@gmail.com>
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 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 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright 2021 Petr Mrazek <peterix@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
+ *
+ * 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
@@ -37,7 +56,7 @@ public:
/**
* Requests a user interaction to select which optional mods should be installed.
*/
- virtual QVector<QString> chooseOptionalMods(QVector<ATLauncher::VersionMod> mods) = 0;
+ virtual QVector<QString> chooseOptionalMods(PackVersion version, QVector<ATLauncher::VersionMod> mods) = 0;
/**
* Requests a user interaction to select a component version from a given version list
@@ -45,6 +64,10 @@ public:
*/
virtual QString chooseVersion(Meta::VersionListPtr vlist, QString minecraftVersion) = 0;
+ /**
+ * Requests a user interaction to display a message.
+ */
+ virtual void displayMessage(QString message) = 0;
};
class PackInstallTask : public InstanceTask
diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.cpp b/launcher/modplatform/atlauncher/ATLPackManifest.cpp
index 40be6d53..3af02a09 100644
--- a/launcher/modplatform/atlauncher/ATLPackManifest.cpp
+++ b/launcher/modplatform/atlauncher/ATLPackManifest.cpp
@@ -1,18 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-only
/*
- * Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
- * Copyright 2021 Petr Mrazek <peterix@gmail.com>
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 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 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright 2021 Petr Mrazek <peterix@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
+ *
+ * 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 "ATLPackManifest.h"
@@ -178,6 +197,8 @@ static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) {
p.depends.append(Json::requireString(depends));
}
}
+ p.colour = Json::ensureString(obj, QString("colour"), "");
+ p.warning = Json::ensureString(obj, QString("warning"), "");
p.client = Json::ensureBoolean(obj, QString("client"), false);
@@ -185,6 +206,24 @@ static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) {
p.effectively_hidden = p.hidden || p.library;
}
+static void loadVersionMessages(ATLauncher::VersionMessages& m, QJsonObject& obj)
+{
+ m.install = Json::ensureString(obj, "install", "");
+ m.update = Json::ensureString(obj, "update", "");
+}
+
+static void loadVersionMainClass(ATLauncher::PackVersionMainClass& m, QJsonObject& obj)
+{
+ m.mainClass = Json::ensureString(obj, "mainClass", "");
+ m.depends = Json::ensureString(obj, "depends", "");
+}
+
+static void loadVersionExtraArguments(ATLauncher::PackVersionExtraArguments& a, QJsonObject& obj)
+{
+ a.arguments = Json::ensureString(obj, "arguments", "");
+ a.depends = Json::ensureString(obj, "depends", "");
+}
+
void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj)
{
v.version = Json::requireString(obj, "version");
@@ -193,12 +232,12 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj)
if(obj.contains("mainClass")) {
auto main = Json::requireObject(obj, "mainClass");
- v.mainClass = Json::ensureString(main, "mainClass", "");
+ loadVersionMainClass(v.mainClass, main);
}
if(obj.contains("extraArguments")) {
auto arguments = Json::requireObject(obj, "extraArguments");
- v.extraArguments = Json::ensureString(arguments, "arguments", "");
+ loadVersionExtraArguments(v.extraArguments, arguments);
}
if(obj.contains("loader")) {
@@ -232,4 +271,17 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj)
auto configsObj = Json::requireObject(obj, "configs");
loadVersionConfigs(v.configs, configsObj);
}
+
+ auto colourObj = Json::ensureObject(obj, "colours");
+ for (const auto &key : colourObj.keys()) {
+ v.colours[key] = Json::requireString(colourObj.value(key), "colour");
+ }
+
+ auto warningsObj = Json::ensureObject(obj, "warnings");
+ for (const auto &key : warningsObj.keys()) {
+ v.warnings[key] = Json::requireString(warningsObj.value(key), "warning");
+ }
+
+ auto messages = Json::ensureObject(obj, "messages");
+ loadVersionMessages(v.messages, messages);
}
diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.h b/launcher/modplatform/atlauncher/ATLPackManifest.h
index 673f2f8b..43510c50 100644
--- a/launcher/modplatform/atlauncher/ATLPackManifest.h
+++ b/launcher/modplatform/atlauncher/ATLPackManifest.h
@@ -1,24 +1,44 @@
+// SPDX-License-Identifier: GPL-3.0-only
/*
- * Copyright 2020 Jamie Mansfield <jmansfield@cadixdev.org>
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 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 2020 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
+ *
+ * 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 <QJsonObject>
+#include <QMap>
#include <QString>
#include <QVector>
-#include <QJsonObject>
namespace ATLauncher
{
@@ -109,6 +129,8 @@ struct VersionMod
bool library;
QString group;
QVector<QString> depends;
+ QString colour;
+ QString warning;
bool client;
@@ -122,18 +144,40 @@ struct VersionConfigs
QString sha1;
};
+struct VersionMessages
+{
+ QString install;
+ QString update;
+};
+
+struct PackVersionMainClass
+{
+ QString mainClass;
+ QString depends;
+};
+
+struct PackVersionExtraArguments
+{
+ QString arguments;
+ QString depends;
+};
+
struct PackVersion
{
QString version;
QString minecraft;
bool noConfigs;
- QString mainClass;
- QString extraArguments;
+ PackVersionMainClass mainClass;
+ PackVersionExtraArguments extraArguments;
VersionLoader loader;
QVector<VersionLibrary> libraries;
QVector<VersionMod> mods;
VersionConfigs configs;
+
+ QMap<QString, QString> colours;
+ QMap<QString, QString> warnings;
+ VersionMessages messages;
};
void loadVersion(PackVersion & v, QJsonObject & obj);
diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp
index 0deb99c4..a790ab9c 100644
--- a/launcher/modplatform/flame/FileResolvingTask.cpp
+++ b/launcher/modplatform/flame/FileResolvingTask.cpp
@@ -1,7 +1,9 @@
#include "FileResolvingTask.h"
+
#include "Json.h"
+#include "net/Upload.h"
-Flame::FileResolvingTask::FileResolvingTask(shared_qobject_ptr<QNetworkAccessManager> network, Flame::Manifest& toProcess)
+Flame::FileResolvingTask::FileResolvingTask(const shared_qobject_ptr<QNetworkAccessManager>& network, Flame::Manifest& toProcess)
: m_network(network), m_toProcess(toProcess)
{}
@@ -10,54 +12,116 @@ void Flame::FileResolvingTask::executeTask()
setStatus(tr("Resolving mod IDs..."));
setProgress(0, m_toProcess.files.size());
m_dljob = new NetJob("Mod id resolver", m_network);
- results.resize(m_toProcess.files.size());
- int index = 0;
- for (auto& file : m_toProcess.files) {
- auto projectIdStr = QString::number(file.projectId);
- auto fileIdStr = QString::number(file.fileId);
- QString metaurl = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(projectIdStr, fileIdStr);
- auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results[index]);
- m_dljob->addNetAction(dl);
- index++;
- }
+ result.reset(new QByteArray());
+ //build json data to send
+ QJsonObject object;
+
+ object["fileIds"] = QJsonArray::fromVariantList(std::accumulate(m_toProcess.files.begin(), m_toProcess.files.end(), QVariantList(), [](QVariantList& l, const File& s) {
+ l.push_back(s.fileId);
+ return l;
+ }));
+ QByteArray data = Json::toText(object);
+ auto dl = Net::Upload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result.get(), data);
+ m_dljob->addNetAction(dl);
connect(m_dljob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::netJobFinished);
m_dljob->start();
}
void Flame::FileResolvingTask::netJobFinished()
{
- bool failed = false;
int index = 0;
- for (auto& bytes : results) {
- auto& out = m_toProcess.files[index];
+ // job to check modrinth for blocked projects
+ auto job = new NetJob("Modrinth check", m_network);
+ blockedProjects = QMap<File *,QByteArray *>();
+ auto doc = Json::requireDocument(*result);
+ auto array = Json::requireArray(doc.object()["data"]);
+ for (QJsonValueRef file : array) {
+ auto fileid = Json::requireInteger(Json::requireObject(file)["id"]);
+ auto& out = m_toProcess.files[fileid];
try {
- bool fail = (!out.parseFromBytes(bytes));
- if(fail){
- //failed :( probably disabled mod, try to add to the list
- auto doc = Json::requireDocument(bytes);
- if (!doc.isObject()) {
- throw JSONValidationError(QString("data is not an object? that's not supposed to happen"));
- }
- auto obj = Json::ensureObject(doc.object(), "data");
- //FIXME : HACK, MAY NOT WORK FOR LONG
- out.url = QUrl(QString("https://media.forgecdn.net/files/%1/%2/%3")
- .arg(QString::number(QString::number(out.fileId).leftRef(4).toInt())
- ,QString::number(QString::number(out.fileId).rightRef(3).toInt())
- ,QUrl::toPercentEncoding(out.fileName)), QUrl::TolerantMode);
- }
- failed &= fail;
+ out.parseFromObject(Json::requireObject(file));
} catch (const JSONValidationError& e) {
- qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
- qCritical() << e.cause();
- qCritical() << "JSON:";
- qCritical() << bytes;
- failed = true;
+ qDebug() << "Blocked mod on curseforge" << out.fileName;
+ auto hash = out.hash;
+ if(!hash.isEmpty()) {
+ auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash);
+ auto output = new QByteArray();
+ auto dl = Net::Download::makeByteArray(QUrl(url), output);
+ QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() {
+ out.resolved = true;
+ });
+
+ job->addNetAction(dl);
+ blockedProjects.insert(&out, output);
+ }
}
index++;
}
- if (!failed) {
- emitSucceeded();
+ connect(job, &NetJob::finished, this, &Flame::FileResolvingTask::modrinthCheckFinished);
+
+ job->start();
+}
+
+void Flame::FileResolvingTask::modrinthCheckFinished() {
+ qDebug() << "Finished with blocked mods : " << blockedProjects.size();
+
+ for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) {
+ auto &out = *it;
+ auto bytes = blockedProjects[out];
+ if (!out->resolved) {
+ delete bytes;
+ continue;
+ }
+ QJsonDocument doc = QJsonDocument::fromJson(*bytes);
+ auto obj = doc.object();
+ auto array = Json::requireArray(obj,"files");
+ for (auto file: array) {
+ auto fileObj = Json::requireObject(file);
+ auto primary = Json::requireBoolean(fileObj,"primary");
+ if (primary) {
+ out->url = Json::requireUrl(fileObj,"url");
+ qDebug() << "Found alternative on modrinth " << out->fileName;
+ break;
+ }
+ }
+ delete bytes;
+ }
+ //copy to an output list and filter out projects found on modrinth
+ auto block = new QList<File *>();
+ auto it = blockedProjects.keys();
+ std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File *f) {
+ return !f->resolved;
+ });
+ //Display not found mods early
+ if (!block->empty()) {
+ //blocked mods found, we need the slug for displaying.... we need another job :D !
+ auto slugJob = new NetJob("Slug Job", m_network);
+ auto slugs = QVector<QByteArray>(block->size());
+ auto index = 0;
+ for (auto fileInfo: *block) {
+ auto projectId = fileInfo->projectId;
+ slugs[index] = QByteArray();
+ auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
+ auto dl = Net::Download::makeByteArray(url, &slugs[index]);
+ slugJob->addNetAction(dl);
+ index++;
+ }
+ connect(slugJob, &NetJob::succeeded, this, [slugs, this, slugJob, block]() {
+ slugJob->deleteLater();
+ auto index = 0;
+ for (const auto &slugResult: slugs) {
+ auto json = QJsonDocument::fromJson(slugResult);
+ auto base = Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json),"data"),"links"),
+ "websiteUrl");
+ auto mod = block->at(index);
+ auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
+ mod->websiteUrl = link;
+ index++;
+ }
+ emitSucceeded();
+ });
+ slugJob->start();
} else {
- emitFailed(tr("Some mod ID resolving tasks failed."));
+ emitSucceeded();
}
}
diff --git a/launcher/modplatform/flame/FileResolvingTask.h b/launcher/modplatform/flame/FileResolvingTask.h
index 5e5adcd7..87981f0a 100644
--- a/launcher/modplatform/flame/FileResolvingTask.h
+++ b/launcher/modplatform/flame/FileResolvingTask.h
@@ -10,7 +10,7 @@ class FileResolvingTask : public Task
{
Q_OBJECT
public:
- explicit FileResolvingTask(shared_qobject_ptr<QNetworkAccessManager> network, Flame::Manifest &toProcess);
+ explicit FileResolvingTask(const shared_qobject_ptr<QNetworkAccessManager>& network, Flame::Manifest &toProcess);
virtual ~FileResolvingTask() {};
const Flame::Manifest &getResults() const
@@ -27,7 +27,11 @@ protected slots:
private: /* data */
shared_qobject_ptr<QNetworkAccessManager> m_network;
Flame::Manifest m_toProcess;
- QVector<QByteArray> results;
+ std::shared_ptr<QByteArray> result;
NetJob::Ptr m_dljob;
+
+ void modrinthCheckFinished();
+
+ QMap<File *, QByteArray *> blockedProjects;
};
}
diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h
index 61628e60..8bb33d47 100644
--- a/launcher/modplatform/flame/FlameAPI.h
+++ b/launcher/modplatform/flame/FlameAPI.h
@@ -37,14 +37,14 @@ class FlameAPI : public NetworkModAPI {
.arg(args.offset)
.arg(args.search)
.arg(getSortFieldInt(args.sorting))
- .arg(getMappedModLoader(args.mod_loader))
+ .arg(getMappedModLoader(args.loaders))
.arg(gameVersionStr);
};
inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override
{
QString gameVersionQuery = args.mcVersions.size() == 1 ? QString("gameVersion=%1&").arg(args.mcVersions.front().toString()) : "";
- QString modLoaderQuery = QString("modLoaderType=%1&").arg(getMappedModLoader(args.loader));
+ QString modLoaderQuery = QString("modLoaderType=%1&").arg(getMappedModLoader(args.loaders));
return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&%2%3")
.arg(args.addonId)
@@ -53,11 +53,16 @@ class FlameAPI : public NetworkModAPI {
};
public:
- static auto getMappedModLoader(const ModLoaderType type) -> const ModLoaderType
+ static auto getMappedModLoader(const ModLoaderTypes loaders) -> const int
{
+ // https://docs.curseforge.com/?http#tocS_ModLoaderType
+ if (loaders & Forge)
+ return 1;
+ if (loaders & Fabric)
+ return 4;
// 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;
+ if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently*
+ return 4; // Quilt would probably be 5
+ return 0;
}
};
diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp
index 9846b156..ba0824cf 100644
--- a/launcher/modplatform/flame/FlameModIndex.cpp
+++ b/launcher/modplatform/flame/FlameModIndex.cpp
@@ -56,15 +56,8 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
file.fileId = Json::requireInteger(obj, "id");
file.date = Json::requireString(obj, "fileDate");
file.version = Json::requireString(obj, "displayName");
+ file.downloadUrl = Json::requireString(obj, "downloadUrl");
file.fileName = Json::requireString(obj, "fileName");
- file.downloadUrl = Json::ensureString(obj, "downloadUrl", "");
- if(file.downloadUrl.isEmpty()){
- //FIXME : HACK, MAY NOT WORK FOR LONG
- file.downloadUrl = QString("https://media.forgecdn.net/files/%1/%2/%3")
- .arg(QString::number(QString::number(file.fileId.toInt()).leftRef(4).toInt())
- ,QString::number(QString::number(file.fileId.toInt()).rightRef(3).toInt())
- ,QUrl::toPercentEncoding(file.fileName));
- }
unsortedVersions.append(file);
}
diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp
index 6d48a3bf..bece7843 100644
--- a/launcher/modplatform/flame/FlamePackIndex.cpp
+++ b/launcher/modplatform/flame/FlamePackIndex.cpp
@@ -65,16 +65,12 @@ void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr)
// pick the latest version supported
file.mcVersion = versionArray[0].toString();
file.version = Json::requireString(version, "displayName");
- file.fileName = Json::requireString(version, "fileName");
file.downloadUrl = Json::ensureString(version, "downloadUrl");
- if(file.downloadUrl.isEmpty()){
- //FIXME : HACK, MAY NOT WORK FOR LONG
- file.downloadUrl = QString("https://media.forgecdn.net/files/%1/%2/%3")
- .arg(QString::number(QString::number(file.fileId).leftRef(4).toInt())
- ,QString::number(QString::number(file.fileId).rightRef(3).toInt())
- ,QUrl::toPercentEncoding(file.fileName));
+
+ // only add if we have a download URL (third party distribution is enabled)
+ if (!file.downloadUrl.isEmpty()) {
+ unsortedVersions.append(file);
}
- unsortedVersions.append(file);
}
auto orderSortPredicate = [](const IndexedVersion& a, const IndexedVersion& b) -> bool { return a.fileId > b.fileId; };
diff --git a/launcher/modplatform/flame/FlamePackIndex.h b/launcher/modplatform/flame/FlamePackIndex.h
index a8bb15be..7ffa29c3 100644
--- a/launcher/modplatform/flame/FlamePackIndex.h
+++ b/launcher/modplatform/flame/FlamePackIndex.h
@@ -18,7 +18,6 @@ struct IndexedVersion {
QString version;
QString mcVersion;
QString downloadUrl;
- QString fileName;
};
struct IndexedPack
diff --git a/launcher/modplatform/flame/PackManifest.cpp b/launcher/modplatform/flame/PackManifest.cpp
index 3217a756..12a4b990 100644
--- a/launcher/modplatform/flame/PackManifest.cpp
+++ b/launcher/modplatform/flame/PackManifest.cpp
@@ -41,7 +41,7 @@ static void loadManifestV1(Flame::Manifest& m, QJsonObject& manifest)
auto obj = Json::requireObject(item);
Flame::File file;
loadFileV1(file, obj);
- m.files.append(file);
+ m.files.insert(file.fileId,file);
}
m.overrides = Json::ensureString(manifest, "overrides", "overrides");
}
@@ -61,16 +61,9 @@ void Flame::loadManifest(Flame::Manifest& m, const QString& filepath)
loadManifestV1(m, obj);
}
-bool Flame::File::parseFromBytes(const QByteArray& bytes)
+bool Flame::File::parseFromObject(const QJsonObject& obj)
{
- auto doc = Json::requireDocument(bytes);
- if (!doc.isObject()) {
- throw JSONValidationError(QString("data is not an object? that's not supposed to happen"));
- }
- auto obj = Json::ensureObject(doc.object(), "data");
-
fileName = Json::requireString(obj, "fileName");
-
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
// It is also optional
type = File::Type::SingleFile;
@@ -82,17 +75,26 @@ bool Flame::File::parseFromBytes(const QByteArray& bytes)
// this is probably a mod, dunno what else could modpacks download
targetFolder = "mods";
}
- QString rawUrl = Json::ensureString(obj, "downloadUrl");
-
- if(rawUrl.isEmpty()){
- //either there somehow is an emtpy string as a link, or it's null either way it's invalid
- //soft failing
- return false;
+ // get the hash
+ hash = QString();
+ auto hashes = Json::ensureArray(obj, "hashes");
+ for(QJsonValueRef item : hashes) {
+ auto hobj = Json::requireObject(item);
+ auto algo = Json::requireInteger(hobj, "algo");
+ auto value = Json::requireString(hobj, "value");
+ if (algo == 1) {
+ hash = value;
+ }
}
+
+
+ // may throw, if the project is blocked
+ QString rawUrl = Json::ensureString(obj, "downloadUrl");
url = QUrl(rawUrl, QUrl::TolerantMode);
if (!url.isValid()) {
throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
}
+
resolved = true;
return true;
}
diff --git a/launcher/modplatform/flame/PackManifest.h b/launcher/modplatform/flame/PackManifest.h
index 02f39f0e..26a48d1c 100644
--- a/launcher/modplatform/flame/PackManifest.h
+++ b/launcher/modplatform/flame/PackManifest.h
@@ -2,19 +2,24 @@
#include <QString>
#include <QVector>
+#include <QMap>
#include <QUrl>
+#include <QJsonObject>
namespace Flame
{
struct File
{
// NOTE: throws JSONValidationError
- bool parseFromBytes(const QByteArray &bytes);
+ bool parseFromObject(const QJsonObject& object);
int projectId = 0;
int fileId = 0;
// NOTE: the opposite to 'optional'. This is at the time of writing unused.
bool required = true;
+ QString hash;
+ // NOTE: only set on blocked files ! Empty otherwise.
+ QString websiteUrl;
// our
bool resolved = false;
@@ -54,7 +59,8 @@ struct Manifest
QString name;
QString version;
QString author;
- QVector<Flame::File> files;
+ //File id -> File
+ QMap<int,Flame::File> files;
QString overrides;
};
diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h
index 6d642b5e..6119a4df 100644
--- a/launcher/modplatform/modrinth/ModrinthAPI.h
+++ b/launcher/modplatform/modrinth/ModrinthAPI.h
@@ -28,30 +28,25 @@ class ModrinthAPI : public NetworkModAPI {
public:
inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; };
- static auto getModLoaderStrings(ModLoaderType type) -> const QStringList
+ static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
{
QStringList l;
- switch (type)
+ for (auto loader : {Forge, Fabric, Quilt})
{
- case Unspecified:
- for (auto loader : {Forge, Fabric, Quilt})
- {
- l << ModAPI::getModLoaderString(loader);
- }
- break;
-
- case Quilt:
- l << ModAPI::getModLoaderString(Fabric);
- default:
- l << ModAPI::getModLoaderString(type);
+ if ((types & loader) || types == Unspecified)
+ {
+ l << ModAPI::getModLoaderString(loader);
+ }
}
+ if ((types & Quilt) && (~types & Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there
+ l << ModAPI::getModLoaderString(Fabric);
return l;
}
- static auto getModLoaderFilters(ModLoaderType type) -> const QString
+ static auto getModLoaderFilters(ModLoaderTypes types) -> const QString
{
QStringList l;
- for (auto loader : getModLoaderStrings(type))
+ for (auto loader : getModLoaderStrings(types))
{
l << QString("\"categories:%1\"").arg(loader);
}
@@ -61,7 +56,7 @@ class ModrinthAPI : public NetworkModAPI {
private:
inline auto getModSearchURL(SearchArgs& args) const -> QString override
{
- if (!validateModLoader(args.mod_loader)) {
+ if (!validateModLoaders(args.loaders)) {
qWarning() << "Modrinth only have Forge and Fabric-compatible mods!";
return "";
}
@@ -76,7 +71,7 @@ class ModrinthAPI : public NetworkModAPI {
.arg(args.offset)
.arg(args.search)
.arg(args.sorting)
- .arg(getModLoaderFilters(args.mod_loader))
+ .arg(getModLoaderFilters(args.loaders))
.arg(getGameVersionsArray(args.versions));
};
@@ -84,11 +79,11 @@ class ModrinthAPI : public NetworkModAPI {
{
return QString(BuildConfig.MODRINTH_PROD_URL +
"/project/%1/version?"
- "game_versions=[%2]"
+ "game_versions=[%2]&"
"loaders=[\"%3\"]")
- .arg(args.addonId)
- .arg(getGameVersionsString(args.mcVersions))
- .arg(getModLoaderStrings(args.loader).join("\",\""));
+ .arg(args.addonId,
+ getGameVersionsString(args.mcVersions),
+ getModLoaderStrings(args.loaders).join("\",\""));
};
auto getGameVersionsArray(std::list<Version> mcVersions) const -> QString
@@ -101,9 +96,9 @@ class ModrinthAPI : public NetworkModAPI {
return s.isEmpty() ? QString() : QString("[%1],").arg(s);
}
- inline auto validateModLoader(ModLoaderType modLoader) const -> bool
+ inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool
{
- return modLoader == Unspecified || modLoader == Forge || modLoader == Fabric || modLoader == Quilt;
+ return (loaders == Unspecified) || (loaders & (Forge | Fabric | Quilt));
}
};
diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp
index f1ad39ce..33116231 100644
--- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp
+++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp
@@ -42,6 +42,8 @@
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
+#include <QSet>
+
static ModrinthAPI api;
namespace Modrinth {
@@ -95,19 +97,15 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc)
auto validateDownloadUrl(QUrl url) -> bool
{
+ static QSet<QString> domainWhitelist{
+ "cdn.modrinth.com",
+ "github.com",
+ "raw.githubusercontent.com",
+ "gitlab.com"
+ };
+
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;
+ return domainWhitelist.contains(domain);
}
auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion