1
Fork 0
mirror of https://github.com/redstrate/Astra.git synced 2025-04-20 11:47:46 +00:00

Improve patching experience further

Now there is more visible and consistent information such as the number
of patches and how far you're into the patching process. It's harder to
close or quit the window while patching to prevent errors, and
installing patches no longer freezes the window.

And fixes an issue where the boot component would never properly update.
This commit is contained in:
Joshua Goins 2023-10-11 17:45:02 -04:00
parent af407cbf7c
commit 81bd81e1b6
7 changed files with 110 additions and 14 deletions

View file

@ -105,6 +105,7 @@ public:
[[nodiscard]] bool isLoadingFinished() const;
[[nodiscard]] bool isSteam() const;
[[nodiscard]] bool isSteamDeck() const;
[[nodiscard]] Q_INVOKABLE bool isPatching() const;
[[nodiscard]] QNetworkAccessManager *mgr();
[[nodiscard]] LauncherSettings *settings();
@ -112,19 +113,24 @@ public:
[[nodiscard]] AccountManager *accountManager();
[[nodiscard]] Headline *headline() const;
signals:
Q_SIGNALS:
void loadingFinished();
void successfulLaunch();
void gameClosed();
void loginError(QString message);
void dalamudError(QString message);
void stageChanged(QString message);
void stageChanged(QString message, QString explanation = {});
void stageIndeterminate();
void stageDeterminate(int min, int max, int value);
void newsChanged();
void currentProfileChanged();
void autoLoginProfileChanged();
protected:
friend class Patcher;
bool m_isPatching = false;
private:
QCoro::Task<> beginLogin(LoginInformation &info);

View file

@ -5,6 +5,7 @@
#include "patchlist.h"
#include <QDir>
#include <QMutex>
#include <QNetworkAccessManager>
#include <QString>
#include <physis.hpp>
@ -21,6 +22,7 @@ class Patcher : public QObject
public:
Patcher(LauncherCore &launcher, const QString &baseDirectory, GameData &gameData, QObject *parent = nullptr);
Patcher(LauncherCore &launcher, const QString &baseDirectory, BootData &bootData, QObject *parent = nullptr);
~Patcher() override;
QCoro::Task<bool> patch(const PatchList &patchList);
@ -40,6 +42,9 @@ private:
long length;
bool isBoot;
long bytesDownloaded;
bool downloaded = false;
[[nodiscard]] QString getVersion() const
{
if (isBoot) {
@ -62,4 +67,10 @@ private:
int m_remainingPatches = -1;
LauncherCore &m_launcher;
QMutex m_finishedPatchesMutex;
int m_finishedPatches = 0;
void updateDownloadProgress(int index, int received);
void updateMessage();
};

View file

@ -224,6 +224,11 @@ bool LauncherCore::isSteamDeck() const
}
}
bool LauncherCore::isPatching() const
{
return m_isPatching;
}
QNetworkAccessManager *LauncherCore::mgr()
{
return m_mgr;

View file

@ -24,9 +24,11 @@ Patcher::Patcher(LauncherCore &launcher, const QString &baseDirectory, BootData
, m_bootData(&bootData)
, m_launcher(launcher)
{
m_launcher.m_isPatching = true;
setupDirectories();
Q_EMIT m_launcher.stageChanged(i18n("Checking %1 version.", getBaseString()));
Q_EMIT m_launcher.stageChanged(i18n("Checking %1 version", getBaseString()));
}
Patcher::Patcher(LauncherCore &launcher, const QString &baseDirectory, GameData &gameData, QObject *parent)
@ -35,9 +37,16 @@ Patcher::Patcher(LauncherCore &launcher, const QString &baseDirectory, GameData
, m_gameData(&gameData)
, m_launcher(launcher)
{
m_launcher.m_isPatching = true;
setupDirectories();
Q_EMIT m_launcher.stageChanged(i18n("Checking %1 version.", getBaseString()));
Q_EMIT m_launcher.stageChanged(i18n("Checking %1 version", getBaseString()));
}
Patcher::~Patcher()
{
m_launcher.m_isPatching = false;
}
QCoro::Task<bool> Patcher::patch(const PatchList &patchList)
@ -47,7 +56,7 @@ QCoro::Task<bool> Patcher::patch(const PatchList &patchList)
}
Q_EMIT m_launcher.stageIndeterminate();
Q_EMIT m_launcher.stageChanged(i18n("Checking %1 version.", getBaseString()));
Q_EMIT m_launcher.stageChanged(i18n("Updating %1", getBaseString()));
m_remainingPatches = patchList.patches().size();
m_patchQueue.resize(m_remainingPatches);
@ -86,18 +95,27 @@ QCoro::Task<bool> Patcher::patch(const PatchList &patchList)
auto patchReply = m_launcher.mgr()->get(patchRequest);
connect(patchReply, &QNetworkReply::downloadProgress, this, [this, queuedPatch](int received, int total) {
Q_EMIT m_launcher.stageChanged(i18n("Updating %1.\nDownloading %2", getBaseString(), queuedPatch.getVersion()));
Q_EMIT m_launcher.stageDeterminate(0, total, received);
connect(patchReply, &QNetworkReply::downloadProgress, this, [this, ourIndex, queuedPatch](int received, int total) {
updateDownloadProgress(ourIndex, received);
});
synchronizer.addFuture(QtFuture::connect(patchReply, &QNetworkReply::finished).then([patchPath, patchReply] {
synchronizer.addFuture(QtFuture::connect(patchReply, &QNetworkReply::finished).then([this, ourIndex, patchPath, patchReply] {
qDebug(ASTRA_PATCHER) << "Downloaded to" << patchPath;
QFile file(patchPath);
file.open(QIODevice::WriteOnly);
file.write(patchReply->readAll());
file.close();
QMutexLocker locker(&m_finishedPatchesMutex);
m_finishedPatches++;
m_patchQueue[ourIndex].downloaded = true;
updateMessage();
}));
} else {
m_patchQueue[ourIndex].downloaded = true;
m_finishedPatches++;
qDebug(ASTRA_PATCHER) << "Found existing patch: " << patch.name;
}
}
@ -109,10 +127,17 @@ QCoro::Task<bool> Patcher::patch(const PatchList &patchList)
// This must happen synchronously
size_t i = 0;
for (const auto &patch : m_patchQueue) {
Q_EMIT m_launcher.stageChanged(i18n("Updating %1.\nInstalling %2", getBaseString(), patch.getVersion()));
QString repositoryName = patch.repository;
if (repositoryName == QStringLiteral("game")) {
repositoryName = QStringLiteral("ffxiv");
}
Q_EMIT m_launcher.stageChanged(i18n("Installing %1 - %2 [%3/%4]", repositoryName, patch.version, i++, m_remainingPatches));
Q_EMIT m_launcher.stageDeterminate(0, m_patchQueue.size(), i++);
processPatch(patch);
co_await QtConcurrent::run([this, patch] {
processPatch(patch);
});
}
co_return true;
@ -191,3 +216,33 @@ QString Patcher::getBaseString() const
return i18n("FINAL FANTASY XIV Game");
}
}
void Patcher::updateDownloadProgress(const int index, int received)
{
QMutexLocker locker(&m_finishedPatchesMutex);
m_patchQueue[index].bytesDownloaded = received;
updateMessage();
}
void Patcher::updateMessage()
{
// Find first not-downloaded patch
for (const auto &patch : m_patchQueue) {
if (!patch.downloaded) {
QString repositoryName = patch.repository;
if (repositoryName == QStringLiteral("game")) {
repositoryName = QStringLiteral("ffxiv");
}
const float progress = ((float)patch.bytesDownloaded / (float)patch.length) * 100.0f;
const QString progressStr = QStringLiteral("%1").arg(progress, 1, 'f', 1, '0');
Q_EMIT m_launcher.stageChanged(i18n("Downloading %1 - %2 [%3/%4]", repositoryName, patch.version, m_finishedPatches, m_remainingPatches),
i18n("%1%", progressStr));
Q_EMIT m_launcher.stageDeterminate(0, patch.length, patch.bytesDownloaded);
return;
}
}
}

View file

@ -86,12 +86,15 @@ QCoro::Task<> SquareEnixLogin::checkBootUpdates()
Q_EMIT m_launcher.stageChanged(i18n("Checking for launcher updates..."));
qInfo(ASTRA_LOG) << "Checking for updates to boot components...";
const QUrlQuery query{{QStringLiteral("time"), QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyy-MM-dd-HH-mm"))}};
QString formattedDate = QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyy-MM-dd-HH-mm"));
formattedDate[15] = '0';
const QUrlQuery query{{QStringLiteral("time"), formattedDate}};
QUrl url;
url.setScheme(QStringLiteral("http"));
url.setHost(QStringLiteral("patch-bootver.%1").arg(m_launcher.settings()->squareEnixServer()));
url.setPath(QStringLiteral("/http/win32/ffxivneo_release_boot/%1").arg(m_info->profile->bootVersion()));
url.setPath(QStringLiteral("/http/win32/ffxivneo_release_boot/%1/").arg(m_info->profile->bootVersion()));
url.setQuery(query);
auto request = QNetworkRequest(url);

View file

@ -29,6 +29,13 @@ Kirigami.ApplicationWindow {
}
}
onClosing: (close) => {
if (LauncherCore.isPatching()) {
applicationWindow().showPassiveNotification(i18n("Please do not quit while patching!"));
}
close.accepted = !LauncherCore.isPatching();
}
function checkSetup() {
if (!LauncherCore.loadingFinished) {
return

View file

@ -12,6 +12,14 @@ Kirigami.Page {
title: i18n("Logging in...")
onBackRequested: (event) => {
if (LauncherCore.isPatching()) {
// Prevent going back
applicationWindow().showPassiveNotification(i18n("Please do not quit while patching!"));
event.accepted = true;
}
}
Kirigami.LoadingPlaceholder {
id: placeholder
@ -48,8 +56,9 @@ Kirigami.Page {
Connections {
target: LauncherCore
function onStageChanged(message) {
function onStageChanged(message, explanation) {
placeholder.text = message
placeholder.explanation = explanation
}
function onStageIndeterminate() {