GH-3516 Display available versions for Solder packs

This commit is contained in:
Jamie Mansfield 2021-12-27 03:52:54 +00:00
parent b8a736c673
commit b6290ac254
No known key found for this signature in database
GPG Key ID: 36F61598F39F67B0
6 changed files with 191 additions and 92 deletions

View File

@ -1,5 +1,5 @@
/* Copyright 2013-2021 MultiMC Contributors
* Copyright 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,10 +26,12 @@
Technic::SolderPackInstallTask::SolderPackInstallTask(
shared_qobject_ptr<QNetworkAccessManager> network,
const QUrl &sourceUrl,
const QString &version,
const QString &minecraftVersion
) {
m_sourceUrl = sourceUrl;
m_minecraftVersion = minecraftVersion;
m_version = version;
m_network = network;
}
@ -42,43 +44,13 @@ bool Technic::SolderPackInstallTask::abort() {
}
void Technic::SolderPackInstallTask::executeTask()
{
setStatus(tr("Finding recommended version:\n%1").arg(m_sourceUrl.toString()));
m_filesNetJob = new NetJob(tr("Finding recommended version"), m_network);
m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::versionSucceeded);
connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
m_filesNetJob->start();
}
void Technic::SolderPackInstallTask::versionSucceeded()
{
setStatus(tr("Resolving modpack files"));
QJsonParseError parse_error {};
QJsonDocument doc = QJsonDocument::fromJson(m_response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
qWarning() << m_response;
return;
}
auto obj = doc.object();
TechnicSolder::Pack pack;
try {
TechnicSolder::loadPack(pack, obj);
}
catch (const JSONValidationError& e) {
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
m_filesNetJob.reset();
return;
}
m_sourceUrl = m_sourceUrl.toString() + '/' + pack.recommended;
m_filesNetJob = new NetJob(tr("Resolving modpack files"), m_network);
m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
auto sourceUrl = QString("%1/%2").arg(m_sourceUrl.toString(), m_version);
m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, &m_response));
auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded);
connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);

View File

@ -1,4 +1,5 @@
/* Copyright 2013-2021 MultiMC Contributors
* Copyright 2021 Jamie Mansfield <jmansfield@cadixdev.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,7 +28,7 @@ namespace Technic
{
Q_OBJECT
public:
explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &sourceUrl, const QString &minecraftVersion);
explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &sourceUrl, const QString& version, const QString &minecraftVersion);
bool canAbort() const override { return true; }
bool abort() override;
@ -37,7 +38,6 @@ namespace Technic
virtual void executeTask() override;
private slots:
void versionSucceeded();
void fileListSucceeded();
void downloadSucceeded();
void downloadFailed(QString reason);
@ -52,6 +52,7 @@ namespace Technic
NetJob::Ptr m_filesNetJob;
QUrl m_sourceUrl;
QString m_version;
QString m_minecraftVersion;
QByteArray m_response;
QTemporaryDir m_outputDir;

View File

@ -1,4 +1,5 @@
/* Copyright 2020-2021 MultiMC Contributors
* Copyright 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +18,7 @@
#include <QList>
#include <QString>
#include <QVector>
namespace Technic {
struct Modpack {
@ -37,6 +39,10 @@ struct Modpack {
QString author;
QString description;
QString currentVersion;
bool versionsLoaded = false;
QString recommended;
QVector<QString> versions;
};
}

View File

@ -1,4 +1,5 @@
/* Copyright 2013-2022 MultiMC Contributors
* Copyright 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,6 +25,7 @@
#include "TechnicModel.h"
#include "modplatform/technic/SingleZipPackInstallTask.h"
#include "modplatform/technic/SolderPackInstallTask.h"
#include "modplatform/technic/SolderPackManifest.h"
#include "Json.h"
#include "Application.h"
@ -36,7 +38,9 @@ TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
ui->searchEdit->installEventFilter(this);
model = new Technic::ListModel(this);
ui->packView->setModel(model);
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TechnicPage::onSelectionChanged);
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &TechnicPage::onVersionSelectionChanged);
}
bool TechnicPage::eventFilter(QObject* watched, QEvent* event)
@ -74,13 +78,14 @@ void TechnicPage::triggerSearch() {
void TechnicPage::onSelectionChanged(QModelIndex first, QModelIndex second)
{
ui->versionSelectionBox->clear();
if(!first.isValid())
{
if(isOpened)
{
dialog->setSuggestedPack();
}
//ui->frame->clear();
return;
}
@ -113,17 +118,19 @@ void TechnicPage::suggestCurrent()
}
NetJob *netJob = new NetJob(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network());
std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
QString slug = current.slug;
netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.technicpack.net/modpack/%1?build=multimc").arg(slug), response.get()));
QObject::connect(netJob, &NetJob::succeeded, this, [this, response, slug]
netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.technicpack.net/modpack/%1?build=multimc").arg(slug), &response));
QObject::connect(netJob, &NetJob::succeeded, this, [this, slug]
{
jobPtr.reset();
if (current.slug != slug)
{
return;
}
QJsonParseError parse_error;
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
QJsonParseError parse_error {};
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
QJsonObject obj = doc.object();
if(parse_error.error != QJsonParseError::NoError)
{
@ -167,9 +174,12 @@ void TechnicPage::suggestCurrent()
current.description = Json::ensureString(obj, "description", QString(), "__placeholder__");
current.currentVersion = Json::ensureString(obj, "version", QString(), "__placeholder__");
current.metadataLoaded = true;
metadataLoaded();
});
netJob->start();
jobPtr = netJob;
jobPtr->start();
}
// expects current.metadataLoaded to be true
@ -190,13 +200,108 @@ void TechnicPage::metadataLoaded()
text += "<br><br>";
ui->packDescription->setHtml(text + current.description);
// Strip trailing forward-slashes from Solder URL's
if (current.isSolder) {
while (current.url.endsWith('/')) current.url.chop(1);
}
// Display versions from Solder
if (!current.isSolder) {
// If the pack isn't a Solder pack, it only has the single version
ui->versionSelectionBox->addItem(current.currentVersion);
}
else if (current.versionsLoaded) {
// reverse foreach, so that the newest versions are first
for (auto i = current.versions.size(); i--;) {
ui->versionSelectionBox->addItem(current.versions.at(i));
}
ui->versionSelectionBox->setCurrentText(current.recommended);
}
else {
// For now, until the versions are pulled from the Solder instance, display the current
// version so we can display something quicker
ui->versionSelectionBox->addItem(current.currentVersion);
auto* netJob = new NetJob(QString("Technic::SolderMeta(%1)").arg(current.name), APPLICATION->network());
auto url = QString("%1/modpack/%2").arg(current.url, current.slug);
netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
QObject::connect(netJob, &NetJob::succeeded, this, &TechnicPage::onSolderLoaded);
jobPtr = netJob;
jobPtr->start();
}
selectVersion();
}
void TechnicPage::selectVersion() {
if (!isOpened) {
return;
}
if (current.broken) {
dialog->setSuggestedPack();
return;
}
if (!current.isSolder)
{
dialog->setSuggestedPack(current.name + " " + current.currentVersion, new Technic::SingleZipPackInstallTask(current.url, current.minecraftVersion));
dialog->setSuggestedPack(current.name + " " + selectedVersion, new Technic::SingleZipPackInstallTask(current.url, current.minecraftVersion));
}
else
{
while (current.url.endsWith('/')) current.url.chop(1);
dialog->setSuggestedPack(current.name + " " + current.currentVersion, new Technic::SolderPackInstallTask(APPLICATION->network(), current.url + "/modpack/" + current.slug, current.minecraftVersion));
dialog->setSuggestedPack(current.name + " " + selectedVersion, new Technic::SolderPackInstallTask(APPLICATION->network(), current.url + "/modpack/" + current.slug, selectedVersion, current.minecraftVersion));
}
}
void TechnicPage::onSolderLoaded() {
jobPtr.reset();
auto fallback = [this]() {
current.versionsLoaded = true;
current.versions.clear();
current.versions.append(current.currentVersion);
};
current.versions.clear();
QJsonParseError parse_error {};
auto doc = QJsonDocument::fromJson(response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
qWarning() << response;
fallback();
return;
}
auto obj = doc.object();
TechnicSolder::Pack pack;
try {
TechnicSolder::loadPack(pack, obj);
}
catch (const JSONValidationError &err) {
qCritical() << "Couldn't parse Solder pack metadata:" << err.cause();
fallback();
return;
}
current.versionsLoaded = true;
current.recommended = pack.recommended;
current.versions.append(pack.builds);
// Finally, let's reload :)
ui->versionSelectionBox->clear();
metadataLoaded();
}
void TechnicPage::onVersionSelectionChanged(QString data) {
if (data.isNull() || data.isEmpty()) {
selectedVersion = "";
return;
}
selectedVersion = data;
selectVersion();
}

View File

@ -1,4 +1,5 @@
/* Copyright 2013-2021 MultiMC Contributors
* Copyright 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +20,7 @@
#include "ui/pages/BasePage.h"
#include <Application.h>
#include "net/NetJob.h"
#include "tasks/Task.h"
#include "TechnicData.h"
@ -65,14 +67,22 @@ public:
private:
void suggestCurrent();
void metadataLoaded();
void selectVersion();
private slots:
void triggerSearch();
void onSelectionChanged(QModelIndex first, QModelIndex second);
void onSolderLoaded();
void onVersionSelectionChanged(QString data);
private:
Ui::TechnicPage *ui = nullptr;
NewInstanceDialog* dialog = nullptr;
Technic::ListModel* model = nullptr;
Technic::Modpack current;
QString selectedVersion;
NetJob::Ptr jobPtr;
QByteArray response;
};

View File

@ -10,52 +10,44 @@
<height>405</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="searchEdit">
<property name="placeholderText">
<string>Search and filter ...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="searchButton">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="rightMargin">
<number>0</number>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="2">
<widget class="QComboBox" name="versionSelectionBox"/>
</item>
<item row="0" column="1">
<widget class="QTextBrowser" name="packDescription"/>
<widget class="QLabel" name="label">
<property name="text">
<string>Version selected:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QListView" name="packView">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QListView" name="packView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
@ -67,14 +59,27 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QTextBrowser" name="packDescription"/>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLineEdit" name="searchEdit">
<property name="placeholderText">
<string>Search and filter ...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="searchButton">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>searchEdit</tabstop>
<tabstop>searchButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>