mirror of
https://github.com/redstrate/Astra.git
synced 2025-04-20 19:57:45 +00:00
Parallelize and speed up the login process even more
Now the asset updating process is parallelized (especially asset file downloading). There's less wasteful usage of the patcher and game version reading when no patches need to be installed.
This commit is contained in:
parent
05bf6fff4f
commit
90a5ffc6c8
10 changed files with 287 additions and 352 deletions
|
@ -6,6 +6,7 @@
|
|||
#include <QJsonArray>
|
||||
#include <QObject>
|
||||
#include <QTemporaryDir>
|
||||
#include <qcorotask.h>
|
||||
|
||||
#include "launchercore.h"
|
||||
|
||||
|
@ -19,17 +20,16 @@ class AssetUpdater : public QObject
|
|||
public:
|
||||
explicit AssetUpdater(Profile &profile, LauncherCore &launcher, QObject *parent = nullptr);
|
||||
|
||||
void update();
|
||||
void beginInstall();
|
||||
|
||||
void checkIfCheckingIsDone();
|
||||
void checkIfDalamudAssetsDone();
|
||||
void checkIfFinished();
|
||||
|
||||
Q_SIGNALS:
|
||||
void finishedUpdating();
|
||||
QCoro::Task<> update();
|
||||
|
||||
private:
|
||||
QCoro::Task<> checkRemoteDalamudAssetVersion();
|
||||
QCoro::Task<> checkRemoteDalamudVersion();
|
||||
|
||||
QCoro::Task<> installDalamudAssets();
|
||||
QCoro::Task<> installDalamud();
|
||||
QCoro::Task<> installRuntime();
|
||||
|
||||
[[nodiscard]] QUrl dalamudVersionManifestUrl(Profile::DalamudChannel channel) const;
|
||||
[[nodiscard]] QUrl dalamudLatestPackageUrl(Profile::DalamudChannel channel) const;
|
||||
[[nodiscard]] QUrl dalamudAssetManifestUrl() const;
|
||||
|
@ -48,14 +48,7 @@ private:
|
|||
QDir dalamudAssetDir;
|
||||
QDir dalamudRuntimeDir;
|
||||
|
||||
bool doneDownloadingDalamud = false;
|
||||
bool doneDownloadingRuntimeCore = false;
|
||||
bool doneDownloadingRuntimeDesktop = false;
|
||||
bool needsRuntimeInstall = false;
|
||||
bool needsDalamudInstall = false;
|
||||
|
||||
int remoteDalamudAssetVersion = -1;
|
||||
QList<QString> dalamudAssetNeededFilenames;
|
||||
QJsonArray remoteDalamudAssetArray;
|
||||
|
||||
Profile &m_profile;
|
||||
|
|
|
@ -179,6 +179,8 @@ signals:
|
|||
void currentProfileChanged();
|
||||
|
||||
private:
|
||||
QCoro::Task<> beginLogin(LoginInformation &info);
|
||||
|
||||
/*
|
||||
* Begins the game executable, but calls to Dalamud if needed.
|
||||
*/
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
Patcher(LauncherCore &launcher, const QString &baseDirectory, GameData &gameData, QObject *parent = nullptr);
|
||||
Patcher(LauncherCore &launcher, const QString &baseDirectory, BootData &bootData, QObject *parent = nullptr);
|
||||
|
||||
QCoro::Task<> patch(const QString &patchList);
|
||||
QCoro::Task<bool> patch(const QString &patchList);
|
||||
|
||||
private:
|
||||
void setupDirectories();
|
||||
|
|
|
@ -18,11 +18,11 @@ class SquareBoot : public QObject
|
|||
public:
|
||||
SquareBoot(LauncherCore &window, SquareLauncher &launcher, QObject *parent = nullptr);
|
||||
|
||||
QCoro::Task<> checkGateStatus(LoginInformation *info);
|
||||
|
||||
QCoro::Task<> bootCheck(const LoginInformation &info);
|
||||
QCoro::Task<> checkGateStatus(const LoginInformation &info);
|
||||
|
||||
private:
|
||||
QCoro::Task<> bootCheck(const LoginInformation &info);
|
||||
|
||||
Patcher *patcher = nullptr;
|
||||
|
||||
LauncherCore &window;
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
QCoro::Task<> registerSession(const LoginInformation &info);
|
||||
|
||||
private:
|
||||
QString getBootHash(const LoginInformation &info);
|
||||
static QCoro::Task<QString> getBootHash(const LoginInformation &info);
|
||||
|
||||
Patcher *patcher = nullptr;
|
||||
|
||||
|
|
|
@ -3,15 +3,19 @@
|
|||
|
||||
#include "assetupdater.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QNetworkReply>
|
||||
#include <QStandardPaths>
|
||||
#include <qcorofuture.h>
|
||||
#include <qcoronetworkreply.h>
|
||||
|
||||
#include <JlCompress.h>
|
||||
#include <QtConcurrentRun>
|
||||
|
||||
const QString dotnetRuntimePackageURL = "https://dotnetcli.azureedge.net/dotnet/Runtime/%1/dotnet-runtime-%1-win-x64.zip";
|
||||
const QString dotnetDesktopPackageURL = "https://dotnetcli.azureedge.net/dotnet/WindowsDesktop/%1/windowsdesktop-runtime-%1-win-x64.zip";
|
||||
const QString dotnetRuntimePackageURL = QStringLiteral("https://dotnetcli.azureedge.net/dotnet/Runtime/%1/dotnet-runtime-%1-win-x64.zip");
|
||||
const QString dotnetDesktopPackageURL = QStringLiteral("https://dotnetcli.azureedge.net/dotnet/WindowsDesktop/%1/windowsdesktop-runtime-%1-win-x64.zip");
|
||||
|
||||
AssetUpdater::AssetUpdater(Profile &profile, LauncherCore &launcher, QObject *parent)
|
||||
: QObject(parent)
|
||||
|
@ -20,85 +24,75 @@ AssetUpdater::AssetUpdater(Profile &profile, LauncherCore &launcher, QObject *pa
|
|||
, m_profile(profile)
|
||||
{
|
||||
launcher.mgr->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
}
|
||||
|
||||
QCoro::Task<> AssetUpdater::update()
|
||||
{
|
||||
if (!m_profile.dalamudEnabled()) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
qInfo() << "Starting asset update sequence...";
|
||||
|
||||
dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
dalamudDir = dataDir.absoluteFilePath("dalamud");
|
||||
dalamudAssetDir = dalamudDir.absoluteFilePath("assets");
|
||||
dalamudRuntimeDir = dalamudDir.absoluteFilePath("runtime");
|
||||
dalamudDir = dataDir.absoluteFilePath(QStringLiteral("dalamud"));
|
||||
dalamudAssetDir = dalamudDir.absoluteFilePath(QStringLiteral("assets"));
|
||||
dalamudRuntimeDir = dalamudDir.absoluteFilePath(QStringLiteral("runtime"));
|
||||
|
||||
const auto createIfNeeded = [](const QDir &dir) {
|
||||
if (!QDir().exists(dir.absolutePath()))
|
||||
QDir().mkdir(dir.absolutePath());
|
||||
QDir().mkpath(dir.absolutePath());
|
||||
};
|
||||
|
||||
createIfNeeded(dataDir);
|
||||
createIfNeeded(dalamudDir);
|
||||
createIfNeeded(dalamudAssetDir);
|
||||
createIfNeeded(dalamudRuntimeDir);
|
||||
|
||||
co_await checkRemoteDalamudAssetVersion();
|
||||
co_await checkRemoteDalamudVersion();
|
||||
}
|
||||
|
||||
void AssetUpdater::update()
|
||||
QCoro::Task<> AssetUpdater::checkRemoteDalamudAssetVersion()
|
||||
{
|
||||
// non-dalamud users can bypass this process since it's not needed
|
||||
if (!m_profile.dalamudEnabled()) {
|
||||
Q_EMIT finishedUpdating();
|
||||
return;
|
||||
}
|
||||
|
||||
Q_EMIT launcher.stageChanged("Updating assets...");
|
||||
|
||||
// first, we want to collect all of the remote versions
|
||||
|
||||
qInfo() << "Starting update sequence...";
|
||||
// dialog->setLabelText("Checking for updates...");
|
||||
|
||||
// dalamud assets
|
||||
{
|
||||
// we want to prevent logging in before we actually check the version
|
||||
dalamudAssetNeededFilenames.clear();
|
||||
remoteDalamudAssetVersion = -1;
|
||||
|
||||
dalamudAssetNeededFilenames.append("dummy");
|
||||
|
||||
// first we want to fetch the list of assets required
|
||||
QNetworkRequest request(dalamudAssetManifestUrl());
|
||||
const QNetworkRequest request(dalamudAssetManifestUrl());
|
||||
|
||||
auto reply = launcher.mgr->get(request);
|
||||
connect(reply, &QNetworkReply::finished, [reply, this] {
|
||||
Q_EMIT launcher.stageChanged("Checking for Dalamud asset updates...");
|
||||
const auto reply = launcher.mgr->get(request);
|
||||
co_await reply;
|
||||
|
||||
// TODO: handle asset failure
|
||||
QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
|
||||
|
||||
qInfo() << "Dalamud asset remote version" << doc.object()["Version"].toInt();
|
||||
remoteDalamudAssetVersion = doc.object()[QLatin1String("Version")].toInt();
|
||||
remoteDalamudAssetArray = doc.object()[QLatin1String("Assets")].toArray();
|
||||
|
||||
qInfo() << "Dalamud asset remote version" << remoteDalamudAssetVersion;
|
||||
qInfo() << "Dalamud asset local version" << m_profile.dalamudAssetVersion();
|
||||
|
||||
remoteDalamudAssetVersion = doc.object()["Version"].toInt();
|
||||
// dalamud assets
|
||||
if (remoteDalamudAssetVersion != m_profile.dalamudAssetVersion()) {
|
||||
qInfo() << "Dalamud assets out of date.";
|
||||
|
||||
remoteDalamudAssetArray = doc.object()["Assets"].toArray();
|
||||
|
||||
checkIfCheckingIsDone();
|
||||
});
|
||||
co_await installDalamudAssets();
|
||||
}
|
||||
}
|
||||
|
||||
// dalamud injector / net runtime
|
||||
// they're all updated in unison, so there's no reason to have multiple checks
|
||||
{
|
||||
QNetworkRequest request(dalamudVersionManifestUrl(m_profile.dalamudChannel()));
|
||||
QCoro::Task<> AssetUpdater::checkRemoteDalamudVersion()
|
||||
{
|
||||
const QNetworkRequest request(dalamudVersionManifestUrl(m_profile.dalamudChannel()));
|
||||
|
||||
remoteDalamudVersion.clear();
|
||||
remoteRuntimeVersion.clear();
|
||||
|
||||
auto reply = launcher.mgr->get(request);
|
||||
connect(reply, &QNetworkReply::finished, [this, reply] {
|
||||
Q_EMIT launcher.stageChanged("Checking for Dalamud updates...");
|
||||
const auto reply = launcher.mgr->get(request);
|
||||
co_await reply;
|
||||
|
||||
if (reply->error() != QNetworkReply::NetworkError::NoError) {
|
||||
Q_EMIT launcher.loginError(QStringLiteral("Could not check for Dalamud updates.\n\n%1").arg(reply->errorString()));
|
||||
return;
|
||||
Q_EMIT launcher.loginError(i18n("Could not check for Dalamud updates.\n\n%1", reply->errorString()));
|
||||
co_return;
|
||||
}
|
||||
|
||||
QByteArray str = reply->readAll();
|
||||
const QByteArray str = reply->readAll();
|
||||
|
||||
// for some god forsaken reason, the version string comes back as raw
|
||||
// bytes, ex: \xFF\xFE{\x00\"\x00""A\x00s\x00s\x00""e\x00m\x00 so we
|
||||
|
@ -110,226 +104,150 @@ void AssetUpdater::update()
|
|||
reassmbled += t;
|
||||
}
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(reassmbled.toUtf8());
|
||||
remoteDalamudVersion = doc["AssemblyVersion"].toString();
|
||||
remoteRuntimeVersion = doc["RuntimeVersion"].toString();
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(reassmbled.toUtf8());
|
||||
remoteDalamudVersion = doc[QLatin1String("AssemblyVersion")].toString();
|
||||
remoteRuntimeVersion = doc[QLatin1String("RuntimeVersion")].toString();
|
||||
|
||||
qInfo() << "Latest Dalamud version reported: " << remoteDalamudVersion;
|
||||
qInfo() << "Latest NET runtime reported: " << remoteRuntimeVersion;
|
||||
|
||||
checkIfCheckingIsDone();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AssetUpdater::beginInstall()
|
||||
{
|
||||
if (needsDalamudInstall) {
|
||||
bool success = !JlCompress::extractDir(tempDir.path() + "/latest.zip", dalamudDir.absoluteFilePath(m_profile.dalamudChannelName())).empty();
|
||||
|
||||
if (!success) {
|
||||
// TODO: handle failure here
|
||||
qInfo() << "Failed to install Dalamud!";
|
||||
} else {
|
||||
needsDalamudInstall = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsRuntimeInstall) {
|
||||
bool success = !JlCompress::extractDir(tempDir.path() + "/dotnet-core.zip", dalamudRuntimeDir.absolutePath()).empty();
|
||||
|
||||
success |= !JlCompress::extractDir(tempDir.path() + "/dotnet-desktop.zip", dalamudRuntimeDir.absolutePath()).empty();
|
||||
|
||||
if (!success) {
|
||||
qInfo() << "Failed to install dotnet!";
|
||||
} else {
|
||||
QFile file(dalamudRuntimeDir.absoluteFilePath("runtime.ver"));
|
||||
file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
file.write(remoteRuntimeVersion.toUtf8());
|
||||
file.close();
|
||||
|
||||
needsRuntimeInstall = false;
|
||||
}
|
||||
}
|
||||
|
||||
checkIfFinished();
|
||||
}
|
||||
|
||||
void AssetUpdater::checkIfDalamudAssetsDone()
|
||||
{
|
||||
if (dalamudAssetNeededFilenames.empty()) {
|
||||
qInfo() << "Finished downloading Dalamud assets.";
|
||||
|
||||
m_profile.setDalamudAssetVersion(remoteDalamudAssetVersion);
|
||||
|
||||
QFile file(dalamudAssetDir.absoluteFilePath("asset.ver"));
|
||||
file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
file.write(QString::number(remoteDalamudAssetVersion).toUtf8());
|
||||
file.close();
|
||||
|
||||
checkIfFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void AssetUpdater::checkIfFinished()
|
||||
{
|
||||
if (doneDownloadingDalamud && doneDownloadingRuntimeCore && doneDownloadingRuntimeDesktop && dalamudAssetNeededFilenames.empty()) {
|
||||
if (needsRuntimeInstall || needsDalamudInstall) {
|
||||
beginInstall();
|
||||
} else {
|
||||
Q_EMIT finishedUpdating();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssetUpdater::checkIfCheckingIsDone()
|
||||
{
|
||||
if (remoteDalamudVersion.isEmpty() || remoteRuntimeVersion.isEmpty() || remoteDalamudAssetVersion == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// now that we got all the information we need, let's check if anything is
|
||||
// updateable
|
||||
|
||||
Q_EMIT launcher.stageChanged("Starting Dalamud update...");
|
||||
|
||||
// dalamud injector / net runtime
|
||||
if (m_profile.runtimeVersion() != remoteRuntimeVersion) {
|
||||
needsRuntimeInstall = true;
|
||||
|
||||
// core
|
||||
{
|
||||
QNetworkRequest request(dotnetRuntimePackageURL.arg(remoteRuntimeVersion));
|
||||
|
||||
auto reply = launcher.mgr->get(request);
|
||||
connect(reply, &QNetworkReply::finished, [this, reply] {
|
||||
qInfo() << "Dotnet-core finished downloading!";
|
||||
|
||||
Q_EMIT launcher.stageChanged("Updating Dotnet-core...");
|
||||
|
||||
QFile file(tempDir.path() + "/dotnet-core.zip");
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(reply->readAll());
|
||||
file.close();
|
||||
|
||||
doneDownloadingRuntimeCore = true;
|
||||
|
||||
checkIfFinished();
|
||||
});
|
||||
}
|
||||
|
||||
// desktop
|
||||
{
|
||||
QNetworkRequest request(dotnetDesktopPackageURL.arg(remoteRuntimeVersion));
|
||||
|
||||
auto reply = launcher.mgr->get(request);
|
||||
connect(reply, &QNetworkReply::finished, [this, reply] {
|
||||
qInfo() << "Dotnet-desktop finished downloading!";
|
||||
|
||||
Q_EMIT launcher.stageChanged("Updating Dotnet-desktop...");
|
||||
|
||||
QFile file(tempDir.path() + "/dotnet-desktop.zip");
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(reply->readAll());
|
||||
file.close();
|
||||
|
||||
doneDownloadingRuntimeDesktop = true;
|
||||
|
||||
checkIfFinished();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
doneDownloadingRuntimeCore = true;
|
||||
doneDownloadingRuntimeDesktop = true;
|
||||
needsRuntimeInstall = false;
|
||||
|
||||
checkIfFinished();
|
||||
}
|
||||
qInfo() << "Latest Dalamud version reported:" << remoteDalamudVersion << "local:" << m_profile.dalamudVersion();
|
||||
qInfo() << "Latest NET runtime reported:" << remoteRuntimeVersion;
|
||||
|
||||
if (remoteDalamudVersion != m_profile.dalamudVersion()) {
|
||||
qInfo() << "Downloading Dalamud...";
|
||||
|
||||
needsDalamudInstall = true;
|
||||
|
||||
QNetworkRequest request(dalamudLatestPackageUrl(chosenChannel));
|
||||
|
||||
auto reply = launcher.mgr->get(request);
|
||||
connect(reply, &QNetworkReply::finished, [this, reply] {
|
||||
qInfo() << "Dalamud finished downloading!";
|
||||
|
||||
Q_EMIT launcher.stageChanged("Updating Dalamud...");
|
||||
|
||||
QFile file(tempDir.path() + "/latest.zip");
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(reply->readAll());
|
||||
file.close();
|
||||
|
||||
doneDownloadingDalamud = true;
|
||||
|
||||
m_profile.setDalamudVersion(remoteDalamudVersion);
|
||||
|
||||
checkIfFinished();
|
||||
});
|
||||
} else {
|
||||
qInfo() << "No need to update Dalamud.";
|
||||
|
||||
doneDownloadingDalamud = true;
|
||||
needsDalamudInstall = false;
|
||||
|
||||
checkIfFinished();
|
||||
co_await installDalamud();
|
||||
}
|
||||
|
||||
// dalamud assets
|
||||
if (remoteDalamudAssetVersion != m_profile.dalamudAssetVersion()) {
|
||||
qInfo() << "Dalamud assets out of date.";
|
||||
if (m_profile.runtimeVersion() != remoteRuntimeVersion) {
|
||||
qInfo() << "Downloading Runtime...";
|
||||
|
||||
Q_EMIT launcher.stageChanged("Updating Dalamud assets...");
|
||||
co_await installRuntime();
|
||||
}
|
||||
}
|
||||
|
||||
dalamudAssetNeededFilenames.clear();
|
||||
QCoro::Task<> AssetUpdater::installDalamudAssets()
|
||||
{
|
||||
Q_EMIT launcher.stageChanged(i18n("Updating Dalamud assets..."));
|
||||
|
||||
for (auto assetObject : remoteDalamudAssetArray) {
|
||||
{
|
||||
dalamudAssetNeededFilenames.append(assetObject.toObject()["FileName"].toString());
|
||||
QFutureSynchronizer<void> synchronizer;
|
||||
|
||||
QNetworkRequest assetRequest(assetObject.toObject()["Url"].toString());
|
||||
auto assetReply = launcher.mgr->get(assetRequest);
|
||||
for (const auto &assetObject : remoteDalamudAssetArray) {
|
||||
const QNetworkRequest assetRequest(assetObject.toObject()[QLatin1String("Url")].toString());
|
||||
const auto assetReply = launcher.mgr->get(assetRequest);
|
||||
|
||||
connect(assetReply, &QNetworkReply::finished, [this, assetReply, assetObject = assetObject.toObject()] {
|
||||
const QString fileName = assetObject["FileName"].toString();
|
||||
const QString dirPath = fileName.left(fileName.lastIndexOf("/"));
|
||||
const auto future = QtFuture::connect(assetReply, &QNetworkReply::finished).then([this, assetReply, assetObject] {
|
||||
const QString fileName = assetObject.toObject()[QLatin1String("FileName")].toString();
|
||||
const QString dirPath = fileName.left(fileName.lastIndexOf(QLatin1Char('/')));
|
||||
|
||||
const QString path = dalamudAssetDir.absoluteFilePath(dirPath);
|
||||
|
||||
if (!QDir().exists(path))
|
||||
QDir().mkpath(path);
|
||||
|
||||
QFile file(dalamudAssetDir.absoluteFilePath(assetObject["FileName"].toString()));
|
||||
QFile file(dalamudAssetDir.absoluteFilePath(assetObject.toObject()[QLatin1String("FileName")].toString()));
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(assetReply->readAll());
|
||||
file.close();
|
||||
|
||||
dalamudAssetNeededFilenames.removeOne(assetObject["FileName"].toString());
|
||||
checkIfDalamudAssetsDone();
|
||||
});
|
||||
|
||||
synchronizer.addFuture(future);
|
||||
}
|
||||
|
||||
co_await QtConcurrent::run([&synchronizer] {
|
||||
synchronizer.waitForFinished();
|
||||
});
|
||||
|
||||
qInfo() << "Finished downloading Dalamud assets.";
|
||||
|
||||
m_profile.setDalamudAssetVersion(remoteDalamudAssetVersion);
|
||||
|
||||
QFile file(dalamudAssetDir.absoluteFilePath(QStringLiteral("asset.ver")));
|
||||
file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
file.write(QString::number(remoteDalamudAssetVersion).toUtf8());
|
||||
file.close();
|
||||
}
|
||||
|
||||
QCoro::Task<> AssetUpdater::installDalamud()
|
||||
{
|
||||
Q_EMIT launcher.stageChanged(i18n("Updating Dalamud..."));
|
||||
|
||||
const QNetworkRequest request(dalamudLatestPackageUrl(chosenChannel));
|
||||
|
||||
const auto reply = launcher.mgr->get(request);
|
||||
co_await reply;
|
||||
|
||||
qInfo() << "Dalamud finished downloading!";
|
||||
|
||||
QFile file(tempDir.path() + QStringLiteral("/latest.zip"));
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(reply->readAll());
|
||||
file.close();
|
||||
|
||||
const bool success =
|
||||
!JlCompress::extractDir(tempDir.path() + QLatin1String("/latest.zip"), dalamudDir.absoluteFilePath(m_profile.dalamudChannelName())).empty();
|
||||
|
||||
if (!success) {
|
||||
// TODO: handle failure here
|
||||
qInfo() << "Failed to install Dalamud!";
|
||||
}
|
||||
|
||||
m_profile.setDalamudVersion(remoteDalamudVersion);
|
||||
}
|
||||
|
||||
QCoro::Task<> AssetUpdater::installRuntime()
|
||||
{
|
||||
Q_EMIT launcher.stageChanged(i18n("Updating .NET Runtime..."));
|
||||
|
||||
// core
|
||||
{
|
||||
const QNetworkRequest request(dotnetRuntimePackageURL.arg(remoteRuntimeVersion));
|
||||
|
||||
const auto reply = launcher.mgr->get(request);
|
||||
co_await reply;
|
||||
|
||||
qInfo() << "Dotnet-core finished downloading!";
|
||||
|
||||
QFile file(tempDir.path() + QStringLiteral("/dotnet-core.zip"));
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(reply->readAll());
|
||||
file.close();
|
||||
}
|
||||
|
||||
// desktop
|
||||
{
|
||||
const QNetworkRequest request(dotnetDesktopPackageURL.arg(remoteRuntimeVersion));
|
||||
|
||||
const auto reply = launcher.mgr->get(request);
|
||||
co_await reply;
|
||||
|
||||
qInfo() << "Dotnet-desktop finished downloading!";
|
||||
|
||||
QFile file(tempDir.path() + QStringLiteral("/dotnet-desktop.zip"));
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(reply->readAll());
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool success = !JlCompress::extractDir(tempDir.path() + QStringLiteral("/dotnet-core.zip"), dalamudRuntimeDir.absolutePath()).empty();
|
||||
success |= !JlCompress::extractDir(tempDir.path() + QStringLiteral("/dotnet-desktop.zip"), dalamudRuntimeDir.absolutePath()).empty();
|
||||
|
||||
if (!success) {
|
||||
qInfo() << "Failed to install dotnet!";
|
||||
} else {
|
||||
dalamudAssetNeededFilenames.clear();
|
||||
|
||||
qInfo() << "Dalamud assets up to date.";
|
||||
|
||||
checkIfFinished();
|
||||
QFile file(dalamudRuntimeDir.absoluteFilePath(QStringLiteral("runtime.ver")));
|
||||
file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
file.write(remoteRuntimeVersion.toUtf8());
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
static const QMap<Profile::DalamudChannel, QString> channelToDistribPrefix = {{Profile::DalamudChannel::Stable, "/"},
|
||||
{Profile::DalamudChannel::Staging, "stg/"},
|
||||
{Profile::DalamudChannel::Net5, "net5/"}};
|
||||
static const QMap<Profile::DalamudChannel, QString> channelToDistribPrefix = {{Profile::DalamudChannel::Stable, QStringLiteral("/")},
|
||||
{Profile::DalamudChannel::Staging, QStringLiteral("stg/")},
|
||||
{Profile::DalamudChannel::Net5, QStringLiteral("net5/")}};
|
||||
|
||||
QUrl AssetUpdater::dalamudVersionManifestUrl(const Profile::DalamudChannel channel) const
|
||||
{
|
||||
QUrl url;
|
||||
url.setScheme("https");
|
||||
url.setScheme(QStringLiteral("https"));
|
||||
url.setHost(launcher.dalamudDistribServer());
|
||||
url.setPath(QStringLiteral("/dalamud-distrib/%1version").arg(channelToDistribPrefix[channel]));
|
||||
|
||||
|
@ -339,7 +257,7 @@ QUrl AssetUpdater::dalamudVersionManifestUrl(const Profile::DalamudChannel chann
|
|||
QUrl AssetUpdater::dalamudLatestPackageUrl(Profile::DalamudChannel channel) const
|
||||
{
|
||||
QUrl url;
|
||||
url.setScheme("https");
|
||||
url.setScheme(QStringLiteral("https"));
|
||||
url.setHost(launcher.dalamudDistribServer());
|
||||
url.setPath(QStringLiteral("/dalamud-distrib/%1latest.zip").arg(channelToDistribPrefix[channel]));
|
||||
|
||||
|
@ -349,7 +267,7 @@ QUrl AssetUpdater::dalamudLatestPackageUrl(Profile::DalamudChannel channel) cons
|
|||
QUrl AssetUpdater::dalamudAssetManifestUrl() const
|
||||
{
|
||||
QUrl url;
|
||||
url.setScheme("https");
|
||||
url.setScheme(QStringLiteral("https"));
|
||||
url.setHost(launcher.dalamudDistribServer());
|
||||
url.setPath(QStringLiteral("/DalamudAssets/asset.json"));
|
||||
|
||||
|
|
|
@ -71,6 +71,22 @@ void LauncherCore::launchGame(const Profile &profile, const LoginAuth &auth)
|
|||
#endif
|
||||
}
|
||||
|
||||
QCoro::Task<> LauncherCore::beginLogin(LoginInformation &info)
|
||||
{
|
||||
qDebug() << "Logging in, performing asset update check.";
|
||||
|
||||
auto assetUpdater = new AssetUpdater(*info.profile, *this, this);
|
||||
co_await assetUpdater->update();
|
||||
|
||||
if (info.profile->account()->isSapphire()) {
|
||||
m_sapphireLauncher->login(info.profile->account()->lobbyUrl(), info);
|
||||
} else {
|
||||
m_squareBoot->checkGateStatus(info);
|
||||
}
|
||||
|
||||
assetUpdater->deleteLater();
|
||||
}
|
||||
|
||||
void LauncherCore::beginGameExecutable(const Profile &profile, const LoginAuth &auth)
|
||||
{
|
||||
Q_EMIT stageChanged(i18n("Launching game..."));
|
||||
|
@ -148,7 +164,7 @@ void LauncherCore::beginDalamudGame(const QString &gameExecutablePath, const Pro
|
|||
#endif
|
||||
dalamudProcess->setProcessEnvironment(env);
|
||||
|
||||
auto args = getGameArgs(profile, auth);
|
||||
const auto args = getGameArgs(profile, auth);
|
||||
|
||||
launchExecutable(profile,
|
||||
dalamudProcess,
|
||||
|
@ -312,8 +328,6 @@ void LauncherCore::launchExecutable(const Profile &profile, QProcess *process, c
|
|||
|
||||
arguments.push_back(highestVersion.absoluteFilePath(QStringLiteral("proton")));
|
||||
arguments.push_back(QStringLiteral("run"));
|
||||
|
||||
qInfo() << arguments << env.toStringList();
|
||||
} else {
|
||||
env.insert(QStringLiteral("WINEPREFIX"), profile.winePrefixPath());
|
||||
|
||||
|
@ -419,11 +433,7 @@ void LauncherCore::addRegistryKey(const Profile &settings, QString key, QString
|
|||
|
||||
void LauncherCore::login(Profile *profile, const QString &username, const QString &password, const QString &oneTimePassword)
|
||||
{
|
||||
qDebug() << "Logging in, performing asset update check.";
|
||||
|
||||
auto assetUpdater = new AssetUpdater(*profile, *this, this);
|
||||
connect(assetUpdater, &AssetUpdater::finishedUpdating, this, [this, assetUpdater, profile, username, password, oneTimePassword] {
|
||||
qDebug() << "Assets done updating!";
|
||||
Q_ASSERT(profile != nullptr);
|
||||
|
||||
auto loginInformation = new LoginInformation(this);
|
||||
loginInformation->profile = profile;
|
||||
|
@ -435,15 +445,7 @@ void LauncherCore::login(Profile *profile, const QString &username, const QStrin
|
|||
profile->account()->setPassword(password);
|
||||
}
|
||||
|
||||
if (loginInformation->profile->account()->isSapphire()) {
|
||||
m_sapphireLauncher->login(loginInformation->profile->account()->lobbyUrl(), *loginInformation);
|
||||
} else {
|
||||
m_squareBoot->checkGateStatus(loginInformation);
|
||||
}
|
||||
|
||||
assetUpdater->deleteLater();
|
||||
});
|
||||
assetUpdater->update();
|
||||
beginLogin(*loginInformation);
|
||||
}
|
||||
|
||||
bool LauncherCore::autoLogin(Profile &profile)
|
||||
|
|
|
@ -37,10 +37,10 @@ Patcher::Patcher(LauncherCore &launcher, const QString &baseDirectory, GameData
|
|||
Q_EMIT m_launcher.stageChanged(i18n("Checking %1 version.", getBaseString()));
|
||||
}
|
||||
|
||||
QCoro::Task<> Patcher::patch(const QString &patchList)
|
||||
QCoro::Task<bool> Patcher::patch(const QString &patchList)
|
||||
{
|
||||
if (patchList.isEmpty()) {
|
||||
co_return;
|
||||
co_return false;
|
||||
}
|
||||
|
||||
Q_EMIT m_launcher.stageIndeterminate();
|
||||
|
@ -114,6 +114,8 @@ QCoro::Task<> Patcher::patch(const QString &patchList)
|
|||
|
||||
processPatch(patch);
|
||||
}
|
||||
|
||||
co_return true;
|
||||
}
|
||||
|
||||
void Patcher::processPatch(const QueuedPatch &patch)
|
||||
|
|
|
@ -47,16 +47,21 @@ QCoro::Task<> SquareBoot::bootCheck(const LoginInformation &info)
|
|||
const auto reply = window.mgr->get(request);
|
||||
co_await reply;
|
||||
|
||||
const QString patchList = reply->readAll();
|
||||
if (!patchList.isEmpty()) {
|
||||
patcher = new Patcher(window, info.profile->gamePath() + QStringLiteral("/boot"), *info.profile->bootData(), this);
|
||||
co_await patcher->patch(reply->readAll());
|
||||
|
||||
const bool hasPatched = co_await patcher->patch(reply->readAll());
|
||||
if (hasPatched) {
|
||||
// update game version information
|
||||
info.profile->readGameVersion();
|
||||
}
|
||||
patcher->deleteLater();
|
||||
}
|
||||
|
||||
launcher.login(info);
|
||||
}
|
||||
|
||||
QCoro::Task<> SquareBoot::checkGateStatus(LoginInformation *info)
|
||||
QCoro::Task<> SquareBoot::checkGateStatus(const LoginInformation &info)
|
||||
{
|
||||
Q_EMIT window.stageChanged(i18n("Checking gate..."));
|
||||
qDebug() << "Checking gate...";
|
||||
|
@ -70,7 +75,7 @@ QCoro::Task<> SquareBoot::checkGateStatus(LoginInformation *info)
|
|||
QNetworkRequest request(url);
|
||||
|
||||
// TODO: really?
|
||||
window.buildRequest(*info->profile, request);
|
||||
window.buildRequest(*info.profile, request);
|
||||
|
||||
const auto reply = window.mgr->get(request);
|
||||
co_await reply;
|
||||
|
@ -79,7 +84,7 @@ QCoro::Task<> SquareBoot::checkGateStatus(LoginInformation *info)
|
|||
const bool isGateOpen = !document.isEmpty() && document.object()[QLatin1String("status")].toInt() != 0;
|
||||
|
||||
if (isGateOpen) {
|
||||
bootCheck(*info);
|
||||
bootCheck(info);
|
||||
} else {
|
||||
Q_EMIT window.loginError(i18n("The login gate is closed, the game may be under maintenance.\n\n%1", reply->errorString()));
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <QNetworkReply>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QUrlQuery>
|
||||
#include <QtConcurrentMap>
|
||||
#include <qcorofuture.h>
|
||||
#include <qcoronetworkreply.h>
|
||||
|
||||
#include "account.h"
|
||||
|
@ -99,6 +101,7 @@ QCoro::Task<> SquareLauncher::login(const LoginInformation &info)
|
|||
if (storedResult == std::nullopt) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
const auto [stored, referer] = *storedResult;
|
||||
|
||||
QUrlQuery postData;
|
||||
|
@ -168,7 +171,7 @@ QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info)
|
|||
request.setRawHeader(QByteArrayLiteral("User-Agent"), QByteArrayLiteral("FFXIV PATCH CLIENT"));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/x-www-form-urlencoded"));
|
||||
|
||||
QString report = QStringLiteral("%1=%2").arg(info.profile->bootVersion(), getBootHash(info));
|
||||
QString report = QStringLiteral("%1=%2").arg(info.profile->bootVersion(), co_await getBootHash(info));
|
||||
for (int i = 0; i < auth.maxExpansion; i++) {
|
||||
if (i < static_cast<int>(info.profile->numInstalledExpansions())) {
|
||||
report += QStringLiteral("\nex%1\t%2").arg(QString::number(i + 1), info.profile->expansionVersion(i));
|
||||
|
@ -177,8 +180,6 @@ QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info)
|
|||
}
|
||||
}
|
||||
|
||||
qInfo() << report;
|
||||
|
||||
const auto reply = window.mgr->post(request, report.toUtf8());
|
||||
co_await reply;
|
||||
|
||||
|
@ -186,10 +187,15 @@ QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info)
|
|||
if (reply->rawHeaderList().contains(QByteArrayLiteral("X-Patch-Unique-Id"))) {
|
||||
const QString body = reply->readAll();
|
||||
|
||||
if (!body.isEmpty()) {
|
||||
patcher = new Patcher(window, info.profile->gamePath() + QStringLiteral("/game"), *info.profile->gameData(), this);
|
||||
co_await patcher->patch(body);
|
||||
|
||||
const bool hasPatched = co_await patcher->patch(body);
|
||||
if (hasPatched) {
|
||||
// re-read game version if it has updated
|
||||
info.profile->readGameVersion();
|
||||
}
|
||||
patcher->deleteLater();
|
||||
}
|
||||
|
||||
auth.SID = reply->rawHeader(QByteArrayLiteral("X-Patch-Unique-Id"));
|
||||
|
||||
|
@ -209,7 +215,7 @@ QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info)
|
|||
}
|
||||
}
|
||||
|
||||
QString SquareLauncher::getBootHash(const LoginInformation &info)
|
||||
QCoro::Task<QString> SquareLauncher::getBootHash(const LoginInformation &info)
|
||||
{
|
||||
const QList<QString> fileList = {QStringLiteral("ffxivboot.exe"),
|
||||
QStringLiteral("ffxivboot64.exe"),
|
||||
|
@ -218,13 +224,20 @@ QString SquareLauncher::getBootHash(const LoginInformation &info)
|
|||
QStringLiteral("ffxivupdater.exe"),
|
||||
QStringLiteral("ffxivupdater64.exe")};
|
||||
|
||||
const auto hashFuture = QtConcurrent::mapped(fileList, [&info](const auto &filename) -> QString {
|
||||
return getFileHash(info.profile->gamePath() + QStringLiteral("/boot/") + filename);
|
||||
});
|
||||
|
||||
co_await hashFuture;
|
||||
const QList<QString> hashes = hashFuture.results();
|
||||
|
||||
QString result;
|
||||
for (int i = 0; i < fileList.count(); i++) {
|
||||
result += fileList[i] + QStringLiteral("/") + getFileHash(info.profile->gamePath() + QStringLiteral("/boot/") + fileList[i]);
|
||||
result += fileList[i] + QStringLiteral("/") + hashes[i];
|
||||
|
||||
if (i != fileList.length() - 1)
|
||||
result += QStringLiteral(",");
|
||||
}
|
||||
|
||||
return result;
|
||||
co_return result;
|
||||
}
|
Loading…
Add table
Reference in a new issue