removed some duplicate code
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
@@ -488,6 +488,7 @@ set(API_SOURCES
|
||||
modplatform/ResourceType.cpp
|
||||
|
||||
modplatform/ResourceAPI.h
|
||||
modplatform/ResourceAPI.cpp
|
||||
|
||||
modplatform/EnsureMetadataTask.h
|
||||
modplatform/EnsureMetadataTask.cpp
|
||||
@@ -498,8 +499,6 @@ set(API_SOURCES
|
||||
modplatform/flame/FlameAPI.cpp
|
||||
modplatform/modrinth/ModrinthAPI.h
|
||||
modplatform/modrinth/ModrinthAPI.cpp
|
||||
modplatform/helpers/NetworkResourceAPI.h
|
||||
modplatform/helpers/NetworkResourceAPI.cpp
|
||||
modplatform/helpers/HashUtils.h
|
||||
modplatform/helpers/HashUtils.cpp
|
||||
modplatform/helpers/OverrideUtils.h
|
||||
@@ -527,8 +526,6 @@ set(FTB_SOURCES
|
||||
|
||||
set(FLAME_SOURCES
|
||||
# Flame
|
||||
modplatform/flame/FlamePackIndex.cpp
|
||||
modplatform/flame/FlamePackIndex.h
|
||||
modplatform/flame/FlameModIndex.cpp
|
||||
modplatform/flame/FlameModIndex.h
|
||||
modplatform/flame/PackManifest.h
|
||||
@@ -546,8 +543,6 @@ set(FLAME_SOURCES
|
||||
set(MODRINTH_SOURCES
|
||||
modplatform/modrinth/ModrinthPackIndex.cpp
|
||||
modplatform/modrinth/ModrinthPackIndex.h
|
||||
modplatform/modrinth/ModrinthPackManifest.cpp
|
||||
modplatform/modrinth/ModrinthPackManifest.h
|
||||
modplatform/modrinth/ModrinthCheckUpdate.cpp
|
||||
modplatform/modrinth/ModrinthCheckUpdate.h
|
||||
modplatform/modrinth/ModrinthInstanceCreationTask.cpp
|
||||
@@ -1035,8 +1030,6 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/modplatform/OptionalModDialog.cpp
|
||||
ui/pages/modplatform/OptionalModDialog.h
|
||||
|
||||
ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp
|
||||
ui/pages/modplatform/modrinth/ModrinthResourceModels.h
|
||||
ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp
|
||||
ui/pages/modplatform/modrinth/ModrinthResourcePages.h
|
||||
|
||||
|
||||
@@ -27,12 +27,8 @@
|
||||
#include "minecraft/mod/MetadataHandler.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "ui/pages/modplatform/ModModel.h"
|
||||
#include "ui/pages/modplatform/flame/FlameResourceModels.h"
|
||||
#include "ui/pages/modplatform/modrinth/ModrinthResourceModels.h"
|
||||
|
||||
static Version mcVersion(BaseInstance* inst)
|
||||
{
|
||||
@@ -55,14 +51,7 @@ static bool checkDependencies(std::shared_ptr<GetModDependenciesTask::PackDepend
|
||||
GetModDependenciesTask::GetModDependenciesTask(BaseInstance* instance,
|
||||
ModFolderModel* folder,
|
||||
QList<std::shared_ptr<PackDependency>> selected)
|
||||
: SequentialTask(tr("Get dependencies"))
|
||||
, m_selected(selected)
|
||||
, m_flame_provider{ ModPlatform::ResourceProvider::FLAME, std::make_shared<ResourceDownload::FlameModModel>(*instance),
|
||||
std::make_shared<FlameAPI>() }
|
||||
, m_modrinth_provider{ ModPlatform::ResourceProvider::MODRINTH, std::make_shared<ResourceDownload::ModrinthModModel>(*instance),
|
||||
std::make_shared<ModrinthAPI>() }
|
||||
, m_version(mcVersion(instance))
|
||||
, m_loaderType(mcLoaders(instance))
|
||||
: SequentialTask(tr("Get dependencies")), m_selected(selected), m_version(mcVersion(instance)), m_loaderType(mcLoaders(instance))
|
||||
{
|
||||
for (auto mod : folder->allMods()) {
|
||||
m_mods_file_names << mod->fileinfo().fileName();
|
||||
@@ -144,9 +133,9 @@ QList<ModPlatform::Dependency> GetModDependenciesTask::getDependenciesForVersion
|
||||
|
||||
Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDependency> pDep)
|
||||
{
|
||||
auto provider = pDep->pack->provider == m_flame_provider.name ? m_flame_provider : m_modrinth_provider;
|
||||
auto provider = pDep->pack->provider;
|
||||
auto responseInfo = std::make_shared<QByteArray>();
|
||||
auto info = provider.api->getProject(pDep->pack->addonId.toString(), responseInfo);
|
||||
auto info = getAPI(provider)->getProject(pDep->pack->addonId.toString(), responseInfo);
|
||||
connect(info.get(), &NetJob::succeeded, [this, responseInfo, provider, pDep] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error);
|
||||
@@ -158,9 +147,10 @@ Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDepende
|
||||
return;
|
||||
}
|
||||
try {
|
||||
auto obj = provider.name == ModPlatform::ResourceProvider::FLAME ? Json::requireObject(Json::requireObject(doc), "data")
|
||||
: Json::requireObject(doc);
|
||||
provider.mod->loadIndexedPack(*pDep->pack, obj);
|
||||
auto obj = provider == ModPlatform::ResourceProvider::FLAME ? Json::requireObject(Json::requireObject(doc), "data")
|
||||
: Json::requireObject(doc);
|
||||
|
||||
getAPI(provider)->loadIndexedPack(*pDep->pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
removePack(pDep->pack->addonId);
|
||||
qDebug() << doc;
|
||||
@@ -181,7 +171,8 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
pDep->pack->provider = providerName;
|
||||
|
||||
m_pack_dependencies.append(pDep);
|
||||
auto provider = providerName == m_flame_provider.name ? m_flame_provider : m_modrinth_provider;
|
||||
|
||||
auto provider = providerName;
|
||||
|
||||
auto tasks = makeShared<SequentialTask>(
|
||||
QString("DependencyInfo: %1").arg(dep.addonId.toString().isEmpty() ? dep.version : dep.addonId.toString()));
|
||||
@@ -191,46 +182,30 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
}
|
||||
|
||||
ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType };
|
||||
ResourceAPI::DependencySearchCallbacks callbacks;
|
||||
ResourceAPI::Callback<ModPlatform::IndexedVersion> callbacks;
|
||||
callbacks.on_fail = [](QString reason, int) {
|
||||
qCritical() << tr("A network error occurred. Could not load project dependencies:%1").arg(reason);
|
||||
};
|
||||
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) {
|
||||
try {
|
||||
QJsonArray arr;
|
||||
if (dep.version.length() != 0 && doc.isObject()) {
|
||||
arr.append(doc.object());
|
||||
} else {
|
||||
arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array();
|
||||
}
|
||||
pDep->version = provider.mod->loadDependencyVersions(dep, arr);
|
||||
if (!pDep->version.addonId.isValid()) {
|
||||
if (m_loaderType & ModPlatform::Quilt) { // falback for quilt
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, provider](const auto& o) {
|
||||
return o.provider == provider.name && dep.addonId == o.quilt;
|
||||
});
|
||||
if (over != overide.cend()) {
|
||||
removePack(dep.addonId);
|
||||
addTask(prepareDependencyTask({ over->fabric, dep.type }, provider.name, level));
|
||||
return;
|
||||
}
|
||||
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& pack) {
|
||||
pDep->version = pack;
|
||||
if (!pDep->version.addonId.isValid()) {
|
||||
if (m_loaderType & ModPlatform::Quilt) { // falback for quilt
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(),
|
||||
[dep, provider](auto o) { return o.provider == provider && dep.addonId == o.quilt; });
|
||||
if (over != overide.cend()) {
|
||||
removePack(dep.addonId);
|
||||
addTask(prepareDependencyTask({ over->fabric, dep.type }, provider, level));
|
||||
return;
|
||||
}
|
||||
removePack(dep.addonId);
|
||||
qWarning() << "Error while reading mod version empty ";
|
||||
qDebug() << doc;
|
||||
return;
|
||||
}
|
||||
pDep->version.is_currently_selected = true;
|
||||
pDep->pack->versions = { pDep->version };
|
||||
pDep->pack->versionsLoaded = true;
|
||||
|
||||
} catch (const JSONValidationError& e) {
|
||||
removePack(dep.addonId);
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading mod version: " << e.cause();
|
||||
return;
|
||||
}
|
||||
pDep->version.is_currently_selected = true;
|
||||
pDep->pack->versions = { pDep->version };
|
||||
pDep->pack->versionsLoaded = true;
|
||||
|
||||
if (level == 0) {
|
||||
removePack(dep.addonId);
|
||||
qWarning() << "Dependency cycle exceeded";
|
||||
@@ -238,10 +213,10 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
}
|
||||
if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) {
|
||||
pDep->pack->addonId = pDep->version.addonId;
|
||||
auto dep_ = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name);
|
||||
auto dep_ = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider);
|
||||
if (dep_.addonId != pDep->version.addonId) {
|
||||
removePack(pDep->version.addonId);
|
||||
addTask(prepareDependencyTask(dep_, provider.name, level));
|
||||
addTask(prepareDependencyTask(dep_, provider, level));
|
||||
} else {
|
||||
addTask(getProjectInfoTask(pDep));
|
||||
}
|
||||
@@ -250,12 +225,12 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
removePack(pDep->version.addonId);
|
||||
return;
|
||||
}
|
||||
for (auto dep_ : getDependenciesForVersion(pDep->version, provider.name)) {
|
||||
addTask(prepareDependencyTask(dep_, provider.name, level - 1));
|
||||
for (auto dep_ : getDependenciesForVersion(pDep->version, provider)) {
|
||||
addTask(prepareDependencyTask(dep_, provider, level - 1));
|
||||
}
|
||||
};
|
||||
|
||||
auto version = provider.api->getDependencyVersion(std::move(args), std::move(callbacks));
|
||||
auto version = getAPI(provider)->getDependencyVersion(std::move(args), std::move(callbacks));
|
||||
tasks->addTask(version);
|
||||
return tasks;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "ui/pages/modplatform/ModModel.h"
|
||||
@@ -54,17 +56,20 @@ class GetModDependenciesTask : public SequentialTask {
|
||||
QStringList required_by;
|
||||
};
|
||||
|
||||
struct Provider {
|
||||
ModPlatform::ResourceProvider name;
|
||||
std::shared_ptr<ResourceDownload::ModModel> mod;
|
||||
std::shared_ptr<ResourceAPI> api;
|
||||
};
|
||||
|
||||
explicit GetModDependenciesTask(BaseInstance* instance, ModFolderModel* folder, QList<std::shared_ptr<PackDependency>> selected);
|
||||
|
||||
auto getDependecies() const -> QList<std::shared_ptr<PackDependency>> { return m_pack_dependencies; }
|
||||
QHash<QString, PackDependencyExtraInfo> getExtraInfo();
|
||||
|
||||
private:
|
||||
inline ResourceAPI* getAPI(ModPlatform::ResourceProvider provider)
|
||||
{
|
||||
if (provider == ModPlatform::ResourceProvider::FLAME)
|
||||
return &m_flameAPI;
|
||||
else
|
||||
return &m_modrinthAPI;
|
||||
}
|
||||
|
||||
protected slots:
|
||||
Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, ModPlatform::ResourceProvider, int);
|
||||
QList<ModPlatform::Dependency> getDependenciesForVersion(const ModPlatform::IndexedVersion&,
|
||||
@@ -82,9 +87,10 @@ class GetModDependenciesTask : public SequentialTask {
|
||||
QList<std::shared_ptr<Metadata::ModStruct>> m_mods;
|
||||
QList<std::shared_ptr<PackDependency>> m_selected;
|
||||
QStringList m_mods_file_names;
|
||||
Provider m_flame_provider;
|
||||
Provider m_modrinth_provider;
|
||||
|
||||
Version m_version;
|
||||
ModPlatform::ModLoaderTypes m_loaderType;
|
||||
|
||||
ModrinthAPI m_modrinthAPI;
|
||||
FlameAPI m_flameAPI;
|
||||
};
|
||||
|
||||
@@ -128,6 +128,23 @@ struct IndexedVersion {
|
||||
|
||||
// For internal use, not provided by APIs
|
||||
bool is_currently_selected = false;
|
||||
|
||||
QString getVersionDisplayString() const
|
||||
{
|
||||
auto release_type = version_type.isValid() ? QString(" [%1]").arg(version_type.toString()) : "";
|
||||
auto versionStr = !version.contains(version_number) ? version_number : "";
|
||||
QString gameVersion = "";
|
||||
for (auto v : mcVersion) {
|
||||
if (version.contains(v)) {
|
||||
gameVersion = "";
|
||||
break;
|
||||
}
|
||||
if (gameVersion.isEmpty()) {
|
||||
gameVersion = QObject::tr(" for %1").arg(v);
|
||||
}
|
||||
}
|
||||
return QString("%1%2 — %3%4").arg(version, gameVersion, versionStr, release_type);
|
||||
}
|
||||
};
|
||||
|
||||
struct ExtraPackData {
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
// SPDX-FileCopyrightText: 2023 flowln <flowlnlnln@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "NetworkResourceAPI.h"
|
||||
#include <memory>
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "Json.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const
|
||||
Task::Ptr ResourceAPI::searchProjects(SearchArgs&& args, Callback<QList<ModPlatform::IndexedPack::Ptr>>&& callbacks) const
|
||||
{
|
||||
auto search_url_optional = getSearchURL(args);
|
||||
if (!search_url_optional.has_value()) {
|
||||
@@ -40,7 +36,23 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
|
||||
return;
|
||||
}
|
||||
|
||||
callbacks.on_succeed(doc);
|
||||
QList<ModPlatform::IndexedPack::Ptr> newList;
|
||||
auto packs = documentToArray(doc);
|
||||
|
||||
for (auto packRaw : packs) {
|
||||
auto packObj = packRaw.toObject();
|
||||
|
||||
ModPlatform::IndexedPack::Ptr pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
try {
|
||||
loadIndexedPack(*pack, packObj);
|
||||
newList << pack;
|
||||
} catch (const JSONValidationError& e) {
|
||||
qWarning() << "Error while loading resource from " << debugName() << ": " << e.cause();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
callbacks.on_succeed(newList);
|
||||
});
|
||||
|
||||
// Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues.
|
||||
@@ -60,29 +72,7 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&
|
||||
return netJob;
|
||||
}
|
||||
|
||||
Task::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfoCallbacks&& callbacks) const
|
||||
{
|
||||
auto response = std::make_shared<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);
|
||||
});
|
||||
QObject::connect(job.get(), &NetJob::failed, [callbacks](QString reason) { callbacks.on_fail(reason); });
|
||||
QObject::connect(job.get(), &NetJob::aborted, [callbacks] { callbacks.on_abort(); });
|
||||
return job;
|
||||
}
|
||||
|
||||
Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, VersionSearchCallbacks&& callbacks) const
|
||||
Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback<QVector<ModPlatform::IndexedVersion>>&& callbacks) const
|
||||
{
|
||||
auto versions_url_optional = getVersionsURL(args);
|
||||
if (!versions_url_optional.has_value())
|
||||
@@ -95,7 +85,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
|
||||
|
||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response));
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, [response, callbacks, args] {
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks, args] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
@@ -105,7 +95,32 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
|
||||
return;
|
||||
}
|
||||
|
||||
callbacks.on_succeed(doc, args.pack);
|
||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||
try {
|
||||
auto arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array();
|
||||
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
|
||||
auto file = loadIndexedPackVersion(obj, args.resourceType);
|
||||
if (!file.addonId.isValid())
|
||||
file.addonId = args.pack.addonId;
|
||||
|
||||
if (file.fileId.isValid() && !file.downloadUrl.isEmpty()) // Heuristic to check if the returned value is valid
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading " << debugName() << " resource version: " << e.cause();
|
||||
}
|
||||
|
||||
callbacks.on_succeed(unsortedVersions);
|
||||
});
|
||||
|
||||
// Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues.
|
||||
@@ -120,11 +135,147 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi
|
||||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
});
|
||||
QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] { callbacks.on_abort(); });
|
||||
|
||||
return netJob;
|
||||
}
|
||||
|
||||
Task::Ptr NetworkResourceAPI::getProject(QString addonId, std::shared_ptr<QByteArray> response) const
|
||||
Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback<ModPlatform::IndexedPack>&& callbacks) const
|
||||
{
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
auto job = getProject(args.pack.addonId.toString(), response);
|
||||
|
||||
QObject::connect(job.get(), &NetJob::succeeded, [this, response, callbacks, args] {
|
||||
auto pack = args.pack;
|
||||
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;
|
||||
}
|
||||
try {
|
||||
auto obj = Json::requireObject(doc);
|
||||
if (obj.contains("data"))
|
||||
obj = Json::requireObject(obj, "data");
|
||||
loadIndexedPack(pack, obj);
|
||||
loadExtraPackInfo(pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading " << debugName() << " resource info: " << e.cause();
|
||||
}
|
||||
callbacks.on_succeed(pack);
|
||||
});
|
||||
// Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues.
|
||||
// This prevents the lambda from extending the lifetime of the shared resource,
|
||||
// as it only temporarily locks the resource when needed.
|
||||
auto weak = job.toWeakRef();
|
||||
QObject::connect(job.get(), &NetJob::failed, [weak, callbacks](const QString& reason) {
|
||||
int network_error_code = -1;
|
||||
if (auto job = weak.lock()) {
|
||||
if (auto netJob = qSharedPointerDynamicCast<NetJob>(job)) {
|
||||
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action) {
|
||||
network_error_code = failed_action->replyStatusCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
});
|
||||
QObject::connect(job.get(), &NetJob::aborted, [callbacks] { callbacks.on_abort(); });
|
||||
return job;
|
||||
}
|
||||
|
||||
Task::Ptr ResourceAPI::getDependencyVersion(DependencySearchArgs&& args, Callback<ModPlatform::IndexedVersion>&& callbacks) const
|
||||
{
|
||||
auto versions_url_optional = getDependencyURL(args);
|
||||
if (!versions_url_optional.has_value())
|
||||
return nullptr;
|
||||
|
||||
auto versions_url = versions_url_optional.value();
|
||||
|
||||
auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network());
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response));
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, [this, 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 getting versions at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonArray arr;
|
||||
if (args.dependency.version.length() != 0 && doc.isObject()) {
|
||||
arr.append(doc.object());
|
||||
} else {
|
||||
arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array();
|
||||
}
|
||||
|
||||
QVector<ModPlatform::IndexedVersion> versions;
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
|
||||
auto file = loadIndexedPackVersion(obj, ModPlatform::ResourceType::Mod);
|
||||
if (!file.addonId.isValid())
|
||||
file.addonId = args.dependency.addonId;
|
||||
|
||||
if (file.fileId.isValid() &&
|
||||
(!file.loaders || args.loader & file.loaders)) // Heuristic to check if the returned value is valid
|
||||
versions.append(file);
|
||||
}
|
||||
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
||||
auto bestMatch = versions.size() != 0 ? versions.front() : ModPlatform::IndexedVersion();
|
||||
callbacks.on_succeed(bestMatch);
|
||||
});
|
||||
|
||||
// Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues.
|
||||
// This prevents the lambda from extending the lifetime of the shared resource,
|
||||
// as it only temporarily locks the resource when needed.
|
||||
auto weak = netJob.toWeakRef();
|
||||
QObject::connect(netJob.get(), &NetJob::failed, [weak, callbacks](const QString& reason) {
|
||||
int network_error_code = -1;
|
||||
if (auto netJob = weak.lock()) {
|
||||
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action)
|
||||
network_error_code = failed_action->replyStatusCode();
|
||||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
});
|
||||
return netJob;
|
||||
}
|
||||
|
||||
QString ResourceAPI::getGameVersionsString(std::list<Version> mcVersions) const
|
||||
{
|
||||
QString s;
|
||||
for (auto& ver : mcVersions) {
|
||||
s += QString("\"%1\",").arg(mapMCVersionToModrinth(ver));
|
||||
}
|
||||
s.remove(s.length() - 1, 1); // remove last comma
|
||||
return s;
|
||||
}
|
||||
|
||||
QString ResourceAPI::mapMCVersionToModrinth(Version v) const
|
||||
{
|
||||
static const QString preString = " Pre-Release ";
|
||||
auto verStr = v.toString();
|
||||
|
||||
if (verStr.contains(preString)) {
|
||||
verStr.replace(preString, "-pre");
|
||||
}
|
||||
verStr.replace(" ", "-");
|
||||
return verStr;
|
||||
}
|
||||
|
||||
Task::Ptr ResourceAPI::getProject(QString addonId, std::shared_ptr<QByteArray> response) const
|
||||
{
|
||||
auto project_url_optional = getInfoURL(addonId);
|
||||
if (!project_url_optional.has_value())
|
||||
@@ -138,44 +289,3 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, std::shared_ptr<QByteA
|
||||
|
||||
return netJob;
|
||||
}
|
||||
|
||||
Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args, DependencySearchCallbacks&& callbacks) const
|
||||
{
|
||||
auto versions_url_optional = getDependencyURL(args);
|
||||
if (!versions_url_optional.has_value())
|
||||
return nullptr;
|
||||
|
||||
auto versions_url = versions_url_optional.value();
|
||||
|
||||
auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network());
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response));
|
||||
|
||||
QObject::connect(netJob.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 getting versions at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
callbacks.on_succeed(doc, args.dependency);
|
||||
});
|
||||
|
||||
// Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues.
|
||||
// This prevents the lambda from extending the lifetime of the shared resource,
|
||||
// as it only temporarily locks the resource when needed.
|
||||
auto weak = netJob.toWeakRef();
|
||||
QObject::connect(netJob.get(), &NetJob::failed, [weak, callbacks](const QString& reason) {
|
||||
int network_error_code = -1;
|
||||
if (auto netJob = weak.lock()) {
|
||||
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action)
|
||||
network_error_code = failed_action->replyStatusCode();
|
||||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
});
|
||||
return netJob;
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
* Copyright (c) 2023-2025 Trial97 <alexandru.tripon97@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
|
||||
@@ -67,6 +67,13 @@ class ResourceAPI {
|
||||
QString readable_name;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Callback {
|
||||
std::function<void(T&)> on_succeed;
|
||||
std::function<void(QString const& reason, int network_error_code)> on_fail;
|
||||
std::function<void()> on_abort;
|
||||
};
|
||||
|
||||
struct SearchArgs {
|
||||
ModPlatform::ResourceType type{};
|
||||
int offset = 0;
|
||||
@@ -79,31 +86,18 @@ class ResourceAPI {
|
||||
std::optional<QStringList> categoryIds;
|
||||
bool openSource;
|
||||
};
|
||||
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<ModPlatform::ModLoaderTypes> loaders;
|
||||
};
|
||||
struct VersionSearchCallbacks {
|
||||
std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
|
||||
std::function<void(QString const& reason, int network_error_code)> on_fail;
|
||||
ModPlatform::ResourceType resourceType;
|
||||
};
|
||||
|
||||
struct ProjectInfoArgs {
|
||||
ModPlatform::IndexedPack pack;
|
||||
};
|
||||
struct ProjectInfoCallbacks {
|
||||
std::function<void(QJsonDocument&, const ModPlatform::IndexedPack&)> on_succeed;
|
||||
std::function<void(QString const& reason)> on_fail;
|
||||
std::function<void()> on_abort;
|
||||
};
|
||||
|
||||
struct DependencySearchArgs {
|
||||
ModPlatform::Dependency dependency;
|
||||
@@ -111,73 +105,52 @@ class ResourceAPI {
|
||||
ModPlatform::ModLoaderTypes loader;
|
||||
};
|
||||
|
||||
struct DependencySearchCallbacks {
|
||||
std::function<void(QJsonDocument&, const ModPlatform::Dependency&)> on_succeed;
|
||||
std::function<void(QString const& reason, int network_error_code)> on_fail;
|
||||
};
|
||||
|
||||
public:
|
||||
/** Gets a list of available sorting methods for this API. */
|
||||
virtual auto getSortingMethods() const -> QList<SortingMethod> = 0;
|
||||
|
||||
public slots:
|
||||
[[nodiscard]] virtual Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const
|
||||
{
|
||||
qWarning() << "TODO: ResourceAPI::searchProjects";
|
||||
return nullptr;
|
||||
}
|
||||
virtual Task::Ptr getProject([[maybe_unused]] QString addonId,
|
||||
[[maybe_unused]] std::shared_ptr<QByteArray> response) const
|
||||
{
|
||||
qWarning() << "TODO: ResourceAPI::getProject";
|
||||
return nullptr;
|
||||
}
|
||||
virtual Task::Ptr getProjects([[maybe_unused]] QStringList addonIds,
|
||||
[[maybe_unused]] std::shared_ptr<QByteArray> response) const
|
||||
{
|
||||
qWarning() << "TODO: ResourceAPI::getProjects";
|
||||
return nullptr;
|
||||
}
|
||||
virtual Task::Ptr searchProjects(SearchArgs&&, Callback<QList<ModPlatform::IndexedPack::Ptr>>&&) const;
|
||||
|
||||
virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const
|
||||
{
|
||||
qWarning() << "TODO: ResourceAPI::getProjectInfo";
|
||||
return nullptr;
|
||||
}
|
||||
virtual Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const
|
||||
{
|
||||
qWarning() << "TODO: ResourceAPI::getProjectVersions";
|
||||
return nullptr;
|
||||
}
|
||||
virtual Task::Ptr getProject(QString addonId, std::shared_ptr<QByteArray> response) const;
|
||||
virtual Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const = 0;
|
||||
|
||||
virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, DependencySearchCallbacks&&) const
|
||||
{
|
||||
qWarning() << "TODO";
|
||||
return nullptr;
|
||||
}
|
||||
virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback<ModPlatform::IndexedPack>&&) const;
|
||||
Task::Ptr getProjectVersions(VersionSearchArgs&& args, Callback<QVector<ModPlatform::IndexedVersion>>&& callbacks) const;
|
||||
virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, Callback<ModPlatform::IndexedVersion>&&) const;
|
||||
|
||||
protected:
|
||||
inline QString debugName() const { return "External resource API"; }
|
||||
|
||||
inline QString mapMCVersionToModrinth(Version v) const
|
||||
{
|
||||
static const QString preString = " Pre-Release ";
|
||||
auto verStr = v.toString();
|
||||
QString mapMCVersionToModrinth(Version v) const;
|
||||
|
||||
if (verStr.contains(preString)) {
|
||||
verStr.replace(preString, "-pre");
|
||||
}
|
||||
verStr.replace(" ", "-");
|
||||
return verStr;
|
||||
}
|
||||
QString getGameVersionsString(std::list<Version> mcVersions) const;
|
||||
|
||||
inline QString getGameVersionsString(std::list<Version> mcVersions) const
|
||||
{
|
||||
QString s;
|
||||
for (auto& ver : mcVersions) {
|
||||
s += QString("\"%1\",").arg(mapMCVersionToModrinth(ver));
|
||||
}
|
||||
s.remove(s.length() - 1, 1); // remove last comma
|
||||
return s;
|
||||
}
|
||||
public:
|
||||
virtual auto getSearchURL(SearchArgs const& args) const -> std::optional<QString> = 0;
|
||||
virtual auto getInfoURL(QString const& id) const -> std::optional<QString> = 0;
|
||||
virtual auto getVersionsURL(VersionSearchArgs const& args) const -> std::optional<QString> = 0;
|
||||
virtual auto getDependencyURL(DependencySearchArgs const& args) const -> std::optional<QString> = 0;
|
||||
|
||||
/** Functions to load data into a pack.
|
||||
*
|
||||
* Those are needed for the same reason as documentToArray, and NEED to be re-implemented in the same way.
|
||||
*/
|
||||
|
||||
virtual void loadIndexedPack(ModPlatform::IndexedPack&, QJsonObject&) const = 0;
|
||||
virtual ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, ModPlatform::ResourceType) const = 0;
|
||||
|
||||
/** Converts a JSON document to a common array format.
|
||||
*
|
||||
* This is needed so that different providers, with different JSON structures, can be parsed
|
||||
* uniformally. You NEED to re-implement this if you intend on using the default callbacks.
|
||||
*/
|
||||
virtual QJsonArray documentToArray(QJsonDocument& obj) const = 0;
|
||||
|
||||
/** Functions to load data into a pack.
|
||||
*
|
||||
* Those are needed for the same reason as documentToArray, and NEED to be re-implemented in the same way.
|
||||
*/
|
||||
|
||||
virtual void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&) const = 0;
|
||||
};
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
#include <QList>
|
||||
#include <memory>
|
||||
#include "BuildConfig.h"
|
||||
#include "Json.h"
|
||||
#include "Version.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "modplatform/helpers/NetworkResourceAPI.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
|
||||
class FlameAPI : public NetworkResourceAPI {
|
||||
class FlameAPI : public ResourceAPI {
|
||||
public:
|
||||
QString getModFileChangelog(int modId, int fileId);
|
||||
QString getModDescription(int modId);
|
||||
@@ -138,6 +140,25 @@ class FlameAPI : public NetworkResourceAPI {
|
||||
return url;
|
||||
}
|
||||
|
||||
QJsonArray documentToArray(QJsonDocument& obj) const override { return Json::ensureArray(obj.object(), "data"); }
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) const override { FlameMod::loadIndexedPack(m, obj); }
|
||||
ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, ModPlatform::ResourceType resourceType) const override
|
||||
{
|
||||
auto arr = FlameMod::loadIndexedPackVersion(obj);
|
||||
if (resourceType != ModPlatform::ResourceType::TexturePack) {
|
||||
return arr;
|
||||
}
|
||||
// FIXME: Client-side version filtering. This won't take into account any user-selected filtering.
|
||||
auto const& mc_versions = arr.mcVersion;
|
||||
|
||||
if (std::any_of(mc_versions.constBegin(), mc_versions.constEnd(),
|
||||
[](auto const& mc_version) { return Version(mc_version) <= Version("1.6"); })) {
|
||||
return arr;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, [[maybe_unused]] QJsonObject&) const override { FlameMod::loadBody(m); }
|
||||
|
||||
private:
|
||||
std::optional<QString> getInfoURL(QString const& id) const override
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@ void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
pack.extraDataLoaded = true;
|
||||
}
|
||||
|
||||
void FlameMod::loadBody(ModPlatform::IndexedPack& pack, [[maybe_unused]] QJsonObject& obj)
|
||||
void FlameMod::loadBody(ModPlatform::IndexedPack& pack)
|
||||
{
|
||||
pack.extraData.body = api.getModDescription(pack.addonId.toInt());
|
||||
|
||||
@@ -204,31 +204,3 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst)
|
||||
{
|
||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||
auto loaders = profile->getSupportedModLoaders();
|
||||
QList<ModPlatform::IndexedVersion> versions;
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
|
||||
auto file = loadIndexedPackVersion(obj);
|
||||
if (!file.addonId.isValid())
|
||||
file.addonId = m.addonId;
|
||||
|
||||
if (file.fileId.isValid() &&
|
||||
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
|
||||
versions.append(file);
|
||||
}
|
||||
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
||||
if (versions.size() != 0)
|
||||
return versions.front();
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ namespace FlameMod {
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadURLs(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadBody(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadBody(ModPlatform::IndexedPack& m);
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr);
|
||||
ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false);
|
||||
ModPlatform::IndexedVersion loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst);
|
||||
} // namespace FlameMod
|
||||
@@ -1,150 +0,0 @@
|
||||
#include "FlamePackIndex.h"
|
||||
#include <QFileInfo>
|
||||
#include <QUrl>
|
||||
|
||||
#include "Json.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
void Flame::loadIndexedPack(Flame::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.addonId = Json::requireInteger(obj, "id");
|
||||
pack.name = Json::requireString(obj, "name");
|
||||
pack.description = Json::ensureString(obj, "summary", "");
|
||||
|
||||
auto logo = Json::requireObject(obj, "logo");
|
||||
pack.logoUrl = Json::requireString(logo, "thumbnailUrl");
|
||||
pack.logoName = Json::requireString(obj, "slug") + "." + QFileInfo(QUrl(pack.logoUrl).fileName()).suffix();
|
||||
|
||||
auto authors = Json::requireArray(obj, "authors");
|
||||
for (auto authorIter : authors) {
|
||||
auto author = Json::requireObject(authorIter);
|
||||
Flame::ModpackAuthor packAuthor;
|
||||
packAuthor.name = Json::requireString(author, "name");
|
||||
packAuthor.url = Json::requireString(author, "url");
|
||||
pack.authors.append(packAuthor);
|
||||
}
|
||||
int defaultFileId = Json::requireInteger(obj, "mainFileId");
|
||||
|
||||
bool found = false;
|
||||
// check if there are some files before adding the pack
|
||||
auto files = Json::requireArray(obj, "latestFiles");
|
||||
for (auto fileIter : files) {
|
||||
auto file = Json::requireObject(fileIter);
|
||||
int id = Json::requireInteger(file, "id");
|
||||
|
||||
// NOTE: for now, ignore everything that's not the default...
|
||||
if (id != defaultFileId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto versionArray = Json::requireArray(file, "gameVersions");
|
||||
if (versionArray.size() < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
throw JSONValidationError(QString("Pack with no good file, skipping: %1").arg(pack.name));
|
||||
}
|
||||
|
||||
loadIndexedInfo(pack, obj);
|
||||
}
|
||||
|
||||
void Flame::loadIndexedInfo(IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
auto links_obj = Json::ensureObject(obj, "links");
|
||||
|
||||
pack.extra.websiteUrl = Json::ensureString(links_obj, "websiteUrl");
|
||||
if (pack.extra.websiteUrl.endsWith('/'))
|
||||
pack.extra.websiteUrl.chop(1);
|
||||
|
||||
pack.extra.issuesUrl = Json::ensureString(links_obj, "issuesUrl");
|
||||
if (pack.extra.issuesUrl.endsWith('/'))
|
||||
pack.extra.issuesUrl.chop(1);
|
||||
|
||||
pack.extra.sourceUrl = Json::ensureString(links_obj, "sourceUrl");
|
||||
if (pack.extra.sourceUrl.endsWith('/'))
|
||||
pack.extra.sourceUrl.chop(1);
|
||||
|
||||
pack.extra.wikiUrl = Json::ensureString(links_obj, "wikiUrl");
|
||||
if (pack.extra.wikiUrl.endsWith('/'))
|
||||
pack.extra.wikiUrl.chop(1);
|
||||
|
||||
pack.extraInfoLoaded = true;
|
||||
}
|
||||
|
||||
void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr)
|
||||
{
|
||||
QList<Flame::IndexedVersion> unsortedVersions;
|
||||
for (auto versionIter : arr) {
|
||||
auto version = Json::requireObject(versionIter);
|
||||
Flame::IndexedVersion file;
|
||||
|
||||
file.addonId = pack.addonId;
|
||||
file.fileId = Json::requireInteger(version, "id");
|
||||
auto versionArray = Json::requireArray(version, "gameVersions");
|
||||
if (versionArray.size() < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto mcVer : versionArray) {
|
||||
auto str = mcVer.toString();
|
||||
|
||||
if (str.contains('.'))
|
||||
file.mcVersion.append(str);
|
||||
|
||||
if (auto loader = str.toLower(); loader == "neoforge")
|
||||
file.loaders |= ModPlatform::NeoForge;
|
||||
else if (loader == "forge")
|
||||
file.loaders |= ModPlatform::Forge;
|
||||
else if (loader == "cauldron")
|
||||
file.loaders |= ModPlatform::Cauldron;
|
||||
else if (loader == "liteloader")
|
||||
file.loaders |= ModPlatform::LiteLoader;
|
||||
else if (loader == "fabric")
|
||||
file.loaders |= ModPlatform::Fabric;
|
||||
else if (loader == "quilt")
|
||||
file.loaders |= ModPlatform::Quilt;
|
||||
}
|
||||
|
||||
// pick the latest version supported
|
||||
file.version = Json::requireString(version, "displayName");
|
||||
|
||||
ModPlatform::IndexedVersionType::VersionType ver_type;
|
||||
switch (Json::requireInteger(version, "releaseType")) {
|
||||
case 1:
|
||||
ver_type = ModPlatform::IndexedVersionType::VersionType::Release;
|
||||
break;
|
||||
case 2:
|
||||
ver_type = ModPlatform::IndexedVersionType::VersionType::Beta;
|
||||
break;
|
||||
case 3:
|
||||
ver_type = ModPlatform::IndexedVersionType::VersionType::Alpha;
|
||||
break;
|
||||
default:
|
||||
ver_type = ModPlatform::IndexedVersionType::VersionType::Unknown;
|
||||
}
|
||||
file.version_type = ModPlatform::IndexedVersionType(ver_type);
|
||||
file.downloadUrl = Json::ensureString(version, "downloadUrl");
|
||||
|
||||
// only add if we have a download URL (third party distribution is enabled)
|
||||
if (!file.downloadUrl.isEmpty()) {
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
}
|
||||
|
||||
auto orderSortPredicate = [](const IndexedVersion& a, const IndexedVersion& b) -> bool { return a.fileId > b.fileId; };
|
||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||
pack.versions = unsortedVersions;
|
||||
pack.versionsLoaded = true;
|
||||
}
|
||||
|
||||
auto Flame::getVersionDisplayString(const IndexedVersion& version) -> QString
|
||||
{
|
||||
auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : "";
|
||||
auto mcVersion =
|
||||
!version.mcVersion.isEmpty() && !version.version.contains(version.mcVersion) ? QObject::tr(" for %1").arg(version.mcVersion) : "";
|
||||
return QString("%1%2%3").arg(version.version, mcVersion, release_type);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
namespace Flame {
|
||||
|
||||
struct ModpackAuthor {
|
||||
QString name;
|
||||
QString url;
|
||||
};
|
||||
|
||||
struct IndexedVersion {
|
||||
int addonId;
|
||||
int fileId;
|
||||
QString version;
|
||||
ModPlatform::IndexedVersionType version_type;
|
||||
ModPlatform::ModLoaderTypes loaders = {};
|
||||
QString mcVersion;
|
||||
QString downloadUrl;
|
||||
};
|
||||
|
||||
struct ModpackExtra {
|
||||
QString websiteUrl;
|
||||
QString wikiUrl;
|
||||
QString issuesUrl;
|
||||
QString sourceUrl;
|
||||
};
|
||||
|
||||
struct IndexedPack {
|
||||
int addonId;
|
||||
QString name;
|
||||
QString description;
|
||||
QList<ModpackAuthor> authors;
|
||||
QString logoName;
|
||||
QString logoUrl;
|
||||
|
||||
bool versionsLoaded = false;
|
||||
QList<IndexedVersion> versions;
|
||||
|
||||
bool extraInfoLoaded = false;
|
||||
ModpackExtra extra;
|
||||
};
|
||||
|
||||
void loadIndexedPack(IndexedPack& m, QJsonObject& obj);
|
||||
void loadIndexedInfo(IndexedPack&, QJsonObject&);
|
||||
void loadIndexedPackVersions(IndexedPack& m, QJsonArray& arr);
|
||||
|
||||
auto getVersionDisplayString(const IndexedVersion&) -> QString;
|
||||
} // namespace Flame
|
||||
|
||||
Q_DECLARE_METATYPE(Flame::IndexedPack)
|
||||
Q_DECLARE_METATYPE(QList<Flame::IndexedVersion>)
|
||||
@@ -1,25 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 flowln <flowlnlnln@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
|
||||
class NetworkResourceAPI : public ResourceAPI {
|
||||
public:
|
||||
Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&&) const override;
|
||||
|
||||
Task::Ptr getProject(QString addonId, std::shared_ptr<QByteArray> response) const override;
|
||||
|
||||
Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override;
|
||||
Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override;
|
||||
Task::Ptr getDependencyVersion(DependencySearchArgs&&, DependencySearchCallbacks&&) const override;
|
||||
|
||||
protected:
|
||||
virtual auto getSearchURL(SearchArgs const& args) const -> std::optional<QString> = 0;
|
||||
virtual auto getInfoURL(QString const& id) const -> std::optional<QString> = 0;
|
||||
virtual auto getVersionsURL(VersionSearchArgs const& args) const -> std::optional<QString> = 0;
|
||||
virtual auto getDependencyURL(DependencySearchArgs const& args) const -> std::optional<QString> = 0;
|
||||
};
|
||||
@@ -5,12 +5,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "BuildConfig.h"
|
||||
#include "Json.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/helpers/NetworkResourceAPI.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
class ModrinthAPI : public NetworkResourceAPI {
|
||||
class ModrinthAPI : public ResourceAPI {
|
||||
public:
|
||||
Task::Ptr currentVersion(QString hash, QString hash_format, std::shared_ptr<QByteArray> response);
|
||||
|
||||
@@ -214,4 +216,12 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
.arg(mapMCVersionToModrinth(args.mcVersion))
|
||||
.arg(getModLoaderStrings(args.loader).join("\",\""));
|
||||
};
|
||||
|
||||
QJsonArray documentToArray(QJsonDocument& obj) const override { return obj.object().value("hits").toArray(); }
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) const override { Modrinth::loadIndexedPack(m, obj); }
|
||||
ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, ModPlatform::ResourceType) const override
|
||||
{
|
||||
return Modrinth::loadIndexedPackVersion(obj);
|
||||
};
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) const override { Modrinth::loadExtraPackData(m, obj); }
|
||||
};
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "modplatform/EnsureMetadataTask.h"
|
||||
#include "modplatform/helpers/OverrideUtils.h"
|
||||
|
||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
@@ -85,7 +84,7 @@ bool ModrinthCreationTask::updateInstance()
|
||||
QString old_index_path(FS::PathCombine(old_index_folder, "modrinth.index.json"));
|
||||
QFileInfo old_index_file(old_index_path);
|
||||
if (old_index_file.exists()) {
|
||||
std::vector<Modrinth::File> old_files;
|
||||
std::vector<File> old_files;
|
||||
parseManifest(old_index_path, old_files, false, false);
|
||||
|
||||
// Let's remove all duplicated, identical resources!
|
||||
@@ -356,7 +355,7 @@ bool ModrinthCreationTask::createInstance()
|
||||
}
|
||||
|
||||
bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
||||
std::vector<Modrinth::File>& files,
|
||||
std::vector<File>& files,
|
||||
bool set_internal_data,
|
||||
bool show_optional_dialog)
|
||||
{
|
||||
@@ -377,9 +376,9 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
||||
}
|
||||
|
||||
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
|
||||
std::vector<Modrinth::File> optionalFiles;
|
||||
std::vector<File> optionalFiles;
|
||||
for (const auto& modInfo : jsonFiles) {
|
||||
Modrinth::File file;
|
||||
File file;
|
||||
file.path = Json::requireString(modInfo, "path").replace("\\", "/");
|
||||
|
||||
auto env = Json::ensureObject(modInfo, "env");
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QCryptographicHash>
|
||||
#include <QQueue>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QVector>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "InstanceCreationTask.h"
|
||||
|
||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||
|
||||
class ModrinthCreationTask final : public InstanceCreationTask {
|
||||
Q_OBJECT
|
||||
struct File {
|
||||
QString path;
|
||||
|
||||
QCryptographicHash::Algorithm hashAlgorithm;
|
||||
QByteArray hash;
|
||||
QQueue<QUrl> downloads;
|
||||
bool required = true;
|
||||
};
|
||||
|
||||
public:
|
||||
ModrinthCreationTask(QString staging_path,
|
||||
@@ -30,7 +44,7 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
||||
bool createInstance() override;
|
||||
|
||||
private:
|
||||
bool parseManifest(const QString&, std::vector<Modrinth::File>&, bool set_internal_data = true, bool show_optional_dialog = true);
|
||||
bool parseManifest(const QString&, std::vector<File>&, bool set_internal_data = true, bool show_optional_dialog = true);
|
||||
|
||||
private:
|
||||
QWidget* m_parent = nullptr;
|
||||
@@ -38,7 +52,7 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
||||
QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version, m_neoForge_version;
|
||||
QString m_managed_id, m_managed_version_id, m_managed_name;
|
||||
|
||||
std::vector<Modrinth::File> m_files;
|
||||
std::vector<File> m_files;
|
||||
Task::Ptr m_task;
|
||||
|
||||
std::optional<InstancePtr> m_instance;
|
||||
|
||||
@@ -112,25 +112,6 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
|
||||
pack.extraDataLoaded = true;
|
||||
}
|
||||
|
||||
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr)
|
||||
{
|
||||
QList<ModPlatform::IndexedVersion> unsortedVersions;
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
auto file = loadIndexedPackVersion(obj);
|
||||
|
||||
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||
pack.versions = unsortedVersions;
|
||||
pack.versionsLoaded = true;
|
||||
}
|
||||
|
||||
ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_type, QString preferred_file_name)
|
||||
{
|
||||
ModPlatform::IndexedVersion file;
|
||||
@@ -244,28 +225,3 @@ ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj, Q
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ModPlatform::IndexedVersion Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m,
|
||||
QJsonArray& arr,
|
||||
const BaseInstance* inst)
|
||||
{
|
||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||
auto loaders = profile->getSupportedModLoaders();
|
||||
|
||||
QList<ModPlatform::IndexedVersion> versions;
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
auto file = loadIndexedPackVersion(obj);
|
||||
|
||||
if (file.fileId.isValid() &&
|
||||
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
|
||||
versions.append(file);
|
||||
}
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
||||
return versions.length() != 0 ? versions.front() : ModPlatform::IndexedVersion();
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ namespace Modrinth {
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadExtraPackData(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr);
|
||||
auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion;
|
||||
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst) -> ModPlatform::IndexedVersion;
|
||||
|
||||
} // namespace Modrinth
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
* Copyright 2022 kb1000
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ModrinthPackManifest.h"
|
||||
#include <QFileInfo>
|
||||
#include "Json.h"
|
||||
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
|
||||
#include <QSet>
|
||||
|
||||
static ModrinthAPI api;
|
||||
|
||||
namespace Modrinth {
|
||||
|
||||
void loadIndexedPack(Modpack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.id = Json::ensureString(obj, "project_id");
|
||||
|
||||
pack.name = Json::ensureString(obj, "title");
|
||||
pack.description = Json::ensureString(obj, "description");
|
||||
auto temp_author_name = Json::ensureString(obj, "author");
|
||||
pack.author = std::make_tuple(temp_author_name, api.getAuthorURL(temp_author_name));
|
||||
pack.iconUrl = Json::ensureString(obj, "icon_url");
|
||||
pack.iconName = QString("modrinth_%1.%2").arg(Json::ensureString(obj, "slug"), QFileInfo(pack.iconUrl.fileName()).suffix());
|
||||
}
|
||||
|
||||
void loadIndexedInfo(Modpack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.extra.body = Json::ensureString(obj, "body");
|
||||
pack.extra.projectUrl = QString("https://modrinth.com/modpack/%1").arg(Json::ensureString(obj, "slug"));
|
||||
|
||||
pack.extra.issuesUrl = Json::ensureString(obj, "issues_url");
|
||||
if (pack.extra.issuesUrl.endsWith('/'))
|
||||
pack.extra.issuesUrl.chop(1);
|
||||
|
||||
pack.extra.sourceUrl = Json::ensureString(obj, "source_url");
|
||||
if (pack.extra.sourceUrl.endsWith('/'))
|
||||
pack.extra.sourceUrl.chop(1);
|
||||
|
||||
pack.extra.wikiUrl = Json::ensureString(obj, "wiki_url");
|
||||
if (pack.extra.wikiUrl.endsWith('/'))
|
||||
pack.extra.wikiUrl.chop(1);
|
||||
|
||||
pack.extra.discordUrl = Json::ensureString(obj, "discord_url");
|
||||
if (pack.extra.discordUrl.endsWith('/'))
|
||||
pack.extra.discordUrl.chop(1);
|
||||
|
||||
auto donate_arr = Json::ensureArray(obj, "donation_urls");
|
||||
for (auto d : donate_arr) {
|
||||
auto d_obj = Json::requireObject(d);
|
||||
|
||||
DonationData donate;
|
||||
|
||||
donate.id = Json::ensureString(d_obj, "id");
|
||||
donate.platform = Json::ensureString(d_obj, "platform");
|
||||
donate.url = Json::ensureString(d_obj, "url");
|
||||
|
||||
pack.extra.donate.append(donate);
|
||||
}
|
||||
|
||||
pack.extra.status = Json::ensureString(obj, "status");
|
||||
|
||||
pack.extraInfoLoaded = true;
|
||||
}
|
||||
|
||||
void loadIndexedVersions(Modpack& pack, QJsonDocument& doc)
|
||||
{
|
||||
QList<ModpackVersion> unsortedVersions;
|
||||
|
||||
auto arr = Json::requireArray(doc);
|
||||
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = Json::requireObject(versionIter);
|
||||
auto file = loadIndexedVersion(obj);
|
||||
|
||||
if (!file.id.isEmpty()) // Heuristic to check if the returned value is valid
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool {
|
||||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
};
|
||||
|
||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||
|
||||
pack.versions.swap(unsortedVersions);
|
||||
|
||||
pack.versionsLoaded = true;
|
||||
}
|
||||
|
||||
auto loadIndexedVersion(QJsonObject& obj) -> ModpackVersion
|
||||
{
|
||||
ModpackVersion file;
|
||||
|
||||
file.name = Json::requireString(obj, "name");
|
||||
file.version = Json::requireString(obj, "version_number");
|
||||
auto gameVersions = Json::ensureArray(obj, "game_versions");
|
||||
if (!gameVersions.isEmpty()) {
|
||||
file.gameVersion = Json::ensureString(gameVersions[0]);
|
||||
file.gameVersion = ModrinthAPI::mapMCVersionFromModrinth(file.gameVersion);
|
||||
}
|
||||
auto loaders = Json::requireArray(obj, "loaders");
|
||||
for (auto loader : loaders) {
|
||||
if (loader == "neoforge")
|
||||
file.loaders |= ModPlatform::NeoForge;
|
||||
else if (loader == "forge")
|
||||
file.loaders |= ModPlatform::Forge;
|
||||
else if (loader == "cauldron")
|
||||
file.loaders |= ModPlatform::Cauldron;
|
||||
else if (loader == "liteloader")
|
||||
file.loaders |= ModPlatform::LiteLoader;
|
||||
else if (loader == "fabric")
|
||||
file.loaders |= ModPlatform::Fabric;
|
||||
else if (loader == "quilt")
|
||||
file.loaders |= ModPlatform::Quilt;
|
||||
}
|
||||
file.version_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type"));
|
||||
file.changelog = Json::ensureString(obj, "changelog");
|
||||
|
||||
file.id = Json::requireString(obj, "id");
|
||||
file.project_id = Json::requireString(obj, "project_id");
|
||||
|
||||
file.date = Json::requireString(obj, "date_published");
|
||||
|
||||
auto files = Json::requireArray(obj, "files");
|
||||
|
||||
for (auto file_iter : files) {
|
||||
File indexed_file;
|
||||
auto parent = Json::requireObject(file_iter);
|
||||
auto is_primary = Json::ensureBoolean(parent, (const QString)QStringLiteral("primary"), false);
|
||||
if (!is_primary) {
|
||||
auto filename = Json::ensureString(parent, "filename");
|
||||
// Checking suffix here is fine because it's the response from Modrinth,
|
||||
// so one would assume it will always be in English.
|
||||
if (!filename.endsWith("mrpack") && !filename.endsWith("zip"))
|
||||
continue;
|
||||
}
|
||||
|
||||
auto url = Json::requireString(parent, "url");
|
||||
|
||||
file.download_url = url;
|
||||
if (is_primary)
|
||||
break;
|
||||
}
|
||||
|
||||
if (file.download_url.isEmpty())
|
||||
return {};
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
auto getVersionDisplayString(const ModpackVersion& version) -> QString
|
||||
{
|
||||
auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : "";
|
||||
auto mcVersion = !version.gameVersion.isEmpty() && !version.name.contains(version.gameVersion)
|
||||
? QObject::tr(" for %1").arg(version.gameVersion)
|
||||
: "";
|
||||
auto versionStr = !version.name.contains(version.version) ? version.version : "";
|
||||
return QString("%1%2 — %3%4").arg(version.name, mcVersion, versionStr, release_type);
|
||||
}
|
||||
|
||||
} // namespace Modrinth
|
||||
@@ -1,129 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
* Copyright 2022 kb1000
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QCryptographicHash>
|
||||
#include <QList>
|
||||
#include <QQueue>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
|
||||
namespace Modrinth {
|
||||
|
||||
struct File {
|
||||
QString path;
|
||||
|
||||
QCryptographicHash::Algorithm hashAlgorithm;
|
||||
QByteArray hash;
|
||||
QQueue<QUrl> downloads;
|
||||
bool required = true;
|
||||
};
|
||||
|
||||
struct DonationData {
|
||||
QString id;
|
||||
QString platform;
|
||||
QString url;
|
||||
};
|
||||
|
||||
struct ModpackExtra {
|
||||
QString body;
|
||||
|
||||
QString projectUrl;
|
||||
|
||||
QString issuesUrl;
|
||||
QString sourceUrl;
|
||||
QString wikiUrl;
|
||||
QString discordUrl;
|
||||
|
||||
QList<DonationData> donate;
|
||||
|
||||
QString status;
|
||||
};
|
||||
|
||||
struct ModpackVersion {
|
||||
QString name;
|
||||
QString version;
|
||||
QString gameVersion;
|
||||
ModPlatform::IndexedVersionType version_type;
|
||||
QString changelog;
|
||||
ModPlatform::ModLoaderTypes loaders = {};
|
||||
|
||||
QString id;
|
||||
QString project_id;
|
||||
|
||||
QString date;
|
||||
|
||||
QString download_url;
|
||||
};
|
||||
|
||||
struct Modpack {
|
||||
QString id;
|
||||
|
||||
QString name;
|
||||
QString description;
|
||||
std::tuple<QString, QUrl> author;
|
||||
QString iconName;
|
||||
QUrl iconUrl;
|
||||
|
||||
bool versionsLoaded = false;
|
||||
bool extraInfoLoaded = false;
|
||||
|
||||
ModpackExtra extra;
|
||||
QList<ModpackVersion> versions;
|
||||
};
|
||||
|
||||
void loadIndexedPack(Modpack&, QJsonObject&);
|
||||
void loadIndexedInfo(Modpack&, QJsonObject&);
|
||||
void loadIndexedVersions(Modpack&, QJsonDocument&);
|
||||
auto loadIndexedVersion(QJsonObject&) -> ModpackVersion;
|
||||
|
||||
auto validateDownloadUrl(QUrl) -> bool;
|
||||
|
||||
auto getVersionDisplayString(const ModpackVersion&) -> QString;
|
||||
|
||||
} // namespace Modrinth
|
||||
|
||||
Q_DECLARE_METATYPE(Modrinth::Modpack)
|
||||
Q_DECLARE_METATYPE(Modrinth::ModpackVersion)
|
||||
Q_DECLARE_METATYPE(QList<Modrinth::ModpackVersion>)
|
||||
@@ -22,8 +22,6 @@
|
||||
#include "Markdown.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||
|
||||
#include "ui/InstanceWindow.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
@@ -256,36 +254,13 @@ void ModrinthManagedPackPage::parseManagedPack()
|
||||
if (m_fetch_job && m_fetch_job->isRunning())
|
||||
m_fetch_job->abort();
|
||||
|
||||
m_fetch_job.reset(new NetJob(QString("Modrinth::PackVersions(%1)").arg(m_inst->getManagedPackName()), APPLICATION->network()));
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
ResourceAPI::Callback<QVector<ModPlatform::IndexedVersion>> callbacks{};
|
||||
m_pack = { m_inst->getManagedPackID() };
|
||||
|
||||
QString id = m_inst->getManagedPackID();
|
||||
|
||||
m_fetch_job->addNetAction(
|
||||
Net::ApiDownload::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response));
|
||||
|
||||
connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
|
||||
setFailState();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Modrinth::loadIndexedVersions(m_pack, doc);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << *response;
|
||||
qWarning() << "Error while reading modrinth modpack version: " << e.cause();
|
||||
|
||||
setFailState();
|
||||
return;
|
||||
}
|
||||
// Use default if no callbacks are set
|
||||
callbacks.on_succeed = [this](auto& doc) {
|
||||
m_pack.versions = doc;
|
||||
m_pack.versionsLoaded = true;
|
||||
|
||||
// We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
|
||||
ui->versionsComboBox->blockSignals(true);
|
||||
@@ -293,22 +268,23 @@ void ModrinthManagedPackPage::parseManagedPack()
|
||||
ui->versionsComboBox->blockSignals(false);
|
||||
|
||||
for (const auto& version : m_pack.versions) {
|
||||
QString name = Modrinth::getVersionDisplayString(version);
|
||||
QString name = version.getVersionDisplayString();
|
||||
|
||||
// NOTE: the id from version isn't the same id in the modpack format spec...
|
||||
// e.g. HexMC's 4.4.0 has versionId 4.0.0 in the modpack index..............
|
||||
if (version.version == m_inst->getManagedPackVersionName())
|
||||
name = tr("%1 (Current)").arg(name);
|
||||
|
||||
ui->versionsComboBox->addItem(name, QVariant(version.id));
|
||||
ui->versionsComboBox->addItem(name, version.fileId);
|
||||
}
|
||||
|
||||
suggestVersion();
|
||||
|
||||
m_loaded = true;
|
||||
});
|
||||
connect(m_fetch_job.get(), &NetJob::failed, this, &ModrinthManagedPackPage::setFailState);
|
||||
connect(m_fetch_job.get(), &NetJob::aborted, this, &ModrinthManagedPackPage::setFailState);
|
||||
};
|
||||
callbacks.on_fail = [this](QString reason, int) { setFailState(); };
|
||||
callbacks.on_abort = [this]() { setFailState(); };
|
||||
m_fetch_job = m_api.getProjectVersions({ m_pack, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
|
||||
ui->changelogTextBrowser->setText(tr("Fetching changelogs..."));
|
||||
|
||||
@@ -370,10 +346,10 @@ void ModrinthManagedPackPage::update()
|
||||
QMap<QString, QString> extra_info;
|
||||
// NOTE: Don't use 'm_pack.id' here, since we didn't completely parse all the metadata for the pack, including this field.
|
||||
extra_info.insert("pack_id", m_inst->getManagedPackID());
|
||||
extra_info.insert("pack_version_id", version.id);
|
||||
extra_info.insert("pack_version_id", version.fileId.toString());
|
||||
extra_info.insert("original_instance_id", m_inst->id());
|
||||
|
||||
auto extracted = new InstanceImportTask(version.download_url, this, std::move(extra_info));
|
||||
auto extracted = new InstanceImportTask(version.downloadUrl, this, std::move(extra_info));
|
||||
|
||||
InstanceName inst_name(m_inst->getManagedPackName(), version.version);
|
||||
inst_name.setName(m_inst->name().replace(m_inst->getManagedPackVersionName(), version.version));
|
||||
@@ -449,37 +425,15 @@ void FlameManagedPackPage::parseManagedPack()
|
||||
if (m_fetch_job && m_fetch_job->isRunning())
|
||||
m_fetch_job->abort();
|
||||
|
||||
m_fetch_job.reset(new NetJob(QString("Flame::PackVersions(%1)").arg(m_inst->getManagedPackName()), APPLICATION->network()));
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
QString id = m_inst->getManagedPackID();
|
||||
m_pack = { id };
|
||||
|
||||
m_fetch_job->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response));
|
||||
ResourceAPI::Callback<QVector<ModPlatform::IndexedVersion>> callbacks{};
|
||||
|
||||
connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
|
||||
setFailState();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto obj = doc.object();
|
||||
auto data = Json::ensureArray(obj, "data");
|
||||
Flame::loadIndexedPackVersions(m_pack, data);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << *response;
|
||||
qWarning() << "Error while reading flame modpack version: " << e.cause();
|
||||
|
||||
setFailState();
|
||||
return;
|
||||
}
|
||||
// Use default if no callbacks are set
|
||||
callbacks.on_succeed = [this](auto& doc) {
|
||||
m_pack.versions = doc;
|
||||
m_pack.versionsLoaded = true;
|
||||
|
||||
// We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
|
||||
ui->versionsComboBox->blockSignals(true);
|
||||
@@ -487,7 +441,7 @@ void FlameManagedPackPage::parseManagedPack()
|
||||
ui->versionsComboBox->blockSignals(false);
|
||||
|
||||
for (const auto& version : m_pack.versions) {
|
||||
QString name = Flame::getVersionDisplayString(version);
|
||||
QString name = version.getVersionDisplayString();
|
||||
|
||||
if (version.fileId == m_inst->getManagedPackVersionID().toInt())
|
||||
name = tr("%1 (Current)").arg(name);
|
||||
@@ -498,9 +452,10 @@ void FlameManagedPackPage::parseManagedPack()
|
||||
suggestVersion();
|
||||
|
||||
m_loaded = true;
|
||||
});
|
||||
connect(m_fetch_job.get(), &NetJob::failed, this, &FlameManagedPackPage::setFailState);
|
||||
connect(m_fetch_job.get(), &NetJob::aborted, this, &FlameManagedPackPage::setFailState);
|
||||
};
|
||||
callbacks.on_fail = [this](QString reason, int) { setFailState(); };
|
||||
callbacks.on_abort = [this]() { setFailState(); };
|
||||
m_fetch_job = m_api.getProjectVersions({ m_pack, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
|
||||
m_fetch_job->start();
|
||||
}
|
||||
@@ -521,7 +476,7 @@ void FlameManagedPackPage::suggestVersion()
|
||||
auto version = m_pack.versions.at(index);
|
||||
|
||||
ui->changelogTextBrowser->setHtml(
|
||||
StringUtils::htmlListPatch(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId)));
|
||||
StringUtils::htmlListPatch(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId.toInt())));
|
||||
|
||||
ManagedPackPage::suggestVersion();
|
||||
}
|
||||
@@ -537,7 +492,7 @@ void FlameManagedPackPage::update()
|
||||
|
||||
QMap<QString, QString> extra_info;
|
||||
extra_info.insert("pack_id", m_inst->getManagedPackID());
|
||||
extra_info.insert("pack_version_id", QString::number(version.fileId));
|
||||
extra_info.insert("pack_version_id", version.fileId.toString());
|
||||
extra_info.insert("original_instance_id", m_inst->id());
|
||||
|
||||
auto extracted = new InstanceImportTask(version.downloadUrl, this, std::move(extra_info));
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlamePackIndex.h"
|
||||
|
||||
#include "net/NetJob.h"
|
||||
|
||||
@@ -130,9 +129,9 @@ class ModrinthManagedPackPage final : public ManagedPackPage {
|
||||
void updateFromFile() override;
|
||||
|
||||
private:
|
||||
NetJob::Ptr m_fetch_job = nullptr;
|
||||
Task::Ptr m_fetch_job = nullptr;
|
||||
|
||||
Modrinth::Modpack m_pack;
|
||||
ModPlatform::IndexedPack m_pack;
|
||||
ModrinthAPI m_api;
|
||||
};
|
||||
|
||||
@@ -154,8 +153,8 @@ class FlameManagedPackPage final : public ManagedPackPage {
|
||||
void updateFromFile() override;
|
||||
|
||||
private:
|
||||
NetJob::Ptr m_fetch_job = nullptr;
|
||||
Task::Ptr m_fetch_job = nullptr;
|
||||
|
||||
Flame::IndexedPack m_pack;
|
||||
ModPlatform::IndexedPack m_pack;
|
||||
FlameAPI m_api;
|
||||
};
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
DataPackResourceModel::DataPackResourceModel(BaseInstance const& base_inst, ResourceAPI* api)
|
||||
: ResourceModel(api), m_base_instance(base_inst)
|
||||
DataPackResourceModel::DataPackResourceModel(BaseInstance const& base_inst, ResourceAPI* api, QString debugName, QString metaEntryBase)
|
||||
: ResourceModel(api), m_base_instance(base_inst), m_debugName(debugName + " (Model)"), m_metaEntryBase(metaEntryBase)
|
||||
{}
|
||||
|
||||
/******** Make data requests ********/
|
||||
|
||||
@@ -21,14 +21,13 @@ class DataPackResourceModel : public ResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DataPackResourceModel(BaseInstance const&, ResourceAPI*);
|
||||
DataPackResourceModel(BaseInstance const&, ResourceAPI*, QString, QString);
|
||||
|
||||
/* Ask the API for more information */
|
||||
void searchWithTerm(const QString& term, unsigned int sort);
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack&, QJsonObject&) override = 0;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&) override = 0;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&) override = 0;
|
||||
[[nodiscard]] QString debugName() const override { return m_debugName; }
|
||||
[[nodiscard]] QString metaEntryBase() const override { return m_metaEntryBase; }
|
||||
|
||||
public slots:
|
||||
ResourceAPI::SearchArgs createSearchArguments() override;
|
||||
@@ -38,7 +37,9 @@ class DataPackResourceModel : public ResourceModel {
|
||||
protected:
|
||||
const BaseInstance& m_base_instance;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0;
|
||||
private:
|
||||
QString m_debugName;
|
||||
QString m_metaEntryBase;
|
||||
};
|
||||
|
||||
} // namespace ResourceDownload
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
ModModel::ModModel(BaseInstance& base_inst, ResourceAPI* api) : ResourceModel(api), m_base_instance(base_inst) {}
|
||||
ModModel::ModModel(BaseInstance& base_inst, ResourceAPI* api, QString debugName, QString metaEntryBase)
|
||||
: ResourceModel(api), m_base_instance(base_inst), m_debugName(debugName + " (Model)"), m_metaEntryBase(metaEntryBase)
|
||||
{}
|
||||
|
||||
/******** Make data requests ********/
|
||||
|
||||
@@ -60,7 +62,7 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(const QModelInd
|
||||
if (m_filter->loaders)
|
||||
loaders = m_filter->loaders;
|
||||
|
||||
return { pack, versions, loaders };
|
||||
return { pack, versions, loaders, ModPlatform::ResourceType::Mod };
|
||||
}
|
||||
|
||||
ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(const QModelIndex& entry)
|
||||
|
||||
@@ -24,26 +24,23 @@ class ModModel : public ResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModModel(BaseInstance&, ResourceAPI* api);
|
||||
ModModel(BaseInstance&, ResourceAPI* api, QString debugName, QString metaEntryBase);
|
||||
|
||||
/* Ask the API for more information */
|
||||
void searchWithTerm(const QString& term, unsigned int sort, bool filter_changed);
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override = 0;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override = 0;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override = 0;
|
||||
virtual ModPlatform::IndexedVersion loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) = 0;
|
||||
|
||||
void setFilter(std::shared_ptr<ModFilterWidget::Filter> filter) { m_filter = filter; }
|
||||
virtual QVariant getInstalledPackVersion(ModPlatform::IndexedPack::Ptr) const override;
|
||||
|
||||
[[nodiscard]] QString debugName() const override { return m_debugName; }
|
||||
[[nodiscard]] QString metaEntryBase() const override { return m_metaEntryBase; }
|
||||
|
||||
public slots:
|
||||
ResourceAPI::SearchArgs createSearchArguments() override;
|
||||
ResourceAPI::VersionSearchArgs createVersionsArguments(const QModelIndex&) override;
|
||||
ResourceAPI::ProjectInfoArgs createInfoArguments(const QModelIndex&) override;
|
||||
|
||||
protected:
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0;
|
||||
virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const override;
|
||||
|
||||
virtual bool checkFilters(ModPlatform::IndexedPack::Ptr) override;
|
||||
@@ -53,6 +50,10 @@ class ModModel : public ResourceModel {
|
||||
BaseInstance& m_base_instance;
|
||||
|
||||
std::shared_ptr<ModFilterWidget::Filter> m_filter = nullptr;
|
||||
|
||||
private:
|
||||
QString m_debugName;
|
||||
QString m_metaEntryBase;
|
||||
};
|
||||
|
||||
} // namespace ResourceDownload
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "Json.h"
|
||||
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
@@ -141,9 +141,9 @@ void ResourceModel::search()
|
||||
if (m_search_term.startsWith("#")) {
|
||||
auto projectId = m_search_term.mid(1);
|
||||
if (!projectId.isEmpty()) {
|
||||
ResourceAPI::ProjectInfoCallbacks callbacks;
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack> callbacks;
|
||||
|
||||
callbacks.on_fail = [this](QString reason) {
|
||||
callbacks.on_fail = [this](QString reason, int) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
searchRequestFailed(reason, -1);
|
||||
@@ -154,10 +154,10 @@ void ResourceModel::search()
|
||||
searchRequestAborted();
|
||||
};
|
||||
|
||||
callbacks.on_succeed = [this](auto& doc, auto& pack) {
|
||||
callbacks.on_succeed = [this](auto& pack) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
searchRequestForOneSucceeded(doc);
|
||||
searchRequestForOneSucceeded(pack);
|
||||
};
|
||||
if (auto job = m_api->getProjectInfo({ projectId }, std::move(callbacks)); job)
|
||||
runSearchJob(job);
|
||||
@@ -166,27 +166,23 @@ void ResourceModel::search()
|
||||
}
|
||||
auto args{ createSearchArguments() };
|
||||
|
||||
auto callbacks{ createSearchCallbacks() };
|
||||
ResourceAPI::Callback<QList<ModPlatform::IndexedPack::Ptr>> callbacks{};
|
||||
|
||||
// Use defaults if no callbacks are set
|
||||
if (!callbacks.on_succeed)
|
||||
callbacks.on_succeed = [this](auto& doc) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
searchRequestSucceeded(doc);
|
||||
};
|
||||
if (!callbacks.on_fail)
|
||||
callbacks.on_fail = [this](QString reason, int network_error_code) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
searchRequestFailed(reason, network_error_code);
|
||||
};
|
||||
if (!callbacks.on_abort)
|
||||
callbacks.on_abort = [this] {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
searchRequestAborted();
|
||||
};
|
||||
callbacks.on_succeed = [this](auto& doc) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
searchRequestSucceeded(doc);
|
||||
};
|
||||
callbacks.on_fail = [this](QString reason, int network_error_code) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
searchRequestFailed(reason, network_error_code);
|
||||
};
|
||||
callbacks.on_abort = [this] {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
searchRequestAborted();
|
||||
};
|
||||
|
||||
if (auto job = m_api->searchProjects(std::move(args), std::move(callbacks)); job)
|
||||
runSearchJob(job);
|
||||
@@ -201,14 +197,15 @@ void ResourceModel::loadEntry(const QModelIndex& entry)
|
||||
|
||||
if (!pack->versionsLoaded) {
|
||||
auto args{ createVersionsArguments(entry) };
|
||||
auto callbacks{ createVersionsCallbacks(entry) };
|
||||
ResourceAPI::Callback<QVector<ModPlatform::IndexedVersion>> callbacks{};
|
||||
|
||||
auto addonId = pack->addonId;
|
||||
// Use default if no callbacks are set
|
||||
if (!callbacks.on_succeed)
|
||||
callbacks.on_succeed = [this, entry](auto& doc, auto pack) {
|
||||
callbacks.on_succeed = [this, entry, addonId](auto& doc) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
versionRequestSucceeded(doc, pack, entry);
|
||||
versionRequestSucceeded(doc, addonId, entry);
|
||||
};
|
||||
if (!callbacks.on_fail)
|
||||
callbacks.on_fail = [](QString reason, int) {
|
||||
@@ -222,28 +219,23 @@ void ResourceModel::loadEntry(const QModelIndex& entry)
|
||||
|
||||
if (!pack->extraDataLoaded) {
|
||||
auto args{ createInfoArguments(entry) };
|
||||
auto callbacks{ createInfoCallbacks(entry) };
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack> callbacks{};
|
||||
|
||||
// Use default if no callbacks are set
|
||||
if (!callbacks.on_succeed)
|
||||
callbacks.on_succeed = [this, entry](auto& doc, auto& newpack) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
auto pack = newpack;
|
||||
infoRequestSucceeded(doc, pack, entry);
|
||||
};
|
||||
if (!callbacks.on_fail)
|
||||
callbacks.on_fail = [this](QString reason) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info: %1").arg(reason));
|
||||
};
|
||||
if (!callbacks.on_abort)
|
||||
callbacks.on_abort = [this] {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
qCritical() << tr("The request was aborted for an unknown reason");
|
||||
};
|
||||
callbacks.on_succeed = [this, entry](auto& newpack) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
infoRequestSucceeded(newpack, entry);
|
||||
};
|
||||
callbacks.on_fail = [this](QString reason, int) {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info: %1").arg(reason));
|
||||
};
|
||||
callbacks.on_abort = [this] {
|
||||
if (!s_running_models.constFind(this).value())
|
||||
return;
|
||||
qCritical() << tr("The request was aborted for an unknown reason");
|
||||
};
|
||||
|
||||
if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job)
|
||||
runInfoJob(job);
|
||||
@@ -358,68 +350,35 @@ std::optional<QIcon> ResourceModel::getIcon(QModelIndex& index, const QUrl& url)
|
||||
return {};
|
||||
}
|
||||
|
||||
// No 'forgor to implement' shall pass here :blobfox_knife:
|
||||
#define NEED_FOR_CALLBACK_ASSERT(name) \
|
||||
Q_ASSERT_X(0 != 0, #name, "You NEED to re-implement this if you intend on using the default callbacks.")
|
||||
|
||||
QJsonArray ResourceModel::documentToArray([[maybe_unused]] QJsonDocument& doc) const
|
||||
{
|
||||
NEED_FOR_CALLBACK_ASSERT("documentToArray");
|
||||
return {};
|
||||
}
|
||||
void ResourceModel::loadIndexedPack(ModPlatform::IndexedPack&, QJsonObject&)
|
||||
{
|
||||
NEED_FOR_CALLBACK_ASSERT("loadIndexedPack");
|
||||
}
|
||||
void ResourceModel::loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&)
|
||||
{
|
||||
NEED_FOR_CALLBACK_ASSERT("loadExtraPackInfo");
|
||||
}
|
||||
void ResourceModel::loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&)
|
||||
{
|
||||
NEED_FOR_CALLBACK_ASSERT("loadIndexedPackVersions");
|
||||
}
|
||||
|
||||
/* Default callbacks */
|
||||
|
||||
void ResourceModel::searchRequestSucceeded(QJsonDocument& doc)
|
||||
void ResourceModel::searchRequestSucceeded(QList<ModPlatform::IndexedPack::Ptr>& newList)
|
||||
{
|
||||
QList<ModPlatform::IndexedPack::Ptr> newList;
|
||||
auto packs = documentToArray(doc);
|
||||
|
||||
for (auto packRaw : packs) {
|
||||
auto packObj = packRaw.toObject();
|
||||
|
||||
ModPlatform::IndexedPack::Ptr pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
try {
|
||||
loadIndexedPack(*pack, packObj);
|
||||
if (auto sel = std::find_if(m_selected.begin(), m_selected.end(),
|
||||
[&pack](const DownloadTaskPtr i) {
|
||||
const auto ipack = i->getPack();
|
||||
return ipack->provider == pack->provider && ipack->addonId == pack->addonId;
|
||||
});
|
||||
sel != m_selected.end()) {
|
||||
newList.append(sel->get()->getPack());
|
||||
} else
|
||||
newList.append(pack);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qWarning() << "Error while loading resource from " << debugName() << ": " << e.cause();
|
||||
continue;
|
||||
QList<ModPlatform::IndexedPack::Ptr> filteredNewList;
|
||||
for (auto pack : newList) {
|
||||
ModPlatform::IndexedPack::Ptr p;
|
||||
if (auto sel = std::find_if(m_selected.begin(), m_selected.end(),
|
||||
[&pack](const DownloadTaskPtr i) {
|
||||
const auto ipack = i->getPack();
|
||||
return ipack->provider == pack->provider && ipack->addonId == pack->addonId;
|
||||
});
|
||||
sel != m_selected.end()) {
|
||||
p = sel->get()->getPack();
|
||||
} else {
|
||||
p = pack;
|
||||
}
|
||||
if (checkFilters(p)) {
|
||||
filteredNewList << p;
|
||||
}
|
||||
}
|
||||
|
||||
if (packs.size() < 25) {
|
||||
if (newList.size() < 25) {
|
||||
m_search_state = SearchState::Finished;
|
||||
} else {
|
||||
m_next_search_offset += 25;
|
||||
m_search_state = SearchState::CanFetchMore;
|
||||
}
|
||||
|
||||
QList<ModPlatform::IndexedPack::Ptr> filteredNewList;
|
||||
for (auto p : newList)
|
||||
if (checkFilters(p))
|
||||
filteredNewList << p;
|
||||
|
||||
// When you have a Qt build with assertions turned on, proceeding here will abort the application
|
||||
if (filteredNewList.size() == 0)
|
||||
return;
|
||||
@@ -429,24 +388,12 @@ void ResourceModel::searchRequestSucceeded(QJsonDocument& doc)
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void ResourceModel::searchRequestForOneSucceeded(QJsonDocument& doc)
|
||||
void ResourceModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack& pack)
|
||||
{
|
||||
ModPlatform::IndexedPack::Ptr pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
|
||||
try {
|
||||
auto obj = Json::requireObject(doc);
|
||||
if (obj.contains("data"))
|
||||
obj = Json::requireObject(obj, "data");
|
||||
loadIndexedPack(*pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading " << debugName() << " resource info: " << e.cause();
|
||||
}
|
||||
|
||||
m_search_state = SearchState::Finished;
|
||||
|
||||
beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + 1);
|
||||
m_packs.append(pack);
|
||||
m_packs.append(std::make_shared<ModPlatform::IndexedPack>(pack));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
@@ -479,21 +426,16 @@ void ResourceModel::searchRequestAborted()
|
||||
search();
|
||||
}
|
||||
|
||||
void ResourceModel::versionRequestSucceeded(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index)
|
||||
void ResourceModel::versionRequestSucceeded(QVector<ModPlatform::IndexedVersion>& doc, QVariant pack, const QModelIndex& index)
|
||||
{
|
||||
auto current_pack = data(index, Qt::UserRole).value<ModPlatform::IndexedPack::Ptr>();
|
||||
|
||||
// Check if the index is still valid for this resource or not
|
||||
if (pack.addonId != current_pack->addonId)
|
||||
if (pack != current_pack->addonId)
|
||||
return;
|
||||
|
||||
try {
|
||||
auto arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array();
|
||||
loadIndexedPackVersions(*current_pack, arr);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading " << debugName() << " resource version: " << e.cause();
|
||||
}
|
||||
current_pack->versions = doc;
|
||||
current_pack->versionsLoaded = true;
|
||||
|
||||
// Cache info :^)
|
||||
QVariant new_pack;
|
||||
@@ -506,7 +448,7 @@ void ResourceModel::versionRequestSucceeded(QJsonDocument& doc, ModPlatform::Ind
|
||||
emit versionListUpdated(index);
|
||||
}
|
||||
|
||||
void ResourceModel::infoRequestSucceeded(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index)
|
||||
void ResourceModel::infoRequestSucceeded(ModPlatform::IndexedPack& pack, const QModelIndex& index)
|
||||
{
|
||||
auto current_pack = data(index, Qt::UserRole).value<ModPlatform::IndexedPack::Ptr>();
|
||||
|
||||
@@ -514,14 +456,7 @@ void ResourceModel::infoRequestSucceeded(QJsonDocument& doc, ModPlatform::Indexe
|
||||
if (pack.addonId != current_pack->addonId)
|
||||
return;
|
||||
|
||||
try {
|
||||
auto obj = Json::requireObject(doc);
|
||||
loadExtraPackInfo(*current_pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading " << debugName() << " resource info: " << e.cause();
|
||||
}
|
||||
|
||||
*current_pack = pack;
|
||||
// Cache info :^)
|
||||
QVariant new_pack;
|
||||
new_pack.setValue(current_pack);
|
||||
|
||||
@@ -43,10 +43,7 @@ class ResourceModel : public QAbstractListModel {
|
||||
virtual auto debugName() const -> QString;
|
||||
virtual auto metaEntryBase() const -> QString = 0;
|
||||
|
||||
inline int rowCount(const QModelIndex& parent) const override
|
||||
{
|
||||
return parent.isValid() ? 0 : static_cast<int>(m_packs.size());
|
||||
}
|
||||
inline int rowCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : static_cast<int>(m_packs.size()); }
|
||||
inline int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : 1; }
|
||||
inline auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); }
|
||||
|
||||
@@ -77,13 +74,10 @@ class ResourceModel : public QAbstractListModel {
|
||||
void setSearchTerm(QString term) { m_search_term = term; }
|
||||
|
||||
virtual ResourceAPI::SearchArgs createSearchArguments() = 0;
|
||||
virtual ResourceAPI::SearchCallbacks createSearchCallbacks() { return {}; }
|
||||
|
||||
virtual ResourceAPI::VersionSearchArgs createVersionsArguments(const QModelIndex&) = 0;
|
||||
virtual ResourceAPI::VersionSearchCallbacks createVersionsCallbacks(const QModelIndex&) { return {}; }
|
||||
|
||||
virtual ResourceAPI::ProjectInfoArgs createInfoArguments(const QModelIndex&) = 0;
|
||||
virtual ResourceAPI::ProjectInfoCallbacks createInfoCallbacks(const QModelIndex&) { return {}; }
|
||||
|
||||
/** Requests the API for more entries. */
|
||||
virtual void search();
|
||||
@@ -114,22 +108,6 @@ class ResourceModel : public QAbstractListModel {
|
||||
|
||||
auto getCurrentSortingMethodByIndex() const -> std::optional<ResourceAPI::SortingMethod>;
|
||||
|
||||
/** Converts a JSON document to a common array format.
|
||||
*
|
||||
* This is needed so that different providers, with different JSON structures, can be parsed
|
||||
* uniformally. You NEED to re-implement this if you intend on using the default callbacks.
|
||||
*/
|
||||
virtual auto documentToArray(QJsonDocument&) const -> QJsonArray;
|
||||
|
||||
/** Functions to load data into a pack.
|
||||
*
|
||||
* Those are needed for the same reason as documentToArray, and NEED to be re-implemented in the same way.
|
||||
*/
|
||||
|
||||
virtual void loadIndexedPack(ModPlatform::IndexedPack&, QJsonObject&);
|
||||
virtual void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&);
|
||||
virtual void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&);
|
||||
|
||||
virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const { return false; }
|
||||
|
||||
protected:
|
||||
@@ -159,14 +137,14 @@ class ResourceModel : public QAbstractListModel {
|
||||
|
||||
private:
|
||||
/* Default search request callbacks */
|
||||
void searchRequestSucceeded(QJsonDocument&);
|
||||
void searchRequestForOneSucceeded(QJsonDocument&);
|
||||
void searchRequestSucceeded(QList<ModPlatform::IndexedPack::Ptr>&);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack&);
|
||||
void searchRequestFailed(QString reason, int network_error_code);
|
||||
void searchRequestAborted();
|
||||
|
||||
void versionRequestSucceeded(QJsonDocument&, ModPlatform::IndexedPack&, const QModelIndex&);
|
||||
void versionRequestSucceeded(QVector<ModPlatform::IndexedVersion>&, QVariant, const QModelIndex&);
|
||||
|
||||
void infoRequestSucceeded(QJsonDocument&, ModPlatform::IndexedPack&, const QModelIndex&);
|
||||
void infoRequestSucceeded(ModPlatform::IndexedPack&, const QModelIndex&);
|
||||
|
||||
signals:
|
||||
void versionListUpdated(const QModelIndex& index);
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
ResourcePackResourceModel::ResourcePackResourceModel(BaseInstance const& base_inst, ResourceAPI* api)
|
||||
: ResourceModel(api), m_base_instance(base_inst)
|
||||
ResourcePackResourceModel::ResourcePackResourceModel(BaseInstance const& base_inst,
|
||||
ResourceAPI* api,
|
||||
QString debugName,
|
||||
QString metaEntryBase)
|
||||
: ResourceModel(api), m_base_instance(base_inst), m_debugName(debugName + " (Model)"), m_metaEntryBase(metaEntryBase)
|
||||
{}
|
||||
|
||||
/******** Make data requests ********/
|
||||
@@ -23,7 +26,7 @@ ResourceAPI::SearchArgs ResourcePackResourceModel::createSearchArguments()
|
||||
ResourceAPI::VersionSearchArgs ResourcePackResourceModel::createVersionsArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto& pack = m_packs[entry.row()];
|
||||
return { *pack };
|
||||
return { *pack, {}, {}, ModPlatform::ResourceType::ResourcePack };
|
||||
}
|
||||
|
||||
ResourceAPI::ProjectInfoArgs ResourcePackResourceModel::createInfoArguments(const QModelIndex& entry)
|
||||
|
||||
@@ -20,14 +20,13 @@ class ResourcePackResourceModel : public ResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ResourcePackResourceModel(BaseInstance const&, ResourceAPI*);
|
||||
ResourcePackResourceModel(BaseInstance const&, ResourceAPI*, QString debugName, QString metaEntryBase);
|
||||
|
||||
/* Ask the API for more information */
|
||||
void searchWithTerm(const QString& term, unsigned int sort);
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack&, QJsonObject&) override = 0;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&) override = 0;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&) override = 0;
|
||||
[[nodiscard]] QString debugName() const override { return m_debugName; }
|
||||
[[nodiscard]] QString metaEntryBase() const override { return m_metaEntryBase; }
|
||||
|
||||
public slots:
|
||||
ResourceAPI::SearchArgs createSearchArguments() override;
|
||||
@@ -37,7 +36,9 @@ class ResourcePackResourceModel : public ResourceModel {
|
||||
protected:
|
||||
const BaseInstance& m_base_instance;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0;
|
||||
private:
|
||||
QString m_debugName;
|
||||
QString m_metaEntryBase;
|
||||
};
|
||||
|
||||
} // namespace ResourceDownload
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
ShaderPackResourceModel::ShaderPackResourceModel(BaseInstance const& base_inst, ResourceAPI* api)
|
||||
: ResourceModel(api), m_base_instance(base_inst)
|
||||
ShaderPackResourceModel::ShaderPackResourceModel(BaseInstance const& base_inst, ResourceAPI* api, QString debugName, QString metaEntryBase)
|
||||
: ResourceModel(api), m_base_instance(base_inst), m_debugName(debugName + " (Model)"), m_metaEntryBase(metaEntryBase)
|
||||
{}
|
||||
|
||||
/******** Make data requests ********/
|
||||
@@ -23,7 +23,7 @@ ResourceAPI::SearchArgs ShaderPackResourceModel::createSearchArguments()
|
||||
ResourceAPI::VersionSearchArgs ShaderPackResourceModel::createVersionsArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto& pack = m_packs[entry.row()];
|
||||
return { *pack };
|
||||
return { *pack, {}, {}, ModPlatform::ResourceType::ShaderPack };
|
||||
}
|
||||
|
||||
ResourceAPI::ProjectInfoArgs ShaderPackResourceModel::createInfoArguments(const QModelIndex& entry)
|
||||
|
||||
@@ -20,14 +20,13 @@ class ShaderPackResourceModel : public ResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShaderPackResourceModel(BaseInstance const&, ResourceAPI*);
|
||||
ShaderPackResourceModel(BaseInstance const&, ResourceAPI*, QString debugName, QString metaEntryBase);
|
||||
|
||||
/* Ask the API for more information */
|
||||
void searchWithTerm(const QString& term, unsigned int sort);
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack&, QJsonObject&) override = 0;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&) override = 0;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&) override = 0;
|
||||
[[nodiscard]] QString debugName() const override { return m_debugName; }
|
||||
[[nodiscard]] QString metaEntryBase() const override { return m_metaEntryBase; }
|
||||
|
||||
public slots:
|
||||
ResourceAPI::SearchArgs createSearchArguments() override;
|
||||
@@ -37,7 +36,9 @@ class ShaderPackResourceModel : public ResourceModel {
|
||||
protected:
|
||||
const BaseInstance& m_base_instance;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0;
|
||||
private:
|
||||
QString m_debugName;
|
||||
QString m_metaEntryBase;
|
||||
};
|
||||
|
||||
} // namespace ResourceDownload
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
static std::list<Version> s_availableVersions = {};
|
||||
|
||||
namespace ResourceDownload {
|
||||
TexturePackResourceModel::TexturePackResourceModel(BaseInstance const& inst, ResourceAPI* api)
|
||||
: ResourcePackResourceModel(inst, api), m_version_list(APPLICATION->metadataIndex()->get("net.minecraft"))
|
||||
TexturePackResourceModel::TexturePackResourceModel(BaseInstance const& inst, ResourceAPI* api, QString debugName, QString metaEntryBase)
|
||||
: ResourcePackResourceModel(inst, api, debugName, metaEntryBase), m_version_list(APPLICATION->metadataIndex()->get("net.minecraft"))
|
||||
{
|
||||
if (!m_version_list->isLoaded()) {
|
||||
qDebug() << "Loading version list...";
|
||||
@@ -73,6 +73,7 @@ ResourceAPI::SearchArgs TexturePackResourceModel::createSearchArguments()
|
||||
ResourceAPI::VersionSearchArgs TexturePackResourceModel::createVersionsArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto args = ResourcePackResourceModel::createVersionsArguments(entry);
|
||||
args.resourceType = ModPlatform::ResourceType::TexturePack;
|
||||
if (!m_version_list->isLoaded()) {
|
||||
qCritical() << "The version list could not be loaded. Falling back to showing all entries.";
|
||||
return args;
|
||||
|
||||
@@ -13,7 +13,7 @@ class TexturePackResourceModel : public ResourcePackResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TexturePackResourceModel(BaseInstance const& inst, ResourceAPI* api);
|
||||
TexturePackResourceModel(BaseInstance const& inst, ResourceAPI* api, QString debugName, QString metaEntryBase);
|
||||
|
||||
inline ::Version maximumTexturePackVersion() const { return { "1.6" }; }
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ ListModel::~ListModel() {}
|
||||
|
||||
int ListModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : modpacks.size();
|
||||
return parent.isValid() ? 0 : m_modpacks.size();
|
||||
}
|
||||
|
||||
int ListModel::columnCount(const QModelIndex& parent) const
|
||||
@@ -31,27 +31,27 @@ int ListModel::columnCount(const QModelIndex& parent) const
|
||||
QVariant ListModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
int pos = index.row();
|
||||
if (pos >= modpacks.size() || pos < 0 || !index.isValid()) {
|
||||
if (pos >= m_modpacks.size() || pos < 0 || !index.isValid()) {
|
||||
return QString("INVALID INDEX %1").arg(pos);
|
||||
}
|
||||
|
||||
IndexedPack pack = modpacks.at(pos);
|
||||
auto pack = m_modpacks.at(pos);
|
||||
switch (role) {
|
||||
case Qt::ToolTipRole: {
|
||||
if (pack.description.length() > 100) {
|
||||
if (pack->description.length() > 100) {
|
||||
// some magic to prevent to long tooltips and replace html linebreaks
|
||||
QString edit = pack.description.left(97);
|
||||
QString edit = pack->description.left(97);
|
||||
edit = edit.left(edit.lastIndexOf("<br>")).left(edit.lastIndexOf(" ")).append("...");
|
||||
return edit;
|
||||
}
|
||||
return pack.description;
|
||||
return pack->description;
|
||||
}
|
||||
case Qt::DecorationRole: {
|
||||
if (m_logoMap.contains(pack.logoName)) {
|
||||
return (m_logoMap.value(pack.logoName));
|
||||
if (m_logoMap.contains(pack->logoName)) {
|
||||
return (m_logoMap.value(pack->logoName));
|
||||
}
|
||||
QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder");
|
||||
((ListModel*)this)->requestLogo(pack.logoName, pack.logoUrl);
|
||||
((ListModel*)this)->requestLogo(pack->logoName, pack->logoUrl);
|
||||
return icon;
|
||||
}
|
||||
case Qt::UserRole: {
|
||||
@@ -62,9 +62,9 @@ QVariant ListModel::data(const QModelIndex& index, int role) const
|
||||
case Qt::SizeHintRole:
|
||||
return QSize(0, 58);
|
||||
case UserDataTypes::TITLE:
|
||||
return pack.name;
|
||||
return pack->name;
|
||||
case UserDataTypes::DESCRIPTION:
|
||||
return pack.description;
|
||||
return pack->description;
|
||||
case UserDataTypes::INSTALLED:
|
||||
return false;
|
||||
default:
|
||||
@@ -76,11 +76,10 @@ QVariant ListModel::data(const QModelIndex& index, int role) const
|
||||
bool ListModel::setData(const QModelIndex& index, const QVariant& value, [[maybe_unused]] int role)
|
||||
{
|
||||
int pos = index.row();
|
||||
if (pos >= modpacks.size() || pos < 0 || !index.isValid())
|
||||
if (pos >= m_modpacks.size() || pos < 0 || !index.isValid())
|
||||
return false;
|
||||
|
||||
Q_ASSERT(value.canConvert<Flame::IndexedPack>());
|
||||
modpacks[pos] = value.value<Flame::IndexedPack>();
|
||||
m_modpacks[pos] = value.value<ModPlatform::IndexedPack::Ptr>();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -89,8 +88,8 @@ void ListModel::logoLoaded(QString logo, QIcon out)
|
||||
{
|
||||
m_loadingLogos.removeAll(logo);
|
||||
m_logoMap.insert(logo, out);
|
||||
for (int i = 0; i < modpacks.size(); i++) {
|
||||
if (modpacks[i].logoName == logo) {
|
||||
for (int i = 0; i < m_modpacks.size(); i++) {
|
||||
if (m_modpacks[i]->logoName == logo) {
|
||||
emit dataChanged(createIndex(i, 0), createIndex(i, 0), { Qt::DecorationRole });
|
||||
}
|
||||
}
|
||||
@@ -117,8 +116,8 @@ void ListModel::requestLogo(QString logo, QString url)
|
||||
connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] {
|
||||
job->deleteLater();
|
||||
emit logoLoaded(logo, QIcon(fullPath));
|
||||
if (waitingCallbacks.contains(logo)) {
|
||||
waitingCallbacks.value(logo)(fullPath);
|
||||
if (m_waitingCallbacks.contains(logo)) {
|
||||
m_waitingCallbacks.value(logo)(fullPath);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -148,14 +147,14 @@ Qt::ItemFlags ListModel::flags(const QModelIndex& index) const
|
||||
|
||||
bool ListModel::canFetchMore([[maybe_unused]] const QModelIndex& parent) const
|
||||
{
|
||||
return searchState == CanPossiblyFetchMore;
|
||||
return m_searchState == CanPossiblyFetchMore;
|
||||
}
|
||||
|
||||
void ListModel::fetchMore(const QModelIndex& parent)
|
||||
{
|
||||
if (parent.isValid())
|
||||
return;
|
||||
if (nextSearchOffset == 0) {
|
||||
if (m_nextSearchOffset == 0) {
|
||||
qWarning() << "fetchMore with 0 offset is wrong...";
|
||||
return;
|
||||
}
|
||||
@@ -164,138 +163,106 @@ void ListModel::fetchMore(const QModelIndex& parent)
|
||||
|
||||
void ListModel::performPaginatedSearch()
|
||||
{
|
||||
if (currentSearchTerm.startsWith("#")) {
|
||||
auto projectId = currentSearchTerm.mid(1);
|
||||
static const FlameAPI api;
|
||||
if (m_currentSearchTerm.startsWith("#")) {
|
||||
auto projectId = m_currentSearchTerm.mid(1);
|
||||
if (!projectId.isEmpty()) {
|
||||
ResourceAPI::ProjectInfoCallbacks callbacks;
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack> callbacks;
|
||||
|
||||
callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); };
|
||||
callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); };
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
|
||||
callbacks.on_abort = [this] {
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
};
|
||||
static const FlameAPI api;
|
||||
if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) {
|
||||
jobPtr = job;
|
||||
jobPtr->start();
|
||||
if (auto job = api.getProjectInfo({ { projectId } }, std::move(callbacks)); job) {
|
||||
m_jobPtr = job;
|
||||
m_jobPtr->start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
ResourceAPI::SortingMethod sort{};
|
||||
sort.index = currentSort + 1;
|
||||
sort.index = m_currentSort + 1;
|
||||
|
||||
auto netJob = makeShared<NetJob>("Flame::Search", APPLICATION->network());
|
||||
auto searchUrl =
|
||||
FlameAPI().getSearchURL({ ModPlatform::ResourceType::Modpack, nextSearchOffset, currentSearchTerm, sort, m_filter->loaders,
|
||||
m_filter->versions, ModPlatform::Side::NoSide, m_filter->categoryIds, m_filter->openSource });
|
||||
ResourceAPI::Callback<QList<ModPlatform::IndexedPack::Ptr>> callbacks{};
|
||||
|
||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl.value()), response));
|
||||
jobPtr = netJob;
|
||||
jobPtr->start();
|
||||
connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished);
|
||||
connect(netJob.get(), &NetJob::failed, this, &ListModel::searchRequestFailed);
|
||||
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
|
||||
auto netJob = api.searchProjects({ ModPlatform::ResourceType::Modpack, m_nextSearchOffset, m_currentSearchTerm, sort, m_filter->loaders,
|
||||
m_filter->versions, ModPlatform::Side::NoSide, m_filter->categoryIds, m_filter->openSource },
|
||||
std::move(callbacks));
|
||||
|
||||
m_jobPtr = netJob;
|
||||
m_jobPtr->start();
|
||||
}
|
||||
|
||||
void ListModel::searchWithTerm(const QString& term, int sort, std::shared_ptr<ModFilterWidget::Filter> filter, bool filterChanged)
|
||||
{
|
||||
if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort && !filterChanged) {
|
||||
if (m_currentSearchTerm == term && m_currentSearchTerm.isNull() == term.isNull() && m_currentSort == sort && !filterChanged) {
|
||||
return;
|
||||
}
|
||||
currentSearchTerm = term;
|
||||
currentSort = sort;
|
||||
m_currentSearchTerm = term;
|
||||
m_currentSort = sort;
|
||||
m_filter = filter;
|
||||
if (hasActiveSearchJob()) {
|
||||
jobPtr->abort();
|
||||
searchState = ResetRequested;
|
||||
m_jobPtr->abort();
|
||||
m_searchState = ResetRequested;
|
||||
return;
|
||||
}
|
||||
beginResetModel();
|
||||
modpacks.clear();
|
||||
m_modpacks.clear();
|
||||
endResetModel();
|
||||
searchState = None;
|
||||
m_searchState = None;
|
||||
|
||||
nextSearchOffset = 0;
|
||||
m_nextSearchOffset = 0;
|
||||
performPaginatedSearch();
|
||||
}
|
||||
|
||||
void Flame::ListModel::searchRequestFinished()
|
||||
void Flame::ListModel::searchRequestFinished(QList<ModPlatform::IndexedPack::Ptr>& newList)
|
||||
{
|
||||
if (hasActiveSearchJob())
|
||||
return;
|
||||
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from CurseForge at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
QList<Flame::IndexedPack> newList;
|
||||
auto packs = Json::ensureArray(doc.object(), "data");
|
||||
for (auto packRaw : packs) {
|
||||
auto packObj = packRaw.toObject();
|
||||
|
||||
Flame::IndexedPack pack;
|
||||
try {
|
||||
Flame::loadIndexedPack(pack, packObj);
|
||||
newList.append(pack);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qWarning() << "Error while loading pack from CurseForge: " << e.cause();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (packs.size() < 25) {
|
||||
searchState = Finished;
|
||||
if (newList.size() < 25) {
|
||||
m_searchState = Finished;
|
||||
} else {
|
||||
nextSearchOffset += 25;
|
||||
searchState = CanPossiblyFetchMore;
|
||||
m_nextSearchOffset += 25;
|
||||
m_searchState = CanPossiblyFetchMore;
|
||||
}
|
||||
|
||||
// When you have a Qt build with assertions turned on, proceeding here will abort the application
|
||||
if (newList.size() == 0)
|
||||
return;
|
||||
|
||||
beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1);
|
||||
modpacks.append(newList);
|
||||
beginInsertRows(QModelIndex(), m_modpacks.size(), m_modpacks.size() + newList.size() - 1);
|
||||
m_modpacks.append(newList);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void Flame::ListModel::searchRequestForOneSucceeded(QJsonDocument& doc)
|
||||
void Flame::ListModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack& pack)
|
||||
{
|
||||
jobPtr.reset();
|
||||
m_jobPtr.reset();
|
||||
|
||||
auto packObj = Json::ensureObject(doc.object(), "data");
|
||||
|
||||
Flame::IndexedPack pack;
|
||||
try {
|
||||
Flame::loadIndexedPack(pack, packObj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qWarning() << "Error while loading pack from CurseForge: " << e.cause();
|
||||
return;
|
||||
}
|
||||
|
||||
beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + 1);
|
||||
modpacks.append({ pack });
|
||||
beginInsertRows(QModelIndex(), m_modpacks.size(), m_modpacks.size() + 1);
|
||||
m_modpacks.append(std::make_shared<ModPlatform::IndexedPack>(pack));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void Flame::ListModel::searchRequestFailed(QString reason)
|
||||
{
|
||||
jobPtr.reset();
|
||||
m_jobPtr.reset();
|
||||
|
||||
if (searchState == ResetRequested) {
|
||||
if (m_searchState == ResetRequested) {
|
||||
beginResetModel();
|
||||
modpacks.clear();
|
||||
m_modpacks.clear();
|
||||
endResetModel();
|
||||
|
||||
nextSearchOffset = 0;
|
||||
m_nextSearchOffset = 0;
|
||||
performPaginatedSearch();
|
||||
} else {
|
||||
searchState = Finished;
|
||||
m_searchState = Finished;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
#include <functional>
|
||||
#include "ui/widgets/ModFilterWidget.h"
|
||||
|
||||
#include <modplatform/flame/FlamePackIndex.h>
|
||||
|
||||
namespace Flame {
|
||||
|
||||
using LogoMap = QMap<QString, QIcon>;
|
||||
@@ -41,8 +39,8 @@ class ListModel : public QAbstractListModel {
|
||||
void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback);
|
||||
void searchWithTerm(const QString& term, int sort, std::shared_ptr<ModFilterWidget::Filter> filter, bool filterChanged);
|
||||
|
||||
bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); }
|
||||
Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; }
|
||||
bool hasActiveSearchJob() const { return m_jobPtr && m_jobPtr->isRunning(); }
|
||||
Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? m_jobPtr : nullptr; }
|
||||
|
||||
private slots:
|
||||
void performPaginatedSearch();
|
||||
@@ -50,27 +48,26 @@ class ListModel : public QAbstractListModel {
|
||||
void logoFailed(QString logo);
|
||||
void logoLoaded(QString logo, QIcon out);
|
||||
|
||||
void searchRequestFinished();
|
||||
void searchRequestFinished(QList<ModPlatform::IndexedPack::Ptr>&);
|
||||
void searchRequestFailed(QString reason);
|
||||
void searchRequestForOneSucceeded(QJsonDocument&);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack&);
|
||||
|
||||
private:
|
||||
void requestLogo(QString file, QString url);
|
||||
|
||||
private:
|
||||
QList<IndexedPack> modpacks;
|
||||
QList<ModPlatform::IndexedPack::Ptr> m_modpacks;
|
||||
QStringList m_failedLogos;
|
||||
QStringList m_loadingLogos;
|
||||
LogoMap m_logoMap;
|
||||
QMap<QString, LogoCallback> waitingCallbacks;
|
||||
QMap<QString, LogoCallback> m_waitingCallbacks;
|
||||
|
||||
QString currentSearchTerm;
|
||||
int currentSort = 0;
|
||||
QString m_currentSearchTerm;
|
||||
int m_currentSort = 0;
|
||||
std::shared_ptr<ModFilterWidget::Filter> m_filter;
|
||||
int nextSearchOffset = 0;
|
||||
enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None;
|
||||
Task::Ptr jobPtr;
|
||||
std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
|
||||
int m_nextSearchOffset = 0;
|
||||
enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } m_searchState = None;
|
||||
Task::Ptr m_jobPtr;
|
||||
};
|
||||
|
||||
} // namespace Flame
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
|
||||
#include "FlamePage.h"
|
||||
#include "Version.h"
|
||||
#include "modplatform/flame/FlamePackIndex.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/widgets/ModFilterWidget.h"
|
||||
#include "ui_FlamePage.h"
|
||||
@@ -43,29 +44,25 @@
|
||||
#include <QKeyEvent>
|
||||
#include <memory>
|
||||
|
||||
#include "Application.h"
|
||||
#include "FlameModel.h"
|
||||
#include "InstanceImportTask.h"
|
||||
#include "Json.h"
|
||||
#include "StringUtils.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "ui/dialogs/NewInstanceDialog.h"
|
||||
#include "ui/widgets/ProjectItem.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
static FlameAPI api;
|
||||
|
||||
FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent)
|
||||
: QWidget(parent), ui(new Ui::FlamePage), dialog(dialog), m_fetch_progress(this, false)
|
||||
: QWidget(parent), m_ui(new Ui::FlamePage), m_dialog(dialog), m_fetch_progress(this, false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->searchEdit->installEventFilter(this);
|
||||
listModel = new Flame::ListModel(this);
|
||||
ui->packView->setModel(listModel);
|
||||
m_ui->setupUi(this);
|
||||
m_ui->searchEdit->installEventFilter(this);
|
||||
m_listModel = new Flame::ListModel(this);
|
||||
m_ui->packView->setModel(m_listModel);
|
||||
|
||||
ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
|
||||
m_ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
m_ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
|
||||
|
||||
m_search_timer.setTimerType(Qt::TimerType::CoarseTimer);
|
||||
m_search_timer.setSingleShot(true);
|
||||
@@ -76,33 +73,33 @@ FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent)
|
||||
m_fetch_progress.setFixedHeight(24);
|
||||
m_fetch_progress.progressFormat("");
|
||||
|
||||
ui->verticalLayout->insertWidget(2, &m_fetch_progress);
|
||||
m_ui->verticalLayout->insertWidget(2, &m_fetch_progress);
|
||||
|
||||
// index is used to set the sorting with the curseforge api
|
||||
ui->sortByBox->addItem(tr("Sort by Featured"));
|
||||
ui->sortByBox->addItem(tr("Sort by Popularity"));
|
||||
ui->sortByBox->addItem(tr("Sort by Last Updated"));
|
||||
ui->sortByBox->addItem(tr("Sort by Name"));
|
||||
ui->sortByBox->addItem(tr("Sort by Author"));
|
||||
ui->sortByBox->addItem(tr("Sort by Total Downloads"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Featured"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Popularity"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Last Updated"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Name"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Author"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Total Downloads"));
|
||||
|
||||
connect(ui->sortByBox, &QComboBox::currentIndexChanged, this, &FlamePage::triggerSearch);
|
||||
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlamePage::onSelectionChanged);
|
||||
connect(ui->versionSelectionBox, &QComboBox::currentIndexChanged, this, &FlamePage::onVersionSelectionChanged);
|
||||
connect(m_ui->sortByBox, &QComboBox::currentIndexChanged, this, &FlamePage::triggerSearch);
|
||||
connect(m_ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlamePage::onSelectionChanged);
|
||||
connect(m_ui->versionSelectionBox, &QComboBox::currentIndexChanged, this, &FlamePage::onVersionSelectionChanged);
|
||||
|
||||
ui->packView->setItemDelegate(new ProjectItemDelegate(this));
|
||||
ui->packDescription->setMetaEntry("FlamePacks");
|
||||
m_ui->packView->setItemDelegate(new ProjectItemDelegate(this));
|
||||
m_ui->packDescription->setMetaEntry("FlamePacks");
|
||||
createFilterWidget();
|
||||
}
|
||||
|
||||
FlamePage::~FlamePage()
|
||||
{
|
||||
delete ui;
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
bool FlamePage::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
|
||||
if (watched == m_ui->searchEdit && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->key() == Qt::Key_Return) {
|
||||
triggerSearch();
|
||||
@@ -125,7 +122,7 @@ bool FlamePage::shouldDisplay() const
|
||||
|
||||
void FlamePage::retranslate()
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
m_ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void FlamePage::openedImpl()
|
||||
@@ -136,109 +133,91 @@ void FlamePage::openedImpl()
|
||||
|
||||
void FlamePage::triggerSearch()
|
||||
{
|
||||
ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect);
|
||||
ui->packView->clearSelection();
|
||||
ui->packDescription->clear();
|
||||
ui->versionSelectionBox->clear();
|
||||
m_ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect);
|
||||
m_ui->packView->clearSelection();
|
||||
m_ui->packDescription->clear();
|
||||
m_ui->versionSelectionBox->clear();
|
||||
bool filterChanged = m_filterWidget->changed();
|
||||
listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex(), m_filterWidget->getFilter(), filterChanged);
|
||||
m_fetch_progress.watch(listModel->activeSearchJob().get());
|
||||
}
|
||||
|
||||
bool checkVersionFilters(const Flame::IndexedVersion& v, std::shared_ptr<ModFilterWidget::Filter> filter)
|
||||
{
|
||||
if (!filter)
|
||||
return true;
|
||||
return ((!filter->loaders || !v.loaders || filter->loaders & v.loaders) && // loaders
|
||||
(filter->releases.empty() || // releases
|
||||
std::find(filter->releases.cbegin(), filter->releases.cend(), v.version_type) != filter->releases.cend()) &&
|
||||
filter->checkMcVersions({ v.mcVersion })); // mcVersions}
|
||||
m_listModel->searchWithTerm(m_ui->searchEdit->text(), m_ui->sortByBox->currentIndex(), m_filterWidget->getFilter(), filterChanged);
|
||||
m_fetch_progress.watch(m_listModel->activeSearchJob().get());
|
||||
}
|
||||
|
||||
void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev)
|
||||
{
|
||||
ui->versionSelectionBox->clear();
|
||||
m_ui->versionSelectionBox->clear();
|
||||
|
||||
if (!curr.isValid()) {
|
||||
if (isOpened) {
|
||||
dialog->setSuggestedPack();
|
||||
m_dialog->setSuggestedPack();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant raw = listModel->data(curr, Qt::UserRole);
|
||||
Q_ASSERT(raw.canConvert<Flame::IndexedPack>());
|
||||
current = raw.value<Flame::IndexedPack>();
|
||||
m_current = m_listModel->data(curr, Qt::UserRole).value<ModPlatform::IndexedPack::Ptr>();
|
||||
|
||||
if (!current.versionsLoaded || m_filterWidget->changed()) {
|
||||
if (!m_current->versionsLoaded || m_filterWidget->changed()) {
|
||||
qDebug() << "Loading flame modpack versions";
|
||||
auto netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name), APPLICATION->network());
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
int addonId = current.addonId;
|
||||
netJob->addNetAction(
|
||||
Net::ApiDownload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/mods/%1/files").arg(addonId), response));
|
||||
|
||||
connect(netJob, &NetJob::succeeded, this, [this, response, addonId, curr] {
|
||||
if (addonId != current.addonId) {
|
||||
ResourceAPI::Callback<QVector<ModPlatform::IndexedVersion> > callbacks{};
|
||||
|
||||
auto addonId = m_current->addonId;
|
||||
// Use default if no callbacks are set
|
||||
callbacks.on_succeed = [this, curr, addonId](auto& doc) {
|
||||
if (addonId != m_current->addonId) {
|
||||
return; // wrong request
|
||||
}
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from CurseForge at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
auto arr = Json::ensureArray(doc.object(), "data");
|
||||
try {
|
||||
Flame::loadIndexedPackVersions(current, arr);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << *response;
|
||||
qWarning() << "Error while reading flame modpack version: " << e.cause();
|
||||
}
|
||||
|
||||
auto pred = [this](const Flame::IndexedVersion& v) { return !checkVersionFilters(v, m_filterWidget->getFilter()); };
|
||||
m_current->versions = doc;
|
||||
m_current->versionsLoaded = true;
|
||||
auto pred = [this](const ModPlatform::IndexedVersion& v) {
|
||||
if (auto filter = m_filterWidget->getFilter())
|
||||
return !filter->checkModpackFilters(v);
|
||||
return false;
|
||||
};
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
|
||||
current.versions.removeIf(pred);
|
||||
m_current->versions.removeIf(pred);
|
||||
#else
|
||||
for (auto it = current.versions.begin(); it != current.versions.end();)
|
||||
if (pred(*it))
|
||||
it = current.versions.erase(it);
|
||||
else
|
||||
++it;
|
||||
for (auto it = m_current->versions.begin(); it != m_current->versions.end();)
|
||||
if (pred(*it))
|
||||
it = m_current->versions.erase(it);
|
||||
else
|
||||
++it;
|
||||
#endif
|
||||
for (const auto& version : current.versions) {
|
||||
ui->versionSelectionBox->addItem(Flame::getVersionDisplayString(version), QVariant(version.downloadUrl));
|
||||
for (auto version : m_current->versions) {
|
||||
m_ui->versionSelectionBox->addItem(version.getVersionDisplayString(), QVariant(version.downloadUrl));
|
||||
}
|
||||
|
||||
QVariant current_updated;
|
||||
current_updated.setValue(current);
|
||||
current_updated.setValue(m_current);
|
||||
|
||||
if (!listModel->setData(curr, current_updated, Qt::UserRole))
|
||||
if (!m_listModel->setData(curr, current_updated, Qt::UserRole))
|
||||
qWarning() << "Failed to cache versions for the current pack!";
|
||||
|
||||
// TODO: Check whether it's a connection issue or the project disabled 3rd-party distribution.
|
||||
if (current.versionsLoaded && ui->versionSelectionBox->count() < 1) {
|
||||
ui->versionSelectionBox->addItem(tr("No version is available!"), -1);
|
||||
if (m_current->versionsLoaded && m_ui->versionSelectionBox->count() < 1) {
|
||||
m_ui->versionSelectionBox->addItem(tr("No version is available!"), -1);
|
||||
}
|
||||
suggestCurrent();
|
||||
});
|
||||
connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); });
|
||||
connect(netJob, &NetJob::failed,
|
||||
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
};
|
||||
callbacks.on_fail = [this](QString reason, int) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
|
||||
};
|
||||
|
||||
auto netJob = api.getProjectVersions({ *m_current, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
|
||||
m_job = netJob;
|
||||
netJob->start();
|
||||
} else {
|
||||
for (auto version : current.versions) {
|
||||
ui->versionSelectionBox->addItem(version.version, QVariant(version.downloadUrl));
|
||||
for (auto version : m_current->versions) {
|
||||
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.downloadUrl));
|
||||
}
|
||||
|
||||
suggestCurrent();
|
||||
}
|
||||
|
||||
// TODO: Check whether it's a connection issue or the project disabled 3rd-party distribution.
|
||||
if (current.versionsLoaded && ui->versionSelectionBox->count() < 1) {
|
||||
ui->versionSelectionBox->addItem(tr("No version is available!"), -1);
|
||||
if (m_current->versionsLoaded && m_ui->versionSelectionBox->count() < 1) {
|
||||
m_ui->versionSelectionBox->addItem(tr("No version is available!"), -1);
|
||||
}
|
||||
|
||||
updateUi();
|
||||
@@ -251,26 +230,26 @@ void FlamePage::suggestCurrent()
|
||||
}
|
||||
|
||||
if (m_selected_version_index == -1) {
|
||||
dialog->setSuggestedPack();
|
||||
m_dialog->setSuggestedPack();
|
||||
return;
|
||||
}
|
||||
|
||||
auto version = current.versions.at(m_selected_version_index);
|
||||
auto version = m_current->versions.at(m_selected_version_index);
|
||||
|
||||
QMap<QString, QString> extra_info;
|
||||
extra_info.insert("pack_id", QString::number(current.addonId));
|
||||
extra_info.insert("pack_version_id", QString::number(version.fileId));
|
||||
extra_info.insert("pack_id", m_current->addonId.toString());
|
||||
extra_info.insert("pack_version_id", version.fileId.toString());
|
||||
|
||||
dialog->setSuggestedPack(current.name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info)));
|
||||
QString editedLogoName = "curseforge_" + current.logoName;
|
||||
listModel->getLogo(current.logoName, current.logoUrl,
|
||||
[this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); });
|
||||
m_dialog->setSuggestedPack(m_current->name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info)));
|
||||
QString editedLogoName = "curseforge_" + m_current->logoName;
|
||||
m_listModel->getLogo(m_current->logoName, m_current->logoUrl,
|
||||
[this, editedLogoName](QString logo) { m_dialog->setSuggestedIconFromFile(logo, editedLogoName); });
|
||||
}
|
||||
|
||||
void FlamePage::onVersionSelectionChanged(int index)
|
||||
{
|
||||
bool is_blocked = false;
|
||||
ui->versionSelectionBox->itemData(index).toInt(&is_blocked);
|
||||
m_ui->versionSelectionBox->itemData(index).toInt(&is_blocked);
|
||||
|
||||
if (index == -1 || is_blocked) {
|
||||
m_selected_version_index = -1;
|
||||
@@ -279,7 +258,7 @@ void FlamePage::onVersionSelectionChanged(int index)
|
||||
|
||||
m_selected_version_index = index;
|
||||
|
||||
Q_ASSERT(current.versions.at(m_selected_version_index).downloadUrl == ui->versionSelectionBox->currentData().toString());
|
||||
Q_ASSERT(m_current->versions.at(m_selected_version_index).downloadUrl == m_ui->versionSelectionBox->currentData().toString());
|
||||
|
||||
suggestCurrent();
|
||||
}
|
||||
@@ -287,66 +266,67 @@ void FlamePage::onVersionSelectionChanged(int index)
|
||||
void FlamePage::updateUi()
|
||||
{
|
||||
QString text = "";
|
||||
QString name = current.name;
|
||||
QString name = m_current->name;
|
||||
|
||||
if (current.extra.websiteUrl.isEmpty())
|
||||
if (m_current->websiteUrl.isEmpty())
|
||||
text = name;
|
||||
else
|
||||
text = "<a href=\"" + current.extra.websiteUrl + "\">" + name + "</a>";
|
||||
if (!current.authors.empty()) {
|
||||
auto authorToStr = [](Flame::ModpackAuthor& author) {
|
||||
text = "<a href=\"" + m_current->websiteUrl + "\">" + name + "</a>";
|
||||
if (!m_current->authors.empty()) {
|
||||
auto authorToStr = [](ModPlatform::ModpackAuthor& author) {
|
||||
if (author.url.isEmpty()) {
|
||||
return author.name;
|
||||
}
|
||||
return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name);
|
||||
};
|
||||
QStringList authorStrs;
|
||||
for (auto& author : current.authors) {
|
||||
for (auto& author : m_current->authors) {
|
||||
authorStrs.push_back(authorToStr(author));
|
||||
}
|
||||
text += "<br>" + tr(" by ") + authorStrs.join(", ");
|
||||
}
|
||||
|
||||
if (current.extraInfoLoaded) {
|
||||
if (!current.extra.issuesUrl.isEmpty() || !current.extra.sourceUrl.isEmpty() || !current.extra.wikiUrl.isEmpty()) {
|
||||
if (m_current->extraDataLoaded) {
|
||||
if (!m_current->extraData.issuesUrl.isEmpty() || !m_current->extraData.sourceUrl.isEmpty() ||
|
||||
!m_current->extraData.wikiUrl.isEmpty()) {
|
||||
text += "<br><br>" + tr("External links:") + "<br>";
|
||||
}
|
||||
|
||||
if (!current.extra.issuesUrl.isEmpty())
|
||||
text += "- " + tr("Issues: <a href=%1>%1</a>").arg(current.extra.issuesUrl) + "<br>";
|
||||
if (!current.extra.wikiUrl.isEmpty())
|
||||
text += "- " + tr("Wiki: <a href=%1>%1</a>").arg(current.extra.wikiUrl) + "<br>";
|
||||
if (!current.extra.sourceUrl.isEmpty())
|
||||
text += "- " + tr("Source code: <a href=%1>%1</a>").arg(current.extra.sourceUrl) + "<br>";
|
||||
if (!m_current->extraData.issuesUrl.isEmpty())
|
||||
text += "- " + tr("Issues: <a href=%1>%1</a>").arg(m_current->extraData.issuesUrl) + "<br>";
|
||||
if (!m_current->extraData.wikiUrl.isEmpty())
|
||||
text += "- " + tr("Wiki: <a href=%1>%1</a>").arg(m_current->extraData.wikiUrl) + "<br>";
|
||||
if (!m_current->extraData.sourceUrl.isEmpty())
|
||||
text += "- " + tr("Source code: <a href=%1>%1</a>").arg(m_current->extraData.sourceUrl) + "<br>";
|
||||
}
|
||||
|
||||
text += "<hr>";
|
||||
text += api.getModDescription(current.addonId).toUtf8();
|
||||
text += api.getModDescription(m_current->addonId.toInt()).toUtf8();
|
||||
|
||||
ui->packDescription->setHtml(StringUtils::htmlListPatch(text + current.description));
|
||||
ui->packDescription->flush();
|
||||
m_ui->packDescription->setHtml(StringUtils::htmlListPatch(text + m_current->description));
|
||||
m_ui->packDescription->flush();
|
||||
}
|
||||
QString FlamePage::getSerachTerm() const
|
||||
{
|
||||
return ui->searchEdit->text();
|
||||
return m_ui->searchEdit->text();
|
||||
}
|
||||
|
||||
void FlamePage::setSearchTerm(QString term)
|
||||
{
|
||||
ui->searchEdit->setText(term);
|
||||
m_ui->searchEdit->setText(term);
|
||||
}
|
||||
|
||||
void FlamePage::createFilterWidget()
|
||||
{
|
||||
auto widget = ModFilterWidget::create(nullptr, false);
|
||||
m_filterWidget.swap(widget);
|
||||
auto old = ui->splitter->replaceWidget(0, m_filterWidget.get());
|
||||
auto old = m_ui->splitter->replaceWidget(0, m_filterWidget.get());
|
||||
// because we replaced the widget we also need to delete it
|
||||
if (old) {
|
||||
delete old;
|
||||
}
|
||||
|
||||
connect(ui->filterButton, &QPushButton::clicked, this, [this] { m_filterWidget->setHidden(!m_filterWidget->isHidden()); });
|
||||
connect(m_ui->filterButton, &QPushButton::clicked, this, [this] { m_filterWidget->setHidden(!m_filterWidget->isHidden()); });
|
||||
|
||||
connect(m_filterWidget.get(), &ModFilterWidget::filterChanged, this, &FlamePage::triggerSearch);
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
#include <QWidget>
|
||||
|
||||
#include <Application.h>
|
||||
#include <modplatform/flame/FlamePackIndex.h>
|
||||
#include <QTimer>
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "ui/pages/modplatform/ModpackProviderBasePage.h"
|
||||
#include "ui/widgets/ModFilterWidget.h"
|
||||
#include "ui/widgets/ProgressWidget.h"
|
||||
@@ -88,10 +88,10 @@ class FlamePage : public QWidget, public ModpackProviderBasePage {
|
||||
void createFilterWidget();
|
||||
|
||||
private:
|
||||
Ui::FlamePage* ui = nullptr;
|
||||
NewInstanceDialog* dialog = nullptr;
|
||||
Flame::ListModel* listModel = nullptr;
|
||||
Flame::IndexedPack current;
|
||||
Ui::FlamePage* m_ui = nullptr;
|
||||
NewInstanceDialog* m_dialog = nullptr;
|
||||
Flame::ListModel* m_listModel = nullptr;
|
||||
ModPlatform::IndexedPack::Ptr m_current;
|
||||
|
||||
int m_selected_version_index = -1;
|
||||
|
||||
@@ -102,4 +102,5 @@ class FlamePage : public QWidget, public ModpackProviderBasePage {
|
||||
|
||||
std::unique_ptr<ModFilterWidget> m_filterWidget;
|
||||
Task::Ptr m_categoriesTask;
|
||||
Task::Ptr m_job;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
#include "ui/pages/modplatform/flame/FlameResourcePages.h"
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
@@ -17,97 +17,9 @@ static bool isOptedOut(const ModPlatform::IndexedVersion& ver)
|
||||
return ver.downloadUrl.isEmpty();
|
||||
}
|
||||
|
||||
FlameModModel::FlameModModel(BaseInstance& base) : ModModel(base, new FlameAPI) {}
|
||||
|
||||
void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
FlameMod::loadIndexedPack(m, obj);
|
||||
}
|
||||
|
||||
// We already deal with the URLs when initializing the pack, due to the API response's structure
|
||||
void FlameModModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
FlameMod::loadBody(m, obj);
|
||||
}
|
||||
|
||||
void FlameModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
|
||||
{
|
||||
FlameMod::loadIndexedPackVersions(m, arr);
|
||||
}
|
||||
|
||||
auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
|
||||
{
|
||||
return FlameMod::loadDependencyVersions(m, arr, &m_base_instance);
|
||||
}
|
||||
|
||||
bool FlameModModel::optedOut(const ModPlatform::IndexedVersion& ver) const
|
||||
{
|
||||
return isOptedOut(ver);
|
||||
}
|
||||
|
||||
auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return Json::ensureArray(obj.object(), "data");
|
||||
}
|
||||
|
||||
FlameResourcePackModel::FlameResourcePackModel(const BaseInstance& base) : ResourcePackResourceModel(base, new FlameAPI) {}
|
||||
|
||||
void FlameResourcePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
FlameMod::loadIndexedPack(m, obj);
|
||||
}
|
||||
|
||||
// We already deal with the URLs when initializing the pack, due to the API response's structure
|
||||
void FlameResourcePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
FlameMod::loadBody(m, obj);
|
||||
}
|
||||
|
||||
void FlameResourcePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
|
||||
{
|
||||
FlameMod::loadIndexedPackVersions(m, arr);
|
||||
}
|
||||
|
||||
bool FlameResourcePackModel::optedOut(const ModPlatform::IndexedVersion& ver) const
|
||||
{
|
||||
return isOptedOut(ver);
|
||||
}
|
||||
|
||||
auto FlameResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return Json::ensureArray(obj.object(), "data");
|
||||
}
|
||||
|
||||
FlameTexturePackModel::FlameTexturePackModel(const BaseInstance& base) : TexturePackResourceModel(base, new FlameAPI) {}
|
||||
|
||||
void FlameTexturePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
FlameMod::loadIndexedPack(m, obj);
|
||||
}
|
||||
|
||||
// We already deal with the URLs when initializing the pack, due to the API response's structure
|
||||
void FlameTexturePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
FlameMod::loadBody(m, obj);
|
||||
}
|
||||
|
||||
void FlameTexturePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
|
||||
{
|
||||
FlameMod::loadIndexedPackVersions(m, arr);
|
||||
|
||||
QList<ModPlatform::IndexedVersion> filtered_versions(m.versions.size());
|
||||
|
||||
// FIXME: Client-side version filtering. This won't take into account any user-selected filtering.
|
||||
for (auto const& version : m.versions) {
|
||||
auto const& mc_versions = version.mcVersion;
|
||||
|
||||
if (std::any_of(mc_versions.constBegin(), mc_versions.constEnd(),
|
||||
[this](auto const& mc_version) { return Version(mc_version) <= maximumTexturePackVersion(); }))
|
||||
filtered_versions.push_back(version);
|
||||
}
|
||||
|
||||
m.versions = filtered_versions;
|
||||
}
|
||||
FlameTexturePackModel::FlameTexturePackModel(const BaseInstance& base)
|
||||
: TexturePackResourceModel(base, new FlameAPI, Flame::debugName(), Flame::metaEntryBase())
|
||||
{}
|
||||
|
||||
ResourceAPI::SearchArgs FlameTexturePackModel::createSearchArguments()
|
||||
{
|
||||
@@ -137,65 +49,4 @@ bool FlameTexturePackModel::optedOut(const ModPlatform::IndexedVersion& ver) con
|
||||
return isOptedOut(ver);
|
||||
}
|
||||
|
||||
auto FlameTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return Json::ensureArray(obj.object(), "data");
|
||||
}
|
||||
|
||||
FlameShaderPackModel::FlameShaderPackModel(const BaseInstance& base) : ShaderPackResourceModel(base, new FlameAPI) {}
|
||||
|
||||
void FlameShaderPackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
FlameMod::loadIndexedPack(m, obj);
|
||||
}
|
||||
|
||||
// We already deal with the URLs when initializing the pack, due to the API response's structure
|
||||
void FlameShaderPackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
FlameMod::loadBody(m, obj);
|
||||
}
|
||||
|
||||
void FlameShaderPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
|
||||
{
|
||||
FlameMod::loadIndexedPackVersions(m, arr);
|
||||
}
|
||||
|
||||
bool FlameShaderPackModel::optedOut(const ModPlatform::IndexedVersion& ver) const
|
||||
{
|
||||
return isOptedOut(ver);
|
||||
}
|
||||
|
||||
auto FlameShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return Json::ensureArray(obj.object(), "data");
|
||||
}
|
||||
|
||||
FlameDataPackModel::FlameDataPackModel(const BaseInstance& base) : DataPackResourceModel(base, new FlameAPI) {}
|
||||
|
||||
void FlameDataPackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
FlameMod::loadIndexedPack(m, obj);
|
||||
}
|
||||
|
||||
// We already deal with the URLs when initializing the pack, due to the API response's structure
|
||||
void FlameDataPackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
FlameMod::loadBody(m, obj);
|
||||
}
|
||||
|
||||
void FlameDataPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
|
||||
{
|
||||
FlameMod::loadIndexedPackVersions(m, arr);
|
||||
}
|
||||
|
||||
bool FlameDataPackModel::optedOut(const ModPlatform::IndexedVersion& ver) const
|
||||
{
|
||||
return isOptedOut(ver);
|
||||
}
|
||||
|
||||
auto FlameDataPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return Json::ensureArray(obj.object(), "data");
|
||||
}
|
||||
|
||||
} // namespace ResourceDownload
|
||||
|
||||
@@ -5,52 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/pages/modplatform/ModModel.h"
|
||||
#include "ui/pages/modplatform/ResourcePackModel.h"
|
||||
#include "ui/pages/modplatform/flame/FlameResourcePages.h"
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
class FlameModModel : public ModModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameModModel(BaseInstance&);
|
||||
~FlameModModel() override = default;
|
||||
|
||||
bool optedOut(const ModPlatform::IndexedVersion& ver) const override;
|
||||
|
||||
private:
|
||||
QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
||||
QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion override;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
||||
class FlameResourcePackModel : public ResourcePackResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameResourcePackModel(const BaseInstance&);
|
||||
~FlameResourcePackModel() override = default;
|
||||
|
||||
bool optedOut(const ModPlatform::IndexedVersion& ver) const override;
|
||||
|
||||
private:
|
||||
QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
||||
QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
||||
class FlameTexturePackModel : public TexturePackResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -64,52 +22,8 @@ class FlameTexturePackModel : public TexturePackResourceModel {
|
||||
QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
||||
QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
|
||||
ResourceAPI::SearchArgs createSearchArguments() override;
|
||||
ResourceAPI::VersionSearchArgs createVersionsArguments(const QModelIndex&) override;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
||||
class FlameShaderPackModel : public ShaderPackResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameShaderPackModel(const BaseInstance&);
|
||||
~FlameShaderPackModel() override = default;
|
||||
|
||||
bool optedOut(const ModPlatform::IndexedVersion& ver) const override;
|
||||
|
||||
private:
|
||||
QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
||||
QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
||||
class FlameDataPackModel : public DataPackResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameDataPackModel(const BaseInstance&);
|
||||
~FlameDataPackModel() override = default;
|
||||
|
||||
bool optedOut(const ModPlatform::IndexedVersion& ver) const override;
|
||||
|
||||
private:
|
||||
QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
||||
QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
||||
} // namespace ResourceDownload
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#include "FlameResourcePages.h"
|
||||
#include <QList>
|
||||
#include <memory>
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "ui_ResourcePage.h"
|
||||
|
||||
@@ -51,7 +50,7 @@ namespace ResourceDownload {
|
||||
|
||||
FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance)
|
||||
{
|
||||
m_model = new FlameModModel(instance);
|
||||
m_model = new ModModel(instance, new FlameAPI(), Flame::debugName(), Flame::metaEntryBase());
|
||||
m_ui->packView->setModel(m_model);
|
||||
|
||||
addSortings();
|
||||
@@ -85,7 +84,7 @@ void FlameModPage::openUrl(const QUrl& url)
|
||||
FlameResourcePackPage::FlameResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance)
|
||||
: ResourcePackResourcePage(dialog, instance)
|
||||
{
|
||||
m_model = new FlameResourcePackModel(instance);
|
||||
m_model = new ResourcePackResourceModel(instance, new FlameAPI(), Flame::debugName(), Flame::metaEntryBase());
|
||||
m_ui->packView->setModel(m_model);
|
||||
|
||||
addSortings();
|
||||
@@ -169,7 +168,7 @@ void FlameDataPackPage::openUrl(const QUrl& url)
|
||||
FlameShaderPackPage::FlameShaderPackPage(ShaderPackDownloadDialog* dialog, BaseInstance& instance)
|
||||
: ShaderPackResourcePage(dialog, instance)
|
||||
{
|
||||
m_model = new FlameShaderPackModel(instance);
|
||||
m_model = new ShaderPackResourceModel(instance, new FlameAPI(), Flame::debugName(), Flame::metaEntryBase());
|
||||
m_ui->packView->setModel(m_model);
|
||||
|
||||
addSortings();
|
||||
@@ -184,10 +183,9 @@ FlameShaderPackPage::FlameShaderPackPage(ShaderPackDownloadDialog* dialog, BaseI
|
||||
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
||||
}
|
||||
|
||||
FlameDataPackPage::FlameDataPackPage(DataPackDownloadDialog* dialog, BaseInstance& instance)
|
||||
: DataPackResourcePage(dialog, instance)
|
||||
FlameDataPackPage::FlameDataPackPage(DataPackDownloadDialog* dialog, BaseInstance& instance) : DataPackResourcePage(dialog, instance)
|
||||
{
|
||||
m_model = new FlameDataPackModel(instance);
|
||||
m_model = new DataPackResourceModel(instance, new FlameAPI(), Flame::debugName(), Flame::metaEntryBase());
|
||||
m_ui->packView->setModel(m_model);
|
||||
|
||||
addSortings();
|
||||
|
||||
@@ -62,7 +62,7 @@ void ModpackListModel::fetchMore(const QModelIndex& parent)
|
||||
{
|
||||
if (parent.isValid())
|
||||
return;
|
||||
if (nextSearchOffset == 0) {
|
||||
if (m_nextSearchOffset == 0) {
|
||||
qWarning() << "fetchMore with 0 offset is wrong...";
|
||||
return;
|
||||
}
|
||||
@@ -72,27 +72,27 @@ void ModpackListModel::fetchMore(const QModelIndex& parent)
|
||||
auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVariant
|
||||
{
|
||||
int pos = index.row();
|
||||
if (pos >= modpacks.size() || pos < 0 || !index.isValid()) {
|
||||
if (pos >= m_modpacks.size() || pos < 0 || !index.isValid()) {
|
||||
return QString("INVALID INDEX %1").arg(pos);
|
||||
}
|
||||
|
||||
Modrinth::Modpack pack = modpacks.at(pos);
|
||||
auto pack = m_modpacks.at(pos);
|
||||
switch (role) {
|
||||
case Qt::ToolTipRole: {
|
||||
if (pack.description.length() > 100) {
|
||||
if (pack->description.length() > 100) {
|
||||
// some magic to prevent to long tooltips and replace html linebreaks
|
||||
QString edit = pack.description.left(97);
|
||||
QString edit = pack->description.left(97);
|
||||
edit = edit.left(edit.lastIndexOf("<br>")).left(edit.lastIndexOf(" ")).append("...");
|
||||
return edit;
|
||||
}
|
||||
return pack.description;
|
||||
return pack->description;
|
||||
}
|
||||
case Qt::DecorationRole: {
|
||||
if (m_logoMap.contains(pack.iconName))
|
||||
return m_logoMap.value(pack.iconName);
|
||||
if (m_logoMap.contains(pack->logoName))
|
||||
return m_logoMap.value(pack->logoName);
|
||||
|
||||
QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder");
|
||||
((ModpackListModel*)this)->requestLogo(pack.iconName, pack.iconUrl.toString());
|
||||
((ModpackListModel*)this)->requestLogo(pack->logoName, pack->logoUrl);
|
||||
return icon;
|
||||
}
|
||||
case Qt::UserRole: {
|
||||
@@ -104,9 +104,9 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian
|
||||
return QSize(0, 58);
|
||||
// Custom data
|
||||
case UserDataTypes::TITLE:
|
||||
return pack.name;
|
||||
return pack->name;
|
||||
case UserDataTypes::DESCRIPTION:
|
||||
return pack.description;
|
||||
return pack->description;
|
||||
case UserDataTypes::INSTALLED:
|
||||
return false;
|
||||
default:
|
||||
@@ -119,11 +119,10 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian
|
||||
bool ModpackListModel::setData(const QModelIndex& index, const QVariant& value, [[maybe_unused]] int role)
|
||||
{
|
||||
int pos = index.row();
|
||||
if (pos >= modpacks.size() || pos < 0 || !index.isValid())
|
||||
if (pos >= m_modpacks.size() || pos < 0 || !index.isValid())
|
||||
return false;
|
||||
|
||||
Q_ASSERT(value.canConvert<Modrinth::Modpack>());
|
||||
modpacks[pos] = value.value<Modrinth::Modpack>();
|
||||
m_modpacks[pos] = value.value<ModPlatform::IndexedPack::Ptr>();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -132,68 +131,56 @@ void ModpackListModel::performPaginatedSearch()
|
||||
{
|
||||
if (hasActiveSearchJob())
|
||||
return;
|
||||
static const ModrinthAPI api;
|
||||
|
||||
if (currentSearchTerm.startsWith("#")) {
|
||||
auto projectId = currentSearchTerm.mid(1);
|
||||
if (m_currentSearchTerm.startsWith("#")) {
|
||||
auto projectId = m_currentSearchTerm.mid(1);
|
||||
if (!projectId.isEmpty()) {
|
||||
ResourceAPI::ProjectInfoCallbacks callbacks;
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack> callbacks;
|
||||
|
||||
callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); };
|
||||
callbacks.on_succeed = [this](auto& doc, auto&) { searchRequestForOneSucceeded(doc); };
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
|
||||
callbacks.on_abort = [this] {
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
};
|
||||
static const ModrinthAPI api;
|
||||
if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) {
|
||||
jobPtr = job;
|
||||
jobPtr->start();
|
||||
m_jobPtr = job;
|
||||
m_jobPtr->start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
} // TODO: Move to standalone API
|
||||
ResourceAPI::SortingMethod sort{};
|
||||
sort.name = currentSort;
|
||||
auto searchUrl =
|
||||
ModrinthAPI().getSearchURL({ ModPlatform::ResourceType::Modpack, nextSearchOffset, currentSearchTerm, sort, m_filter->loaders,
|
||||
m_filter->versions, ModPlatform::Side::NoSide, m_filter->categoryIds, m_filter->openSource });
|
||||
sort.name = m_currentSort;
|
||||
|
||||
auto netJob = makeShared<NetJob>("Modrinth::SearchModpack", APPLICATION->network());
|
||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl.value()), m_allResponse));
|
||||
ResourceAPI::Callback<QList<ModPlatform::IndexedPack::Ptr>> callbacks{};
|
||||
|
||||
connect(netJob.get(), &NetJob::succeeded, this, [this] {
|
||||
QJsonParseError parseError{};
|
||||
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*m_allResponse, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from " << debugName() << " at " << parseError.offset
|
||||
<< " reason: " << parseError.errorString();
|
||||
qWarning() << *m_allResponse;
|
||||
return;
|
||||
}
|
||||
auto netJob = api.searchProjects({ ModPlatform::ResourceType::Modpack, m_nextSearchOffset, m_currentSearchTerm, sort, m_filter->loaders,
|
||||
m_filter->versions, ModPlatform::Side::NoSide, m_filter->categoryIds, m_filter->openSource },
|
||||
std::move(callbacks));
|
||||
|
||||
searchRequestFinished(doc);
|
||||
});
|
||||
connect(netJob.get(), &NetJob::failed, this, &ModpackListModel::searchRequestFailed);
|
||||
|
||||
jobPtr = netJob;
|
||||
jobPtr->start();
|
||||
m_jobPtr = netJob;
|
||||
m_jobPtr->start();
|
||||
}
|
||||
|
||||
void ModpackListModel::refresh()
|
||||
{
|
||||
if (hasActiveSearchJob()) {
|
||||
jobPtr->abort();
|
||||
searchState = ResetRequested;
|
||||
m_jobPtr->abort();
|
||||
m_searchState = ResetRequested;
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
modpacks.clear();
|
||||
m_modpacks.clear();
|
||||
endResetModel();
|
||||
searchState = None;
|
||||
m_searchState = None;
|
||||
|
||||
nextSearchOffset = 0;
|
||||
m_nextSearchOffset = 0;
|
||||
performPaginatedSearch();
|
||||
}
|
||||
|
||||
@@ -224,12 +211,12 @@ void ModpackListModel::searchWithTerm(const QString& term,
|
||||
|
||||
auto sort_str = sortFromIndex(sort);
|
||||
|
||||
if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort_str && !filterChanged) {
|
||||
if (m_currentSearchTerm == term && m_currentSearchTerm.isNull() == term.isNull() && m_currentSort == sort_str && !filterChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentSearchTerm = term;
|
||||
currentSort = sort_str;
|
||||
m_currentSearchTerm = term;
|
||||
m_currentSort = sort_str;
|
||||
m_filter = filter;
|
||||
|
||||
refresh();
|
||||
@@ -259,8 +246,8 @@ void ModpackListModel::requestLogo(QString logo, QString url)
|
||||
connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] {
|
||||
job->deleteLater();
|
||||
emit logoLoaded(logo, QIcon(fullPath));
|
||||
if (waitingCallbacks.contains(logo)) {
|
||||
waitingCallbacks.value(logo)(fullPath);
|
||||
if (m_waitingCallbacks.contains(logo)) {
|
||||
m_waitingCallbacks.value(logo)(fullPath);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -279,8 +266,8 @@ void ModpackListModel::logoLoaded(QString logo, QIcon out)
|
||||
{
|
||||
m_loadingLogos.removeAll(logo);
|
||||
m_logoMap.insert(logo, out);
|
||||
for (int i = 0; i < modpacks.size(); i++) {
|
||||
if (modpacks[i].iconName == logo) {
|
||||
for (int i = 0; i < m_modpacks.size(); i++) {
|
||||
if (m_modpacks[i]->logoName == logo) {
|
||||
emit dataChanged(createIndex(i, 0), createIndex(i, 0), { Qt::DecorationRole });
|
||||
}
|
||||
}
|
||||
@@ -292,65 +279,38 @@ void ModpackListModel::logoFailed(QString logo)
|
||||
m_loadingLogos.removeAll(logo);
|
||||
}
|
||||
|
||||
void ModpackListModel::searchRequestFinished(QJsonDocument& doc_all)
|
||||
void ModpackListModel::searchRequestFinished(QList<ModPlatform::IndexedPack::Ptr>& newList)
|
||||
{
|
||||
jobPtr.reset();
|
||||
m_jobPtr.reset();
|
||||
|
||||
QList<Modrinth::Modpack> newList;
|
||||
|
||||
auto packs_all = doc_all.object().value("hits").toArray();
|
||||
for (auto packRaw : packs_all) {
|
||||
auto packObj = packRaw.toObject();
|
||||
|
||||
Modrinth::Modpack pack;
|
||||
try {
|
||||
Modrinth::loadIndexedPack(pack, packObj);
|
||||
newList.append(pack);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qWarning() << "Error while loading mod from " << m_parent->debugName() << ": " << e.cause();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (packs_all.size() < m_modpacks_per_page) {
|
||||
searchState = Finished;
|
||||
if (newList.size() < m_modpacks_per_page) {
|
||||
m_searchState = Finished;
|
||||
} else {
|
||||
nextSearchOffset += m_modpacks_per_page;
|
||||
searchState = CanPossiblyFetchMore;
|
||||
m_nextSearchOffset += m_modpacks_per_page;
|
||||
m_searchState = CanPossiblyFetchMore;
|
||||
}
|
||||
|
||||
// When you have a Qt build with assertions turned on, proceeding here will abort the application
|
||||
if (newList.size() == 0)
|
||||
return;
|
||||
|
||||
beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1);
|
||||
modpacks.append(newList);
|
||||
beginInsertRows(QModelIndex(), m_modpacks.size(), m_modpacks.size() + newList.size() - 1);
|
||||
m_modpacks.append(newList);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void ModpackListModel::searchRequestForOneSucceeded(QJsonDocument& doc)
|
||||
void ModpackListModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack& pack)
|
||||
{
|
||||
jobPtr.reset();
|
||||
m_jobPtr.reset();
|
||||
|
||||
auto packObj = doc.object();
|
||||
|
||||
Modrinth::Modpack pack;
|
||||
try {
|
||||
Modrinth::loadIndexedPack(pack, packObj);
|
||||
pack.id = Json::ensureString(packObj, "id", pack.id);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qWarning() << "Error while loading mod from " << m_parent->debugName() << ": " << e.cause();
|
||||
return;
|
||||
}
|
||||
|
||||
beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + 1);
|
||||
modpacks.append({ pack });
|
||||
beginInsertRows(QModelIndex(), m_modpacks.size(), m_modpacks.size() + 1);
|
||||
m_modpacks.append(std::make_shared<ModPlatform::IndexedPack>(pack));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void ModpackListModel::searchRequestFailed(QString)
|
||||
{
|
||||
auto failed_action = dynamic_cast<NetJob*>(jobPtr.get())->getFailedActions().at(0);
|
||||
auto failed_action = dynamic_cast<NetJob*>(m_jobPtr.get())->getFailedActions().at(0);
|
||||
if (failed_action->replyStatusCode() == -1) {
|
||||
// Network error
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load modpacks."));
|
||||
@@ -362,17 +322,17 @@ void ModpackListModel::searchRequestFailed(QString)
|
||||
.arg(m_parent->displayName())
|
||||
.arg(tr("API version too old!\nPlease update %1!").arg(BuildConfig.LAUNCHER_DISPLAYNAME)));
|
||||
}
|
||||
jobPtr.reset();
|
||||
m_jobPtr.reset();
|
||||
|
||||
if (searchState == ResetRequested) {
|
||||
if (m_searchState == ResetRequested) {
|
||||
beginResetModel();
|
||||
modpacks.clear();
|
||||
m_modpacks.clear();
|
||||
endResetModel();
|
||||
|
||||
nextSearchOffset = 0;
|
||||
m_nextSearchOffset = 0;
|
||||
performPaginatedSearch();
|
||||
} else {
|
||||
searchState = Finished;
|
||||
m_searchState = Finished;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "ui/pages/modplatform/modrinth/ModrinthPage.h"
|
||||
|
||||
@@ -56,7 +56,7 @@ class ModpackListModel : public QAbstractListModel {
|
||||
ModpackListModel(ModrinthPage* parent);
|
||||
~ModpackListModel() override = default;
|
||||
|
||||
inline auto rowCount(const QModelIndex& parent) const -> int override { return parent.isValid() ? 0 : modpacks.size(); };
|
||||
inline auto rowCount(const QModelIndex& parent) const -> int override { return parent.isValid() ? 0 : m_modpacks.size(); };
|
||||
inline auto columnCount(const QModelIndex& parent) const -> int override { return parent.isValid() ? 0 : 1; };
|
||||
inline auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); };
|
||||
|
||||
@@ -66,27 +66,27 @@ class ModpackListModel : public QAbstractListModel {
|
||||
auto data(const QModelIndex& index, int role) const -> QVariant override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
|
||||
|
||||
inline void setActiveJob(NetJob::Ptr ptr) { jobPtr = ptr; }
|
||||
inline void setActiveJob(NetJob::Ptr ptr) { m_jobPtr = ptr; }
|
||||
|
||||
/* Ask the API for more information */
|
||||
void fetchMore(const QModelIndex& parent) override;
|
||||
void refresh();
|
||||
void searchWithTerm(const QString& term, int sort, std::shared_ptr<ModFilterWidget::Filter> filter, bool filterChanged);
|
||||
|
||||
bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); }
|
||||
Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; }
|
||||
bool hasActiveSearchJob() const { return m_jobPtr && m_jobPtr->isRunning(); }
|
||||
Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? m_jobPtr : nullptr; }
|
||||
|
||||
void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback);
|
||||
|
||||
inline auto canFetchMore(const QModelIndex& parent) const -> bool override
|
||||
{
|
||||
return parent.isValid() ? false : searchState == CanPossiblyFetchMore;
|
||||
return parent.isValid() ? false : m_searchState == CanPossiblyFetchMore;
|
||||
};
|
||||
|
||||
public slots:
|
||||
void searchRequestFinished(QJsonDocument& doc_all);
|
||||
void searchRequestFinished(QList<ModPlatform::IndexedPack::Ptr>& doc_all);
|
||||
void searchRequestFailed(QString reason);
|
||||
void searchRequestForOneSucceeded(QJsonDocument&);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack&);
|
||||
|
||||
protected slots:
|
||||
|
||||
@@ -103,20 +103,20 @@ class ModpackListModel : public QAbstractListModel {
|
||||
protected:
|
||||
ModrinthPage* m_parent;
|
||||
|
||||
QList<Modrinth::Modpack> modpacks;
|
||||
QList<ModPlatform::IndexedPack::Ptr> m_modpacks;
|
||||
|
||||
LogoMap m_logoMap;
|
||||
QMap<QString, LogoCallback> waitingCallbacks;
|
||||
QMap<QString, LogoCallback> m_waitingCallbacks;
|
||||
QStringList m_failedLogos;
|
||||
QStringList m_loadingLogos;
|
||||
|
||||
QString currentSearchTerm;
|
||||
QString currentSort;
|
||||
QString m_currentSearchTerm;
|
||||
QString m_currentSort;
|
||||
std::shared_ptr<ModFilterWidget::Filter> m_filter;
|
||||
int nextSearchOffset = 0;
|
||||
enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None;
|
||||
int m_nextSearchOffset = 0;
|
||||
enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } m_searchState = None;
|
||||
|
||||
Task::Ptr jobPtr;
|
||||
Task::Ptr m_jobPtr;
|
||||
|
||||
std::shared_ptr<QByteArray> m_allResponse = std::make_shared<QByteArray>();
|
||||
QByteArray m_specific_response;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
#include "ModrinthPage.h"
|
||||
#include "Version.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui_ModrinthPage.h"
|
||||
@@ -57,17 +58,17 @@
|
||||
#include <QPushButton>
|
||||
|
||||
ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent)
|
||||
: QWidget(parent), ui(new Ui::ModrinthPage), dialog(dialog), m_fetch_progress(this, false)
|
||||
: QWidget(parent), m_ui(new Ui::ModrinthPage), m_dialog(dialog), m_fetch_progress(this, false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ui->setupUi(this);
|
||||
createFilterWidget();
|
||||
|
||||
ui->searchEdit->installEventFilter(this);
|
||||
m_ui->searchEdit->installEventFilter(this);
|
||||
m_model = new Modrinth::ModpackListModel(this);
|
||||
ui->packView->setModel(m_model);
|
||||
m_ui->packView->setModel(m_model);
|
||||
|
||||
ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
|
||||
m_ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
m_ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
|
||||
|
||||
m_search_timer.setTimerType(Qt::TimerType::CoarseTimer);
|
||||
m_search_timer.setSingleShot(true);
|
||||
@@ -78,30 +79,30 @@ ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent)
|
||||
m_fetch_progress.setFixedHeight(24);
|
||||
m_fetch_progress.progressFormat("");
|
||||
|
||||
ui->verticalLayout->insertWidget(1, &m_fetch_progress);
|
||||
m_ui->verticalLayout->insertWidget(1, &m_fetch_progress);
|
||||
|
||||
ui->sortByBox->addItem(tr("Sort by Relevance"));
|
||||
ui->sortByBox->addItem(tr("Sort by Total Downloads"));
|
||||
ui->sortByBox->addItem(tr("Sort by Follows"));
|
||||
ui->sortByBox->addItem(tr("Sort by Newest"));
|
||||
ui->sortByBox->addItem(tr("Sort by Last Updated"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Relevance"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Total Downloads"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Follows"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Newest"));
|
||||
m_ui->sortByBox->addItem(tr("Sort by Last Updated"));
|
||||
|
||||
connect(ui->sortByBox, &QComboBox::currentIndexChanged, this, &ModrinthPage::triggerSearch);
|
||||
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthPage::onSelectionChanged);
|
||||
connect(ui->versionSelectionBox, &QComboBox::currentIndexChanged, this, &ModrinthPage::onVersionSelectionChanged);
|
||||
connect(m_ui->sortByBox, &QComboBox::currentIndexChanged, this, &ModrinthPage::triggerSearch);
|
||||
connect(m_ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthPage::onSelectionChanged);
|
||||
connect(m_ui->versionSelectionBox, &QComboBox::currentIndexChanged, this, &ModrinthPage::onVersionSelectionChanged);
|
||||
|
||||
ui->packView->setItemDelegate(new ProjectItemDelegate(this));
|
||||
ui->packDescription->setMetaEntry(metaEntryBase());
|
||||
m_ui->packView->setItemDelegate(new ProjectItemDelegate(this));
|
||||
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
||||
}
|
||||
|
||||
ModrinthPage::~ModrinthPage()
|
||||
{
|
||||
delete ui;
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
void ModrinthPage::retranslate()
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
m_ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void ModrinthPage::openedImpl()
|
||||
@@ -113,7 +114,7 @@ void ModrinthPage::openedImpl()
|
||||
|
||||
bool ModrinthPage::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
|
||||
if (watched == m_ui->searchEdit && event->type() == QEvent::KeyPress) {
|
||||
auto* keyEvent = reinterpret_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->key() == Qt::Key_Return) {
|
||||
this->triggerSearch();
|
||||
@@ -129,146 +130,108 @@ bool ModrinthPage::eventFilter(QObject* watched, QEvent* event)
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
bool checkVersionFilters(const Modrinth::ModpackVersion& v, std::shared_ptr<ModFilterWidget::Filter> filter)
|
||||
{
|
||||
if (!filter)
|
||||
return true;
|
||||
return ((!filter->loaders || !v.loaders || filter->loaders & v.loaders) && // loaders
|
||||
(filter->releases.empty() || // releases
|
||||
std::find(filter->releases.cbegin(), filter->releases.cend(), v.version_type) != filter->releases.cend()) &&
|
||||
filter->checkMcVersions({ v.gameVersion })); // gameVersion}
|
||||
}
|
||||
|
||||
void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev)
|
||||
{
|
||||
ui->versionSelectionBox->clear();
|
||||
m_ui->versionSelectionBox->clear();
|
||||
|
||||
if (!curr.isValid()) {
|
||||
if (isOpened) {
|
||||
dialog->setSuggestedPack();
|
||||
m_dialog->setSuggestedPack();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant raw = m_model->data(curr, Qt::UserRole);
|
||||
Q_ASSERT(raw.canConvert<Modrinth::Modpack>());
|
||||
current = raw.value<Modrinth::Modpack>();
|
||||
auto name = current.name;
|
||||
m_current = m_model->data(curr, Qt::UserRole).value<ModPlatform::IndexedPack::Ptr>();
|
||||
auto name = m_current->name;
|
||||
|
||||
if (!current.extraInfoLoaded) {
|
||||
if (!m_current->extraDataLoaded) {
|
||||
qDebug() << "Loading modrinth modpack information";
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack> callbacks;
|
||||
|
||||
auto netJob = new NetJob(QString("Modrinth::PackInformation(%1)").arg(current.name), APPLICATION->network());
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
QString id = current.id;
|
||||
|
||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_PROD_URL, id), response));
|
||||
|
||||
connect(netJob, &NetJob::succeeded, this, [this, response, id, curr] {
|
||||
if (id != current.id) {
|
||||
auto id = m_current->addonId;
|
||||
callbacks.on_fail = [this](QString reason, int) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
|
||||
};
|
||||
callbacks.on_succeed = [this, id, curr](auto& pack) {
|
||||
if (id != m_current->addonId) {
|
||||
return; // wrong request?
|
||||
}
|
||||
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
auto obj = Json::requireObject(doc);
|
||||
|
||||
try {
|
||||
Modrinth::loadIndexedInfo(current, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << *response;
|
||||
qWarning() << "Error while reading modrinth modpack version: " << e.cause();
|
||||
}
|
||||
|
||||
updateUI();
|
||||
*m_current = pack;
|
||||
|
||||
QVariant current_updated;
|
||||
current_updated.setValue(current);
|
||||
current_updated.setValue(m_current);
|
||||
|
||||
if (!m_model->setData(curr, current_updated, Qt::UserRole))
|
||||
qWarning() << "Failed to cache extra info for the current pack!";
|
||||
|
||||
suggestCurrent();
|
||||
});
|
||||
connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); });
|
||||
connect(netJob, &NetJob::failed,
|
||||
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
netJob->start();
|
||||
updateUI();
|
||||
};
|
||||
if (auto netJob = m_api.getProjectInfo({ { m_current->addonId } }, std::move(callbacks)); netJob) {
|
||||
m_job = netJob;
|
||||
m_job->start();
|
||||
}
|
||||
|
||||
} else
|
||||
updateUI();
|
||||
|
||||
if (!current.versionsLoaded || m_filterWidget->changed()) {
|
||||
if (!m_current->versionsLoaded || m_filterWidget->changed()) {
|
||||
qDebug() << "Loading modrinth modpack versions";
|
||||
|
||||
auto netJob = new NetJob(QString("Modrinth::PackVersions(%1)").arg(current.name), APPLICATION->network());
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
ResourceAPI::Callback<QVector<ModPlatform::IndexedVersion>> callbacks{};
|
||||
|
||||
QString id = current.id;
|
||||
|
||||
netJob->addNetAction(
|
||||
Net::ApiDownload::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response));
|
||||
|
||||
connect(netJob, &NetJob::succeeded, this, [this, response, id, curr] {
|
||||
if (id != current.id) {
|
||||
return; // wrong request?
|
||||
auto addonId = m_current->addonId;
|
||||
// Use default if no callbacks are set
|
||||
callbacks.on_succeed = [this, curr, addonId](auto& doc) {
|
||||
if (addonId != m_current->addonId) {
|
||||
return; // wrong request
|
||||
}
|
||||
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Modrinth::loadIndexedVersions(current, doc);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << *response;
|
||||
qWarning() << "Error while reading modrinth modpack version: " << e.cause();
|
||||
}
|
||||
auto pred = [this](const Modrinth::ModpackVersion& v) { return !checkVersionFilters(v, m_filterWidget->getFilter()); };
|
||||
m_current->versions = doc;
|
||||
m_current->versionsLoaded = true;
|
||||
auto pred = [this](const ModPlatform::IndexedVersion& v) {
|
||||
if (auto filter = m_filterWidget->getFilter())
|
||||
return !filter->checkModpackFilters(v);
|
||||
return false;
|
||||
};
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
|
||||
current.versions.removeIf(pred);
|
||||
m_current->versions.removeIf(pred);
|
||||
#else
|
||||
for (auto it = current.versions.begin(); it != current.versions.end();)
|
||||
if (pred(*it))
|
||||
it = current.versions.erase(it);
|
||||
else
|
||||
++it;
|
||||
for (auto it = m_current->versions.begin(); it != m_current->versions.end();)
|
||||
if (pred(*it))
|
||||
it = m_current->versions.erase(it);
|
||||
else
|
||||
++it;
|
||||
#endif
|
||||
for (const auto& version : current.versions) {
|
||||
ui->versionSelectionBox->addItem(Modrinth::getVersionDisplayString(version), QVariant(version.id));
|
||||
for (const auto& version : m_current->versions) {
|
||||
m_ui->versionSelectionBox->addItem(version.getVersionDisplayString(), QVariant(version.addonId));
|
||||
}
|
||||
|
||||
QVariant current_updated;
|
||||
current_updated.setValue(current);
|
||||
current_updated.setValue(m_current);
|
||||
|
||||
if (!m_model->setData(curr, current_updated, Qt::UserRole))
|
||||
qWarning() << "Failed to cache versions for the current pack!";
|
||||
|
||||
suggestCurrent();
|
||||
});
|
||||
connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); });
|
||||
connect(netJob, &NetJob::failed,
|
||||
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
netJob->start();
|
||||
};
|
||||
callbacks.on_fail = [this](QString reason, int) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
|
||||
};
|
||||
|
||||
auto netJob = m_api.getProjectVersions({ *m_current, {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
|
||||
|
||||
m_job2 = netJob;
|
||||
m_job2->start();
|
||||
|
||||
} else {
|
||||
for (auto version : current.versions) {
|
||||
if (!version.name.contains(version.version))
|
||||
ui->versionSelectionBox->addItem(QString("%1 - %2").arg(version.name, version.version), QVariant(version.id));
|
||||
for (auto version : m_current->versions) {
|
||||
if (!version.version.contains(version.version))
|
||||
m_ui->versionSelectionBox->addItem(QString("%1 - %2").arg(version.version, version.version_number),
|
||||
QVariant(version.addonId));
|
||||
else
|
||||
ui->versionSelectionBox->addItem(version.name, QVariant(version.id));
|
||||
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.addonId));
|
||||
}
|
||||
|
||||
suggestCurrent();
|
||||
@@ -279,53 +242,64 @@ void ModrinthPage::updateUI()
|
||||
{
|
||||
QString text = "";
|
||||
|
||||
if (current.extra.projectUrl.isEmpty())
|
||||
text = current.name;
|
||||
if (m_current->websiteUrl.isEmpty())
|
||||
text = m_current->name;
|
||||
else
|
||||
text = "<a href=\"" + current.extra.projectUrl + "\">" + current.name + "</a>";
|
||||
text = "<a href=\"" + m_current->websiteUrl + "\">" + m_current->name + "</a>";
|
||||
|
||||
// TODO: Implement multiple authors with links
|
||||
text += "<br>" + tr(" by ") + QString("<a href=%1>%2</a>").arg(std::get<1>(current.author).toString(), std::get<0>(current.author));
|
||||
if (!m_current->authors.empty()) {
|
||||
auto authorToStr = [](ModPlatform::ModpackAuthor& author) {
|
||||
if (author.url.isEmpty()) {
|
||||
return author.name;
|
||||
}
|
||||
return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name);
|
||||
};
|
||||
QStringList authorStrs;
|
||||
for (auto& author : m_current->authors) {
|
||||
authorStrs.push_back(authorToStr(author));
|
||||
}
|
||||
text += "<br>" + tr(" by ") + authorStrs.join(", ");
|
||||
}
|
||||
|
||||
if (current.extraInfoLoaded) {
|
||||
if (current.extra.status == "archived") {
|
||||
if (m_current->extraDataLoaded) {
|
||||
if (m_current->extraData.status == "archived") {
|
||||
text += "<br><br>" + tr("<b>This project has been archived. It will not receive any further updates unless the author decides "
|
||||
"to unarchive the project.</b>");
|
||||
}
|
||||
|
||||
if (!current.extra.donate.isEmpty()) {
|
||||
if (!m_current->extraData.donate.isEmpty()) {
|
||||
text += "<br><br>" + tr("Donate information: ");
|
||||
auto donateToStr = [](Modrinth::DonationData& donate) -> QString {
|
||||
auto donateToStr = [](ModPlatform::DonationData& donate) -> QString {
|
||||
return QString("<a href=\"%1\">%2</a>").arg(donate.url, donate.platform);
|
||||
};
|
||||
QStringList donates;
|
||||
for (auto& donate : current.extra.donate) {
|
||||
for (auto& donate : m_current->extraData.donate) {
|
||||
donates.append(donateToStr(donate));
|
||||
}
|
||||
text += donates.join(", ");
|
||||
}
|
||||
|
||||
if (!current.extra.issuesUrl.isEmpty() || !current.extra.sourceUrl.isEmpty() || !current.extra.wikiUrl.isEmpty() ||
|
||||
!current.extra.discordUrl.isEmpty()) {
|
||||
if (!m_current->extraData.issuesUrl.isEmpty() || !m_current->extraData.sourceUrl.isEmpty() ||
|
||||
!m_current->extraData.wikiUrl.isEmpty() || !m_current->extraData.discordUrl.isEmpty()) {
|
||||
text += "<br><br>" + tr("External links:") + "<br>";
|
||||
}
|
||||
|
||||
if (!current.extra.issuesUrl.isEmpty())
|
||||
text += "- " + tr("Issues: <a href=%1>%1</a>").arg(current.extra.issuesUrl) + "<br>";
|
||||
if (!current.extra.wikiUrl.isEmpty())
|
||||
text += "- " + tr("Wiki: <a href=%1>%1</a>").arg(current.extra.wikiUrl) + "<br>";
|
||||
if (!current.extra.sourceUrl.isEmpty())
|
||||
text += "- " + tr("Source code: <a href=%1>%1</a>").arg(current.extra.sourceUrl) + "<br>";
|
||||
if (!current.extra.discordUrl.isEmpty())
|
||||
text += "- " + tr("Discord: <a href=%1>%1</a>").arg(current.extra.discordUrl) + "<br>";
|
||||
if (!m_current->extraData.issuesUrl.isEmpty())
|
||||
text += "- " + tr("Issues: <a href=%1>%1</a>").arg(m_current->extraData.issuesUrl) + "<br>";
|
||||
if (!m_current->extraData.wikiUrl.isEmpty())
|
||||
text += "- " + tr("Wiki: <a href=%1>%1</a>").arg(m_current->extraData.wikiUrl) + "<br>";
|
||||
if (!m_current->extraData.sourceUrl.isEmpty())
|
||||
text += "- " + tr("Source code: <a href=%1>%1</a>").arg(m_current->extraData.sourceUrl) + "<br>";
|
||||
if (!m_current->extraData.discordUrl.isEmpty())
|
||||
text += "- " + tr("Discord: <a href=%1>%1</a>").arg(m_current->extraData.discordUrl) + "<br>";
|
||||
}
|
||||
|
||||
text += "<hr>";
|
||||
|
||||
text += markdownToHTML(current.extra.body.toUtf8());
|
||||
text += markdownToHTML(m_current->extraData.body.toUtf8());
|
||||
|
||||
ui->packDescription->setHtml(StringUtils::htmlListPatch(text + current.description));
|
||||
ui->packDescription->flush();
|
||||
m_ui->packDescription->setHtml(StringUtils::htmlListPatch(text + m_current->description));
|
||||
m_ui->packDescription->flush();
|
||||
}
|
||||
|
||||
void ModrinthPage::suggestCurrent()
|
||||
@@ -334,21 +308,21 @@ void ModrinthPage::suggestCurrent()
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedVersion.isEmpty()) {
|
||||
dialog->setSuggestedPack();
|
||||
if (m_selectedVersion.isEmpty()) {
|
||||
m_dialog->setSuggestedPack();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& ver : current.versions) {
|
||||
if (ver.id == selectedVersion) {
|
||||
for (auto& ver : m_current->versions) {
|
||||
if (ver.addonId == m_selectedVersion) {
|
||||
QMap<QString, QString> extra_info;
|
||||
extra_info.insert("pack_id", current.id);
|
||||
extra_info.insert("pack_version_id", ver.id);
|
||||
extra_info.insert("pack_id", m_current->addonId.toString());
|
||||
extra_info.insert("pack_version_id", ver.fileId.toString());
|
||||
|
||||
dialog->setSuggestedPack(current.name, ver.version, new InstanceImportTask(ver.download_url, this, std::move(extra_info)));
|
||||
auto iconName = current.iconName;
|
||||
m_model->getLogo(iconName, current.iconUrl.toString(),
|
||||
[this, iconName](QString logo) { dialog->setSuggestedIconFromFile(logo, iconName); });
|
||||
m_dialog->setSuggestedPack(m_current->name, ver.version, new InstanceImportTask(ver.downloadUrl, this, std::move(extra_info)));
|
||||
auto iconName = m_current->logoName;
|
||||
m_model->getLogo(iconName, m_current->logoUrl,
|
||||
[this, iconName](QString logo) { m_dialog->setSuggestedIconFromFile(logo, iconName); });
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -357,46 +331,46 @@ void ModrinthPage::suggestCurrent()
|
||||
|
||||
void ModrinthPage::triggerSearch()
|
||||
{
|
||||
ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect);
|
||||
ui->packView->clearSelection();
|
||||
ui->packDescription->clear();
|
||||
ui->versionSelectionBox->clear();
|
||||
m_ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect);
|
||||
m_ui->packView->clearSelection();
|
||||
m_ui->packDescription->clear();
|
||||
m_ui->versionSelectionBox->clear();
|
||||
bool filterChanged = m_filterWidget->changed();
|
||||
m_model->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex(), m_filterWidget->getFilter(), filterChanged);
|
||||
m_model->searchWithTerm(m_ui->searchEdit->text(), m_ui->sortByBox->currentIndex(), m_filterWidget->getFilter(), filterChanged);
|
||||
m_fetch_progress.watch(m_model->activeSearchJob().get());
|
||||
}
|
||||
|
||||
void ModrinthPage::onVersionSelectionChanged(int index)
|
||||
{
|
||||
if (index == -1) {
|
||||
selectedVersion = "";
|
||||
m_selectedVersion = "";
|
||||
return;
|
||||
}
|
||||
selectedVersion = ui->versionSelectionBox->itemData(index).toString();
|
||||
m_selectedVersion = m_ui->versionSelectionBox->itemData(index).toString();
|
||||
suggestCurrent();
|
||||
}
|
||||
|
||||
void ModrinthPage::setSearchTerm(QString term)
|
||||
{
|
||||
ui->searchEdit->setText(term);
|
||||
m_ui->searchEdit->setText(term);
|
||||
}
|
||||
|
||||
QString ModrinthPage::getSerachTerm() const
|
||||
{
|
||||
return ui->searchEdit->text();
|
||||
return m_ui->searchEdit->text();
|
||||
}
|
||||
|
||||
void ModrinthPage::createFilterWidget()
|
||||
{
|
||||
auto widget = ModFilterWidget::create(nullptr, true);
|
||||
m_filterWidget.swap(widget);
|
||||
auto old = ui->splitter->replaceWidget(0, m_filterWidget.get());
|
||||
auto old = m_ui->splitter->replaceWidget(0, m_filterWidget.get());
|
||||
// because we replaced the widget we also need to delete it
|
||||
if (old) {
|
||||
delete old;
|
||||
}
|
||||
|
||||
connect(ui->filterButton, &QPushButton::clicked, this, [this] { m_filterWidget->setHidden(!m_filterWidget->isHidden()); });
|
||||
connect(m_ui->filterButton, &QPushButton::clicked, this, [this] { m_filterWidget->setHidden(!m_filterWidget->isHidden()); });
|
||||
|
||||
connect(m_filterWidget.get(), &ModFilterWidget::filterChanged, this, &ModrinthPage::triggerSearch);
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
@@ -37,9 +37,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "Application.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
#include "ui/dialogs/NewInstanceDialog.h"
|
||||
|
||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||
#include "ui/pages/modplatform/ModpackProviderBasePage.h"
|
||||
#include "ui/widgets/ModFilterWidget.h"
|
||||
#include "ui/widgets/ProgressWidget.h"
|
||||
@@ -67,10 +68,10 @@ class ModrinthPage : public QWidget, public ModpackProviderBasePage {
|
||||
QString id() const override { return "modrinth"; }
|
||||
QString helpPage() const override { return "Modrinth-platform"; }
|
||||
|
||||
inline auto debugName() const -> QString { return "Modrinth"; }
|
||||
inline auto metaEntryBase() const -> QString { return "ModrinthModpacks"; };
|
||||
inline QString debugName() const { return "Modrinth"; }
|
||||
inline QString metaEntryBase() const { return "ModrinthModpacks"; };
|
||||
|
||||
auto getCurrent() -> Modrinth::Modpack& { return current; }
|
||||
ModPlatform::IndexedPack::Ptr getCurrent() { return m_current; }
|
||||
void suggestCurrent();
|
||||
|
||||
void updateUI();
|
||||
@@ -91,12 +92,12 @@ class ModrinthPage : public QWidget, public ModpackProviderBasePage {
|
||||
void createFilterWidget();
|
||||
|
||||
private:
|
||||
Ui::ModrinthPage* ui;
|
||||
NewInstanceDialog* dialog;
|
||||
Ui::ModrinthPage* m_ui;
|
||||
NewInstanceDialog* m_dialog;
|
||||
Modrinth::ModpackListModel* m_model;
|
||||
|
||||
Modrinth::Modpack current;
|
||||
QString selectedVersion;
|
||||
ModPlatform::IndexedPack::Ptr m_current;
|
||||
QString m_selectedVersion;
|
||||
|
||||
ProgressWidget m_fetch_progress;
|
||||
|
||||
@@ -105,4 +106,8 @@ class ModrinthPage : public QWidget, public ModpackProviderBasePage {
|
||||
|
||||
std::unique_ptr<ModFilterWidget> m_filterWidget;
|
||||
Task::Ptr m_categoriesTask;
|
||||
|
||||
ModrinthAPI m_api;
|
||||
Task::Ptr m_job;
|
||||
Task::Ptr m_job2;
|
||||
};
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 flowln <flowlnlnln@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - 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/>.
|
||||
*/
|
||||
|
||||
#include "ModrinthResourceModels.h"
|
||||
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
ModrinthModModel::ModrinthModModel(BaseInstance& base) : ModModel(base, new ModrinthAPI) {}
|
||||
|
||||
void ModrinthModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
::Modrinth::loadIndexedPack(m, obj);
|
||||
}
|
||||
|
||||
void ModrinthModModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
::Modrinth::loadExtraPackData(m, obj);
|
||||
}
|
||||
|
||||
void ModrinthModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
|
||||
{
|
||||
::Modrinth::loadIndexedPackVersions(m, arr);
|
||||
}
|
||||
|
||||
auto ModrinthModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
|
||||
{
|
||||
return ::Modrinth::loadDependencyVersions(m, arr, &m_base_instance);
|
||||
}
|
||||
|
||||
auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return obj.object().value("hits").toArray();
|
||||
}
|
||||
|
||||
ModrinthResourcePackModel::ModrinthResourcePackModel(const BaseInstance& base) : ResourcePackResourceModel(base, new ModrinthAPI) {}
|
||||
|
||||
void ModrinthResourcePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
::Modrinth::loadIndexedPack(m, obj);
|
||||
}
|
||||
|
||||
void ModrinthResourcePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
::Modrinth::loadExtraPackData(m, obj);
|
||||
}
|
||||
|
||||
void ModrinthResourcePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
|
||||
{
|
||||
::Modrinth::loadIndexedPackVersions(m, arr);
|
||||
}
|
||||
|
||||
auto ModrinthResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return obj.object().value("hits").toArray();
|
||||
}
|
||||
|
||||
ModrinthTexturePackModel::ModrinthTexturePackModel(const BaseInstance& base) : TexturePackResourceModel(base, new ModrinthAPI) {}
|
||||
|
||||
void ModrinthTexturePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
::Modrinth::loadIndexedPack(m, obj);
|
||||
}
|
||||
|
||||
void ModrinthTexturePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
::Modrinth::loadExtraPackData(m, obj);
|
||||
}
|
||||
|
||||
void ModrinthTexturePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
|
||||
{
|
||||
::Modrinth::loadIndexedPackVersions(m, arr);
|
||||
}
|
||||
|
||||
auto ModrinthTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return obj.object().value("hits").toArray();
|
||||
}
|
||||
|
||||
ModrinthShaderPackModel::ModrinthShaderPackModel(const BaseInstance& base) : ShaderPackResourceModel(base, new ModrinthAPI) {}
|
||||
|
||||
void ModrinthShaderPackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
::Modrinth::loadIndexedPack(m, obj);
|
||||
}
|
||||
|
||||
void ModrinthShaderPackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
::Modrinth::loadExtraPackData(m, obj);
|
||||
}
|
||||
|
||||
void ModrinthShaderPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
|
||||
{
|
||||
::Modrinth::loadIndexedPackVersions(m, arr);
|
||||
}
|
||||
|
||||
auto ModrinthShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return obj.object().value("hits").toArray();
|
||||
}
|
||||
|
||||
ModrinthDataPackModel::ModrinthDataPackModel(const BaseInstance& base) : DataPackResourceModel(base, new ModrinthAPI) {}
|
||||
|
||||
void ModrinthDataPackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
::Modrinth::loadIndexedPack(m, obj);
|
||||
}
|
||||
|
||||
void ModrinthDataPackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
::Modrinth::loadExtraPackData(m, obj);
|
||||
}
|
||||
|
||||
void ModrinthDataPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
|
||||
{
|
||||
::Modrinth::loadIndexedPackVersions(m, arr);
|
||||
}
|
||||
|
||||
auto ModrinthDataPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return obj.object().value("hits").toArray();
|
||||
}
|
||||
|
||||
|
||||
} // namespace ResourceDownload
|
||||
@@ -1,121 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 flowln <flowlnlnln@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui/pages/modplatform/DataPackModel.h"
|
||||
#include "ui/pages/modplatform/ModModel.h"
|
||||
#include "ui/pages/modplatform/ResourcePackModel.h"
|
||||
#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h"
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
class ModrinthModModel : public ModModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthModModel(BaseInstance&);
|
||||
~ModrinthModModel() override = default;
|
||||
|
||||
private:
|
||||
QString debugName() const override { return Modrinth::debugName() + " (Model)"; }
|
||||
QString metaEntryBase() const override { return Modrinth::metaEntryBase(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion override;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
||||
class ModrinthResourcePackModel : public ResourcePackResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthResourcePackModel(const BaseInstance&);
|
||||
~ModrinthResourcePackModel() override = default;
|
||||
|
||||
private:
|
||||
QString debugName() const override { return Modrinth::debugName() + " (Model)"; }
|
||||
QString metaEntryBase() const override { return Modrinth::metaEntryBase(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
||||
class ModrinthTexturePackModel : public TexturePackResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthTexturePackModel(const BaseInstance&);
|
||||
~ModrinthTexturePackModel() override = default;
|
||||
|
||||
private:
|
||||
QString debugName() const override { return Modrinth::debugName() + " (Model)"; }
|
||||
QString metaEntryBase() const override { return Modrinth::metaEntryBase(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
||||
class ModrinthShaderPackModel : public ShaderPackResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthShaderPackModel(const BaseInstance&);
|
||||
~ModrinthShaderPackModel() override = default;
|
||||
|
||||
private:
|
||||
QString debugName() const override { return Modrinth::debugName() + " (Model)"; }
|
||||
QString metaEntryBase() const override { return Modrinth::metaEntryBase(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
||||
class ModrinthDataPackModel : public DataPackResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthDataPackModel(const BaseInstance&);
|
||||
~ModrinthDataPackModel() override = default;
|
||||
|
||||
private:
|
||||
QString debugName() const override { return Modrinth::debugName() + " (Model)"; }
|
||||
QString metaEntryBase() const override { return Modrinth::metaEntryBase(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
||||
} // namespace ResourceDownload
|
||||
@@ -37,19 +37,18 @@
|
||||
*/
|
||||
|
||||
#include "ModrinthResourcePages.h"
|
||||
#include "ui/pages/modplatform/DataPackModel.h"
|
||||
#include "ui_ResourcePage.h"
|
||||
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
|
||||
#include "ui/dialogs/ResourceDownloadDialog.h"
|
||||
|
||||
#include "ui/pages/modplatform/modrinth/ModrinthResourceModels.h"
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance)
|
||||
{
|
||||
m_model = new ModrinthModModel(instance);
|
||||
m_model = new ModModel(instance, new ModrinthAPI(), Modrinth::debugName(), Modrinth::metaEntryBase());
|
||||
m_ui->packView->setModel(m_model);
|
||||
|
||||
addSortings();
|
||||
@@ -67,7 +66,7 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instan
|
||||
ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance)
|
||||
: ResourcePackResourcePage(dialog, instance)
|
||||
{
|
||||
m_model = new ModrinthResourcePackModel(instance);
|
||||
m_model = new ResourcePackResourceModel(instance, new ModrinthAPI(), Modrinth::debugName(), Modrinth::metaEntryBase());
|
||||
m_ui->packView->setModel(m_model);
|
||||
|
||||
addSortings();
|
||||
@@ -85,7 +84,7 @@ ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* d
|
||||
ModrinthTexturePackPage::ModrinthTexturePackPage(TexturePackDownloadDialog* dialog, BaseInstance& instance)
|
||||
: TexturePackResourcePage(dialog, instance)
|
||||
{
|
||||
m_model = new ModrinthTexturePackModel(instance);
|
||||
m_model = new TexturePackResourceModel(instance, new ModrinthAPI(), Modrinth::debugName(), Modrinth::metaEntryBase());
|
||||
m_ui->packView->setModel(m_model);
|
||||
|
||||
addSortings();
|
||||
@@ -103,7 +102,7 @@ ModrinthTexturePackPage::ModrinthTexturePackPage(TexturePackDownloadDialog* dial
|
||||
ModrinthShaderPackPage::ModrinthShaderPackPage(ShaderPackDownloadDialog* dialog, BaseInstance& instance)
|
||||
: ShaderPackResourcePage(dialog, instance)
|
||||
{
|
||||
m_model = new ModrinthShaderPackModel(instance);
|
||||
m_model = new ShaderPackResourceModel(instance, new ModrinthAPI(), Modrinth::debugName(), Modrinth::metaEntryBase());
|
||||
m_ui->packView->setModel(m_model);
|
||||
|
||||
addSortings();
|
||||
@@ -118,10 +117,9 @@ ModrinthShaderPackPage::ModrinthShaderPackPage(ShaderPackDownloadDialog* dialog,
|
||||
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
||||
}
|
||||
|
||||
ModrinthDataPackPage::ModrinthDataPackPage(DataPackDownloadDialog* dialog, BaseInstance& instance)
|
||||
: DataPackResourcePage(dialog, instance)
|
||||
ModrinthDataPackPage::ModrinthDataPackPage(DataPackDownloadDialog* dialog, BaseInstance& instance) : DataPackResourcePage(dialog, instance)
|
||||
{
|
||||
m_model = new ModrinthDataPackModel(instance);
|
||||
m_model = new DataPackResourceModel(instance, new ModrinthAPI(), Modrinth::debugName(), Modrinth::metaEntryBase());
|
||||
m_ui->packView->setModel(m_model);
|
||||
|
||||
addSortings();
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
*/
|
||||
|
||||
#include "MinecraftSettingsWidget.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "ui_MinecraftSettingsWidget.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
@@ -81,6 +81,14 @@ class ModFilterWidget : public QTabWidget {
|
||||
|
||||
return versions.empty();
|
||||
}
|
||||
|
||||
bool checkModpackFilters(const ModPlatform::IndexedVersion& v)
|
||||
{
|
||||
return ((!loaders || !v.loaders || loaders & v.loaders) && // loaders
|
||||
(releases.empty() || // releases
|
||||
std::find(releases.cbegin(), releases.cend(), v.version_type) != releases.cend()) &&
|
||||
checkMcVersions({ v.mcVersion })); // gameVersion}
|
||||
}
|
||||
};
|
||||
|
||||
static std::unique_ptr<ModFilterWidget> create(MinecraftInstance* instance, bool extended);
|
||||
|
||||
@@ -21,9 +21,6 @@ ecm_add_test(ResourceFolderModel_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_V
|
||||
ecm_add_test(ResourcePackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME ResourcePackParse)
|
||||
|
||||
ecm_add_test(ResourceModel_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME ResourceModel)
|
||||
|
||||
ecm_add_test(TexturePackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME TexturePackParse)
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <modplatform/ResourceAPI.h>
|
||||
|
||||
class SearchTask : public Task {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void executeTask() override { emitSucceeded(); }
|
||||
};
|
||||
|
||||
class DummyResourceAPI : public ResourceAPI {
|
||||
public:
|
||||
static auto searchRequestResult()
|
||||
{
|
||||
static QByteArray json_response =
|
||||
"{\"hits\":["
|
||||
"{"
|
||||
"\"author\":\"flowln\","
|
||||
"\"description\":\"the bestest mod\","
|
||||
"\"project_id\":\"something\","
|
||||
"\"project_type\":\"mod\","
|
||||
"\"slug\":\"bip_bop\","
|
||||
"\"title\":\"AAAAAAAA\","
|
||||
"\"versions\":[\"2.71\"]"
|
||||
"}"
|
||||
"]}";
|
||||
|
||||
return QJsonDocument::fromJson(json_response);
|
||||
}
|
||||
|
||||
DummyResourceAPI() : ResourceAPI() {}
|
||||
auto getSortingMethods() const -> QList<SortingMethod> override { return {}; }
|
||||
|
||||
Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&& callbacks) const override
|
||||
{
|
||||
auto task = makeShared<SearchTask>();
|
||||
QObject::connect(task.get(), &Task::succeeded, [callbacks] {
|
||||
auto json = searchRequestResult();
|
||||
callbacks.on_succeed(json);
|
||||
});
|
||||
return task;
|
||||
}
|
||||
};
|
||||
@@ -1,95 +0,0 @@
|
||||
#include <QAbstractItemModelTester>
|
||||
#include <QTest>
|
||||
#include <QTimer>
|
||||
|
||||
#include <Json.h>
|
||||
|
||||
#include <ui/pages/modplatform/ResourceModel.h>
|
||||
|
||||
#include "DummyResourceAPI.h"
|
||||
|
||||
using ResourceDownload::ResourceModel;
|
||||
|
||||
#define EXEC_TASK(EXEC) \
|
||||
QEventLoop loop; \
|
||||
\
|
||||
connect(model, &ResourceModel::dataChanged, &loop, &QEventLoop::quit); \
|
||||
\
|
||||
QTimer expire_timer; \
|
||||
expire_timer.callOnTimeout(&loop, &QEventLoop::quit); \
|
||||
expire_timer.setSingleShot(true); \
|
||||
expire_timer.start(4000); \
|
||||
\
|
||||
EXEC; \
|
||||
if (model->hasActiveSearchJob()) \
|
||||
loop.exec(); \
|
||||
\
|
||||
QVERIFY2(expire_timer.isActive(), "Timer has expired. The search never finished."); \
|
||||
expire_timer.stop(); \
|
||||
\
|
||||
disconnect(model, nullptr, &loop, nullptr)
|
||||
|
||||
class ResourceModelTest;
|
||||
|
||||
class DummyResourceModel : public ResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
friend class ResourceModelTest;
|
||||
|
||||
public:
|
||||
DummyResourceModel() : ResourceModel(new DummyResourceAPI) {}
|
||||
~DummyResourceModel() {}
|
||||
|
||||
auto metaEntryBase() const -> QString override { return ""; }
|
||||
|
||||
ResourceAPI::SearchArgs createSearchArguments() override { return {}; }
|
||||
ResourceAPI::VersionSearchArgs createVersionsArguments(const QModelIndex&) override { return {}; }
|
||||
ResourceAPI::ProjectInfoArgs createInfoArguments(const QModelIndex&) override { return {}; }
|
||||
|
||||
QJsonArray documentToArray(QJsonDocument& doc) const override { return doc.object().value("hits").toArray(); }
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) override
|
||||
{
|
||||
pack.authors.append({ Json::requireString(obj, "author"), "" });
|
||||
pack.description = Json::requireString(obj, "description");
|
||||
pack.addonId = Json::requireString(obj, "project_id");
|
||||
}
|
||||
};
|
||||
|
||||
class ResourceModelTest : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void test_abstract_item_model()
|
||||
{
|
||||
auto dummy = DummyResourceModel();
|
||||
auto tester = QAbstractItemModelTester(&dummy);
|
||||
}
|
||||
|
||||
void test_search()
|
||||
{
|
||||
auto model = new DummyResourceModel;
|
||||
|
||||
QVERIFY(model->m_packs.isEmpty());
|
||||
|
||||
EXEC_TASK(model->search());
|
||||
|
||||
QVERIFY(model->m_packs.size() == 1);
|
||||
QVERIFY(model->m_search_state == DummyResourceModel::SearchState::Finished);
|
||||
|
||||
auto processed_pack = model->m_packs.at(0);
|
||||
auto search_json = DummyResourceAPI::searchRequestResult();
|
||||
auto processed_response = model->documentToArray(search_json).first().toObject();
|
||||
|
||||
QVERIFY(processed_pack->addonId.toString() == Json::requireString(processed_response, "project_id"));
|
||||
QVERIFY(processed_pack->description == Json::requireString(processed_response, "description"));
|
||||
QVERIFY(processed_pack->authors.first().name == Json::requireString(processed_response, "author"));
|
||||
|
||||
delete model;
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(ResourceModelTest)
|
||||
|
||||
#include "ResourceModel_test.moc"
|
||||
|
||||
#include "moc_DummyResourceAPI.cpp"
|
||||
Reference in New Issue
Block a user