From d6dc22d57c33497b5908abaf53770cde1773c0a7 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield <jmansfield@cadixdev.org> Date: Mon, 5 Apr 2021 21:19:05 +0100 Subject: NOISSUE Handle JSON exceptions in ATLauncher support Thanks to phit for pointing this out :) --- application/CMakeLists.txt | 5 +- .../pages/modplatform/atlauncher/AtlListModel.cpp | 194 +++++++++++++++++++++ .../pages/modplatform/atlauncher/AtlListModel.h | 52 ++++++ .../pages/modplatform/atlauncher/AtlModel.cpp | 185 -------------------- .../pages/modplatform/atlauncher/AtlModel.h | 52 ------ application/pages/modplatform/atlauncher/AtlPage.h | 2 +- 6 files changed, 249 insertions(+), 241 deletions(-) create mode 100644 application/pages/modplatform/atlauncher/AtlListModel.cpp create mode 100644 application/pages/modplatform/atlauncher/AtlListModel.h delete mode 100644 application/pages/modplatform/atlauncher/AtlModel.cpp delete mode 100644 application/pages/modplatform/atlauncher/AtlModel.h (limited to 'application') diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt index afd13574..fd49c267 100644 --- a/application/CMakeLists.txt +++ b/application/CMakeLists.txt @@ -125,13 +125,12 @@ SET(MULTIMC_SOURCES pages/modplatform/VanillaPage.cpp pages/modplatform/VanillaPage.h - pages/modplatform/atlauncher/AtlModel.cpp - pages/modplatform/atlauncher/AtlModel.h pages/modplatform/atlauncher/AtlFilterModel.cpp pages/modplatform/atlauncher/AtlFilterModel.h + pages/modplatform/atlauncher/AtlListModel.cpp + pages/modplatform/atlauncher/AtlListModel.h pages/modplatform/atlauncher/AtlPage.cpp pages/modplatform/atlauncher/AtlPage.h - pages/modplatform/atlauncher/AtlPage.h pages/modplatform/ftb/FtbFilterModel.cpp pages/modplatform/ftb/FtbFilterModel.h diff --git a/application/pages/modplatform/atlauncher/AtlListModel.cpp b/application/pages/modplatform/atlauncher/AtlListModel.cpp new file mode 100644 index 00000000..f3be6198 --- /dev/null +++ b/application/pages/modplatform/atlauncher/AtlListModel.cpp @@ -0,0 +1,194 @@ +#include "AtlListModel.h" + +#include <BuildConfig.h> +#include <MultiMC.h> +#include <Env.h> +#include <Json.h> + +namespace Atl { + +ListModel::ListModel(QObject *parent) : QAbstractListModel(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); + } + + ATLauncher::IndexedPack pack = modpacks.at(pos); + if(role == Qt::DisplayRole) + { + return pack.name; + } + else if (role == Qt::ToolTipRole) + { + return pack.name; + } + else if(role == Qt::DecorationRole) + { + if(m_logoMap.contains(pack.safeName)) + { + return (m_logoMap.value(pack.safeName)); + } + auto icon = MMC->getThemedIcon("atlauncher-placeholder"); + + auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower()); + ((ListModel *)this)->requestLogo(pack.safeName, url); + + return icon; + } + else if(role == Qt::UserRole) + { + QVariant v; + v.setValue(pack); + return v; + } + + return QVariant(); +} + +void ListModel::request() +{ + beginResetModel(); + modpacks.clear(); + endResetModel(); + + auto *netJob = new NetJob("Atl::Request"); + auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/json/packsnew.json"); + netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response)); + jobPtr = netJob; + jobPtr->start(); + + QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::requestFinished); + QObject::connect(netJob, &NetJob::failed, this, &ListModel::requestFailed); +} + +void ListModel::requestFinished() +{ + jobPtr.reset(); + + QJsonParseError parse_error; + QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error); + if(parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from ATL at " << parse_error.offset << " reason: " << parse_error.errorString(); + qWarning() << response; + return; + } + + QList<ATLauncher::IndexedPack> newList; + + auto packs = doc.array(); + for(auto packRaw : packs) { + auto packObj = packRaw.toObject(); + + ATLauncher::IndexedPack pack; + + try { + ATLauncher::loadIndexedPack(pack, packObj); + } + catch (const JSONValidationError &e) { + qDebug() << QString::fromUtf8(response); + qWarning() << "Error while reading pack manifest from ATLauncher: " << e.cause(); + return; + } + + // ignore packs without a published version + if(pack.versions.length() == 0) continue; + // only display public packs (for now) + if(pack.type != ATLauncher::PackType::Public) continue; + // ignore "system" packs (Vanilla, Vanilla with Forge, etc) + if(pack.system) continue; + + newList.append(pack); + } + + beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1); + modpacks.append(newList); + endInsertRows(); +} + +void ListModel::requestFailed(QString reason) +{ + jobPtr.reset(); +} + +void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback) +{ + if(m_logoMap.contains(logo)) + { + callback(ENV.metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath()); + } + else + { + requestLogo(logo, logoUrl); + } +} + +void ListModel::logoFailed(QString logo) +{ + m_failedLogos.append(logo); + m_loadingLogos.removeAll(logo); +} + +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].safeName == logo) { + emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole}); + } + } +} + +void ListModel::requestLogo(QString file, QString url) +{ + if(m_loadingLogos.contains(file) || m_failedLogos.contains(file)) + { + return; + } + + MetaEntryPtr entry = ENV.metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file.section(".", 0, 0))); + NetJob *job = new NetJob(QString("ATLauncher Icon Download %1").arg(file)); + job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + + auto fullPath = entry->getFullPath(); + QObject::connect(job, &NetJob::succeeded, this, [this, file, fullPath] + { + emit logoLoaded(file, QIcon(fullPath)); + if(waitingCallbacks.contains(file)) + { + waitingCallbacks.value(file)(fullPath); + } + }); + + QObject::connect(job, &NetJob::failed, this, [this, file] + { + emit logoFailed(file); + }); + + job->start(); + + m_loadingLogos.append(file); +} + +} diff --git a/application/pages/modplatform/atlauncher/AtlListModel.h b/application/pages/modplatform/atlauncher/AtlListModel.h new file mode 100644 index 00000000..2d30a64e --- /dev/null +++ b/application/pages/modplatform/atlauncher/AtlListModel.h @@ -0,0 +1,52 @@ +#pragma once + +#include <QAbstractListModel> + +#include "net/NetJob.h" +#include <QIcon> +#include <modplatform/atlauncher/ATLPackIndex.h> + +namespace Atl { + +typedef QMap<QString, QIcon> LogoMap; +typedef std::function<void(QString)> LogoCallback; + +class ListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + ListModel(QObject *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; + + void request(); + + void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback); + +private slots: + void requestFinished(); + void requestFailed(QString reason); + + void logoFailed(QString logo); + void logoLoaded(QString logo, QIcon out); + +private: + void requestLogo(QString file, QString url); + +private: + QList<ATLauncher::IndexedPack> modpacks; + + QStringList m_failedLogos; + QStringList m_loadingLogos; + LogoMap m_logoMap; + QMap<QString, LogoCallback> waitingCallbacks; + + NetJobPtr jobPtr; + QByteArray response; +}; + +} diff --git a/application/pages/modplatform/atlauncher/AtlModel.cpp b/application/pages/modplatform/atlauncher/AtlModel.cpp deleted file mode 100644 index 4b1b1c8e..00000000 --- a/application/pages/modplatform/atlauncher/AtlModel.cpp +++ /dev/null @@ -1,185 +0,0 @@ -#include "AtlModel.h" - -#include <BuildConfig.h> -#include <MultiMC.h> -#include <Env.h> - -namespace Atl { - -ListModel::ListModel(QObject *parent) : QAbstractListModel(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); - } - - ATLauncher::IndexedPack pack = modpacks.at(pos); - if(role == Qt::DisplayRole) - { - return pack.name; - } - else if (role == Qt::ToolTipRole) - { - return pack.name; - } - else if(role == Qt::DecorationRole) - { - if(m_logoMap.contains(pack.safeName)) - { - return (m_logoMap.value(pack.safeName)); - } - auto icon = MMC->getThemedIcon("atlauncher-placeholder"); - - auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower()); - ((ListModel *)this)->requestLogo(pack.safeName, url); - - return icon; - } - else if(role == Qt::UserRole) - { - QVariant v; - v.setValue(pack); - return v; - } - - return QVariant(); -} - -void ListModel::request() -{ - beginResetModel(); - modpacks.clear(); - endResetModel(); - - auto *netJob = new NetJob("Atl::Request"); - auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/json/packsnew.json"); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response)); - jobPtr = netJob; - jobPtr->start(); - - QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::requestFinished); - QObject::connect(netJob, &NetJob::failed, this, &ListModel::requestFailed); -} - -void ListModel::requestFinished() -{ - jobPtr.reset(); - - QJsonParseError parse_error; - QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error); - if(parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response from ATL at " << parse_error.offset << " reason: " << parse_error.errorString(); - qWarning() << response; - return; - } - - QList<ATLauncher::IndexedPack> newList; - - auto packs = doc.array(); - for(auto packRaw : packs) { - auto packObj = packRaw.toObject(); - - ATLauncher::IndexedPack pack; - ATLauncher::loadIndexedPack(pack, packObj); - - // ignore packs without a published version - if(pack.versions.length() == 0) continue; - // only display public packs (for now) - if(pack.type != ATLauncher::PackType::Public) continue; - // ignore "system" packs (Vanilla, Vanilla with Forge, etc) - if(pack.system) continue; - - newList.append(pack); - } - - beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1); - modpacks.append(newList); - endInsertRows(); -} - -void ListModel::requestFailed(QString reason) -{ - jobPtr.reset(); -} - -void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback) -{ - if(m_logoMap.contains(logo)) - { - callback(ENV.metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath()); - } - else - { - requestLogo(logo, logoUrl); - } -} - -void ListModel::logoFailed(QString logo) -{ - m_failedLogos.append(logo); - m_loadingLogos.removeAll(logo); -} - -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].safeName == logo) { - emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole}); - } - } -} - -void ListModel::requestLogo(QString file, QString url) -{ - if(m_loadingLogos.contains(file) || m_failedLogos.contains(file)) - { - return; - } - - MetaEntryPtr entry = ENV.metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file.section(".", 0, 0))); - NetJob *job = new NetJob(QString("ATLauncher Icon Download %1").arg(file)); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); - - auto fullPath = entry->getFullPath(); - QObject::connect(job, &NetJob::succeeded, this, [this, file, fullPath] - { - emit logoLoaded(file, QIcon(fullPath)); - if(waitingCallbacks.contains(file)) - { - waitingCallbacks.value(file)(fullPath); - } - }); - - QObject::connect(job, &NetJob::failed, this, [this, file] - { - emit logoFailed(file); - }); - - job->start(); - - m_loadingLogos.append(file); -} - -} diff --git a/application/pages/modplatform/atlauncher/AtlModel.h b/application/pages/modplatform/atlauncher/AtlModel.h deleted file mode 100644 index 2d30a64e..00000000 --- a/application/pages/modplatform/atlauncher/AtlModel.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include <QAbstractListModel> - -#include "net/NetJob.h" -#include <QIcon> -#include <modplatform/atlauncher/ATLPackIndex.h> - -namespace Atl { - -typedef QMap<QString, QIcon> LogoMap; -typedef std::function<void(QString)> LogoCallback; - -class ListModel : public QAbstractListModel -{ - Q_OBJECT - -public: - ListModel(QObject *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; - - void request(); - - void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback); - -private slots: - void requestFinished(); - void requestFailed(QString reason); - - void logoFailed(QString logo); - void logoLoaded(QString logo, QIcon out); - -private: - void requestLogo(QString file, QString url); - -private: - QList<ATLauncher::IndexedPack> modpacks; - - QStringList m_failedLogos; - QStringList m_loadingLogos; - LogoMap m_logoMap; - QMap<QString, LogoCallback> waitingCallbacks; - - NetJobPtr jobPtr; - QByteArray response; -}; - -} diff --git a/application/pages/modplatform/atlauncher/AtlPage.h b/application/pages/modplatform/atlauncher/AtlPage.h index 368de666..715cf5f4 100644 --- a/application/pages/modplatform/atlauncher/AtlPage.h +++ b/application/pages/modplatform/atlauncher/AtlPage.h @@ -16,7 +16,7 @@ #pragma once #include "AtlFilterModel.h" -#include "AtlModel.h" +#include "AtlListModel.h" #include <QWidget> -- cgit