aboutsummaryrefslogtreecommitdiff
path: root/launcher/modplatform
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/modplatform')
-rw-r--r--launcher/modplatform/CheckUpdateTask.h14
-rw-r--r--launcher/modplatform/EnsureMetadataTask.cpp102
-rw-r--r--launcher/modplatform/EnsureMetadataTask.h18
-rw-r--r--launcher/modplatform/ModAPI.h118
-rw-r--r--launcher/modplatform/ModIndex.cpp24
-rw-r--r--launcher/modplatform/ModIndex.h40
-rw-r--r--launcher/modplatform/ResourceAPI.h177
-rw-r--r--launcher/modplatform/atlauncher/ATLPackInstallTask.cpp19
-rw-r--r--launcher/modplatform/flame/FileResolvingTask.cpp4
-rw-r--r--launcher/modplatform/flame/FlameAPI.cpp53
-rw-r--r--launcher/modplatform/flame/FlameAPI.h110
-rw-r--r--launcher/modplatform/flame/FlameCheckUpdate.cpp14
-rw-r--r--launcher/modplatform/flame/FlameCheckUpdate.h2
-rw-r--r--launcher/modplatform/flame/FlameInstanceCreationTask.cpp8
-rw-r--r--launcher/modplatform/flame/FlameInstanceCreationTask.h2
-rw-r--r--launcher/modplatform/flame/FlameModIndex.cpp8
-rw-r--r--launcher/modplatform/flame/FlameModIndex.h2
-rw-r--r--launcher/modplatform/helpers/HashUtils.cpp24
-rw-r--r--launcher/modplatform/helpers/HashUtils.h10
-rw-r--r--launcher/modplatform/helpers/NetworkModAPI.cpp97
-rw-r--r--launcher/modplatform/helpers/NetworkModAPI.h17
-rw-r--r--launcher/modplatform/helpers/NetworkResourceAPI.cpp128
-rw-r--r--launcher/modplatform/helpers/NetworkResourceAPI.h22
-rw-r--r--launcher/modplatform/legacy_ftb/PackFetchTask.cpp2
-rw-r--r--launcher/modplatform/legacy_ftb/PackInstallTask.cpp2
-rw-r--r--launcher/modplatform/modpacksch/FTBPackInstallTask.cpp22
-rw-r--r--launcher/modplatform/modrinth/ModrinthAPI.cpp91
-rw-r--r--launcher/modplatform/modrinth/ModrinthAPI.h134
-rw-r--r--launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp27
-rw-r--r--launcher/modplatform/modrinth/ModrinthCheckUpdate.h4
-rw-r--r--launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp21
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackIndex.cpp13
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackIndex.h2
-rw-r--r--launcher/modplatform/packwiz/Packwiz.cpp8
-rw-r--r--launcher/modplatform/packwiz/Packwiz.h2
-rw-r--r--launcher/modplatform/technic/SingleZipPackInstallTask.cpp4
-rw-r--r--launcher/modplatform/technic/SolderPackInstallTask.cpp6
37 files changed, 778 insertions, 573 deletions
diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h
index 91922034..f7582b8f 100644
--- a/launcher/modplatform/CheckUpdateTask.h
+++ b/launcher/modplatform/CheckUpdateTask.h
@@ -1,18 +1,18 @@
#pragma once
#include "minecraft/mod/Mod.h"
-#include "modplatform/ModAPI.h"
+#include "modplatform/ResourceAPI.h"
#include "modplatform/ModIndex.h"
#include "tasks/Task.h"
-class ModDownloadTask;
+class ResourceDownloadTask;
class ModFolderModel;
class CheckUpdateTask : public Task {
Q_OBJECT
public:
- CheckUpdateTask(QList<Mod*>& mods, std::list<Version>& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr<ModFolderModel> mods_folder)
+ CheckUpdateTask(QList<Mod*>& mods, std::list<Version>& mcVersions, std::optional<ResourceAPI::ModLoaderTypes> loaders, std::shared_ptr<ModFolderModel> mods_folder)
: Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder) {};
struct UpdatableMod {
@@ -21,11 +21,11 @@ class CheckUpdateTask : public Task {
QString old_version;
QString new_version;
QString changelog;
- ModPlatform::Provider provider;
- ModDownloadTask* download;
+ ModPlatform::ResourceProvider provider;
+ shared_qobject_ptr<ResourceDownloadTask> download;
public:
- UpdatableMod(QString name, QString old_h, QString old_v, QString new_v, QString changelog, ModPlatform::Provider p, ModDownloadTask* t)
+ UpdatableMod(QString name, QString old_h, QString old_v, QString new_v, QString changelog, ModPlatform::ResourceProvider p, shared_qobject_ptr<ResourceDownloadTask> t)
: name(name), old_hash(old_h), old_version(old_v), new_version(new_v), changelog(changelog), provider(p), download(t)
{}
};
@@ -44,7 +44,7 @@ class CheckUpdateTask : public Task {
protected:
QList<Mod*>& m_mods;
std::list<Version>& m_game_versions;
- ModAPI::ModLoaderTypes m_loaders;
+ std::optional<ResourceAPI::ModLoaderTypes> m_loaders;
std::shared_ptr<ModFolderModel> m_mods_folder;
std::vector<UpdatableMod> m_updatable;
diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp
index 234330a7..34d969f0 100644
--- a/launcher/modplatform/EnsureMetadataTask.cpp
+++ b/launcher/modplatform/EnsureMetadataTask.cpp
@@ -13,14 +13,12 @@
#include "modplatform/modrinth/ModrinthAPI.h"
#include "modplatform/modrinth/ModrinthPackIndex.h"
-#include "net/NetJob.h"
-
static ModPlatform::ProviderCapabilities ProviderCaps;
static ModrinthAPI modrinth_api;
static FlameAPI flame_api;
-EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Provider prov)
+EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::ResourceProvider prov)
: Task(nullptr), m_index_dir(dir), m_provider(prov), m_hashing_task(nullptr), m_current_task(nullptr)
{
auto hash_task = createNewHash(mod);
@@ -31,10 +29,10 @@ EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Provider
hash_task->start();
}
-EnsureMetadataTask::EnsureMetadataTask(QList<Mod*>& mods, QDir dir, ModPlatform::Provider prov)
+EnsureMetadataTask::EnsureMetadataTask(QList<Mod*>& mods, QDir dir, ModPlatform::ResourceProvider prov)
: Task(nullptr), m_index_dir(dir), m_provider(prov), m_current_task(nullptr)
{
- m_hashing_task = new ConcurrentTask(this, "MakeHashesTask", 10);
+ m_hashing_task.reset(new ConcurrentTask(this, "MakeHashesTask", 10));
for (auto* mod : mods) {
auto hash_task = createNewHash(mod);
if (!hash_task)
@@ -107,13 +105,13 @@ void EnsureMetadataTask::executeTask()
}
}
- NetJob::Ptr version_task;
+ Task::Ptr version_task;
switch (m_provider) {
- case (ModPlatform::Provider::MODRINTH):
+ case (ModPlatform::ResourceProvider::MODRINTH):
version_task = modrinthVersionsTask();
break;
- case (ModPlatform::Provider::FLAME):
+ case (ModPlatform::ResourceProvider::FLAME):
version_task = flameVersionsTask();
break;
}
@@ -127,13 +125,13 @@ void EnsureMetadataTask::executeTask()
};
connect(version_task.get(), &Task::finished, this, [this, invalidade_leftover] {
- NetJob::Ptr project_task;
+ Task::Ptr project_task;
switch (m_provider) {
- case (ModPlatform::Provider::MODRINTH):
+ case (ModPlatform::ResourceProvider::MODRINTH):
project_task = modrinthProjectsTask();
break;
- case (ModPlatform::Provider::FLAME):
+ case (ModPlatform::ResourceProvider::FLAME):
project_task = flameProjectsTask();
break;
}
@@ -149,7 +147,7 @@ void EnsureMetadataTask::executeTask()
m_current_task = nullptr;
});
- m_current_task = project_task.get();
+ m_current_task = project_task;
project_task->start();
});
@@ -164,7 +162,7 @@ void EnsureMetadataTask::executeTask()
setStatus(tr("Requesting metadata information from %1 for '%2'...")
.arg(ProviderCaps.readableName(m_provider), m_mods.begin().value()->name()));
- m_current_task = version_task.get();
+ m_current_task = version_task;
version_task->start();
}
@@ -210,18 +208,18 @@ void EnsureMetadataTask::emitFail(Mod* m, QString key, RemoveFromList remove)
// Modrinth
-NetJob::Ptr EnsureMetadataTask::modrinthVersionsTask()
+Task::Ptr EnsureMetadataTask::modrinthVersionsTask()
{
- auto hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first();
+ auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
auto* response = new QByteArray();
auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response);
// Prevents unfortunate timings when aborting the task
if (!ver_task)
- return {};
+ return Task::Ptr{nullptr};
- connect(ver_task.get(), &NetJob::succeeded, this, [this, response] {
+ connect(ver_task.get(), &Task::succeeded, this, [this, response] {
QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
@@ -260,14 +258,14 @@ NetJob::Ptr EnsureMetadataTask::modrinthVersionsTask()
return ver_task;
}
-NetJob::Ptr EnsureMetadataTask::modrinthProjectsTask()
+Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
{
QHash<QString, QString> addonIds;
for (auto const& data : m_temp_versions)
addonIds.insert(data.addonId.toString(), data.hash);
auto response = new QByteArray();
- NetJob::Ptr proj_task;
+ Task::Ptr proj_task;
if (addonIds.isEmpty()) {
qWarning() << "No addonId found!";
@@ -279,9 +277,9 @@ NetJob::Ptr EnsureMetadataTask::modrinthProjectsTask()
// Prevents unfortunate timings when aborting the task
if (!proj_task)
- return {};
+ return Task::Ptr{nullptr};
- connect(proj_task.get(), &NetJob::succeeded, this, [this, response, addonIds] {
+ connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] {
QJsonParseError parse_error{};
auto doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
@@ -291,43 +289,53 @@ NetJob::Ptr EnsureMetadataTask::modrinthProjectsTask()
return;
}
+ QJsonArray entries;
+
try {
- QJsonArray entries;
if (addonIds.size() == 1)
entries = { doc.object() };
else
entries = Json::requireArray(doc);
+ } catch (Json::JsonException& e) {
+ qDebug() << e.cause();
+ qDebug() << doc;
+ }
- for (auto entry : entries) {
+ for (auto entry : entries) {
+ ModPlatform::IndexedPack pack;
+
+ try {
auto entry_obj = Json::requireObject(entry);
- ModPlatform::IndexedPack pack;
Modrinth::loadIndexedPack(pack, entry_obj);
+ } catch (Json::JsonException& e) {
+ qDebug() << e.cause();
+ qDebug() << doc;
- auto hash = addonIds.find(pack.addonId.toString()).value();
+ // Skip this entry, since it has problems
+ continue;
+ }
- auto mod_iter = m_mods.find(hash);
- if (mod_iter == m_mods.end()) {
- qWarning() << "Invalid project id from the API response.";
- continue;
- }
+ auto hash = addonIds.find(pack.addonId.toString()).value();
- auto* mod = mod_iter.value();
+ auto mod_iter = m_mods.find(hash);
+ if (mod_iter == m_mods.end()) {
+ qWarning() << "Invalid project id from the API response.";
+ continue;
+ }
- try {
- setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod->name()));
+ auto* mod = mod_iter.value();
- modrinthCallback(pack, m_temp_versions.find(hash).value(), mod);
- } catch (Json::JsonException& e) {
- qDebug() << e.cause();
- qDebug() << entries;
+ try {
+ setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod->name()));
- emitFail(mod);
- }
+ modrinthCallback(pack, m_temp_versions.find(hash).value(), mod);
+ } catch (Json::JsonException& e) {
+ qDebug() << e.cause();
+ qDebug() << entries;
+
+ emitFail(mod);
}
- } catch (Json::JsonException& e) {
- qDebug() << e.cause();
- qDebug() << doc;
}
});
@@ -335,7 +343,7 @@ NetJob::Ptr EnsureMetadataTask::modrinthProjectsTask()
}
// Flame
-NetJob::Ptr EnsureMetadataTask::flameVersionsTask()
+Task::Ptr EnsureMetadataTask::flameVersionsTask()
{
auto* response = new QByteArray();
@@ -400,7 +408,7 @@ NetJob::Ptr EnsureMetadataTask::flameVersionsTask()
return ver_task;
}
-NetJob::Ptr EnsureMetadataTask::flameProjectsTask()
+Task::Ptr EnsureMetadataTask::flameProjectsTask()
{
QHash<QString, QString> addonIds;
for (auto const& hash : m_mods.keys()) {
@@ -414,7 +422,7 @@ NetJob::Ptr EnsureMetadataTask::flameProjectsTask()
}
auto response = new QByteArray();
- NetJob::Ptr proj_task;
+ Task::Ptr proj_task;
if (addonIds.isEmpty()) {
qWarning() << "No addonId found!";
@@ -426,9 +434,9 @@ NetJob::Ptr EnsureMetadataTask::flameProjectsTask()
// Prevents unfortunate timings when aborting the task
if (!proj_task)
- return {};
+ return Task::Ptr{nullptr};
- connect(proj_task.get(), &NetJob::succeeded, this, [this, response, addonIds] {
+ connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] {
QJsonParseError parse_error{};
auto doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h
index a8b0851e..03cae4e4 100644
--- a/launcher/modplatform/EnsureMetadataTask.h
+++ b/launcher/modplatform/EnsureMetadataTask.h
@@ -14,8 +14,8 @@ class EnsureMetadataTask : public Task {
Q_OBJECT
public:
- EnsureMetadataTask(Mod*, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH);
- EnsureMetadataTask(QList<Mod*>&, QDir, ModPlatform::Provider = ModPlatform::Provider::MODRINTH);
+ EnsureMetadataTask(Mod*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
+ EnsureMetadataTask(QList<Mod*>&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
~EnsureMetadataTask() = default;
@@ -28,11 +28,11 @@ class EnsureMetadataTask : public Task {
private:
// FIXME: Move to their own namespace
- auto modrinthVersionsTask() -> NetJob::Ptr;
- auto modrinthProjectsTask() -> NetJob::Ptr;
+ auto modrinthVersionsTask() -> Task::Ptr;
+ auto modrinthProjectsTask() -> Task::Ptr;
- auto flameVersionsTask() -> NetJob::Ptr;
- auto flameProjectsTask() -> NetJob::Ptr;
+ auto flameVersionsTask() -> Task::Ptr;
+ auto flameProjectsTask() -> Task::Ptr;
// Helpers
enum class RemoveFromList {
@@ -57,9 +57,9 @@ class EnsureMetadataTask : public Task {
private:
QHash<QString, Mod*> m_mods;
QDir m_index_dir;
- ModPlatform::Provider m_provider;
+ ModPlatform::ResourceProvider m_provider;
QHash<QString, ModPlatform::IndexedVersion> m_temp_versions;
- ConcurrentTask* m_hashing_task;
- NetJob* m_current_task;
+ ConcurrentTask::Ptr m_hashing_task;
+ Task::Ptr m_current_task;
};
diff --git a/launcher/modplatform/ModAPI.h b/launcher/modplatform/ModAPI.h
deleted file mode 100644
index 703de143..00000000
--- a/launcher/modplatform/ModAPI.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - Minecraft Launcher
- * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
- *
- * 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
- *
- * 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 <QString>
-#include <QList>
-#include <list>
-
-#include "../Version.h"
-#include "net/NetJob.h"
-
-namespace ModPlatform {
-class ListModel;
-struct IndexedPack;
-}
-
-class ModAPI {
- protected:
- using CallerType = ModPlatform::ListModel;
-
- public:
- virtual ~ModAPI() = default;
-
- 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;
- ModLoaderTypes loaders;
- std::list<Version> versions;
- };
-
- virtual void searchMods(CallerType* caller, SearchArgs&& args) const = 0;
- virtual void getModInfo(ModPlatform::IndexedPack& pack, std::function<void(QJsonDocument&, ModPlatform::IndexedPack&)> callback) = 0;
-
- virtual auto getProject(QString addonId, QByteArray* response) const -> NetJob* = 0;
- virtual auto getProjects(QStringList addonIds, QByteArray* response) const -> NetJob* = 0;
-
-
- struct VersionSearchArgs {
- QString addonId;
- std::list<Version> mcVersions;
- ModLoaderTypes loaders;
- };
-
- virtual void getVersions(VersionSearchArgs&& args, std::function<void(QJsonDocument&, QString)> callback) 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/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp
index 34fd9f30..6a507caf 100644
--- a/launcher/modplatform/ModIndex.cpp
+++ b/launcher/modplatform/ModIndex.cpp
@@ -24,47 +24,47 @@
namespace ModPlatform {
-auto ProviderCapabilities::name(Provider p) -> const char*
+auto ProviderCapabilities::name(ResourceProvider p) -> const char*
{
switch (p) {
- case Provider::MODRINTH:
+ case ResourceProvider::MODRINTH:
return "modrinth";
- case Provider::FLAME:
+ case ResourceProvider::FLAME:
return "curseforge";
}
return {};
}
-auto ProviderCapabilities::readableName(Provider p) -> QString
+auto ProviderCapabilities::readableName(ResourceProvider p) -> QString
{
switch (p) {
- case Provider::MODRINTH:
+ case ResourceProvider::MODRINTH:
return "Modrinth";
- case Provider::FLAME:
+ case ResourceProvider::FLAME:
return "CurseForge";
}
return {};
}
-auto ProviderCapabilities::hashType(Provider p) -> QStringList
+auto ProviderCapabilities::hashType(ResourceProvider p) -> QStringList
{
switch (p) {
- case Provider::MODRINTH:
+ case ResourceProvider::MODRINTH:
return { "sha512", "sha1" };
- case Provider::FLAME:
+ case ResourceProvider::FLAME:
// Try newer formats first, fall back to old format
return { "sha1", "md5", "murmur2" };
}
return {};
}
-auto ProviderCapabilities::hash(Provider p, QIODevice* device, QString type) -> QString
+auto ProviderCapabilities::hash(ResourceProvider p, QIODevice* device, QString type) -> QString
{
QCryptographicHash::Algorithm algo = QCryptographicHash::Sha1;
switch (p) {
- case Provider::MODRINTH: {
+ case ResourceProvider::MODRINTH: {
algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Sha512;
break;
}
- case Provider::FLAME:
+ case ResourceProvider::FLAME:
algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Md5;
break;
}
diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h
index 518fed7c..b1f8050d 100644
--- a/launcher/modplatform/ModIndex.h
+++ b/launcher/modplatform/ModIndex.h
@@ -28,17 +28,16 @@ class QIODevice;
namespace ModPlatform {
-enum class Provider {
- MODRINTH,
- FLAME
-};
+enum class ResourceProvider { MODRINTH, FLAME };
+
+enum class ResourceType { MOD, RESOURCE_PACK };
class ProviderCapabilities {
public:
- auto name(Provider) -> const char*;
- auto readableName(Provider) -> QString;
- auto hashType(Provider) -> QStringList;
- auto hash(Provider, QIODevice*, QString type = "") -> QString;
+ auto name(ResourceProvider) -> const char*;
+ auto readableName(ResourceProvider) -> QString;
+ auto hashType(ResourceProvider) -> QStringList;
+ auto hash(ResourceProvider, QIODevice*, QString type = "") -> QString;
};
struct ModpackAuthor {
@@ -66,6 +65,10 @@ struct IndexedVersion {
QString hash;
bool is_preferred = true;
QString changelog;
+
+ // For internal use, not provided by APIs
+ bool is_currently_selected = false;
+ QString custom_target_folder;
};
struct ExtraPackData {
@@ -81,7 +84,7 @@ struct ExtraPackData {
struct IndexedPack {
QVariant addonId;
- Provider provider;
+ ResourceProvider provider;
QString name;
QString slug;
QString description;
@@ -96,9 +99,26 @@ struct IndexedPack {
// Don't load by default, since some modplatform don't have that info
bool extraDataLoaded = true;
ExtraPackData extraData;
+
+ // For internal use, not provided by APIs
+ [[nodiscard]] bool isVersionSelected(size_t index) const
+ {
+ if (!versionsLoaded)
+ return false;
+
+ return versions.at(index).is_currently_selected;
+ }
+ [[nodiscard]] bool isAnyVersionSelected() const
+ {
+ if (!versionsLoaded)
+ return false;
+
+ return std::any_of(versions.constBegin(), versions.constEnd(),
+ [](auto const& v) { return v.is_currently_selected; });
+ }
};
} // namespace ModPlatform
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
-Q_DECLARE_METATYPE(ModPlatform::Provider)
+Q_DECLARE_METATYPE(ModPlatform::ResourceProvider)
diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h
new file mode 100644
index 00000000..34f33779
--- /dev/null
+++ b/launcher/modplatform/ResourceAPI.h
@@ -0,0 +1,177 @@
+// SPDX-FileCopyrightText: 2023 flowln <flowlnlnln@gmail.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ *
+ * 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
+ *
+ * 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 <QDebug>
+#include <QList>
+#include <QString>
+
+#include <list>
+#include <optional>
+
+#include "../Version.h"
+
+#include "modplatform/ModIndex.h"
+#include "tasks/Task.h"
+
+/* Simple class with a common interface for interacting with APIs */
+class ResourceAPI {
+ public:
+ virtual ~ResourceAPI() = default;
+
+ enum ModLoaderType { Forge = 1 << 0, Cauldron = 1 << 1, LiteLoader = 1 << 2, Fabric = 1 << 3, Quilt = 1 << 4 };
+ Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType)
+
+ struct SortingMethod {
+ // The index of the sorting method. Used to allow for arbitrary ordering in the list of methods.
+ // Used by Flame in the API request.
+ unsigned int index;
+ // The real name of the sorting, as used in the respective API specification.
+ // Used by Modrinth in the API request.
+ QString name;
+ // The human-readable name of the sorting, used for display in the UI.
+ QString readable_name;
+ };
+
+ struct SearchArgs {
+ ModPlatform::ResourceType type{};
+ int offset = 0;
+
+ std::optional<QString> search;
+ std::optional<SortingMethod> sorting;
+ std::optional<ModLoaderTypes> loaders;
+ std::optional<std::list<Version> > versions;
+ };
+ struct SearchCallbacks {
+ std::function<void(QJsonDocument&)> on_succeed;
+ std::function<void(QString const& reason, int network_error_code)> on_fail;
+ std::function<void()> on_abort;
+ };
+
+ struct VersionSearchArgs {
+ ModPlatform::IndexedPack pack;
+
+ std::optional<std::list<Version> > mcVersions;
+ std::optional<ModLoaderTypes> loaders;
+
+ VersionSearchArgs(VersionSearchArgs const&) = default;
+ void operator=(VersionSearchArgs other)
+ {
+ pack = other.pack;
+ mcVersions = other.mcVersions;
+ loaders = other.loaders;
+ }
+ };
+ struct VersionSearchCallbacks {
+ std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
+ };
+
+ struct ProjectInfoArgs {
+ ModPlatform::IndexedPack pack;
+
+ ProjectInfoArgs(ProjectInfoArgs const&) = default;
+ void operator=(ProjectInfoArgs other) { pack = other.pack; }
+ };
+ struct ProjectInfoCallbacks {
+ std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
+ };
+
+ public:
+ /** Gets a list of available sorting methods for this API. */
+ [[nodiscard]] virtual auto getSortingMethods() const -> QList<SortingMethod> = 0;
+
+ public slots:
+ [[nodiscard]] virtual Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const
+ {
+ qWarning() << "TODO";
+ return nullptr;
+ }
+ [[nodiscard]] virtual Task::Ptr getProject(QString addonId, QByteArray* response) const
+ {
+ qWarning() << "TODO";
+ return nullptr;
+ }
+ [[nodiscard]] virtual Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const
+ {
+ qWarning() << "TODO";
+ return nullptr;
+ }
+
+ [[nodiscard]] virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const
+ {
+ qWarning() << "TODO";
+ return nullptr;
+ }
+ [[nodiscard]] virtual Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const
+ {
+ qWarning() << "TODO";
+ return nullptr;
+ }
+
+ static auto getModLoaderString(ModLoaderType type) -> const QString
+ {
+ switch (type) {
+ case Forge:
+ return "forge";
+ case Cauldron:
+ return "cauldron";
+ case LiteLoader:
+ return "liteloader";
+ case Fabric:
+ return "fabric";
+ case Quilt:
+ return "quilt";
+ default:
+ break;
+ }
+ return "";
+ }
+
+ protected:
+ [[nodiscard]] inline QString debugName() const { return "External resource API"; }
+
+ [[nodiscard]] 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/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
index 291ad916..4bd8b7f2 100644
--- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
+++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
@@ -81,16 +81,17 @@ bool PackInstallTask::abort()
void PackInstallTask::executeTask()
{
qDebug() << "PackInstallTask::executeTask: " << QThread::currentThreadId();
- auto *netJob = new NetJob("ATLauncher::VersionFetch", APPLICATION->network());
+ NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) };
auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json")
.arg(m_pack_safe_name).arg(m_version_name);
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
+
+ QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
+ QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
+ QObject::connect(netJob.get(), &NetJob::aborted, this, &PackInstallTask::onDownloadAborted);
+
jobPtr = netJob;
jobPtr->start();
-
- QObject::connect(netJob, &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
- QObject::connect(netJob, &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
- QObject::connect(netJob, &NetJob::aborted, this, &PackInstallTask::onDownloadAborted);
}
void PackInstallTask::onDownloadSucceeded()
@@ -552,7 +553,7 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared
file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
file.close();
- profile->appendComponent(new Component(profile.get(), target_id, f));
+ profile->appendComponent(ComponentPtr{ new Component(profile.get(), target_id, f) });
return true;
}
@@ -641,7 +642,7 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr<
file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
file.close();
- profile->appendComponent(new Component(profile.get(), target_id, f));
+ profile->appendComponent(ComponentPtr{ new Component(profile.get(), target_id, f) });
return true;
}
@@ -649,7 +650,7 @@ void PackInstallTask::installConfigs()
{
qDebug() << "PackInstallTask::installConfigs: " << QThread::currentThreadId();
setStatus(tr("Downloading configs..."));
- jobPtr = new NetJob(tr("Config download"), APPLICATION->network());
+ jobPtr.reset(new NetJob(tr("Config download"), APPLICATION->network()));
auto path = QString("Configs/%1/%2.zip").arg(m_pack_safe_name).arg(m_version_name);
auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.zip")
@@ -747,7 +748,7 @@ void PackInstallTask::downloadMods()
setStatus(tr("Downloading mods..."));
jarmods.clear();
- jobPtr = new NetJob(tr("Mod download"), APPLICATION->network());
+ jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
for(const auto& mod : m_version.mods) {
// skip non-client mods
if(!mod.client) continue;
diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp
index 7f1beb1a..d3a737bb 100644
--- a/launcher/modplatform/flame/FileResolvingTask.cpp
+++ b/launcher/modplatform/flame/FileResolvingTask.cpp
@@ -23,7 +23,7 @@ void Flame::FileResolvingTask::executeTask()
{
setStatus(tr("Resolving mod IDs..."));
setProgress(0, 3);
- m_dljob = new NetJob("Mod id resolver", m_network);
+ m_dljob.reset(new NetJob("Mod id resolver", m_network));
result.reset(new QByteArray());
//build json data to send
QJsonObject object;
@@ -43,7 +43,7 @@ void Flame::FileResolvingTask::netJobFinished()
{
setProgress(1, 3);
// job to check modrinth for blocked projects
- m_checkJob = new NetJob("Modrinth check", m_network);
+ m_checkJob.reset(new NetJob("Modrinth check", m_network));
blockedProjects = QMap<File *,QByteArray *>();
QJsonDocument doc;
diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp
index 4d71da21..5ef9a409 100644
--- a/launcher/modplatform/flame/FlameAPI.cpp
+++ b/launcher/modplatform/flame/FlameAPI.cpp
@@ -1,15 +1,19 @@
+// SPDX-FileCopyrightText: 2023 flowln <flowlnlnln@gmail.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
#include "FlameAPI.h"
#include "FlameModIndex.h"
#include "Application.h"
#include "BuildConfig.h"
#include "Json.h"
-
+#include "net/NetJob.h"
#include "net/Upload.h"
-auto FlameAPI::matchFingerprints(const QList<uint>& fingerprints, QByteArray* response) -> NetJob::Ptr
+Task::Ptr FlameAPI::matchFingerprints(const QList<uint>& fingerprints, QByteArray* response)
{
- auto* netJob = new NetJob(QString("Flame::MatchFingerprints"), APPLICATION->network());
+ auto netJob = makeShared<NetJob>(QString("Flame::MatchFingerprints"), APPLICATION->network());
QJsonObject body_obj;
QJsonArray fingerprints_arr;
@@ -24,7 +28,7 @@ auto FlameAPI::matchFingerprints(const QList<uint>& fingerprints, QByteArray* re
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/fingerprints"), response, body_raw));
- QObject::connect(netJob, &NetJob::finished, [response] { delete response; });
+ QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
return netJob;
}
@@ -106,13 +110,19 @@ auto FlameAPI::getModDescription(int modId) -> QString
auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::IndexedVersion
{
+ auto versions_url_optional = getVersionsURL(args);
+ if (!versions_url_optional.has_value())
+ return {};
+
+ auto versions_url = versions_url_optional.value();
+
QEventLoop loop;
- auto netJob = new NetJob(QString("Flame::GetLatestVersion(%1)").arg(args.addonId), APPLICATION->network());
+ auto netJob = new NetJob(QString("Flame::GetLatestVersion(%1)").arg(args.pack.name), APPLICATION->network());
auto response = new QByteArray();
ModPlatform::IndexedVersion ver;
- netJob->addNetAction(Net::Download::makeByteArray(getVersionsURL(args), response));
+ netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
QObject::connect(netJob, &NetJob::succeeded, [response, args, &ver] {
QJsonParseError parse_error{};
@@ -161,9 +171,9 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
return ver;
}
-auto FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const -> NetJob*
+Task::Ptr FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const
{
- auto* netJob = new NetJob(QString("Flame::GetProjects"), APPLICATION->network());
+ auto netJob = makeShared<NetJob>(QString("Flame::GetProjects"), APPLICATION->network());
QJsonObject body_obj;
QJsonArray addons_arr;
@@ -178,15 +188,15 @@ auto FlameAPI::getProjects(QStringList addonIds, QByteArray* response) const ->
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods"), response, body_raw));
- QObject::connect(netJob, &NetJob::finished, [response, netJob] { delete response; netJob->deleteLater(); });
- QObject::connect(netJob, &NetJob::failed, [body_raw] { qDebug() << body_raw; });
+ QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
+ QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
return netJob;
}
-auto FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) const -> NetJob*
+Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) const
{
- auto* netJob = new NetJob(QString("Flame::GetFiles"), APPLICATION->network());
+ auto netJob = makeShared<NetJob>(QString("Flame::GetFiles"), APPLICATION->network());
QJsonObject body_obj;
QJsonArray files_arr;
@@ -201,8 +211,23 @@ auto FlameAPI::getFiles(const QStringList& fileIds, QByteArray* response) const
netJob->addNetAction(Net::Upload::makeByteArray(QString("https://api.curseforge.com/v1/mods/files"), response, body_raw));
- QObject::connect(netJob, &NetJob::finished, [response, netJob] { delete response; netJob->deleteLater(); });
- QObject::connect(netJob, &NetJob::failed, [body_raw] { qDebug() << body_raw; });
+ QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
+ QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
return netJob;
}
+
+// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
+static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
+ { 2, "Popularity", QObject::tr("Sort by Popularity") },
+ { 3, "LastUpdated", QObject::tr("Sort by Last Updated") },
+ { 4, "Name", QObject::tr("Sort by Name") },
+ { 5, "Author", QObject::tr("Sort by Author") },
+ { 6, "TotalDownloads", QObject::tr("Sort by Downloads") },
+ { 7, "Category", QObject::tr("Sort by Category") },
+ { 8, "GameVersion", QObject::tr("Sort by Game Version") } };
+
+QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const
+{
+ return s_sorts;
+}
diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h
index 4c6ca64c..06d749e6 100644
--- a/launcher/modplatform/flame/FlameAPI.h
+++ b/launcher/modplatform/flame/FlameAPI.h
@@ -1,84 +1,84 @@
+// SPDX-FileCopyrightText: 2023 flowln <flowlnlnln@gmail.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
#pragma once
#include "modplatform/ModIndex.h"
-#include "modplatform/helpers/NetworkModAPI.h"
+#include "modplatform/helpers/NetworkResourceAPI.h"
-class FlameAPI : public NetworkModAPI {
+class FlameAPI : public NetworkResourceAPI {
public:
- auto matchFingerprints(const QList<uint>& fingerprints, QByteArray* response) -> NetJob::Ptr;
auto getModFileChangelog(int modId, int fileId) -> QString;
auto getModDescription(int modId) -> QString;
auto getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::IndexedVersion;
- auto getProjects(QStringList addonIds, QByteArray* response) const -> NetJob* override;
- auto getFiles(const QStringList& fileIds, QByteArray* response) const -> NetJob*;
+ Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const override;
+ Task::Ptr matchFingerprints(const QList<uint>& fingerprints, QByteArray* response);
+ Task::Ptr getFiles(const QStringList& fileIds, QByteArray* response) const;
+
+ [[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
private:
- inline auto getSortFieldInt(QString sortString) const -> int
+ static int getClassId(ModPlatform::ResourceType type)
{
- return sortString == "Featured" ? 1
- : sortString == "Popularity" ? 2
- : sortString == "LastUpdated" ? 3
- : sortString == "Name" ? 4
- : sortString == "Author" ? 5
- : sortString == "TotalDownloads" ? 6
- : sortString == "Category" ? 7
- : sortString == "GameVersion" ? 8
- : 1;
+ switch (type) {
+ default:
+ case ModPlatform::ResourceType::MOD:
+ return 6;
+ }
+ }
+
+ static int getMappedModLoader(ModLoaderTypes loaders)
+ {
+ // 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 (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently*
+ return 4; // Quilt would probably be 5
+ return 0;
}
private:
- inline auto getModSearchURL(SearchArgs& args) const -> QString override
+ [[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override
{
- auto gameVersionStr = args.versions.size() != 0 ? QString("gameVersion=%1").arg(args.versions.front().toString()) : QString();
+ auto gameVersionStr = args.versions.has_value() ? QString("gameVersion=%1").arg(args.versions.value().front().toString()) : QString();
- return QString(
- "https://api.curseforge.com/v1/mods/search?"
- "gameId=432&"
- "classId=6&"
+ QStringList get_arguments;
+ get_arguments.append(QString("classId=%1").arg(getClassId(args.type)));
+ get_arguments.append(QString("index=%1").arg(args.offset));
+ get_arguments.append("pageSize=25");
+ if (args.search.has_value())
+ get_arguments.append(QString("searchFilter=%1").arg(args.search.value()));
+ if (args.sorting.has_value())
+ get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index));
+ get_arguments.append("sortOrder=desc");
+ if (args.loaders.has_value())
+ get_arguments.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value())));
+ get_arguments.append(gameVersionStr);
- "index=%1&"
- "pageSize=25&"
- "searchFilter=%2&"
- "sortField=%3&"
- "sortOrder=desc&"
- "modLoaderType=%4&"
- "%5")
- .arg(args.offset)
- .arg(args.search)
- .arg(getSortFieldInt(args.sorting))
- .arg(getMappedModLoader(args.loaders))
- .arg(gameVersionStr);
+ return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&');
};
- inline auto getModInfoURL(QString& id) const -> QString override
+ [[nodiscard]] std::optional<QString> getInfoURL(QString const& id) const override
{
return QString("https://api.curseforge.com/v1/mods/%1").arg(id);
};
- inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override
+ [[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
{
- QString gameVersionQuery = args.mcVersions.size() == 1 ? QString("gameVersion=%1&").arg(args.mcVersions.front().toString()) : "";
- QString modLoaderQuery = QString("modLoaderType=%1&").arg(getMappedModLoader(args.loaders));
+ QString url{QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(args.pack.addonId.toString())};
- return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&%2%3")
- .arg(args.addonId)
- .arg(gameVersionQuery)
- .arg(modLoaderQuery);
- };
+ QStringList get_parameters;
+ if (args.mcVersions.has_value())
+ get_parameters.append(QString("gameVersion=%1").arg(args.mcVersions.value().front().toString()));
+ if (args.loaders.has_value())
+ get_parameters.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value())));
- public:
- static auto getMappedModLoader(const ModLoaderTypes loaders) -> 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 (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently*
- return 4; // Quilt would probably be 5
- return 0;
- }
+ return url + get_parameters.join('&');
+ };
};
diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp
index 8dd3a846..06a89502 100644
--- a/launcher/modplatform/flame/FlameCheckUpdate.cpp
+++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp
@@ -7,7 +7,10 @@
#include "FileSystem.h"
#include "Json.h"
-#include "ModDownloadTask.h"
+#include "ResourceDownloadTask.h"
+
+#include "minecraft/mod/ModFolderModel.h"
+#include "minecraft/mod/ResourceFolderModel.h"
static FlameAPI api;
@@ -126,7 +129,8 @@ void FlameCheckUpdate::executeTask()
setStatus(tr("Getting API response from CurseForge for '%1'...").arg(mod->name()));
setProgress(i++, m_mods.size());
- auto latest_ver = api.getLatestVersion({ mod->metadata()->project_id.toString(), m_game_versions, m_loaders });
+ ModPlatform::IndexedPack pack{ mod->metadata()->project_id.toString() };
+ auto latest_ver = api.getLatestVersion({ pack, m_game_versions, m_loaders });
// Check if we were aborted while getting the latest version
if (m_was_aborted) {
@@ -160,7 +164,7 @@ void FlameCheckUpdate::executeTask()
for (auto& author : mod->authors())
pack.authors.append({ author });
pack.description = mod->description();
- pack.provider = ModPlatform::Provider::FLAME;
+ pack.provider = ModPlatform::ResourceProvider::FLAME;
auto old_version = mod->version();
if (old_version.isEmpty() && mod->status() != ModStatus::NotInstalled) {
@@ -168,10 +172,10 @@ void FlameCheckUpdate::executeTask()
old_version = current_ver.version;
}
- auto download_task = new ModDownloadTask(pack, latest_ver, m_mods_folder);
+ auto download_task = makeShared<ResourceDownloadTask>(pack, latest_ver, m_mods_folder);
m_updatable.emplace_back(pack.name, mod->metadata()->hash, old_version, latest_ver.version,
api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()),
- ModPlatform::Provider::FLAME, download_task);
+ ModPlatform::ResourceProvider::FLAME, download_task);
}
}
diff --git a/launcher/modplatform/flame/FlameCheckUpdate.h b/launcher/modplatform/flame/FlameCheckUpdate.h
index 163c706c..4a98d684 100644
--- a/launcher/modplatform/flame/FlameCheckUpdate.h
+++ b/launcher/modplatform/flame/FlameCheckUpdate.h
@@ -8,7 +8,7 @@ class FlameCheckUpdate : public CheckUpdateTask {
Q_OBJECT
public:
- FlameCheckUpdate(QList<Mod*>& mods, std::list<Version>& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr<ModFolderModel> mods_folder)
+ FlameCheckUpdate(QList<Mod*>& mods, std::list<Version>& mcVersions, std::optional<ResourceAPI::ModLoaderTypes> loaders, std::shared_ptr<ModFolderModel> mods_folder)
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
{}
diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
index dc69769a..964b559c 100644
--- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
+++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
@@ -183,7 +183,7 @@ bool FlameCreationTask::updateInstance()
QEventLoop loop;
- connect(job, &NetJob::succeeded, this, [this, raw_response, fileIds, old_inst_dir, &old_files, old_minecraft_dir] {
+ connect(job.get(), &Task::succeeded, this, [this, raw_response, fileIds, old_inst_dir, &old_files, old_minecraft_dir] {
// Parse the API response
QJsonParseError parse_error{};
auto doc = QJsonDocument::fromJson(*raw_response, &parse_error);
@@ -225,7 +225,7 @@ bool FlameCreationTask::updateInstance()
m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path));
}
});
- connect(job, &NetJob::finished, &loop, &QEventLoop::quit);
+ connect(job.get(), &Task::finished, &loop, &QEventLoop::quit);
m_process_update_file_info_job = job;
job->start();
@@ -373,7 +373,7 @@ bool FlameCreationTask::createInstance()
instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
instance.setName(name());
- m_mod_id_resolver = new Flame::FileResolvingTask(APPLICATION->network(), m_pack);
+ m_mod_id_resolver.reset(new Flame::FileResolvingTask(APPLICATION->network(), m_pack));
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::succeeded, this, [this, &loop] { idResolverSucceeded(loop); });
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason) {
m_mod_id_resolver.reset();
@@ -452,7 +452,7 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
{
- m_files_job = new NetJob(tr("Mod download"), APPLICATION->network());
+ m_files_job.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
for (const auto& result : m_mod_id_resolver->getResults().files) {
QString filename = result.fileName;
if (!result.required) {
diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h
index 498e1d6e..0ae4735b 100644
--- a/launcher/modplatform/flame/FlameInstanceCreationTask.h
+++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h
@@ -86,7 +86,7 @@ class FlameCreationTask final : public InstanceCreationTask {
Flame::Manifest m_pack;
// Handle to allow aborting
- NetJob* m_process_update_file_info_job = nullptr;
+ Task::Ptr m_process_update_file_info_job = nullptr;
NetJob::Ptr m_files_job = nullptr;
QString m_managed_id, m_managed_version_id;
diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp
index 32aa4bdb..7498e830 100644
--- a/launcher/modplatform/flame/FlameModIndex.cpp
+++ b/launcher/modplatform/flame/FlameModIndex.cpp
@@ -11,7 +11,7 @@ static ModPlatform::ProviderCapabilities ProviderCaps;
void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
{
pack.addonId = Json::requireInteger(obj, "id");
- pack.provider = ModPlatform::Provider::FLAME;
+ pack.provider = ModPlatform::ResourceProvider::FLAME;
pack.name = Json::requireString(obj, "name");
pack.slug = Json::requireString(obj, "slug");
pack.websiteUrl = Json::ensureString(Json::ensureObject(obj, "links"), "websiteUrl", "");
@@ -76,10 +76,10 @@ static QString enumToString(int hash_algorithm)
void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
QJsonArray& arr,
const shared_qobject_ptr<QNetworkAccessManager>& network,
- BaseInstance* inst)
+ const BaseInstance* inst)
{
QVector<ModPlatform::IndexedVersion> unsortedVersions;
- auto profile = (dynamic_cast<MinecraftInstance*>(inst))->getPackProfile();
+ auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
QString mcVersion = profile->getComponentVersion("net.minecraft");
for (auto versionIter : arr) {
@@ -127,7 +127,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
auto hash_list = Json::ensureArray(obj, "hashes");
for (auto h : hash_list) {
auto hash_entry = Json::ensureObject(h);
- auto hash_types = ProviderCaps.hashType(ModPlatform::Provider::FLAME);
+ auto hash_types = ProviderCaps.hashType(ModPlatform::ResourceProvider::FLAME);
auto hash_algo = enumToString(Json::ensureInteger(hash_entry, "algo", 1, "algorithm"));
if (hash_types.contains(hash_algo)) {
file.hash = Json::requireString(hash_entry, "value");
diff --git a/launcher/modplatform/flame/FlameModIndex.h b/launcher/modplatform/flame/FlameModIndex.h
index db63cdbb..33c4a529 100644
--- a/launcher/modplatform/flame/FlameModIndex.h
+++ b/launcher/modplatform/flame/FlameModIndex.h
@@ -17,7 +17,7 @@ void loadBody(ModPlatform::IndexedPack& m, QJsonObject& obj);
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
QJsonArray& arr,
const shared_qobject_ptr<QNetworkAccessManager>& network,
- BaseInstance* inst);
+ const BaseInstance* inst);
auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion;
} // namespace FlameMod
diff --git a/launcher/modplatform/helpers/HashUtils.cpp b/launcher/modplatform/helpers/HashUtils.cpp
index f1e4759e..2177ddad 100644
--- a/launcher/modplatform/helpers/HashUtils.cpp
+++ b/launcher/modplatform/helpers/HashUtils.cpp
@@ -12,12 +12,12 @@ namespace Hashing {
static ModPlatform::ProviderCapabilities ProviderCaps;
-Hasher::Ptr createHasher(QString file_path, ModPlatform::Provider provider)
+Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider)
{
switch (provider) {
- case ModPlatform::Provider::MODRINTH:
+ case ModPlatform::ResourceProvider::MODRINTH:
return createModrinthHasher(file_path);
- case ModPlatform::Provider::FLAME:
+ case ModPlatform::ResourceProvider::FLAME:
return createFlameHasher(file_path);
default:
qCritical() << "[Hashing]"
@@ -28,22 +28,22 @@ Hasher::Ptr createHasher(QString file_path, ModPlatform::Provider provider)
Hasher::Ptr createModrinthHasher(QString file_path)
{
- return new ModrinthHasher(file_path);
+ return makeShared<ModrinthHasher>(file_path);
}
Hasher::Ptr createFlameHasher(QString file_path)
{
- return new FlameHasher(file_path);
+ return makeShared<FlameHasher>(file_path);
}
-Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider)
+Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider)
{
- return new BlockedModHasher(file_path, provider);
+ return makeShared<BlockedModHasher>(file_path, provider);
}
-Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider, QString type)
+Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider, QString type)
{
- auto hasher = new BlockedModHasher(file_path, provider);
+ auto hasher = makeShared<BlockedModHasher>(file_path, provider);
hasher->useHashType(type);
return hasher;
}
@@ -62,8 +62,8 @@ void ModrinthHasher::executeTask()
return;
}
- auto hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first();
- m_hash = ProviderCaps.hash(ModPlatform::Provider::MODRINTH, &file, hash_type);
+ auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
+ m_hash = ProviderCaps.hash(ModPlatform::ResourceProvider::MODRINTH, &file, hash_type);
file.close();
@@ -92,7 +92,7 @@ void FlameHasher::executeTask()
}
-BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::Provider provider)
+BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider)
: Hasher(file_path), provider(provider) {
setObjectName(QString("BlockedModHasher: %1").arg(file_path));
hash_type = ProviderCaps.hashType(provider).first();
diff --git a/launcher/modplatform/helpers/HashUtils.h b/launcher/modplatform/helpers/HashUtils.h
index fa3244f6..91146a52 100644
--- a/launcher/modplatform/helpers/HashUtils.h
+++ b/launcher/modplatform/helpers/HashUtils.h
@@ -42,21 +42,21 @@ class ModrinthHasher : public Hasher {
class BlockedModHasher : public Hasher {
public:
- BlockedModHasher(QString file_path, ModPlatform::Provider provider);
+ BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider);
void executeTask() override;
QStringList getHashTypes();
bool useHashType(QString type);
private:
- ModPlatform::Provider provider;
+ ModPlatform::ResourceProvider provider;
QString hash_type;
};
-Hasher::Ptr createHasher(QString file_path, ModPlatform::Provider provider);
+Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider);
Hasher::Ptr createFlameHasher(QString file_path);
Hasher::Ptr createModrinthHasher(QString file_path);
-Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider);
-Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider, QString type);
+Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider);
+Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider, QString type);
} // namespace Hashing
diff --git a/launcher/modplatform/helpers/NetworkModAPI.cpp b/launcher/modplatform/helpers/NetworkModAPI.cpp
deleted file mode 100644
index 7633030e..00000000
--- a/launcher/modplatform/helpers/NetworkModAPI.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-#include "NetworkModAPI.h"
-
-#include "ui/pages/modplatform/ModModel.h"
-
-#include "Application.h"
-#include "net/NetJob.h"
-
-void NetworkModAPI::searchMods(CallerType* caller, SearchArgs&& args) const
-{
- auto netJob = new NetJob(QString("%1::Search").arg(caller->debugName()), APPLICATION->network());
- auto searchUrl = getModSearchURL(args);
-
- auto response = new QByteArray();
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response));
-
- QObject::connect(netJob, &NetJob::started, caller, [caller, netJob] { caller->setActiveJob(netJob); });
- QObject::connect(netJob, &NetJob::failed, caller, &CallerType::searchRequestFailed);
- QObject::connect(netJob, &NetJob::aborted, caller, &CallerType::searchRequestAborted);
- QObject::connect(netJob, &NetJob::succeeded, caller, [caller, response] {
- QJsonParseError parse_error{};
- QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
- if (parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from " << caller->debugName() << " at " << parse_error.offset
- << " reason: " << parse_error.errorString();
- qWarning() << *response;
- return;
- }
-
- caller->searchRequestFinished(doc);
- });
-
- netJob->start();
-}
-
-void NetworkModAPI::getModInfo(ModPlatform::IndexedPack& pack, std::function<void(QJsonDocument&, ModPlatform::IndexedPack&)> callback)
-{
- auto response = new QByteArray();
- auto job = getProject(pack.addonId.toString(), response);
-
- QObject::connect(job, &NetJob::succeeded, [callback, &pack, response] {
- QJsonParseError parse_error{};
- QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
- if (parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
- << " reason: " << parse_error.errorString();
- qWarning() << *response;
- return;
- }
-
- callback(doc, pack);
- });
-
- job->start();
-}
-
-void NetworkModAPI::getVersions(VersionSearchArgs&& args, std::function<void(QJsonDocument&, QString)> callback) const
-{
- auto netJob = new NetJob(QString("ModVersions(%2)").arg(args.addonId), APPLICATION->network());
- auto response = new QByteArray();
-
- netJob->addNetAction(Net::Download::makeByteArray(getVersionsURL(args), response));
-
- QObject::connect(netJob, &NetJob::succeeded, [response, callback, args] {
- QJsonParseError parse_error{};
- QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
- if (parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response for getting versions at " << parse_error.offset
- << " reason: " << parse_error.errorString();
- qWarning() << *response;
- return;
- }
-
- callback(doc, args.addonId);
- });
-
- QObject::connect(netJob, &NetJob::finished, [response, netJob] {
- netJob->deleteLater();
- delete response;
- });
-
- netJob->start();
-}
-
-auto NetworkModAPI::getProject(QString addonId, QByteArray* response) const -> NetJob*
-{
- auto netJob = new NetJob(QString("%1::GetProject").arg(addonId), APPLICATION->network());
- auto searchUrl = getModInfoURL(addonId);
-
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response));
-
- QObject::connect(netJob, &NetJob::finished, [response, netJob] {
- netJob->deleteLater();
- delete response;
- });
-
- return netJob;
-}
diff --git a/launcher/modplatform/helpers/NetworkModAPI.h b/launcher/modplatform/helpers/NetworkModAPI.h
deleted file mode 100644
index b8af22c7..00000000
--- a/launcher/modplatform/helpers/NetworkModAPI.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include "modplatform/ModAPI.h"
-
-class NetworkModAPI : public ModAPI {
- public:
- void searchMods(CallerType* caller, SearchArgs&& args) const override;
- void getModInfo(ModPlatform::IndexedPack& pack, std::function<void(QJsonDocument&, ModPlatform::IndexedPack&)> callback) override;
- void getVersions(VersionSearchArgs&& args, std::function<void(QJsonDocument&, QString)> callback) const override;
-
- auto getProject(QString addonId, QByteArray* response) const -> NetJob* override;
-
- protected:
- virtual auto getModSearchURL(SearchArgs& args) const -> QString = 0;
- virtual auto getModInfoURL(QString& id) const -> QString = 0;
- virtual auto getVersionsURL(VersionSearchArgs& args) const -> QString = 0;
-};
diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp
new file mode 100644
index 00000000..010ac15e
--- /dev/null
+++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp
@@ -0,0 +1,128 @@
+// SPDX-FileCopyrightText: 2023 flowln <flowlnlnln@gmail.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+#include "NetworkResourceAPI.h"
+
+#include "Application.h"
+#include "net/NetJob.h"
+
+#include "modplatform/ModIndex.h"
+
+Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const
+{
+ auto search_url_optional = getSearchURL(args);
+ if (!search_url_optional.has_value()) {
+ callbacks.on_fail("Failed to create search URL", -1);
+ return nullptr;
+ }
+
+ auto search_url = search_url_optional.value();
+
+ auto response = new QByteArray();
+ auto netJob = makeShared<NetJob>(QString("%1::Search").arg(debugName()), APPLICATION->network());
+
+ netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response));
+
+ QObject::connect(netJob.get(), &NetJob::succeeded, [=]{
+ QJsonParseError parse_error{};
+ QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
+ if (parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from " << debugName() << " at " << parse_error.offset
+ << " reason: " << parse_error.errorString();
+ qWarning() << *response;
+
+ callbacks.on_fail(parse_error.errorString(), -1);
+
+ return;
+ }
+
+ callbacks.on_succeed(doc);
+ });
+
+ QObject::connect(netJob.get(), &NetJob::failed, [=](QString reason){
+ int network_error_code = -1;
+ if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply)
+ network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ callbacks.on_fail(reason, network_error_code);
+ });
+ QObject::connect(netJob.get(), &NetJob::aborted, [=]{
+ callbacks.on_abort();
+ });
+
+ return netJob;
+}
+
+Task::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfoCallbacks&& callbacks) const
+{
+ auto response = new QByteArray();
+ auto job = getProject(args.pack.addonId.toString(), response);
+
+ QObject::connect(job.get(), &NetJob::succeeded, [response, callbacks, args] {
+ QJsonParseError parse_error{};
+ QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
+ if (parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
+ << " reason: " << parse_error.errorString();
+ qWarning() << *response;
+ return;
+ }
+
+ callbacks.on_succeed(doc, args.pack);
+ });
+
+ return job;
+}
+
+Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, VersionSearchCallbacks&& callbacks) const
+{
+ auto versions_url_optional = getVersionsURL(args);
+ if (!versions_url_optional.has_value())
+ return nullptr;
+
+ auto versions_url = versions_url_optional.value();
+
+ auto netJob = makeShared<NetJob>(QString("%1::Versions").arg(args.pack.name), APPLICATION->network());
+ auto response = new QByteArray();
+
+ netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
+
+ QObject::connect(netJob.get(), &NetJob::succeeded, [=] {
+ QJsonParseError parse_error{};
+ QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
+ if (parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response for getting versions at " << parse_error.offset
+ << " reason: " << parse_error.errorString();
+ qWarning() << *response;
+ return;
+ }
+
+ callbacks.on_succeed(doc, args.pack);
+ });
+
+ QObject::connect(netJob.get(), &NetJob::finished, [response] {
+ delete response;
+ });
+
+ return netJob;
+}
+
+Task::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response) const
+{
+ auto project_url_optional = getInfoURL(addonId);
+ if (!project_url_optional.has_value())
+ return nullptr;
+
+ auto project_url = project_url_optional.value();
+
+ auto netJob = makeShared<NetJob>(QString("%1::GetProject").arg(addonId), APPLICATION->network());
+
+ netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response));
+
+ QObject::connect(netJob.get(), &NetJob::finished, [response] {
+ delete response;
+ });
+
+ return netJob;
+}
diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.h b/launcher/modplatform/helpers/NetworkResourceAPI.h
new file mode 100644
index 00000000..94813bec
--- /dev/null
+++ b/launcher/modplatform/helpers/NetworkResourceAPI.h
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: 2023 flowln <flowlnlnln@gmail.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+#pragma once
+
+#include "modplatform/ResourceAPI.h"
+
+class NetworkResourceAPI : public ResourceAPI {
+ public:
+ Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const override;
+
+ Task::Ptr getProject(QString addonId, QByteArray* response) const override;
+
+ Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override;
+ Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override;
+
+ protected:
+ [[nodiscard]] virtual auto getSearchURL(SearchArgs const& args) const -> std::optional<QString> = 0;
+ [[nodiscard]] virtual auto getInfoURL(QString const& id) const -> std::optional<QString> = 0;
+ [[nodiscard]] virtual auto getVersionsURL(VersionSearchArgs const& args) const -> std::optional<QString> = 0;
+};
diff --git a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp
index 36aa60c7..e8768c5c 100644
--- a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp
+++ b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp
@@ -47,7 +47,7 @@ void PackFetchTask::fetch()
publicPacks.clear();
thirdPartyPacks.clear();
- jobPtr = new NetJob("LegacyFTB::ModpackFetch", m_network);
+ jobPtr.reset(new NetJob("LegacyFTB::ModpackFetch", m_network));
QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml");
qDebug() << "Downloading public version info from" << publicPacksUrl.toString();
diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp
index 06b3788b..8d45fc5c 100644
--- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp
+++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp
@@ -69,7 +69,7 @@ void PackInstallTask::downloadPack()
archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
- netJobContainer = new NetJob("Download FTB Pack", m_network);
+ netJobContainer.reset(new NetJob("Download FTB Pack", m_network));
QString url;
if (m_pack.type == PackType::Private) {
url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(archivePath);
diff --git a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
index 2979663d..68d4751c 100644
--- a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
+++ b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
@@ -87,15 +87,15 @@ void PackInstallTask::executeTask()
auto version = *version_it;
- auto* netJob = new NetJob("ModpacksCH::VersionFetch", APPLICATION->network());
+ auto netJob = makeShared<NetJob>("ModpacksCH::VersionFetch", APPLICATION->network());
auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1/%2").arg(m_pack.id).arg(version.id);
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &m_response));
- QObject::connect(netJob, &NetJob::succeeded, this, &PackInstallTask::onManifestDownloadSucceeded);
- QObject::connect(netJob, &NetJob::failed, this, &PackInstallTask::onManifestDownloadFailed);
- QObject::connect(netJob, &NetJob::aborted, this, &PackInstallTask::abort);
- QObject::connect(netJob, &NetJob::progress, this, &PackInstallTask::setProgress);
+ QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onManifestDownloadSucceeded);
+ QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onManifestDownloadFailed);
+ QObject::connect(netJob.get(), &NetJob::aborted, this, &PackInstallTask::abort);
+ QObject::connect(netJob.get(), &NetJob::progress, this, &PackInstallTask::setProgress);
m_net_job = netJob;
@@ -162,7 +162,7 @@ void PackInstallTask::resolveMods()
index++;
}
- m_mod_id_resolver_task = new Flame::FileResolvingTask(APPLICATION->network(), manifest);
+ m_mod_id_resolver_task.reset(new Flame::FileResolvingTask(APPLICATION->network(), manifest));
connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::succeeded, this, &PackInstallTask::onResolveModsSucceeded);
connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::failed, this, &PackInstallTask::onResolveModsFailed);
@@ -294,7 +294,7 @@ void PackInstallTask::downloadPack()
setStatus(tr("Downloading mods..."));
setAbortable(false);
- auto* jobPtr = new NetJob(tr("Mod download"), APPLICATION->network());
+ auto jobPtr = makeShared<NetJob>(tr("Mod download"), APPLICATION->network());
for (auto const& file : m_version.files) {
if (file.serverOnly || file.url.isEmpty())
continue;
@@ -313,10 +313,10 @@ void PackInstallTask::downloadPack()
jobPtr->addNetAction(dl);
}
- connect(jobPtr, &NetJob::succeeded, this, &PackInstallTask::onModDownloadSucceeded);
- connect(jobPtr, &NetJob::failed, this, &PackInstallTask::onModDownloadFailed);
- connect(jobPtr, &NetJob::aborted, this, &PackInstallTask::abort);
- connect(jobPtr, &NetJob::progress, this, &PackInstallTask::setProgress);
+ connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModDownloadSucceeded);
+ connect(jobPtr.get(), &NetJob::failed, this, &PackInstallTask::onModDownloadFailed);
+ connect(jobPtr.get(), &NetJob::aborted, this, &PackInstallTask::abort);
+ connect(jobPtr.get(), &NetJob::progress, this, &PackInstallTask::setProgress);
m_net_job = jobPtr;
diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp
index 747cf4c3..29e3d129 100644
--- a/launcher/modplatform/modrinth/ModrinthAPI.cpp
+++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp
@@ -1,24 +1,29 @@
+// SPDX-FileCopyrightText: 2023 flowln <flowlnlnln@gmail.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
#include "ModrinthAPI.h"
#include "Application.h"
#include "Json.h"
+#include "net/NetJob.h"
#include "net/Upload.h"
-auto ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response) -> NetJob::Ptr
+Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response)
{
- auto* netJob = new NetJob(QString("Modrinth::GetCurrentVersion"), APPLICATION->network());
+ auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersion"), APPLICATION->network());
netJob->addNetAction(Net::Download::makeByteArray(
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format), response));
- QObject::connect(netJob, &NetJob::finished, [response] { delete response; });
+ QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
return netJob;
}
-auto ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format, QByteArray* response) -> NetJob::Ptr
+Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format, QByteArray* response)
{
- auto* netJob = new NetJob(QString("Modrinth::GetCurrentVersions"), APPLICATION->network());
+ auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersions"), APPLICATION->network());
QJsonObject body_obj;
@@ -30,28 +35,31 @@ auto ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), response, body_raw));
- QObject::connect(netJob, &NetJob::finished, [response] { delete response; });
+ QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
return netJob;
}
-auto ModrinthAPI::latestVersion(QString hash,
- QString hash_format,
- std::list<Version> mcVersions,
- ModLoaderTypes loaders,
- QByteArray* response) -> NetJob::Ptr
+Task::Ptr ModrinthAPI::latestVersion(QString hash,
+ QString hash_format,
+ std::optional<std::list<Version>> mcVersions,
+ std::optional<ModLoaderTypes> loaders,
+ QByteArray* response)
{
- auto* netJob = new NetJob(QString("Modrinth::GetLatestVersion"), APPLICATION->network());
+ auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network());
QJsonObject body_obj;
- Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders));
+ if (loaders.has_value())
+ Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders.value()));
- QStringList game_versions;
- for (auto& ver : mcVersions) {
- game_versions.append(ver.toString());
+ if (mcVersions.has_value()) {
+ QStringList game_versions;
+ for (auto& ver : mcVersions.value()) {
+ game_versions.append(ver.toString());
+ }
+ Json::writeStringList(body_obj, "game_versions", game_versions);
}
- Json::writeStringList(body_obj, "game_versions", game_versions);
QJsonDocument body(body_obj);
auto body_raw = body.toJson();
@@ -59,50 +67,67 @@ auto ModrinthAPI::latestVersion(QString hash,
netJob->addNetAction(Net::Upload::makeByteArray(
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), response, body_raw));
- QObject::connect(netJob, &NetJob::finished, [response] { delete response; });
+ QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
return netJob;
}
-auto ModrinthAPI::latestVersions(const QStringList& hashes,
- QString hash_format,
- std::list<Version> mcVersions,
- ModLoaderTypes loaders,
- QByteArray* response) -> NetJob::Ptr
+Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
+ QString hash_format,
+ std::optional<std::list<Version>> mcVersions,
+ std::optional<ModLoaderTypes> loaders,
+ QByteArray* response)
{
- auto* netJob = new NetJob(QString("Modrinth::GetLatestVersions"), APPLICATION->network());
+ auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network());
QJsonObject body_obj;
Json::writeStringList(body_obj, "hashes", hashes);
Json::writeString(body_obj, "algorithm", hash_format);
- Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders));
+ if (loaders.has_value())
+ Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders.value()));
- QStringList game_versions;
- for (auto& ver : mcVersions) {
- game_versions.append(ver.toString());
+ if (mcVersions.has_value()) {
+ QStringList game_versions;
+ for (auto& ver : mcVersions.value()) {
+ game_versions.append(ver.toString());
+ }
+ Json::writeStringList(body_obj, "game_versions", game_versions);
}
- Json::writeStringList(body_obj, "game_versions", game_versions);
QJsonDocument body(body_obj);
auto body_raw = body.toJson();
netJob->addNetAction(Net::Upload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), response, body_raw));
- QObject::connect(netJob, &NetJob::finished, [response] { delete response; });
+ QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; });
return netJob;
}
-auto ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) const -> NetJob*
+Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) const
{
- auto netJob = new NetJob(QString("Modrinth::GetProjects"), APPLICATION->network());
+ auto netJob = makeShared<NetJob>(QString("Modrinth::GetProjects"), APPLICATION->network());
auto searchUrl = getMultipleModInfoURL(addonIds);
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response));
- QObject::connect(netJob, &NetJob::finished, [response, netJob] { delete response; netJob->deleteLater(); });
+ QObject::connect(netJob.get(), &NetJob::finished, [response, netJob] {
+ delete response;
+ });
return netJob;
}
+
+// https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects
+static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "relevance", QObject::tr("Sort by Relevance") },
+ { 2, "downloads", QObject::tr("Sort by Downloads") },
+ { 3, "follows", QObject::tr("Sort by Follows") },
+ { 4, "newest", QObject::tr("Sort by Last Updated") },
+ { 5, "updated", QObject::tr("Sort by Newest") } };
+
+QList<ResourceAPI::SortingMethod> ModrinthAPI::getSortingMethods() const
+{
+ return s_sorts;
+}
diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h
index e1a18681..dda27303 100644
--- a/launcher/modplatform/modrinth/ModrinthAPI.h
+++ b/launcher/modplatform/modrinth/ModrinthAPI.h
@@ -1,69 +1,54 @@
+// SPDX-FileCopyrightText: 2022-2023 flowln <flowlnlnln@gmail.com>
+//
// 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/ModIndex.h"
-#include "modplatform/helpers/NetworkModAPI.h"
+#include "modplatform/helpers/NetworkResourceAPI.h"
#include <QDebug>
-class ModrinthAPI : public NetworkModAPI {
+class ModrinthAPI : public NetworkResourceAPI {
public:
auto currentVersion(QString hash,
QString hash_format,
- QByteArray* response) -> NetJob::Ptr;
+ QByteArray* response) -> Task::Ptr;
auto currentVersions(const QStringList& hashes,
QString hash_format,
- QByteArray* response) -> NetJob::Ptr;
+ QByteArray* response) -> Task::Ptr;
auto latestVersion(QString hash,
QString hash_format,
- std::list<Version> mcVersions,
- ModLoaderTypes loaders,
- QByteArray* response) -> NetJob::Ptr;
+ std::optional<std::list<Version>> mcVersions,
+ std::optional<ModLoaderTypes> loaders,
+ QByteArray* response) -> Task::Ptr;
auto latestVersions(const QStringList& hashes,
QString hash_format,
- std::list<Version> mcVersions,
- ModLoaderTypes loaders,
- QByteArray* response) -> NetJob::Ptr;
+ std::optional<std::list<Version>> mcVersions,
+ std::optional<ModLoaderTypes> loaders,
+ QByteArray* response) -> Task::Ptr;
- auto getProjects(QStringList addonIds, QByteArray* response) const -> NetJob* override;
+ Task::Ptr getProjects(QStringList addonIds, QByteArray* response) const override;
public:
+ [[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
+
inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; };
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
{
QStringList l;
- for (auto loader : {Forge, Fabric, Quilt})
- {
- if ((types & loader) || types == Unspecified)
- {
- l << ModAPI::getModLoaderString(loader);
+ for (auto loader : {Forge, Fabric, Quilt}) {
+ if (types & loader) {
+ l << getModLoaderString(loader);
}
}
if ((types & Quilt) && (~types & Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there
- l << ModAPI::getModLoaderString(Fabric);
+ l << getModLoaderString(Fabric);
return l;
}
@@ -78,28 +63,54 @@ class ModrinthAPI : public NetworkModAPI {
}
private:
- inline auto getModSearchURL(SearchArgs& args) const -> QString override
+ [[nodiscard]] static QString resourceTypeParameter(ModPlatform::ResourceType type)
+ {
+ switch (type) {
+ case ModPlatform::ResourceType::MOD:
+ return "mod";
+ default:
+ qWarning() << "Invalid resource type for Modrinth API!";
+ break;
+ }
+
+ return "";
+ }
+ [[nodiscard]] QString createFacets(SearchArgs const& args) const
+ {
+ QStringList facets_list;
+
+ if (args.loaders.has_value())
+ facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value())));
+ if (args.versions.has_value())
+ facets_list.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value())));
+ facets_list.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type)));
+
+ return QString("[%1]").arg(facets_list.join(','));
+ }
+
+ public:
+ [[nodiscard]] inline auto getSearchURL(SearchArgs const& args) const -> std::optional<QString> override
{
- if (!validateModLoaders(args.loaders)) {
- qWarning() << "Modrinth only have Forge and Fabric-compatible mods!";
- return "";
+ if (args.loaders.has_value()) {
+ if (!validateModLoaders(args.loaders.value())) {
+ qWarning() << "Modrinth only have Forge and Fabric-compatible mods!";
+ return {};
+ }
}
- 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)
- .arg(getModLoaderFilters(args.loaders))
- .arg(getGameVersionsArray(args.versions));
+ QStringList get_arguments;
+ get_arguments.append(QString("offset=%1").arg(args.offset));
+ get_arguments.append(QString("limit=25"));
+ if (args.search.has_value())
+ get_arguments.append(QString("query=%1").arg(args.search.value()));
+ if (args.sorting.has_value())
+ get_arguments.append(QString("index=%1").arg(args.sorting.value().name));
+ get_arguments.append(QString("facets=%1").arg(createFacets(args)));
+
+ return BuildConfig.MODRINTH_PROD_URL + "/search?" + get_arguments.join('&');
};
- inline auto getModInfoURL(QString& id) const -> QString override
+ inline auto getInfoURL(QString const& id) const -> std::optional<QString> override
{
return BuildConfig.MODRINTH_PROD_URL + "/project/" + id;
};
@@ -109,15 +120,16 @@ class ModrinthAPI : public NetworkModAPI {
return BuildConfig.MODRINTH_PROD_URL + QString("/projects?ids=[\"%1\"]").arg(ids.join("\",\""));
};
- inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override
+ inline auto getVersionsURL(VersionSearchArgs const& args) const -> std::optional<QString> override
{
- return QString(BuildConfig.MODRINTH_PROD_URL +
- "/project/%1/version?"
- "game_versions=[%2]&"
- "loaders=[\"%3\"]")
- .arg(args.addonId,
- getGameVersionsString(args.mcVersions),
- getModLoaderStrings(args.loaders).join("\",\""));
+ QStringList get_arguments;
+ if (args.mcVersions.has_value())
+ get_arguments.append(QString("game_versions=[%1]").arg(getGameVersionsString(args.mcVersions.value())));
+ if (args.loaders.has_value())
+ get_arguments.append(QString("loaders=[\"%1\"]").arg(getModLoaderStrings(args.loaders.value()).join("\",\"")));
+
+ return QString("%1/project/%2/version%3%4")
+ .arg(BuildConfig.MODRINTH_PROD_URL, args.pack.addonId.toString(), get_arguments.isEmpty() ? "" : "?", get_arguments.join('&'));
};
auto getGameVersionsArray(std::list<Version> mcVersions) const -> QString
@@ -127,12 +139,12 @@ class ModrinthAPI : public NetworkModAPI {
s += QString("\"versions:%1\",").arg(ver.toString());
}
s.remove(s.length() - 1, 1); //remove last comma
- return s.isEmpty() ? QString() : QString("[%1],").arg(s);
+ return s.isEmpty() ? QString() : s;
}
inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool
{
- return (loaders == Unspecified) || (loaders & (Forge | Fabric | Quilt));
+ return loaders & (Forge | Fabric | Quilt);
}
};
diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp
index e2d27547..d1be7209 100644
--- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp
+++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp
@@ -4,12 +4,15 @@
#include "Json.h"
-#include "ModDownloadTask.h"
+#include "ResourceDownloadTask.h"
#include "modplatform/helpers/HashUtils.h"
#include "tasks/ConcurrentTask.h"
+#include "minecraft/mod/ModFolderModel.h"
+#include "minecraft/mod/ResourceFolderModel.h"
+
static ModrinthAPI api;
static ModPlatform::ProviderCapabilities ProviderCaps;
@@ -34,7 +37,7 @@ void ModrinthCheckUpdate::executeTask()
// Create all hashes
QStringList hashes;
- auto best_hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first();
+ auto best_hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", 10);
for (auto* mod : m_mods) {
@@ -108,11 +111,13 @@ void ModrinthCheckUpdate::executeTask()
// Sometimes a version may have multiple files, one with "forge" and one with "fabric",
// so we may want to filter it
QString loader_filter;
- static auto flags = { ModAPI::ModLoaderType::Forge, ModAPI::ModLoaderType::Fabric, ModAPI::ModLoaderType::Quilt };
- for (auto flag : flags) {
- if (m_loaders.testFlag(flag)) {
- loader_filter = api.getModLoaderString(flag);
- break;
+ if (m_loaders.has_value()) {
+ static auto flags = { ResourceAPI::ModLoaderType::Forge, ResourceAPI::ModLoaderType::Fabric, ResourceAPI::ModLoaderType::Quilt };
+ for (auto flag : flags) {
+ if (m_loaders.value().testFlag(flag)) {
+ loader_filter = api.getModLoaderString(flag);
+ break;
+ }
}
}
@@ -152,12 +157,12 @@ void ModrinthCheckUpdate::executeTask()
for (auto& author : mod->authors())
pack.authors.append({ author });
pack.description = mod->description();
- pack.provider = ModPlatform::Provider::MODRINTH;
+ pack.provider = ModPlatform::ResourceProvider::MODRINTH;
- auto download_task = new ModDownloadTask(pack, project_ver, m_mods_folder);
+ auto download_task = makeShared<ResourceDownloadTask>(pack, project_ver, m_mods_folder);
m_updatable.emplace_back(pack.name, hash, mod->version(), project_ver.version_number, project_ver.changelog,
- ModPlatform::Provider::MODRINTH, download_task);
+ ModPlatform::ResourceProvider::MODRINTH, download_task);
}
}
} catch (Json::JsonException& e) {
@@ -170,7 +175,7 @@ void ModrinthCheckUpdate::executeTask()
setStatus(tr("Waiting for the API response from Modrinth..."));
setProgress(1, 3);
- m_net_job = job.get();
+ m_net_job = qSharedPointerObjectCast<NetJob, Task>(job);
job->start();
lock.exec();
diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h
index abf8ada1..88e1a675 100644
--- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h
+++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h
@@ -8,7 +8,7 @@ class ModrinthCheckUpdate : public CheckUpdateTask {
Q_OBJECT
public:
- ModrinthCheckUpdate(QList<Mod*>& mods, std::list<Version>& mcVersions, ModAPI::ModLoaderTypes loaders, std::shared_ptr<ModFolderModel> mods_folder)
+ ModrinthCheckUpdate(QList<Mod*>& mods, std::list<Version>& mcVersions, std::optional<ResourceAPI::ModLoaderTypes> loaders, std::shared_ptr<ModFolderModel> mods_folder)
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
{}
@@ -19,5 +19,5 @@ class ModrinthCheckUpdate : public CheckUpdateTask {
void executeTask() override;
private:
- NetJob* m_net_job = nullptr;
+ NetJob::Ptr m_net_job = nullptr;
};
diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
index c5a27c9d..6814e645 100644
--- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
+++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
@@ -223,12 +223,21 @@ bool ModrinthCreationTask::createInstance()
instance.setName(name());
instance.saveNow();
- m_files_job = new NetJob(tr("Mod download"), APPLICATION->network());
+ m_files_job.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
+
+ auto root_modpack_path = FS::PathCombine(m_stagingPath, ".minecraft");
+ auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
for (auto file : m_files) {
- auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path);
- qDebug() << "Will try to download" << file.downloads.front() << "to" << path;
- auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
+ auto file_path = FS::PathCombine(root_modpack_path, file.path);
+ if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) {
+ // This means we somehow got out of the root folder, so abort here to prevent exploits
+ setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.").arg(file.path));
+ return false;
+ }
+
+ qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path;
+ auto dl = Net::Download::makeFile(file.downloads.dequeue(), file_path);
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
m_files_job->addNetAction(dl);
@@ -236,8 +245,8 @@ bool ModrinthCreationTask::createInstance()
// FIXME: This really needs to be put into a ConcurrentTask of
// MultipleOptionsTask's , once those exist :)
auto param = dl.toWeakRef();
- connect(dl.get(), &NetAction::failed, [this, &file, path, param] {
- auto ndl = Net::Download::makeFile(file.downloads.dequeue(), path);
+ connect(dl.get(), &NetAction::failed, [this, &file, file_path, param] {
+ auto ndl = Net::Download::makeFile(file.downloads.dequeue(), file_path);
ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
m_files_job->addNetAction(ndl);
if (auto shared = param.lock()) shared->succeeded();
diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
index ae45e096..7ade131e 100644
--- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
+++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp
@@ -27,13 +27,14 @@
static ModrinthAPI api;
static ModPlatform::ProviderCapabilities ProviderCaps;
+// https://docs.modrinth.com/api-spec/#tag/projects/operation/getProject
void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
{
pack.addonId = Json::ensureString(obj, "project_id");
if (pack.addonId.toString().isEmpty())
pack.addonId = Json::requireString(obj, "id");
- pack.provider = ModPlatform::Provider::MODRINTH;
+ pack.provider = ModPlatform::ResourceProvider::MODRINTH;
pack.name = Json::requireString(obj, "title");
pack.slug = Json::ensureString(obj, "slug", "");
@@ -44,7 +45,7 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
pack.description = Json::ensureString(obj, "description", "");
- pack.logoUrl = Json::requireString(obj, "icon_url");
+ pack.logoUrl = Json::ensureString(obj, "icon_url", "");
pack.logoName = pack.addonId.toString();
ModPlatform::ModpackAuthor modAuthor;
@@ -87,7 +88,7 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
pack.extraData.donate.append(donate);
}
- pack.extraData.body = Json::ensureString(obj, "body");
+ pack.extraData.body = Json::ensureString(obj, "body").remove("<br>");
pack.extraDataLoaded = true;
}
@@ -95,10 +96,10 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
QJsonArray& arr,
const shared_qobject_ptr<QNetworkAccessManager>& network,
- BaseInstance* inst)
+ const BaseInstance* inst)
{
QVector<ModPlatform::IndexedVersion> unsortedVersions;
- QString mcVersion = (static_cast<MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.minecraft");
+ QString mcVersion = (static_cast<const MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.minecraft");
for (auto versionIter : arr) {
auto obj = versionIter.toObject();
@@ -179,7 +180,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
file.hash = Json::requireString(hash_list, preferred_hash_type);
file.hash_type = preferred_hash_type;
} else {
- auto hash_types = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH);
+ auto hash_types = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH);
for (auto& hash_type : hash_types) {
if (hash_list.contains(hash_type)) {
file.hash = Json::requireString(hash_list, hash_type);
diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.h b/launcher/modplatform/modrinth/ModrinthPackIndex.h
index 31881414..e73e4b18 100644
--- a/launcher/modplatform/modrinth/ModrinthPackIndex.h
+++ b/launcher/modplatform/modrinth/ModrinthPackIndex.h
@@ -29,7 +29,7 @@ void loadExtraPackData(ModPlatform::IndexedPack& m, QJsonObject& obj);
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
QJsonArray& arr,
const shared_qobject_ptr<QNetworkAccessManager>& network,
- BaseInstance* inst);
+ const BaseInstance* inst);
auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion;
} // namespace Modrinth
diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp
index 0ed29311..510c7309 100644
--- a/launcher/modplatform/packwiz/Packwiz.cpp
+++ b/launcher/modplatform/packwiz/Packwiz.cpp
@@ -97,7 +97,7 @@ auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, Mo
mod.name = mod_pack.name;
mod.filename = mod_version.fileName;
- if (mod_pack.provider == ModPlatform::Provider::FLAME) {
+ if (mod_pack.provider == ModPlatform::ResourceProvider::FLAME) {
mod.mode = "metadata:curseforge";
} else {
mod.mode = "url";
@@ -176,11 +176,11 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
in_stream << QString("\n[update]\n");
in_stream << QString("[update.%1]\n").arg(ProviderCaps.name(mod.provider));
switch (mod.provider) {
- case (ModPlatform::Provider::FLAME):
+ case (ModPlatform::ResourceProvider::FLAME):
in_stream << QString("file-id = %1\n").arg(mod.file_id.toString());
in_stream << QString("project-id = %1\n").arg(mod.project_id.toString());
break;
- case (ModPlatform::Provider::MODRINTH):
+ case (ModPlatform::ResourceProvider::MODRINTH):
addToStream("mod-id", mod.mod_id().toString());
addToStream("version", mod.version().toString());
break;
@@ -273,7 +273,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
}
{ // [update] info
- using Provider = ModPlatform::Provider;
+ using Provider = ModPlatform::ResourceProvider;
auto update_table = table["update"];
if (!update_table || !update_table.is_table()) {
diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h
index 9754e5c4..4b096eec 100644
--- a/launcher/modplatform/packwiz/Packwiz.h
+++ b/launcher/modplatform/packwiz/Packwiz.h
@@ -49,7 +49,7 @@ class V1 {
QString hash {};
// [update]
- ModPlatform::Provider provider {};
+ ModPlatform::ResourceProvider provider {};
QVariant file_id {};
QVariant project_id {};
diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp
index 6438d9ef..8fd43d21 100644
--- a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp
+++ b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp
@@ -44,7 +44,7 @@ void Technic::SingleZipPackInstallTask::executeTask()
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
entry->setStale(true);
- m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network());
+ m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
m_archivePath = entry->getFullPath();
auto job = m_filesNetJob.get();
@@ -130,7 +130,7 @@ void Technic::SingleZipPackInstallTask::extractFinished()
}
}
- shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
+ auto packProcessor = makeShared<Technic::TechnicPackProcessor>();
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &Technic::SingleZipPackInstallTask::emitSucceeded);
connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &Technic::SingleZipPackInstallTask::emitFailed);
packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath, m_minecraftVersion);
diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp
index 19731b38..77c503f0 100644
--- a/launcher/modplatform/technic/SolderPackInstallTask.cpp
+++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp
@@ -70,7 +70,7 @@ void Technic::SolderPackInstallTask::executeTask()
{
setStatus(tr("Resolving modpack files"));
- m_filesNetJob = new NetJob(tr("Resolving modpack files"), m_network);
+ m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network));
auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version);
m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, &m_response));
@@ -107,7 +107,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
if (!build.minecraft.isEmpty())
m_minecraftVersion = build.minecraft;
- m_filesNetJob = new NetJob(tr("Downloading modpack"), m_network);
+ m_filesNetJob.reset(new NetJob(tr("Downloading modpack"), m_network));
int i = 0;
for (const auto &mod : build.mods) {
@@ -219,7 +219,7 @@ void Technic::SolderPackInstallTask::extractFinished()
}
}
- shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
+ auto packProcessor = makeShared<Technic::TechnicPackProcessor>();
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &Technic::SolderPackInstallTask::emitSucceeded);
connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &Technic::SolderPackInstallTask::emitFailed);
packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath, m_minecraftVersion, true);