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:
parent
af407cbf7c
commit
81bd81e1b6
7 changed files with 110 additions and 14 deletions
|
@ -105,6 +105,7 @@ public:
|
||||||
[[nodiscard]] bool isLoadingFinished() const;
|
[[nodiscard]] bool isLoadingFinished() const;
|
||||||
[[nodiscard]] bool isSteam() const;
|
[[nodiscard]] bool isSteam() const;
|
||||||
[[nodiscard]] bool isSteamDeck() const;
|
[[nodiscard]] bool isSteamDeck() const;
|
||||||
|
[[nodiscard]] Q_INVOKABLE bool isPatching() const;
|
||||||
|
|
||||||
[[nodiscard]] QNetworkAccessManager *mgr();
|
[[nodiscard]] QNetworkAccessManager *mgr();
|
||||||
[[nodiscard]] LauncherSettings *settings();
|
[[nodiscard]] LauncherSettings *settings();
|
||||||
|
@ -112,19 +113,24 @@ public:
|
||||||
[[nodiscard]] AccountManager *accountManager();
|
[[nodiscard]] AccountManager *accountManager();
|
||||||
[[nodiscard]] Headline *headline() const;
|
[[nodiscard]] Headline *headline() const;
|
||||||
|
|
||||||
signals:
|
Q_SIGNALS:
|
||||||
void loadingFinished();
|
void loadingFinished();
|
||||||
void successfulLaunch();
|
void successfulLaunch();
|
||||||
void gameClosed();
|
void gameClosed();
|
||||||
void loginError(QString message);
|
void loginError(QString message);
|
||||||
void dalamudError(QString message);
|
void dalamudError(QString message);
|
||||||
void stageChanged(QString message);
|
void stageChanged(QString message, QString explanation = {});
|
||||||
void stageIndeterminate();
|
void stageIndeterminate();
|
||||||
void stageDeterminate(int min, int max, int value);
|
void stageDeterminate(int min, int max, int value);
|
||||||
void newsChanged();
|
void newsChanged();
|
||||||
void currentProfileChanged();
|
void currentProfileChanged();
|
||||||
void autoLoginProfileChanged();
|
void autoLoginProfileChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class Patcher;
|
||||||
|
|
||||||
|
bool m_isPatching = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QCoro::Task<> beginLogin(LoginInformation &info);
|
QCoro::Task<> beginLogin(LoginInformation &info);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "patchlist.h"
|
#include "patchlist.h"
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QMutex>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <physis.hpp>
|
#include <physis.hpp>
|
||||||
|
@ -21,6 +22,7 @@ class Patcher : public QObject
|
||||||
public:
|
public:
|
||||||
Patcher(LauncherCore &launcher, const QString &baseDirectory, GameData &gameData, QObject *parent = nullptr);
|
Patcher(LauncherCore &launcher, const QString &baseDirectory, GameData &gameData, QObject *parent = nullptr);
|
||||||
Patcher(LauncherCore &launcher, const QString &baseDirectory, BootData &bootData, QObject *parent = nullptr);
|
Patcher(LauncherCore &launcher, const QString &baseDirectory, BootData &bootData, QObject *parent = nullptr);
|
||||||
|
~Patcher() override;
|
||||||
|
|
||||||
QCoro::Task<bool> patch(const PatchList &patchList);
|
QCoro::Task<bool> patch(const PatchList &patchList);
|
||||||
|
|
||||||
|
@ -40,6 +42,9 @@ private:
|
||||||
long length;
|
long length;
|
||||||
bool isBoot;
|
bool isBoot;
|
||||||
|
|
||||||
|
long bytesDownloaded;
|
||||||
|
bool downloaded = false;
|
||||||
|
|
||||||
[[nodiscard]] QString getVersion() const
|
[[nodiscard]] QString getVersion() const
|
||||||
{
|
{
|
||||||
if (isBoot) {
|
if (isBoot) {
|
||||||
|
@ -62,4 +67,10 @@ private:
|
||||||
int m_remainingPatches = -1;
|
int m_remainingPatches = -1;
|
||||||
|
|
||||||
LauncherCore &m_launcher;
|
LauncherCore &m_launcher;
|
||||||
|
|
||||||
|
QMutex m_finishedPatchesMutex;
|
||||||
|
int m_finishedPatches = 0;
|
||||||
|
|
||||||
|
void updateDownloadProgress(int index, int received);
|
||||||
|
void updateMessage();
|
||||||
};
|
};
|
||||||
|
|
|
@ -224,6 +224,11 @@ bool LauncherCore::isSteamDeck() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LauncherCore::isPatching() const
|
||||||
|
{
|
||||||
|
return m_isPatching;
|
||||||
|
}
|
||||||
|
|
||||||
QNetworkAccessManager *LauncherCore::mgr()
|
QNetworkAccessManager *LauncherCore::mgr()
|
||||||
{
|
{
|
||||||
return m_mgr;
|
return m_mgr;
|
||||||
|
|
|
@ -24,9 +24,11 @@ Patcher::Patcher(LauncherCore &launcher, const QString &baseDirectory, BootData
|
||||||
, m_bootData(&bootData)
|
, m_bootData(&bootData)
|
||||||
, m_launcher(launcher)
|
, m_launcher(launcher)
|
||||||
{
|
{
|
||||||
|
m_launcher.m_isPatching = true;
|
||||||
|
|
||||||
setupDirectories();
|
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)
|
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_gameData(&gameData)
|
||||||
, m_launcher(launcher)
|
, m_launcher(launcher)
|
||||||
{
|
{
|
||||||
|
m_launcher.m_isPatching = true;
|
||||||
|
|
||||||
setupDirectories();
|
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)
|
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.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_remainingPatches = patchList.patches().size();
|
||||||
m_patchQueue.resize(m_remainingPatches);
|
m_patchQueue.resize(m_remainingPatches);
|
||||||
|
@ -86,18 +95,27 @@ QCoro::Task<bool> Patcher::patch(const PatchList &patchList)
|
||||||
|
|
||||||
auto patchReply = m_launcher.mgr()->get(patchRequest);
|
auto patchReply = m_launcher.mgr()->get(patchRequest);
|
||||||
|
|
||||||
connect(patchReply, &QNetworkReply::downloadProgress, this, [this, queuedPatch](int received, int total) {
|
connect(patchReply, &QNetworkReply::downloadProgress, this, [this, ourIndex, queuedPatch](int received, int total) {
|
||||||
Q_EMIT m_launcher.stageChanged(i18n("Updating %1.\nDownloading %2", getBaseString(), queuedPatch.getVersion()));
|
updateDownloadProgress(ourIndex, received);
|
||||||
Q_EMIT m_launcher.stageDeterminate(0, total, 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);
|
QFile file(patchPath);
|
||||||
file.open(QIODevice::WriteOnly);
|
file.open(QIODevice::WriteOnly);
|
||||||
file.write(patchReply->readAll());
|
file.write(patchReply->readAll());
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_finishedPatchesMutex);
|
||||||
|
m_finishedPatches++;
|
||||||
|
m_patchQueue[ourIndex].downloaded = true;
|
||||||
|
|
||||||
|
updateMessage();
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
|
m_patchQueue[ourIndex].downloaded = true;
|
||||||
|
m_finishedPatches++;
|
||||||
qDebug(ASTRA_PATCHER) << "Found existing patch: " << patch.name;
|
qDebug(ASTRA_PATCHER) << "Found existing patch: " << patch.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,10 +127,17 @@ QCoro::Task<bool> Patcher::patch(const PatchList &patchList)
|
||||||
// This must happen synchronously
|
// This must happen synchronously
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (const auto &patch : m_patchQueue) {
|
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++);
|
Q_EMIT m_launcher.stageDeterminate(0, m_patchQueue.size(), i++);
|
||||||
|
|
||||||
|
co_await QtConcurrent::run([this, patch] {
|
||||||
processPatch(patch);
|
processPatch(patch);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
co_return true;
|
co_return true;
|
||||||
|
@ -191,3 +216,33 @@ QString Patcher::getBaseString() const
|
||||||
return i18n("FINAL FANTASY XIV Game");
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -86,12 +86,15 @@ QCoro::Task<> SquareEnixLogin::checkBootUpdates()
|
||||||
Q_EMIT m_launcher.stageChanged(i18n("Checking for launcher updates..."));
|
Q_EMIT m_launcher.stageChanged(i18n("Checking for launcher updates..."));
|
||||||
qInfo(ASTRA_LOG) << "Checking for updates to boot components...";
|
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;
|
QUrl url;
|
||||||
url.setScheme(QStringLiteral("http"));
|
url.setScheme(QStringLiteral("http"));
|
||||||
url.setHost(QStringLiteral("patch-bootver.%1").arg(m_launcher.settings()->squareEnixServer()));
|
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);
|
url.setQuery(query);
|
||||||
|
|
||||||
auto request = QNetworkRequest(url);
|
auto request = QNetworkRequest(url);
|
||||||
|
|
|
@ -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() {
|
function checkSetup() {
|
||||||
if (!LauncherCore.loadingFinished) {
|
if (!LauncherCore.loadingFinished) {
|
||||||
return
|
return
|
||||||
|
|
|
@ -12,6 +12,14 @@ Kirigami.Page {
|
||||||
|
|
||||||
title: i18n("Logging in...")
|
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 {
|
Kirigami.LoadingPlaceholder {
|
||||||
id: placeholder
|
id: placeholder
|
||||||
|
|
||||||
|
@ -48,8 +56,9 @@ Kirigami.Page {
|
||||||
Connections {
|
Connections {
|
||||||
target: LauncherCore
|
target: LauncherCore
|
||||||
|
|
||||||
function onStageChanged(message) {
|
function onStageChanged(message, explanation) {
|
||||||
placeholder.text = message
|
placeholder.text = message
|
||||||
|
placeholder.explanation = explanation
|
||||||
}
|
}
|
||||||
|
|
||||||
function onStageIndeterminate() {
|
function onStageIndeterminate() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue