diff options
Diffstat (limited to 'launcher')
23 files changed, 834 insertions, 1297 deletions
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 86c05651..0dcda925 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -721,6 +721,11 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/VanillaPage.cpp ui/pages/modplatform/VanillaPage.h + ui/pages/modplatform/ModPage.cpp + ui/pages/modplatform/ModPage.h + ui/pages/modplatform/ModModel.cpp + ui/pages/modplatform/ModModel.h + ui/pages/modplatform/atlauncher/AtlFilterModel.cpp ui/pages/modplatform/atlauncher/AtlFilterModel.h ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -879,13 +884,12 @@ qt5_wrap_ui(LAUNCHER_UI ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui ui/pages/modplatform/atlauncher/AtlPage.ui ui/pages/modplatform/VanillaPage.ui + ui/pages/modplatform/ModPage.ui ui/pages/modplatform/flame/FlamePage.ui - ui/pages/modplatform/flame/FlameModPage.ui ui/pages/modplatform/legacy_ftb/Page.ui ui/pages/modplatform/ImportPage.ui ui/pages/modplatform/ftb/FtbPage.ui ui/pages/modplatform/technic/TechnicPage.ui - ui/pages/modplatform/modrinth/ModrinthPage.ui ui/widgets/InstanceCardWidget.ui ui/widgets/CustomCommands.ui ui/widgets/MCModInfoFrame.ui diff --git a/launcher/modplatform/ModAPI.h b/launcher/modplatform/ModAPI.h new file mode 100644 index 00000000..e60fa8e0 --- /dev/null +++ b/launcher/modplatform/ModAPI.h @@ -0,0 +1,12 @@ +#pragma once + +#include <QString> + +class ModAPI { + public: + virtual ~ModAPI() = default; + + inline virtual QString getModSearchURL(int, QString, QString, bool, QString) const { return ""; }; + inline virtual QString getVersionsURL(const QString& addonId) const { return ""; }; + inline virtual QString getAuthorURL(const QString& name) const { return ""; }; +}; diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h new file mode 100644 index 00000000..7e1cf254 --- /dev/null +++ b/launcher/modplatform/ModIndex.h @@ -0,0 +1,42 @@ +#pragma once + +#include <QList> +#include <QMetaType> +#include <QString> +#include <QVariant> +#include <QVector> + +namespace ModPlatform { + +struct ModpackAuthor { + QString name; + QString url; +}; + +struct IndexedVersion { + QVariant addonId; + QVariant fileId; + QString version; + QVector<QString> mcVersion; + QString downloadUrl; + QString date; + QString fileName; + QVector<QString> loaders = {}; +}; + +struct IndexedPack { + QVariant addonId; + QString name; + QString description; + QList<ModpackAuthor> authors; + QString logoName; + QString logoUrl; + QString websiteUrl; + + bool versionsLoaded = false; + QVector<IndexedVersion> versions; +}; + +} // namespace ModPlatform + +Q_DECLARE_METATYPE(ModPlatform::IndexedPack) diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h new file mode 100644 index 00000000..6e2b9e25 --- /dev/null +++ b/launcher/modplatform/flame/FlameAPI.h @@ -0,0 +1,27 @@ +#pragma once + +#include "modplatform/ModAPI.h" + +class FlameAPI : public ModAPI { + public: + inline QString getModSearchURL(int index, QString searchFilter, QString sort, bool fabricCompatible, QString version) const override + { + return QString("https://addons-ecs.forgesvc.net/api/v2/addon/search?" + "gameId=432&" "categoryId=0&" "sectionId=6&" + + "index=%1&" "pageSize=25&" "searchFilter=%2&" + "sort=%3&" "modLoaderType=%4&" "gameVersion=%5") + .arg(index) + .arg(searchFilter) + .arg(sort) + .arg(fabricCompatible ? 4 : 1) // Enum: https://docs.curseforge.com/?http#tocS_ModLoaderType + .arg(version); + }; + + inline QString getVersionsURL(const QString& addonId) const override + { + return QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(addonId); + }; + + inline QString getAuthorURL(const QString& name) const override { return ""; }; +}; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 4adaf5f1..61cb534c 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -1,13 +1,11 @@ -#include <QObject> #include "FlameModIndex.h" + #include "Json.h" -#include "net/NetJob.h" -#include "BaseInstance.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" +#include "net/NetJob.h" - -void FlameMod::loadIndexedPack(FlameMod::IndexedPack & pack, QJsonObject & obj) +void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.addonId = Json::requireInteger(obj, "id"); pack.name = Json::requireString(obj, "name"); @@ -16,10 +14,10 @@ void FlameMod::loadIndexedPack(FlameMod::IndexedPack & pack, QJsonObject & obj) bool thumbnailFound = false; auto attachments = Json::requireArray(obj, "attachments"); - for(auto attachmentRaw: attachments) { + for (auto attachmentRaw : attachments) { auto attachmentObj = Json::requireObject(attachmentRaw); bool isDefault = attachmentObj.value("isDefault").toBool(false); - if(isDefault) { + if (isDefault) { thumbnailFound = true; pack.logoName = Json::requireString(attachmentObj, "title"); pack.logoUrl = Json::requireString(attachmentObj, "thumbnailUrl"); @@ -27,37 +25,35 @@ void FlameMod::loadIndexedPack(FlameMod::IndexedPack & pack, QJsonObject & obj) } } - if(!thumbnailFound) { - throw JSONValidationError(QString("Pack without an icon, skipping: %1").arg(pack.name)); - } - + if (!thumbnailFound) { throw JSONValidationError(QString("Pack without an icon, skipping: %1").arg(pack.name)); } auto authors = Json::requireArray(obj, "authors"); - for(auto authorIter: authors) { + for (auto authorIter : authors) { auto author = Json::requireObject(authorIter); - FlameMod::ModpackAuthor packAuthor; + ModPlatform::ModpackAuthor packAuthor; packAuthor.name = Json::requireString(author, "name"); packAuthor.url = Json::requireString(author, "url"); pack.authors.append(packAuthor); } } -void FlameMod::loadIndexedPackVersions(FlameMod::IndexedPack & pack, QJsonArray & arr, const shared_qobject_ptr<QNetworkAccessManager>& network, BaseInstance * inst) +void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, + QJsonArray& arr, + const shared_qobject_ptr<QNetworkAccessManager>& network, + BaseInstance* inst) { - QVector<FlameMod::IndexedVersion> unsortedVersions; - bool hasFabric = !((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty(); - QString mcVersion = ((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.minecraft"); + QVector<ModPlatform::IndexedVersion> unsortedVersions; + bool hasFabric = !((MinecraftInstance*)inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty(); + QString mcVersion = ((MinecraftInstance*)inst)->getPackProfile()->getComponentVersion("net.minecraft"); - for(auto versionIter: arr) { + for (auto versionIter : arr) { auto obj = versionIter.toObject(); auto versionArray = Json::requireArray(obj, "gameVersion"); - if (versionArray.isEmpty()) { - continue; - } + if (versionArray.isEmpty()) { continue; } - FlameMod::IndexedVersion file; - for(auto mcVer : versionArray){ + ModPlatform::IndexedVersion file; + for (auto mcVer : versionArray) { file.mcVersion.append(mcVer.toString()); } @@ -70,29 +66,27 @@ void FlameMod::loadIndexedPackVersions(FlameMod::IndexedPack & pack, QJsonArray auto modules = Json::requireArray(obj, "modules"); bool is_valid_fabric_version = false; - for(auto m : modules){ - auto fname = Json::requireString(m.toObject(),"foldername"); + for (auto m : modules) { + auto fname = Json::requireString(m.toObject(), "foldername"); // FIXME: This does not work properly when a mod supports more than one mod loader, since // they bundle the meta files for all of them in the same arquive, even when that version // doesn't support the given mod loader. - if(hasFabric){ - if(fname == "fabric.mod.json"){ + if (hasFabric) { + if (fname == "fabric.mod.json") { is_valid_fabric_version = true; break; } - } - else break; + } else + break; // NOTE: Since we're not validating forge versions, we can just skip this loop. } - if(hasFabric && !is_valid_fabric_version) - continue; + if (hasFabric && !is_valid_fabric_version) continue; unsortedVersions.append(file); } - auto orderSortPredicate = [](const IndexedVersion & a, const IndexedVersion & b) -> bool - { - //dates are in RFC 3339 format + 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); diff --git a/launcher/modplatform/flame/FlameModIndex.h b/launcher/modplatform/flame/FlameModIndex.h index 0293bb23..34f71498 100644 --- a/launcher/modplatform/flame/FlameModIndex.h +++ b/launcher/modplatform/flame/FlameModIndex.h @@ -3,48 +3,18 @@ // #pragma once -#include <QList> -#include <QMetaType> -#include <QString> -#include <QVector> + +#include "modplatform/ModIndex.h" + #include <QNetworkAccessManager> -#include <QObjectPtr.h> -#include "net/NetJob.h" #include "BaseInstance.h" namespace FlameMod { - struct ModpackAuthor { - QString name; - QString url; - }; - - struct IndexedVersion { - int addonId; - int fileId; - QString version; - QVector<QString> mcVersion; - QString downloadUrl; - QString date; - QString fileName; - }; - - struct IndexedPack - { - int addonId; - QString name; - QString description; - QList<ModpackAuthor> authors; - QString logoName; - QString logoUrl; - QString websiteUrl; - - bool versionsLoaded = false; - QVector<IndexedVersion> versions; - }; - - void loadIndexedPack(IndexedPack & m, QJsonObject & obj); - void loadIndexedPackVersions(IndexedPack &pack, QJsonArray &arr, const shared_qobject_ptr<QNetworkAccessManager> &network, BaseInstance *inst); -} +void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj); +void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, + QJsonArray& arr, + const shared_qobject_ptr<QNetworkAccessManager>& network, + BaseInstance* inst); -Q_DECLARE_METATYPE(FlameMod::IndexedPack) +} // namespace FlameMod diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h new file mode 100644 index 00000000..4ae8b8f9 --- /dev/null +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -0,0 +1,25 @@ +#pragma once + +#include "modplatform/ModAPI.h" + +class ModrinthAPI : public ModAPI { + public: + inline QString getModSearchURL(int offset, QString query, QString sort, bool fabricCompatible, QString version) const override + { + return QString("https://api.modrinth.com/v2/search?" + "offset=%1&" "limit=25&" "query=%2&" "index=%3&" + "facets=[[\"categories:%4\"],[\"versions:%5\"],[\"project_type:mod\"]]") + .arg(offset) + .arg(query) + .arg(sort) + .arg(fabricCompatible ? "fabric" : "forge") + .arg(version); + }; + + inline QString getVersionsURL(const QString& addonId) const override + { + return QString("https://api.modrinth.com/v2/project/%1/version").arg(addonId); + }; + + inline QString getAuthorURL(const QString& name) const override { return "https://modrinth.com/user/" + name; }; +}; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 9017eb67..02aac34d 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -1,14 +1,14 @@ -#include <QObject> #include "ModrinthPackIndex.h" +#include "ModrinthAPI.h" #include "Json.h" -#include "net/NetJob.h" -#include "BaseInstance.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" +#include "net/NetJob.h" +static ModrinthAPI api; -void Modrinth::loadIndexedPack(Modrinth::IndexedPack & pack, QJsonObject & obj) +void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.addonId = Json::requireString(obj, "project_id"); pack.name = Json::requireString(obj, "title"); @@ -16,59 +16,60 @@ void Modrinth::loadIndexedPack(Modrinth::IndexedPack & pack, QJsonObject & obj) pack.description = Json::ensureString(obj, "description", ""); pack.logoUrl = Json::requireString(obj, "icon_url"); - pack.logoName = pack.addonId; + pack.logoName = pack.addonId.toString(); - Modrinth::ModpackAuthor modAuthor; + ModPlatform::ModpackAuthor modAuthor; modAuthor.name = Json::requireString(obj, "author"); - modAuthor.url = "https://modrinth.com/user/"+modAuthor.name; - pack.author = modAuthor; + modAuthor.url = api.getAuthorURL(modAuthor.name); + pack.authors.append(modAuthor); } -void Modrinth::loadIndexedPackVersions(Modrinth::IndexedPack & pack, QJsonArray & arr, const shared_qobject_ptr<QNetworkAccessManager>& network, BaseInstance * inst) +void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, + QJsonArray& arr, + const shared_qobject_ptr<QNetworkAccessManager>& network, + BaseInstance* inst) { - QVector<Modrinth::IndexedVersion> unsortedVersions; - bool hasFabric = !((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty(); - QString mcVersion = ((MinecraftInstance *)inst)->getPackProfile()->getComponentVersion("net.minecraft"); + QVector<ModPlatform::IndexedVersion> unsortedVersions; + bool hasFabric = !((MinecraftInstance*)inst)->getPackProfile()->getComponentVersion("net.fabricmc.fabric-loader").isEmpty(); + QString mcVersion = ((MinecraftInstance*)inst)->getPackProfile()->getComponentVersion("net.minecraft"); - for(auto versionIter: arr) { + for (auto versionIter : arr) { auto obj = versionIter.toObject(); - Modrinth::IndexedVersion file; - file.addonId = Json::requireString(obj,"project_id") ; + ModPlatform::IndexedVersion file; + file.addonId = Json::requireString(obj, "project_id"); file.fileId = Json::requireString(obj, "id"); file.date = Json::requireString(obj, "date_published"); auto versionArray = Json::requireArray(obj, "game_versions"); - if (versionArray.empty()) { - continue; - } - for(auto mcVer : versionArray){ + if (versionArray.empty()) { continue; } + for (auto mcVer : versionArray) { file.mcVersion.append(mcVer.toString()); } - auto loaders = Json::requireArray(obj,"loaders"); - for(auto loader : loaders){ + auto loaders = Json::requireArray(obj, "loaders"); + for (auto loader : loaders) { file.loaders.append(loader.toString()); } file.version = Json::requireString(obj, "name"); auto files = Json::requireArray(obj, "files"); int i = 0; - while (files.count() > 1 && i < files.count()){ - //try to resolve the correct file + while (files.count() > 1 && i < files.count()) { + // try to resolve the correct file auto parent = files[i].toObject(); auto fileName = Json::requireString(parent, "filename"); - //avoid grabbing "dev" files - if(fileName.contains("javadocs",Qt::CaseInsensitive) || fileName.contains("sources",Qt::CaseInsensitive)){ + // avoid grabbing "dev" files + if (fileName.contains("javadocs", Qt::CaseInsensitive) || fileName.contains("sources", Qt::CaseInsensitive)) { i++; continue; } - //grab the correct mod loader - if(fileName.contains("forge",Qt::CaseInsensitive) || fileName.contains("fabric",Qt::CaseInsensitive) ){ - if(hasFabric){ - if(fileName.contains("forge",Qt::CaseInsensitive)){ + // grab the correct mod loader + if (fileName.contains("forge", Qt::CaseInsensitive) || fileName.contains("fabric", Qt::CaseInsensitive)) { + if (hasFabric) { + if (fileName.contains("forge", Qt::CaseInsensitive)) { i++; continue; } - }else{ - if(fileName.contains("fabric",Qt::CaseInsensitive)){ + } else { + if (fileName.contains("fabric", Qt::CaseInsensitive)) { i++; continue; } @@ -77,16 +78,15 @@ void Modrinth::loadIndexedPackVersions(Modrinth::IndexedPack & pack, QJsonArray break; } auto parent = files[i].toObject(); - if(parent.contains("url")) { + if (parent.contains("url")) { file.downloadUrl = Json::requireString(parent, "url"); file.fileName = Json::requireString(parent, "filename"); unsortedVersions.append(file); } } - auto orderSortPredicate = [](const IndexedVersion & a, const IndexedVersion & b) -> bool - { - //dates are in RFC 3339 format + 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); diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.h b/launcher/modplatform/modrinth/ModrinthPackIndex.h index 3a4cd270..abfdabb6 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.h +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.h @@ -1,48 +1,16 @@ #pragma once -#include <QList> -#include <QMetaType> -#include <QString> -#include <QVector> +#include "modplatform/ModIndex.h" + #include <QNetworkAccessManager> -#include <QObjectPtr.h> -#include "net/NetJob.h" #include "BaseInstance.h" namespace Modrinth { -struct ModpackAuthor { - QString name; - QString url; -}; - -struct IndexedVersion { - QString addonId; - QString fileId; - QString version; - QVector<QString> mcVersion; - QString downloadUrl; - QString date; - QString fileName; - QVector<QString> loaders; -}; - -struct IndexedPack -{ - QString addonId; - QString name; - QString description; - ModpackAuthor author; - QString logoName; - QString logoUrl; - QString websiteUrl; - - bool versionsLoaded = false; - QVector<IndexedVersion> versions; -}; - -void loadIndexedPack(IndexedPack & m, QJsonObject & obj); -void loadIndexedPackVersions(IndexedPack &pack, QJsonArray &arr, const shared_qobject_ptr<QNetworkAccessManager> &network, BaseInstance *inst); -} +void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj); +void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, + QJsonArray& arr, + const shared_qobject_ptr<QNetworkAccessManager>& network, + BaseInstance* inst); -Q_DECLARE_METATYPE(Modrinth::IndexedPack) +} // namespace Modrinth diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp new file mode 100644 index 00000000..c71acd35 --- /dev/null +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -0,0 +1,206 @@ +#include "ModModel.h" +#include "ModPage.h" + +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" +#include "ui/dialogs/ModDownloadDialog.h" + +#include <QMessageBox> + +namespace ModPlatform { + +ListModel::ListModel(ModPage* parent) : QAbstractListModel(parent), m_parent(parent) {} + +ListModel::~ListModel() {} + +int ListModel::rowCount(const QModelIndex& parent) const +{ + return modpacks.size(); +} + +int ListModel::columnCount(const QModelIndex& parent) const +{ + return 1; +} + +QVariant ListModel::data(const QModelIndex& index, int role) const +{ + int pos = index.row(); + if (pos >= modpacks.size() || pos < 0 || !index.isValid()) { return QString("INVALID INDEX %1").arg(pos); } + + ModPlatform::IndexedPack pack = modpacks.at(pos); + if (role == Qt::DisplayRole) { + return pack.name; + } else if (role == Qt::ToolTipRole) { + if (pack.description.length() > 100) { + // some magic to prevent to long tooltips and replace html linebreaks + QString edit = pack.description.left(97); + edit = edit.left(edit.lastIndexOf("<br>")).left(edit.lastIndexOf(" ")).append("..."); + return edit; + } + return pack.description; + } else if (role == Qt::DecorationRole) { + 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); + return icon; + } else if (role == Qt::UserRole) { + QVariant v; + v.setValue(pack); + return v; + } + + return QVariant(); +} + +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) { emit dataChanged(createIndex(i, 0), createIndex(i, 0), { Qt::DecorationRole }); } + } +} + +void ListModel::logoFailed(QString logo) +{ + m_failedLogos.append(logo); + m_loadingLogos.removeAll(logo); +} + +Qt::ItemFlags ListModel::flags(const QModelIndex& index) const +{ + return QAbstractListModel::flags(index); +} + +bool ListModel::canFetchMore(const QModelIndex& parent) const +{ + return searchState == CanPossiblyFetchMore; +} + +void ListModel::fetchMore(const QModelIndex& parent) +{ + if (parent.isValid()) return; + if (nextSearchOffset == 0) { + qWarning() << "fetchMore with 0 offset is wrong..."; + return; + } + performPaginatedSearch(); +} + +void ListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback) +{ + if (m_logoMap.contains(logo)) { + callback(APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath()); + } else { + requestLogo(logo, logoUrl); + } +} + +void ListModel::populateVersions(ModPlatform::IndexedPack const& current) +{ + auto netJob = new NetJob(QString("%1::ModVersions(%2)").arg(m_parent->debugName()).arg(current.name), APPLICATION->network()); + auto response = new QByteArray(); + QString addonId = current.addonId.toString(); + + netJob->addNetAction(Net::Download::makeByteArray(m_parent->apiProvider()->getVersionsURL(addonId), response)); + + QObject::connect(netJob, &NetJob::succeeded, this, [this, response, addonId]{ + m_parent->onGetVersionsSucceeded(m_parent, response, addonId); + }); + + QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { + netJob->deleteLater(); + delete response; + }); + + netJob->start(); +} + +void ListModel::performPaginatedSearch() +{ + QString mcVersion = ((MinecraftInstance*)((ModPage*)parent())->m_instance)->getPackProfile()->getComponentVersion("net.minecraft"); + bool hasFabric = !((MinecraftInstance*)((ModPage*)parent())->m_instance) + ->getPackProfile() + ->getComponentVersion("net.fabricmc.fabric-loader") + .isEmpty(); + auto netJob = new NetJob(QString("%1::Search").arg(m_parent->debugName()), APPLICATION->network()); + auto searchUrl = m_parent->apiProvider()->getModSearchURL(nextSearchOffset, currentSearchTerm, getSorts()[currentSort], hasFabric, mcVersion); + + netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); + jobPtr = netJob; + jobPtr->start(); + + QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::searchRequestFinished); + QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed); +} + +void ListModel::searchWithTerm(const QString& term, const int sort) +{ + if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull() && currentSort == sort) { return; } + currentSearchTerm = term; + currentSort = sort; + if (jobPtr) { + jobPtr->abort(); + searchState = ResetRequested; + return; + } else { + beginResetModel(); + modpacks.clear(); + endResetModel(); + searchState = None; + } + nextSearchOffset = 0; + performPaginatedSearch(); +} + +void ListModel::searchRequestFailed(QString reason) +{ + if (jobPtr->first()->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 409) { + // 409 Gone, notify user to update + QMessageBox::critical(nullptr, tr("Error"), + QString("%1 %2") + .arg(m_parent->displayName()) + .arg(tr("API version too old!\nPlease update PolyMC!"))); + // self-destruct + ((ModDownloadDialog*)((ModPage*)parent())->parentWidget())->reject(); + } + jobPtr.reset(); + + if (searchState == ResetRequested) { + beginResetModel(); + modpacks.clear(); + endResetModel(); + + nextSearchOffset = 0; + performPaginatedSearch(); + } else { + searchState = Finished; + } +} + +void ListModel::requestLogo(QString logo, QString url) +{ + if (m_loadingLogos.contains(logo) || m_failedLogos.contains(logo)) { return; } + + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))); + auto job = new NetJob(QString("%1 Icon Download %2").arg(m_parent->debugName()).arg(logo), APPLICATION->network()); + job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + + auto fullPath = entry->getFullPath(); + QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { + job->deleteLater(); + emit logoLoaded(logo, QIcon(fullPath)); + if (waitingCallbacks.contains(logo)) { waitingCallbacks.value(logo)(fullPath); } + }); + + QObject::connect(job, &NetJob::failed, this, [this, logo, job] { + job->deleteLater(); + emit logoFailed(logo); + }); + + job->start(); + m_loadingLogos.append(logo); +} + +} // namespace ModPlatform diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h new file mode 100644 index 00000000..2b8ff65e --- /dev/null +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -0,0 +1,67 @@ +#pragma once + +#include <QAbstractListModel> + +#include "modplatform/ModIndex.h" +#include "modplatform/ModAPI.h" +#include "net/NetJob.h" + +class ModPage; + +namespace ModPlatform { + +typedef QMap<QString, QIcon> LogoMap; +typedef std::function<void(QString)> LogoCallback; + +class ListModel : public QAbstractListModel { + Q_OBJECT + + public: + ListModel(ModPage* parent); + virtual ~ListModel(); + + int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent) const override; + QVariant data(const QModelIndex& index, int role) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + bool canFetchMore(const QModelIndex& parent) const override; + void fetchMore(const QModelI |
