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

Use coroutines in square enix login process

This commit is contained in:
Joshua Goins 2023-09-16 18:52:12 -04:00
parent 6dc8041c71
commit b068d34001
4 changed files with 116 additions and 104 deletions

View file

@ -3,6 +3,8 @@
#pragma once
#include <qcorotask.h>
#include "patcher.h"
class SquareLauncher;
@ -15,9 +17,9 @@ class SquareBoot : public QObject
public:
SquareBoot(LauncherCore &window, SquareLauncher &launcher, QObject *parent = nullptr);
Q_INVOKABLE void checkGateStatus(LoginInformation *info);
QCoro::Task<> checkGateStatus(LoginInformation *info);
void bootCheck(const LoginInformation &info);
QCoro::Task<> bootCheck(const LoginInformation &info);
private:
Patcher *patcher = nullptr;

View file

@ -3,6 +3,8 @@
#pragma once
#include <qcorotask.h>
#include "launchercore.h"
#include "patcher.h"
@ -12,18 +14,19 @@ class SquareLauncher : public QObject
public:
explicit SquareLauncher(LauncherCore &window, QObject *parent = nullptr);
void getStored(const LoginInformation &info);
using StoredInfo = std::pair<QString, QUrl>;
QCoro::Task<std::optional<StoredInfo>> getStored(const LoginInformation &info);
void login(const LoginInformation &info, const QUrl &referer);
QCoro::Task<> login(const LoginInformation &info);
void registerSession(const LoginInformation &info);
QCoro::Task<> registerSession(const LoginInformation &info);
private:
QString getBootHash(const LoginInformation &info);
Patcher *patcher = nullptr;
QString stored, SID, username;
QString SID, username;
LoginAuth auth;
LauncherCore &window;

View file

@ -10,6 +10,7 @@
#include <QStandardPaths>
#include <QUrlQuery>
#include <physis.hpp>
#include <qcoronetworkreply.h>
#include "account.h"
#include "squarelauncher.h"
@ -21,7 +22,7 @@ SquareBoot::SquareBoot(LauncherCore &window, SquareLauncher &launcher, QObject *
{
}
void SquareBoot::bootCheck(const LoginInformation &info)
QCoro::Task<> SquareBoot::bootCheck(const LoginInformation &info)
{
Q_EMIT window.stageChanged(i18n("Checking for launcher updates..."));
qDebug() << "Performing boot check...";
@ -30,7 +31,7 @@ void SquareBoot::bootCheck(const LoginInformation &info)
connect(patcher, &Patcher::done, [this, &info] {
info.profile->readGameVersion();
launcher.getStored(info);
launcher.login(info);
});
QUrlQuery query;
@ -52,14 +53,14 @@ void SquareBoot::bootCheck(const LoginInformation &info)
request.setRawHeader("Host", QStringLiteral("patch-bootver.%1").arg(window.squareEnixServer()).toUtf8());
auto reply = window.mgr->get(request);
connect(reply, &QNetworkReply::finished, [this, reply] {
const QString response = reply->readAll();
co_await reply;
patcher->processPatchList(*window.mgr, response);
});
const QString response = reply->readAll();
patcher->processPatchList(*window.mgr, response);
}
void SquareBoot::checkGateStatus(LoginInformation *info)
QCoro::Task<> SquareBoot::checkGateStatus(LoginInformation *info)
{
Q_EMIT window.stageChanged(i18n("Checking gate..."));
qDebug() << "Checking gate...";
@ -76,15 +77,15 @@ void SquareBoot::checkGateStatus(LoginInformation *info)
window.buildRequest(*info->profile, request);
auto reply = window.mgr->get(request);
connect(reply, &QNetworkReply::finished, [this, reply, info] {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
co_await reply;
const bool isGateOpen = !document.isEmpty() && document.object()["status"].toInt() != 0;
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
if (isGateOpen) {
bootCheck(*info);
} else {
Q_EMIT window.loginError(i18n("The login gate is closed, the game may be under maintenance.\n\n%1", reply->errorString()));
}
});
const bool isGateOpen = !document.isEmpty() && document.object()["status"].toInt() != 0;
if (isGateOpen) {
bootCheck(*info);
} else {
Q_EMIT window.loginError(i18n("The login gate is closed, the game may be under maintenance.\n\n%1", reply->errorString()));
}
}

View file

@ -9,6 +9,7 @@
#include <QNetworkReply>
#include <QRegularExpressionMatch>
#include <QUrlQuery>
#include <qcoronetworkreply.h>
#include "account.h"
#include "launchercore.h"
@ -31,7 +32,7 @@ QString getFileHash(const QString &file)
return QString("%1/%2").arg(QString::number(f.size()), hash.result().toHex());
}
void SquareLauncher::getStored(const LoginInformation &info)
QCoro::Task<std::optional<SquareLauncher::StoredInfo>> SquareLauncher::getStored(const LoginInformation &info)
{
Q_EMIT window.stageChanged(i18n("Logging in..."));
@ -62,39 +63,44 @@ void SquareLauncher::getStored(const LoginInformation &info)
auto request = QNetworkRequest(url);
window.buildRequest(*info.profile, request);
QNetworkReply *reply = window.mgr->get(request);
auto reply = window.mgr->get(request);
co_await reply;
connect(reply, &QNetworkReply::finished, [this, &info, reply, url] {
auto str = QString(reply->readAll());
const auto str = QString(reply->readAll());
// fetches Steam username
if (info.profile->account()->license() == Account::GameLicense::WindowsSteam) {
QRegularExpression re(R"lit(<input name=""sqexid"" type=""hidden"" value=""(?<sqexid>.*)""\/>)lit");
QRegularExpressionMatch match = re.match(str);
if (match.hasMatch()) {
username = match.captured(1);
} else {
Q_EMIT window.loginError(i18n("Could not get Steam username, have you attached your account?"));
}
} else {
username = info.username;
}
QRegularExpression re(R"lit(\t<\s*input .* name="_STORED_" value="(?<stored>.*)">)lit");
// fetches Steam username
if (info.profile->account()->license() == Account::GameLicense::WindowsSteam) {
QRegularExpression re(R"lit(<input name=""sqexid"" type=""hidden"" value=""(?<sqexid>.*)""\/>)lit");
QRegularExpressionMatch match = re.match(str);
if (match.hasMatch()) {
stored = match.captured(1);
login(info, url);
username = match.captured(1);
} else {
Q_EMIT window.loginError(
i18n("Square Enix servers refused to confirm session information. The game may be under maintenance, try the official launcher."));
Q_EMIT window.loginError(i18n("Could not get Steam username, have you attached your account?"));
}
});
} else {
username = info.username;
}
QRegularExpression re(R"lit(\t<\s*input .* name="_STORED_" value="(?<stored>.*)">)lit");
QRegularExpressionMatch match = re.match(str);
if (match.hasMatch()) {
co_return StoredInfo{match.captured(1), url};
} else {
Q_EMIT window.loginError(
i18n("Square Enix servers refused to confirm session information. The game may be under maintenance, try the official launcher."));
co_return {};
}
}
void SquareLauncher::login(const LoginInformation &info, const QUrl &referer)
QCoro::Task<> SquareLauncher::login(const LoginInformation &info)
{
const auto storedResult = co_await getStored(info);
if (storedResult == std::nullopt) {
co_return;
}
const auto [stored, referer] = *storedResult;
QUrlQuery postData;
postData.addQueryItem("_STORED_", stored);
postData.addQueryItem("sqexid", info.username);
@ -113,47 +119,47 @@ void SquareLauncher::login(const LoginInformation &info, const QUrl &referer)
request.setRawHeader("Cache-Control", "no-cache");
auto reply = window.mgr->post(request, postData.toString(QUrl::FullyEncoded).toUtf8());
connect(reply, &QNetworkReply::finished, [this, &info, reply] {
auto str = QString(reply->readAll());
co_await reply;
QRegularExpression re(R"lit(window.external.user\("login=auth,ok,(?<launchParams>.*)\);)lit");
QRegularExpressionMatch match = re.match(str);
if (match.hasMatch()) {
const auto parts = match.captured(1).split(',');
auto str = QString(reply->readAll());
const bool terms = parts[3] == "1";
const bool playable = parts[9] == "1";
QRegularExpression re(R"lit(window.external.user\("login=auth,ok,(?<launchParams>.*)\);)lit");
QRegularExpressionMatch match = re.match(str);
if (match.hasMatch()) {
const auto parts = match.captured(1).split(',');
if (!playable) {
Q_EMIT window.loginError(i18n("Your account is unplayable. Check that you have the correct license, and a valid subscription."));
return;
}
const bool terms = parts[3] == "1";
const bool playable = parts[9] == "1";
if (!terms) {
Q_EMIT window.loginError(i18n("Your account is unplayable. You need to accept the terms of service from the official launcher first."));
return;
}
SID = parts[1];
auth.region = parts[5].toInt();
auth.maxExpansion = parts[13].toInt();
registerSession(info);
} else {
QRegularExpression re(R"lit(window.external.user\("login=auth,ng,err,(?<launchParams>.*)\);)lit");
QRegularExpressionMatch match = re.match(str);
const auto parts = match.captured(1).split(',');
// there's a stray quote at the end of the error string, so let's remove that
QString errorStr = match.captured(1).chopped(1);
Q_EMIT window.loginError(errorStr);
if (!playable) {
Q_EMIT window.loginError(i18n("Your account is unplayable. Check that you have the correct license, and a valid subscription."));
co_return;
}
});
if (!terms) {
Q_EMIT window.loginError(i18n("Your account is unplayable. You need to accept the terms of service from the official launcher first."));
co_return;
}
SID = parts[1];
auth.region = parts[5].toInt();
auth.maxExpansion = parts[13].toInt();
registerSession(info);
} else {
QRegularExpression re(R"lit(window.external.user\("login=auth,ng,err,(?<launchParams>.*)\);)lit");
QRegularExpressionMatch match = re.match(str);
const auto parts = match.captured(1).split(',');
// there's a stray quote at the end of the error string, so let's remove that
QString errorStr = match.captured(1).chopped(1);
Q_EMIT window.loginError(errorStr);
}
}
void SquareLauncher::registerSession(const LoginInformation &info)
QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info)
{
QUrl url;
url.setScheme("https");
@ -177,35 +183,35 @@ void SquareLauncher::registerSession(const LoginInformation &info)
}
auto reply = window.mgr->post(request, report.toUtf8());
connect(reply, &QNetworkReply::finished, [this, &info, reply] {
if (reply->error() == QNetworkReply::NoError) {
if (reply->rawHeaderList().contains("X-Patch-Unique-Id")) {
QString body = reply->readAll();
co_await reply;
patcher = new Patcher(window, info.profile->gamePath() + "/game", info.profile->gameData, this);
connect(patcher, &Patcher::done, [this, &info, reply] {
info.profile->readGameVersion();
if (reply->error() == QNetworkReply::NoError) {
if (reply->rawHeaderList().contains("X-Patch-Unique-Id")) {
QString body = reply->readAll();
auth.SID = reply->rawHeader("X-Patch-Unique-Id");
patcher = new Patcher(window, info.profile->gamePath() + "/game", info.profile->gameData, this);
connect(patcher, &Patcher::done, [this, &info, reply] {
info.profile->readGameVersion();
window.launchGame(*info.profile, auth);
});
auth.SID = reply->rawHeader("X-Patch-Unique-Id");
patcher->processPatchList(*window.mgr, body);
} else {
Q_EMIT window.loginError(i18n("Fatal error, request was successful but X-Patch-Unique-Id was not recieved."));
}
window.launchGame(*info.profile, auth);
});
patcher->processPatchList(*window.mgr, body);
} else {
if (reply->error() == QNetworkReply::SslHandshakeFailedError) {
Q_EMIT window.loginError(
i18n("SSL handshake error detected. If you are using OpenSUSE or Fedora, try running `update-crypto-policies --set LEGACY`."));
} else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 405) {
Q_EMIT window.loginError(i18n("The game failed the anti-tamper check. Restore the game to the original state and try updating again."));
} else {
Q_EMIT window.loginError(i18n("Unknown error when registering the session."));
}
Q_EMIT window.loginError(i18n("Fatal error, request was successful but X-Patch-Unique-Id was not recieved."));
}
});
} else {
if (reply->error() == QNetworkReply::SslHandshakeFailedError) {
Q_EMIT window.loginError(
i18n("SSL handshake error detected. If you are using OpenSUSE or Fedora, try running `update-crypto-policies --set LEGACY`."));
} else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 405) {
Q_EMIT window.loginError(i18n("The game failed the anti-tamper check. Restore the game to the original state and try updating again."));
} else {
Q_EMIT window.loginError(i18n("Unknown error when registering the session."));
}
}
}
QString SquareLauncher::getBootHash(const LoginInformation &info)