mirror of
https://github.com/redstrate/Astra.git
synced 2025-04-23 21:07:45 +00:00
Make patching asynchronous
This commit is contained in:
parent
e211c95e21
commit
21d305fd08
6 changed files with 100 additions and 114 deletions
|
@ -45,7 +45,8 @@ find_package(Qt6 ${QT_MIN_VERSION} REQUIRED COMPONENTS
|
|||
Widgets
|
||||
Network
|
||||
QuickControls2
|
||||
WebView)
|
||||
WebView
|
||||
Concurrent)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 I18n Config CoreAddons)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(QCoro6 REQUIRED COMPONENTS Core Network Qml)
|
||||
|
|
|
@ -82,6 +82,7 @@ target_link_libraries(astra PRIVATE
|
|||
Qt6::Quick
|
||||
Qt6::QuickControls2
|
||||
Qt6::WebView
|
||||
Qt6::Concurrent
|
||||
KF6::Kirigami2
|
||||
KF6::I18n
|
||||
KF6::ConfigCore
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <QNetworkAccessManager>
|
||||
#include <QString>
|
||||
#include <physis.hpp>
|
||||
#include <qcorotask.h>
|
||||
|
||||
class LauncherCore;
|
||||
|
||||
|
@ -19,13 +20,9 @@ public:
|
|||
Patcher(LauncherCore &launcher, QString baseDirectory, GameData *game_data, QObject *parent = nullptr);
|
||||
Patcher(LauncherCore &launcher, QString baseDirectory, BootData *game_data, QObject *parent = nullptr);
|
||||
|
||||
void processPatchList(QNetworkAccessManager &mgr, const QString &patchList);
|
||||
|
||||
signals:
|
||||
void done();
|
||||
QCoro::Task<> patch(QNetworkAccessManager &mgr, const QString &patchList);
|
||||
|
||||
private:
|
||||
void checkIfDone();
|
||||
void setupDirectories();
|
||||
|
||||
[[nodiscard]] bool isBoot() const
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
#include <QNetworkRequest>
|
||||
#include <QRegExp>
|
||||
#include <QStandardPaths>
|
||||
#include <QtConcurrent>
|
||||
#include <physis.hpp>
|
||||
#include <qcorofuture.h>
|
||||
#include <qcoronetworkreply.h>
|
||||
#include <utility>
|
||||
|
||||
#include "launchercore.h"
|
||||
|
@ -37,110 +40,97 @@ Patcher::Patcher(LauncherCore &launcher, QString baseDirectory, GameData *game_d
|
|||
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Game version."));
|
||||
}
|
||||
|
||||
void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchList)
|
||||
QCoro::Task<> Patcher::patch(QNetworkAccessManager &mgr, const QString &patchList)
|
||||
{
|
||||
if (patchList.isEmpty()) {
|
||||
emit done();
|
||||
co_return;
|
||||
}
|
||||
|
||||
if (isBoot()) {
|
||||
Q_EMIT m_launcher.stageIndeterminate();
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Update/Launcher version."));
|
||||
} else {
|
||||
if (isBoot()) {
|
||||
Q_EMIT m_launcher.stageIndeterminate();
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Update/Launcher version."));
|
||||
Q_EMIT m_launcher.stageIndeterminate();
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Game version."));
|
||||
}
|
||||
|
||||
const QStringList parts = patchList.split("\r\n");
|
||||
|
||||
remainingPatches = parts.size() - 7;
|
||||
patchQueue.resize(remainingPatches);
|
||||
|
||||
QFutureSynchronizer<void> synchronizer;
|
||||
|
||||
int patchIndex = 0;
|
||||
|
||||
for (int i = 5; i < parts.size() - 2; i++) {
|
||||
const QStringList patchParts = parts[i].split("\t");
|
||||
|
||||
const int length = patchParts[0].toInt();
|
||||
int ourIndex = patchIndex++;
|
||||
|
||||
const QString version = patchParts[4];
|
||||
const long hashBlockSize = patchParts.size() == 9 ? patchParts[6].toLong() : 0;
|
||||
|
||||
const QString name = version;
|
||||
const QStringList hashes = patchParts.size() == 9 ? (patchParts[7].split(',')) : QStringList();
|
||||
const QString url = patchParts[patchParts.size() == 9 ? 8 : 5];
|
||||
const QString filename = QStringLiteral("%1.patch").arg(name);
|
||||
|
||||
auto url_parts = url.split('/');
|
||||
const QString repository = url_parts[url_parts.size() - 3];
|
||||
|
||||
const QDir repositoryDir = patchesDir.absoluteFilePath(repository);
|
||||
|
||||
if (!QDir().exists(repositoryDir.absolutePath()))
|
||||
QDir().mkpath(repositoryDir.absolutePath());
|
||||
|
||||
const QString patchPath = repositoryDir.absoluteFilePath(filename);
|
||||
if (!QFile::exists(patchPath)) {
|
||||
auto patchReply = mgr.get(QNetworkRequest(url));
|
||||
|
||||
connect(patchReply, &QNetworkReply::downloadProgress, [this, repository, version, length](int recieved, int total) {
|
||||
Q_UNUSED(total)
|
||||
|
||||
if (isBoot()) {
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Updating the FINAL FANTASY XIV Updater/Launcher version.\nDownloading ffxivboot - %1", version));
|
||||
} else {
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Updating the FINAL FANTASY XIV Game version.\nDownloading %1 - %2", repository, version));
|
||||
}
|
||||
|
||||
Q_EMIT m_launcher.stageDeterminate(0, length, recieved);
|
||||
});
|
||||
|
||||
synchronizer.addFuture(QtFuture::connect(patchReply, &QNetworkReply::finished)
|
||||
.then([this, patchPath, patchReply, ourIndex, name, repository, version, hashes, hashBlockSize, length] {
|
||||
QFile file(patchPath);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(patchReply->readAll());
|
||||
file.close();
|
||||
|
||||
patchQueue[ourIndex] = {name, repository, version, patchPath, hashes, hashBlockSize, length};
|
||||
}));
|
||||
} else {
|
||||
Q_EMIT m_launcher.stageIndeterminate();
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Game version."));
|
||||
}
|
||||
qDebug() << "Found existing patch: " << name;
|
||||
|
||||
const QStringList parts = patchList.split("\r\n");
|
||||
patchQueue[ourIndex] = {name, repository, version, patchPath, hashes, hashBlockSize, length};
|
||||
|
||||
remainingPatches = parts.size() - 7;
|
||||
patchQueue.resize(remainingPatches);
|
||||
|
||||
int patchIndex = 0;
|
||||
|
||||
for (int i = 5; i < parts.size() - 2; i++) {
|
||||
const QStringList patchParts = parts[i].split("\t");
|
||||
|
||||
const int length = patchParts[0].toInt();
|
||||
int ourIndex = patchIndex++;
|
||||
|
||||
const QString version = patchParts[4];
|
||||
const long hashBlockSize = patchParts.size() == 9 ? patchParts[6].toLong() : 0;
|
||||
|
||||
const QString name = version;
|
||||
const QStringList hashes = patchParts.size() == 9 ? (patchParts[7].split(',')) : QStringList();
|
||||
const QString url = patchParts[patchParts.size() == 9 ? 8 : 5];
|
||||
const QString filename = QStringLiteral("%1.patch").arg(name);
|
||||
|
||||
auto url_parts = url.split('/');
|
||||
const QString repository = url_parts[url_parts.size() - 3];
|
||||
|
||||
const QDir repositoryDir = patchesDir.absoluteFilePath(repository);
|
||||
|
||||
if (!QDir().exists(repositoryDir.absolutePath()))
|
||||
QDir().mkpath(repositoryDir.absolutePath());
|
||||
|
||||
const QString patchPath = repositoryDir.absoluteFilePath(filename);
|
||||
if (!QFile::exists(patchPath)) {
|
||||
qDebug() << "Need to download " + name;
|
||||
|
||||
QNetworkRequest patchRequest(url);
|
||||
auto patchReply = mgr.get(patchRequest);
|
||||
connect(patchReply, &QNetworkReply::downloadProgress, [this, repository, version, length](int recieved, int total) {
|
||||
Q_UNUSED(total)
|
||||
|
||||
if (isBoot()) {
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Updating the FINAL FANTASY XIV Updater/Launcher version.\nDownloading ffxivboot - %1", version));
|
||||
} else {
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Updating the FINAL FANTASY XIV Game version.\nDownloading %1 - %2", repository, version));
|
||||
}
|
||||
|
||||
Q_EMIT m_launcher.stageDeterminate(0, length, recieved);
|
||||
});
|
||||
|
||||
connect(patchReply,
|
||||
&QNetworkReply::finished,
|
||||
[this, ourIndex, patchPath, name, patchReply, repository, version, hashes, hashBlockSize, length] {
|
||||
QFile file(patchPath);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(patchReply->readAll());
|
||||
file.close();
|
||||
|
||||
patchQueue[ourIndex] = {name, repository, version, patchPath, hashes, hashBlockSize, length};
|
||||
|
||||
remainingPatches--;
|
||||
checkIfDone();
|
||||
});
|
||||
} else {
|
||||
qDebug() << "Found existing patch: " << name;
|
||||
|
||||
patchQueue[ourIndex] = {name, repository, version, patchPath, hashes, hashBlockSize, length};
|
||||
|
||||
remainingPatches--;
|
||||
checkIfDone();
|
||||
}
|
||||
synchronizer.addFuture({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Patcher::checkIfDone()
|
||||
{
|
||||
if (remainingPatches <= 0) {
|
||||
if (isBoot()) {
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Applying updates to the FINAL FANTASY XIV Updater/Launcher."));
|
||||
} else {
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Applying updates to the FINAL FANTASY XIV Game."));
|
||||
}
|
||||
co_await QtConcurrent::run([&synchronizer] {
|
||||
synchronizer.waitForFinished();
|
||||
});
|
||||
|
||||
int i = 0;
|
||||
for (const auto &patch : patchQueue) {
|
||||
Q_EMIT m_launcher.stageDeterminate(0, patchQueue.size(), i++);
|
||||
processPatch(patch);
|
||||
}
|
||||
|
||||
patchQueue.clear();
|
||||
|
||||
emit done();
|
||||
// This must happen synchronously
|
||||
size_t i = 0;
|
||||
for (const auto &patch : patchQueue) {
|
||||
Q_EMIT m_launcher.stageDeterminate(0, patchQueue.size(), i++);
|
||||
processPatch(patch);
|
||||
}
|
||||
|
||||
co_return;
|
||||
}
|
||||
|
||||
void Patcher::processPatch(const QueuedPatch &patch)
|
||||
|
|
|
@ -27,13 +27,6 @@ QCoro::Task<> SquareBoot::bootCheck(const LoginInformation &info)
|
|||
Q_EMIT window.stageChanged(i18n("Checking for launcher updates..."));
|
||||
qDebug() << "Performing boot check...";
|
||||
|
||||
patcher = new Patcher(window, info.profile->gamePath() + QStringLiteral("/boot"), info.profile->bootData, this);
|
||||
connect(patcher, &Patcher::done, [this, &info] {
|
||||
info.profile->readGameVersion();
|
||||
|
||||
launcher.login(info);
|
||||
});
|
||||
|
||||
const QUrlQuery query{{QStringLiteral("time"), QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyy-MM-dd-HH-mm"))}};
|
||||
|
||||
QUrl url;
|
||||
|
@ -54,7 +47,13 @@ QCoro::Task<> SquareBoot::bootCheck(const LoginInformation &info)
|
|||
const auto reply = window.mgr->get(request);
|
||||
co_await reply;
|
||||
|
||||
patcher->processPatchList(*window.mgr, reply->readAll());
|
||||
patcher = new Patcher(window, info.profile->gamePath() + QStringLiteral("/boot"), info.profile->bootData, this);
|
||||
co_await patcher->patch(*window.mgr, reply->readAll());
|
||||
|
||||
// update game version information
|
||||
info.profile->readGameVersion();
|
||||
|
||||
launcher.login(info);
|
||||
}
|
||||
|
||||
QCoro::Task<> SquareBoot::checkGateStatus(LoginInformation *info)
|
||||
|
|
|
@ -170,7 +170,7 @@ QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info)
|
|||
|
||||
QString report = QStringLiteral("%1=%2").arg(info.profile->bootVersion, getBootHash(info));
|
||||
for (int i = 1; i < auth.maxExpansion + 1; i++) {
|
||||
if (i <= static_cast<int>(info.profile->repositories.repositories_count)) {
|
||||
if (i < static_cast<int>(info.profile->repositories.repositories_count)) {
|
||||
report += QStringLiteral("\nex%1\t%2").arg(QString::number(i), info.profile->repositories.repositories[i].version);
|
||||
} else {
|
||||
report += QStringLiteral("\nex%1\t2012.01.01.0000.0000").arg(QString::number(i));
|
||||
|
@ -185,15 +185,13 @@ QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info)
|
|||
const QString body = reply->readAll();
|
||||
|
||||
patcher = new Patcher(window, info.profile->gamePath() + QStringLiteral("/game"), info.profile->gameData, this);
|
||||
connect(patcher, &Patcher::done, [this, &info, reply] {
|
||||
info.profile->readGameVersion();
|
||||
co_await patcher->patch(*window.mgr, body);
|
||||
|
||||
auth.SID = reply->rawHeader(QByteArrayLiteral("X-Patch-Unique-Id"));
|
||||
info.profile->readGameVersion();
|
||||
|
||||
window.launchGame(*info.profile, auth);
|
||||
});
|
||||
auth.SID = reply->rawHeader(QByteArrayLiteral("X-Patch-Unique-Id"));
|
||||
|
||||
patcher->processPatchList(*window.mgr, body);
|
||||
window.launchGame(*info.profile, auth);
|
||||
} else {
|
||||
Q_EMIT window.loginError(i18n("Fatal error, request was successful but X-Patch-Unique-Id was not recieved."));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue