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

Add better logging

Astra's own logs are now stored in a rotating log, the default message
format is improved. The client and DXVK now dump their logs in the same
place instead of in the game directory.
This commit is contained in:
Joshua Goins 2023-10-08 18:02:02 -04:00
parent 2662b0e0bb
commit c05311fccc
22 changed files with 299 additions and 25 deletions

View file

@ -34,6 +34,7 @@ include(ECMPoQmTools)
include(KDEGitCommitHooks) include(KDEGitCommitHooks)
include(KDEClangFormat) include(KDEClangFormat)
include(ECMAddTests) include(ECMAddTests)
include(ECMQtDeclareLoggingCategory)
ecm_setup_version(${PROJECT_VERSION} ecm_setup_version(${PROJECT_VERSION}
VARIABLE_PREFIX ASTRA VARIABLE_PREFIX ASTRA

View file

@ -3,6 +3,30 @@
add_library(astra_static STATIC) add_library(astra_static STATIC)
ecm_qt_declare_logging_category(astra_static
HEADER astra_log.h
IDENTIFIER ASTRA_LOG
CATEGORY_NAME zone.xiv.astra
DESCRIPTION "Astra logs"
EXPORT ASTRA
)
ecm_qt_declare_logging_category(astra_static
HEADER astra_http_log.h
IDENTIFIER ASTRA_HTTP
CATEGORY_NAME zone.xiv.astra.http
DESCRIPTION "Astra HTTP requests"
EXPORT ASTRA
)
ecm_qt_declare_logging_category(astra_static
HEADER astra_patcher_log.h
IDENTIFIER ASTRA_PATCHER
CATEGORY_NAME zone.xiv.astra.patcher
DESCRIPTION "Astra patcher"
EXPORT ASTRA
)
target_sources(astra_static PRIVATE target_sources(astra_static PRIVATE
include/patchlist.h include/patchlist.h
src/patchlist.cpp) src/patchlist.cpp)
@ -25,7 +49,9 @@ target_sources(astra PRIVATE
include/gameinstaller.h include/gameinstaller.h
include/headline.h include/headline.h
include/launchercore.h include/launchercore.h
include/logger.h
include/patcher.h include/patcher.h
include/processlogger.h
include/profile.h include/profile.h
include/profilemanager.h include/profilemanager.h
include/sapphirelauncher.h include/sapphirelauncher.h
@ -41,8 +67,10 @@ target_sources(astra PRIVATE
src/encryptedarg.cpp src/encryptedarg.cpp
src/gameinstaller.cpp src/gameinstaller.cpp
src/launchercore.cpp src/launchercore.cpp
src/logger.cpp
src/main.cpp src/main.cpp
src/patcher.cpp src/patcher.cpp
src/processlogger.cpp
src/profile.cpp src/profile.cpp
src/profilemanager.cpp src/profilemanager.cpp
src/sapphirelauncher.cpp src/sapphirelauncher.cpp

View file

@ -0,0 +1,6 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
void initializeLogging();

View file

@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QFile>
#include <QObject>
#include <QProcess>
class ProcessLogger : public QObject
{
public:
explicit ProcessLogger(QProcess *process);
private:
QFile file;
};

View file

@ -122,6 +122,7 @@ public:
void readGameData(); void readGameData();
Q_INVOKABLE void readGameVersion(); Q_INVOKABLE void readGameVersion();
void readWineInfo(); void readWineInfo();
void readDalamudInfo();
[[nodiscard]] QString expansionVersionText() const; [[nodiscard]] QString expansionVersionText() const;
[[nodiscard]] QString dalamudVersionText() const; [[nodiscard]] QString dalamudVersionText() const;

View file

@ -4,9 +4,11 @@
#pragma once #pragma once
#include <QDir> #include <QDir>
#include <QNetworkRequest>
namespace Utility namespace Utility
{ {
QDir stateDirectory(); QDir stateDirectory();
QString toWindowsPath(const QDir &dir); QString toWindowsPath(const QDir &dir);
void printRequest(const QString &type, const QNetworkRequest &request);
} }

View file

@ -11,6 +11,7 @@
#include <qt6keychain/keychain.h> #include <qt6keychain/keychain.h>
#include "launchercore.h" #include "launchercore.h"
#include "utility.h"
Account::Account(LauncherCore &launcher, const QString &key, QObject *parent) Account::Account(LauncherCore &launcher, const QString &key, QObject *parent)
: QObject(parent) : QObject(parent)
@ -234,11 +235,15 @@ void Account::fetchAvatar()
url.setPath(QStringLiteral("/character/%1").arg(lodestoneId())); url.setPath(QStringLiteral("/character/%1").arg(lodestoneId()));
QNetworkRequest request(url); QNetworkRequest request(url);
Utility::printRequest(QStringLiteral("GET"), request);
const auto reply = m_launcher.mgr->get(request); const auto reply = m_launcher.mgr->get(request);
connect(reply, &QNetworkReply::finished, [this, filename, reply] { connect(reply, &QNetworkReply::finished, [this, filename, reply] {
auto document = QJsonDocument::fromJson(reply->readAll()); auto document = QJsonDocument::fromJson(reply->readAll());
if (document.isObject()) { if (document.isObject()) {
const QNetworkRequest avatarRequest(document.object()[QLatin1String("Character")].toObject()[QLatin1String("Avatar")].toString()); const QNetworkRequest avatarRequest(document.object()[QLatin1String("Character")].toObject()[QLatin1String("Avatar")].toString());
Utility::printRequest(QStringLiteral("GET"), avatarRequest);
auto avatarReply = m_launcher.mgr->get(avatarRequest); auto avatarReply = m_launcher.mgr->get(avatarRequest);
QObject::connect(avatarReply, &QNetworkReply::finished, [this, filename, avatarReply] { QObject::connect(avatarReply, &QNetworkReply::finished, [this, filename, avatarReply] {
QFile file(filename); QFile file(filename);

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "accountmanager.h" #include "accountmanager.h"
#include "astra_log.h"
#include <KSharedConfig> #include <KSharedConfig>
@ -16,7 +17,10 @@ void AccountManager::load()
auto config = KSharedConfig::openStateConfig(); auto config = KSharedConfig::openStateConfig();
for (const auto &id : config->groupList()) { for (const auto &id : config->groupList()) {
if (id.contains(QLatin1String("account-"))) { if (id.contains(QLatin1String("account-"))) {
auto account = new Account(m_launcher, QString(id).remove(QLatin1String("account-")), this); const QString uuid = QString(id).remove(QLatin1String("account-"));
qInfo(ASTRA_LOG) << "Loading account" << uuid;
auto account = new Account(m_launcher, uuid, this);
m_accounts.append(account); m_accounts.append(account);
} }
} }

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "assetupdater.h" #include "assetupdater.h"
#include "utility.h"
#include <KLocalizedString> #include <KLocalizedString>
#include <QFile> #include <QFile>
@ -56,6 +57,7 @@ QCoro::Task<> AssetUpdater::checkRemoteDalamudAssetVersion()
{ {
// first we want to fetch the list of assets required // first we want to fetch the list of assets required
const QNetworkRequest request(dalamudAssetManifestUrl()); const QNetworkRequest request(dalamudAssetManifestUrl());
Utility::printRequest(QStringLiteral("GET"), request);
const auto reply = launcher.mgr->get(request); const auto reply = launcher.mgr->get(request);
co_await reply; co_await reply;
@ -80,6 +82,7 @@ QCoro::Task<> AssetUpdater::checkRemoteDalamudAssetVersion()
QCoro::Task<> AssetUpdater::checkRemoteDalamudVersion() QCoro::Task<> AssetUpdater::checkRemoteDalamudVersion()
{ {
const QNetworkRequest request(dalamudVersionManifestUrl(m_profile.dalamudChannel())); const QNetworkRequest request(dalamudVersionManifestUrl(m_profile.dalamudChannel()));
Utility::printRequest(QStringLiteral("GET"), request);
remoteDalamudVersion.clear(); remoteDalamudVersion.clear();
remoteRuntimeVersion.clear(); remoteRuntimeVersion.clear();
@ -132,6 +135,8 @@ QCoro::Task<> AssetUpdater::installDalamudAssets()
for (const auto &assetObject : remoteDalamudAssetArray) { for (const auto &assetObject : remoteDalamudAssetArray) {
const QNetworkRequest assetRequest(assetObject.toObject()[QLatin1String("Url")].toString()); const QNetworkRequest assetRequest(assetObject.toObject()[QLatin1String("Url")].toString());
Utility::printRequest(QStringLiteral("GET"), assetRequest);
const auto assetReply = launcher.mgr->get(assetRequest); const auto assetReply = launcher.mgr->get(assetRequest);
const auto future = QtFuture::connect(assetReply, &QNetworkReply::finished).then([this, assetReply, assetObject] { const auto future = QtFuture::connect(assetReply, &QNetworkReply::finished).then([this, assetReply, assetObject] {
@ -171,6 +176,7 @@ QCoro::Task<> AssetUpdater::installDalamud()
Q_EMIT launcher.stageChanged(i18n("Updating Dalamud...")); Q_EMIT launcher.stageChanged(i18n("Updating Dalamud..."));
const QNetworkRequest request(dalamudLatestPackageUrl(chosenChannel)); const QNetworkRequest request(dalamudLatestPackageUrl(chosenChannel));
Utility::printRequest(QStringLiteral("GET"), request);
const auto reply = launcher.mgr->get(request); const auto reply = launcher.mgr->get(request);
co_await reply; co_await reply;
@ -200,6 +206,7 @@ QCoro::Task<> AssetUpdater::installRuntime()
// core // core
{ {
const QNetworkRequest request(dotnetRuntimePackageURL.arg(remoteRuntimeVersion)); const QNetworkRequest request(dotnetRuntimePackageURL.arg(remoteRuntimeVersion));
Utility::printRequest(QStringLiteral("GET"), request);
const auto reply = launcher.mgr->get(request); const auto reply = launcher.mgr->get(request);
co_await reply; co_await reply;
@ -215,6 +222,7 @@ QCoro::Task<> AssetUpdater::installRuntime()
// desktop // desktop
{ {
const QNetworkRequest request(dotnetDesktopPackageURL.arg(remoteRuntimeVersion)); const QNetworkRequest request(dotnetDesktopPackageURL.arg(remoteRuntimeVersion));
Utility::printRequest(QStringLiteral("GET"), request);
const auto reply = launcher.mgr->get(request); const auto reply = launcher.mgr->get(request);
co_await reply; co_await reply;

View file

@ -9,8 +9,10 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <physis.hpp> #include <physis.hpp>
#include "astra_log.h"
#include "launchercore.h" #include "launchercore.h"
#include "profile.h" #include "profile.h"
#include "utility.h"
const QString installerUrl = QStringLiteral("https://download.finalfantasyxiv.com/inst/ffxivsetup.exe"); const QString installerUrl = QStringLiteral("https://download.finalfantasyxiv.com/inst/ffxivsetup.exe");
const QByteArray installerSha256 = QByteArray::fromHex("cf70bfaaf4f429794358ef84acbcbdc4193bee109fa1b6aea81bd4de038e500e"); const QByteArray installerSha256 = QByteArray::fromHex("cf70bfaaf4f429794358ef84acbcbdc4193bee109fa1b6aea81bd4de038e500e");
@ -29,6 +31,8 @@ void GameInstaller::installGame()
QNetworkRequest request((QUrl(installerUrl))); QNetworkRequest request((QUrl(installerUrl)));
auto reply = m_launcher.mgr->get(request); auto reply = m_launcher.mgr->get(request);
Utility::printRequest(QStringLiteral("GET"), request);
QObject::connect(reply, &QNetworkReply::finished, [this, reply, installDirectory] { QObject::connect(reply, &QNetworkReply::finished, [this, reply, installDirectory] {
if (reply->error() != QNetworkReply::NetworkError::NoError) { if (reply->error() != QNetworkReply::NetworkError::NoError) {
Q_EMIT error(i18n("An error has occurred when downloading the installer.\n\n%1", reply->errorString())); Q_EMIT error(i18n("An error has occurred when downloading the installer.\n\n%1", reply->errorString()));
@ -55,5 +59,6 @@ void GameInstaller::installGame()
physis_install_game(fileNameStd.c_str(), installDirectoryStd.c_str()); physis_install_game(fileNameStd.c_str(), installDirectoryStd.c_str());
Q_EMIT installFinished(); Q_EMIT installFinished();
qInfo(ASTRA_LOG) << "Installed game in" << installDirectory;
}); });
} }

View file

@ -18,10 +18,12 @@
#include "account.h" #include "account.h"
#include "assetupdater.h" #include "assetupdater.h"
#include "astra_log.h"
#include "compatibilitytoolinstaller.h" #include "compatibilitytoolinstaller.h"
#include "config.h" #include "config.h"
#include "encryptedarg.h" #include "encryptedarg.h"
#include "launchercore.h" #include "launchercore.h"
#include "processlogger.h"
#include "sapphirelauncher.h" #include "sapphirelauncher.h"
#include "squarelauncher.h" #include "squarelauncher.h"
#include "utility.h" #include "utility.h"
@ -74,8 +76,6 @@ void LauncherCore::launchGame(Profile &profile, const LoginAuth &auth)
QCoro::Task<> LauncherCore::beginLogin(LoginInformation &info) QCoro::Task<> LauncherCore::beginLogin(LoginInformation &info)
{ {
qDebug() << "Logging in, performing asset update check.";
auto assetUpdater = new AssetUpdater(*info.profile, *this, this); auto assetUpdater = new AssetUpdater(*info.profile, *this, this);
co_await assetUpdater->update(); co_await assetUpdater->update();
@ -122,6 +122,8 @@ void LauncherCore::beginVanillaGame(const QString &gameExecutablePath, Profile &
auto args = getGameArgs(profile, auth); auto args = getGameArgs(profile, auth);
new ProcessLogger(gameProcess);
launchExecutable(profile, gameProcess, {gameExecutablePath, args}, true, true); launchExecutable(profile, gameProcess, {gameExecutablePath, args}, true, true);
} }
@ -144,7 +146,7 @@ void LauncherCore::beginDalamudGame(const QString &gameExecutablePath, Profile &
// so we need to match typical XIVQuickLauncher behavior here. Why? I have no clue. // so we need to match typical XIVQuickLauncher behavior here. Why? I have no clue.
const QDir dalamudPluginDir = dalamudUserPluginDir.absoluteFilePath(QStringLiteral("installedPlugins")); const QDir dalamudPluginDir = dalamudUserPluginDir.absoluteFilePath(QStringLiteral("installedPlugins"));
const QString logDir = stateDir.absoluteFilePath(QStringLiteral("logs")); const QString logDir = stateDir.absoluteFilePath(QStringLiteral("log"));
if (!QDir().exists(logDir)) if (!QDir().exists(logDir))
QDir().mkpath(logDir); QDir().mkpath(logDir);
@ -171,6 +173,8 @@ void LauncherCore::beginDalamudGame(const QString &gameExecutablePath, Profile &
#endif #endif
dalamudProcess->setProcessEnvironment(env); dalamudProcess->setProcessEnvironment(env);
new ProcessLogger(dalamudProcess);
const auto args = getGameArgs(profile, auth); const auto args = getGameArgs(profile, auth);
launchExecutable(profile, launchExecutable(profile,
@ -286,6 +290,10 @@ void LauncherCore::launchExecutable(const Profile &profile, QProcess *process, c
env.insert(QStringLiteral("WINEFSYNC"), QString::number(1)); env.insert(QStringLiteral("WINEFSYNC"), QString::number(1));
env.insert(QStringLiteral("WINEFSYNC_FUTEX2"), QString::number(1)); env.insert(QStringLiteral("WINEFSYNC_FUTEX2"), QString::number(1));
} }
const QString logDir = Utility::stateDirectory().absoluteFilePath(QStringLiteral("log"));
env.insert(QStringLiteral("DXVK_LOG_PATH"), logDir);
#endif #endif
#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) #if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
@ -371,7 +379,6 @@ void LauncherCore::launchExecutable(const Profile &profile, QProcess *process, c
process->setWorkingDirectory(profile.gamePath() + QStringLiteral("/game/")); process->setWorkingDirectory(profile.gamePath() + QStringLiteral("/game/"));
process->setProcessEnvironment(env); process->setProcessEnvironment(env);
process->setProcessChannelMode(QProcess::ProcessChannelMode::ForwardedChannels);
process->start(executable, arguments); process->start(executable, arguments);
} }
@ -457,13 +464,13 @@ bool LauncherCore::autoLogin(Profile *profile)
QString otp; QString otp;
if (profile->account()->useOTP()) { if (profile->account()->useOTP()) {
if (!profile->account()->rememberOTP()) { if (!profile->account()->rememberOTP()) {
Q_EMIT loginError("This account does not have an OTP secret set, but requires it for login."); Q_EMIT loginError(i18n("This account does not have an OTP secret set, but requires it for login."));
return false; return false;
} }
otp = profile->account()->getOTP(); otp = profile->account()->getOTP();
if (otp.isEmpty()) { if (otp.isEmpty()) {
Q_EMIT loginError("Failed to generate OTP, review the stored secret."); Q_EMIT loginError(i18n("Failed to generate OTP, review the stored secret."));
return false; return false;
} }
} }
@ -681,6 +688,8 @@ void LauncherCore::refreshNews()
QCoro::Task<> LauncherCore::fetchNews() QCoro::Task<> LauncherCore::fetchNews()
{ {
qInfo(ASTRA_LOG) << "Fetching news...";
QUrlQuery query; QUrlQuery query;
query.addQueryItem(QStringLiteral("lang"), QStringLiteral("en-us")); query.addQueryItem(QStringLiteral("lang"), QStringLiteral("en-us"));
query.addQueryItem(QStringLiteral("media"), QStringLiteral("pcapp")); query.addQueryItem(QStringLiteral("media"), QStringLiteral("pcapp"));
@ -698,6 +707,7 @@ QCoro::Task<> LauncherCore::fetchNews()
QStringLiteral("https://launcher.finalfantasyxiv.com/v600/index.html?rc_lang=%1&time=%2") QStringLiteral("https://launcher.finalfantasyxiv.com/v600/index.html?rc_lang=%1&time=%2")
.arg("en-us", QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd-HH")) .arg("en-us", QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd-HH"))
.toUtf8()); .toUtf8());
Utility::printRequest(QStringLiteral("GET"), request);
auto reply = mgr->get(request); auto reply = mgr->get(request);
co_await reply; co_await reply;
@ -790,7 +800,6 @@ void LauncherCore::setCurrentProfile(Profile *profile)
void LauncherCore::clearAvatarCache() void LauncherCore::clearAvatarCache()
{ {
const auto cacheLocation = QStandardPaths::standardLocations(QStandardPaths::CacheLocation)[0] + QStringLiteral("/avatars"); const auto cacheLocation = QStandardPaths::standardLocations(QStandardPaths::CacheLocation)[0] + QStringLiteral("/avatars");
qInfo() << cacheLocation;
if (QDir(cacheLocation).exists()) { if (QDir(cacheLocation).exists()) {
QDir(cacheLocation).removeRecursively(); QDir(cacheLocation).removeRecursively();
} }

114
launcher/src/logger.cpp Normal file
View file

@ -0,0 +1,114 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "logger.h"
#include "utility.h"
#include <QByteArray>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QMutexLocker>
#include <QStandardPaths>
#include <QtLogging>
#include <iostream>
class Logger
{
public:
void log(const QtMsgType type, const QMessageLogContext &context, const QString &message)
{
const QString formattedMsg = qFormatLogMessage(type, context, message);
if (file.isOpen()) {
QMutexLocker locker(&mutex);
QByteArray buf;
QTextStream str(&buf);
str << formattedMsg << QStringLiteral("\n");
str.flush();
file.write(buf.constData(), buf.size());
file.flush();
}
std::cout << formattedMsg.toStdString() << std::endl;
}
void initialize()
{
const QString filename{QStringLiteral("astra.log")};
const QDir logDirectory = Utility::stateDirectory().absoluteFilePath("log");
if (!logDirectory.exists()) {
QDir().mkpath(logDirectory.absolutePath());
}
// Sort them from highest to lowest (4, 3, 2, 1, 0)
auto existingLogEntries = logDirectory.entryList({filename + QStringLiteral(".*")});
std::sort(existingLogEntries.begin(), existingLogEntries.end(), [](const auto &left, const auto &right) {
auto leftIndex = left.split(QStringLiteral(".")).last().toInt();
auto rightIndex = right.split(QStringLiteral(".")).last().toInt();
return leftIndex > rightIndex;
});
// Iterate through the existing logs, prune them and move the oldest up
for (const auto &entry : existingLogEntries) {
bool valid = false;
const auto index = entry.split(QStringLiteral(".")).last().toInt(&valid);
if (!valid) {
continue;
}
const QString entryFullPath = logDirectory.absoluteFilePath(entry);
const QFileInfo info(entryFullPath);
if (info.exists()) {
QFile existingFile(entryFullPath);
if (index > 3) {
existingFile.remove();
continue;
}
const auto &newName = logDirectory.absoluteFilePath(QStringLiteral("%1.%2").arg(filename, QString::number(index + 1)));
const auto success = existingFile.copy(newName);
if (success) {
existingFile.remove();
} else {
qFatal("Cannot move log file '%s' to '%s': %s",
qUtf8Printable(existingFile.fileName()),
qUtf8Printable(newName),
qUtf8Printable(existingFile.errorString()));
}
}
}
file.setFileName(logDirectory.absoluteFilePath(filename + QStringLiteral(".0")));
file.open(QIODevice::WriteOnly | QIODevice::Unbuffered);
}
private:
QMutex mutex;
QFile file;
};
Q_GLOBAL_STATIC(Logger, logger)
void handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
switch (type) {
case QtDebugMsg:
case QtInfoMsg:
case QtWarningMsg:
case QtCriticalMsg:
logger()->log(type, context, message);
break;
case QtFatalMsg:
logger()->log(QtCriticalMsg, context, message);
abort();
}
}
void initializeLogging()
{
logger()->initialize();
qInstallMessageHandler(handler);
}

View file

@ -15,10 +15,13 @@
#include "compatibilitytoolinstaller.h" #include "compatibilitytoolinstaller.h"
#include "gameinstaller.h" #include "gameinstaller.h"
#include "launchercore.h" #include "launchercore.h"
#include "logger.h"
#include "sapphirelauncher.h" #include "sapphirelauncher.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
initializeLogging();
QtWebView::initialize(); QtWebView::initialize();
QApplication app(argc, argv); QApplication app(argc, argv);
@ -28,6 +31,11 @@ int main(int argc, char *argv[])
QQuickStyle::setStyle(QStringLiteral("org.kde.desktop")); QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
} }
// Default to a sensible message pattern
if (qEnvironmentVariableIsEmpty("QT_MESSAGE_PATTERN")) {
qputenv("QT_MESSAGE_PATTERN", "[%{time yyyy-MM-dd h:mm:ss.zzz}] %{if-category}[%{category}] %{endif}[%{type}] %{message}");
}
KLocalizedString::setApplicationDomain("astra"); KLocalizedString::setApplicationDomain("astra");
KAboutData about(QStringLiteral("astra"), KAboutData about(QStringLiteral("astra"),
@ -47,7 +55,11 @@ int main(int argc, char *argv[])
physis_get_physis_version(), physis_get_physis_version(),
QStringLiteral("https://xiv.zone/physis"), QStringLiteral("https://xiv.zone/physis"),
KAboutLicense::GPL_V3); KAboutLicense::GPL_V3);
about.addComponent(QStringLiteral("libphysis"), QStringLiteral("C bindings for physis"), physis_get_libphysis_version(), {}, KAboutLicense::GPL_V3); about.addComponent(QStringLiteral("libphysis"),
QStringLiteral("C bindings for physis"),
physis_get_libphysis_version(),
QStringLiteral("https://git.sr.ht/~redstrate/libphysis"),
KAboutLicense::GPL_V3);
about.setDesktopFileName(QStringLiteral("zone.xiv.astra")); about.setDesktopFileName(QStringLiteral("zone.xiv.astra"));
about.setBugAddress(QByteArrayLiteral("https://lists.sr.ht/~redstrate/public-inbox")); about.setBugAddress(QByteArrayLiteral("https://lists.sr.ht/~redstrate/public-inbox"));
about.setComponentName(QStringLiteral("astra")); about.setComponentName(QStringLiteral("astra"));
@ -58,11 +70,9 @@ int main(int argc, char *argv[])
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription(i18n("Linux FFXIV Launcher")); parser.setApplicationDescription(i18n("Linux FFXIV Launcher"));
#ifdef ENABLE_STEAM
QCommandLineOption steamOption(QStringLiteral("steam"), QStringLiteral("Used for booting the launcher from Steam."), QStringLiteral("verb")); QCommandLineOption steamOption(QStringLiteral("steam"), QStringLiteral("Used for booting the launcher from Steam."), QStringLiteral("verb"));
steamOption.setFlags(QCommandLineOption::HiddenFromHelp); steamOption.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(steamOption); parser.addOption(steamOption);
#endif
about.setupCommandLine(&parser); about.setupCommandLine(&parser);
parser.parse(QCoreApplication::arguments()); parser.parse(QCoreApplication::arguments());

View file

@ -13,8 +13,10 @@
#include <physis.hpp> #include <physis.hpp>
#include <qcorofuture.h> #include <qcorofuture.h>
#include "astra_patcher_log.h"
#include "launchercore.h" #include "launchercore.h"
#include "patchlist.h" #include "patchlist.h"
#include "utility.h"
Patcher::Patcher(LauncherCore &launcher, const QString &baseDirectory, BootData &bootData, QObject *parent) Patcher::Patcher(LauncherCore &launcher, const QString &baseDirectory, BootData &bootData, QObject *parent)
: QObject(parent) : QObject(parent)
@ -67,19 +69,22 @@ QCoro::Task<bool> Patcher::patch(const PatchList &patchList)
const QueuedPatch queuedPatch{patch.name, patch.repository, patch.version, patchPath, patch.hashes, patch.hashBlockSize, patch.length, isBoot()}; const QueuedPatch queuedPatch{patch.name, patch.repository, patch.version, patchPath, patch.hashes, patch.hashBlockSize, patch.length, isBoot()};
qDebug() << "Adding a queued patch:"; qDebug(ASTRA_PATCHER) << "Adding a queued patch:";
qDebug() << "- Name:" << patch.name; qDebug(ASTRA_PATCHER) << "- Name:" << patch.name;
qDebug() << "- Repository or is boot:" << (isBoot() ? QStringLiteral("boot") : patch.repository); qDebug(ASTRA_PATCHER) << "- Repository or is boot:" << (isBoot() ? QStringLiteral("boot") : patch.repository);
qDebug() << "- Version:" << patch.version; qDebug(ASTRA_PATCHER) << "- Version:" << patch.version;
qDebug() << "- Downloaded Path:" << patchPath; qDebug(ASTRA_PATCHER) << "- Downloaded Path:" << patchPath;
qDebug() << "- Hashes:" << patch.hashes; qDebug(ASTRA_PATCHER) << "- Hashes:" << patch.hashes;
qDebug() << "- Hash Block Size:" << patch.hashBlockSize; qDebug(ASTRA_PATCHER) << "- Hash Block Size:" << patch.hashBlockSize;
qDebug() << "- Length:" << patch.length; qDebug(ASTRA_PATCHER) << "- Length:" << patch.length;
m_patchQueue[ourIndex] = queuedPatch; m_patchQueue[ourIndex] = queuedPatch;
if (!QFile::exists(patchPath)) { if (!QFile::exists(patchPath)) {
auto patchReply = m_launcher.mgr->get(QNetworkRequest(patch.url)); const auto patchRequest = QNetworkRequest(patch.url);
Utility::printRequest(QStringLiteral("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, queuedPatch](int received, int total) {
Q_EMIT m_launcher.stageChanged(i18n("Updating %1.\nDownloading %2", getBaseString(), queuedPatch.getVersion())); Q_EMIT m_launcher.stageChanged(i18n("Updating %1.\nDownloading %2", getBaseString(), queuedPatch.getVersion()));
@ -93,7 +98,7 @@ QCoro::Task<bool> Patcher::patch(const PatchList &patchList)
file.close(); file.close();
})); }));
} else { } else {
qDebug() << "Found existing patch: " << patch.name; qDebug(ASTRA_PATCHER) << "Found existing patch: " << patch.name;
} }
} }
@ -147,7 +152,7 @@ void Patcher::processPatch(const QueuedPatch &patch)
physis_gamedata_apply_patch(m_gameData, patch.path.toStdString().c_str()); physis_gamedata_apply_patch(m_gameData, patch.path.toStdString().c_str());
} }
qDebug() << "Installed" << patch.path << "to" << (isBoot() ? QStringLiteral("boot") : patch.repository); qDebug(ASTRA_PATCHER) << "Installed" << patch.path << "to" << (isBoot() ? QStringLiteral("boot") : patch.repository);
QString verFilePath; QString verFilePath;
if (isBoot()) { if (isBoot()) {

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "patchlist.h" #include "patchlist.h"
#include "astra_patcher_log.h"
#include <QDebug> #include <QDebug>
@ -32,7 +33,7 @@ PatchList::PatchList(const QString &patchList)
"PatchList", "PatchList",
"Patch parsing failed, we were given an non-empty patchlist body but nothing were parsed."); "Patch parsing failed, we were given an non-empty patchlist body but nothing were parsed.");
qDebug() << "Finished parsing patch list. Num patches:" << m_patches.size(); qDebug(ASTRA_PATCHER) << "Finished parsing patch list. # of patches:" << m_patches.size();
} }
QVector<Patch> PatchList::patches() const QVector<Patch> PatchList::patches() const

View file

@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "processlogger.h"
#include "astra_log.h"
#include "utility.h"
ProcessLogger::ProcessLogger(QProcess *process)
{
const QDir logDirectory = Utility::stateDirectory().absoluteFilePath("log");
file.setFileName(logDirectory.absoluteFilePath(QStringLiteral("ffxiv.log")));
file.open(QIODevice::WriteOnly | QIODevice::Unbuffered);
connect(process, &QProcess::readyReadStandardOutput, this, [this, process] {
file.write(process->readAllStandardOutput());
file.flush();
});
connect(process, &QProcess::readyReadStandardError, this, [this, process] {
file.write(process->readAllStandardError());
file.flush();
});
connect(process, &QProcess::finished, this, [this] {
deleteLater();
});
qInfo(ASTRA_LOG) << "Client logs are being written to" << file.fileName().toUtf8().constData();
}

View file

@ -9,6 +9,7 @@
#include <QProcess> #include <QProcess>
#include "account.h" #include "account.h"
#include "astra_log.h"
#include "launchercore.h" #include "launchercore.h"
#include "profileconfig.h" #include "profileconfig.h"
@ -44,6 +45,7 @@ Profile::Profile(LauncherCore &launcher, const QString &key, QObject *parent)
} }
m_dalamudVersion = versionString.remove(QLatin1String("Dalamud/")); m_dalamudVersion = versionString.remove(QLatin1String("Dalamud/"));
qInfo(ASTRA_LOG) << "Dalamud version:" << m_dalamudVersion;
} }
const QString dalamudAssetsVer = dalamudAssetsDir.absoluteFilePath(QStringLiteral("asset.ver")); const QString dalamudAssetsVer = dalamudAssetsDir.absoluteFilePath(QStringLiteral("asset.ver"));
@ -52,6 +54,7 @@ Profile::Profile(LauncherCore &launcher, const QString &key, QObject *parent)
assetJson.open(QFile::ReadOnly | QFile::Text); assetJson.open(QFile::ReadOnly | QFile::Text);
m_dalamudAssetVersion = QString(assetJson.readAll()).toInt(); m_dalamudAssetVersion = QString(assetJson.readAll()).toInt();
qInfo(ASTRA_LOG) << "Dalamud asset version:" << m_dalamudVersion;
} }
const QString dalamudRuntimeVer = dalamudRuntimeDir.absoluteFilePath(QStringLiteral("runtime.ver")); const QString dalamudRuntimeVer = dalamudRuntimeDir.absoluteFilePath(QStringLiteral("runtime.ver"));
@ -60,6 +63,7 @@ Profile::Profile(LauncherCore &launcher, const QString &key, QObject *parent)
runtimeVer.open(QFile::ReadOnly | QFile::Text); runtimeVer.open(QFile::ReadOnly | QFile::Text);
m_runtimeVersion = QString(runtimeVer.readAll()); m_runtimeVersion = QString(runtimeVer.readAll());
qInfo(ASTRA_LOG) << "Dalamud runtime version:" << m_dalamudVersion;
} }
} }
} }

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "profilemanager.h" #include "profilemanager.h"
#include "astra_log.h"
#include <KSharedConfig> #include <KSharedConfig>
#include <QDir> #include <QDir>
@ -101,8 +102,9 @@ void ProfileManager::load()
auto config = KSharedConfig::openStateConfig(); auto config = KSharedConfig::openStateConfig();
for (const auto &id : config->groupList()) { for (const auto &id : config->groupList()) {
if (id.contains(QLatin1String("profile-"))) { if (id.contains(QLatin1String("profile-"))) {
qInfo() << "Adding profile" << id; const QString uuid = QString(id).remove(QLatin1String("profile-"));
auto profile = new Profile(m_launcher, QString(id).remove(QLatin1String("profile-")), this); qInfo(ASTRA_LOG) << "Loading profile" << uuid;
auto profile = new Profile(m_launcher, uuid, this);
insertProfile(profile); insertProfile(profile);
} }
} }

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "sapphirelauncher.h" #include "sapphirelauncher.h"
#include "utility.h"
#include <KLocalizedString> #include <KLocalizedString>
#include <QJsonDocument> #include <QJsonDocument>
@ -22,8 +23,10 @@ void SapphireLauncher::login(const QString &lobbyUrl, const LoginInformation &in
QNetworkRequest request(url); QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/x-www-form-urlencoded")); request.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/x-www-form-urlencoded"));
Utility::printRequest(QStringLiteral("POST"), request);
const auto reply = window.mgr->post(request, QJsonDocument(data).toJson(QJsonDocument::JsonFormat::Compact)); const auto reply = window.mgr->post(request, QJsonDocument(data).toJson(QJsonDocument::JsonFormat::Compact));
connect(reply, &QNetworkReply::finished, [this, reply, &info] { connect(reply, &QNetworkReply::finished, [this, reply, &info] {
if (reply->error() != QNetworkReply::NetworkError::NoError) { if (reply->error() != QNetworkReply::NetworkError::NoError) {
Q_EMIT window.loginError(i18n("Could not contact lobby server.\n\n%1", reply->errorString())); Q_EMIT window.loginError(i18n("Could not contact lobby server.\n\n%1", reply->errorString()));
@ -53,6 +56,8 @@ void SapphireLauncher::registerAccount(const QString &lobbyUrl, const LoginInfor
QNetworkRequest request(url); QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/x-www-form-urlencoded")); request.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/x-www-form-urlencoded"));
Utility::printRequest(QStringLiteral("POST"), request);
const auto reply = window.mgr->post(request, QJsonDocument(data).toJson(QJsonDocument::JsonFormat::Compact)); const auto reply = window.mgr->post(request, QJsonDocument(data).toJson(QJsonDocument::JsonFormat::Compact));
connect(reply, &QNetworkReply::finished, [&] { connect(reply, &QNetworkReply::finished, [&] {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());

View file

@ -14,6 +14,7 @@
#include "account.h" #include "account.h"
#include "squarelauncher.h" #include "squarelauncher.h"
#include "utility.h"
SquareBoot::SquareBoot(LauncherCore &window, SquareLauncher &launcher, QObject *parent) SquareBoot::SquareBoot(LauncherCore &window, SquareLauncher &launcher, QObject *parent)
: QObject(parent) : QObject(parent)
@ -43,6 +44,7 @@ QCoro::Task<> SquareBoot::bootCheck(const LoginInformation &info)
} }
request.setRawHeader(QByteArrayLiteral("Host"), QStringLiteral("patch-bootver.%1").arg(window.squareEnixServer()).toUtf8()); request.setRawHeader(QByteArrayLiteral("Host"), QStringLiteral("patch-bootver.%1").arg(window.squareEnixServer()).toUtf8());
Utility::printRequest(QStringLiteral("GET"), request);
const auto reply = window.mgr->get(request); const auto reply = window.mgr->get(request);
co_await reply; co_await reply;
@ -77,6 +79,8 @@ QCoro::Task<> SquareBoot::checkGateStatus(const LoginInformation &info)
// TODO: really? // TODO: really?
window.buildRequest(*info.profile, request); window.buildRequest(*info.profile, request);
Utility::printRequest(QStringLiteral("GET"), request);
const auto reply = window.mgr->get(request); const auto reply = window.mgr->get(request);
window.setupIgnoreSSL(reply); window.setupIgnoreSSL(reply);
co_await reply; co_await reply;

View file

@ -15,6 +15,7 @@
#include "account.h" #include "account.h"
#include "launchercore.h" #include "launchercore.h"
#include "utility.h"
SquareLauncher::SquareLauncher(LauncherCore &window, QObject *parent) SquareLauncher::SquareLauncher(LauncherCore &window, QObject *parent)
: QObject(parent) : QObject(parent)
@ -65,6 +66,8 @@ QCoro::Task<std::optional<SquareLauncher::StoredInfo>> SquareLauncher::getStored
auto request = QNetworkRequest(url); auto request = QNetworkRequest(url);
window.buildRequest(*info.profile, request); window.buildRequest(*info.profile, request);
Utility::printRequest(QStringLiteral("GET"), request);
const auto reply = window.mgr->get(request); const auto reply = window.mgr->get(request);
co_await reply; co_await reply;
@ -123,6 +126,8 @@ QCoro::Task<> SquareLauncher::login(const LoginInformation &info)
request.setRawHeader(QByteArrayLiteral("Referer"), referer.toEncoded()); request.setRawHeader(QByteArrayLiteral("Referer"), referer.toEncoded());
request.setRawHeader(QByteArrayLiteral("Cache-Control"), QByteArrayLiteral("no-cache")); request.setRawHeader(QByteArrayLiteral("Cache-Control"), QByteArrayLiteral("no-cache"));
Utility::printRequest(QStringLiteral("POST"), request);
const auto reply = window.mgr->post(request, postData.toString(QUrl::FullyEncoded).toUtf8()); const auto reply = window.mgr->post(request, postData.toString(QUrl::FullyEncoded).toUtf8());
window.setupIgnoreSSL(reply); window.setupIgnoreSSL(reply);
co_await reply; co_await reply;
@ -183,6 +188,8 @@ QCoro::Task<> SquareLauncher::registerSession(const LoginInformation &info)
} }
} }
Utility::printRequest(QStringLiteral("POST"), request);
const auto reply = window.mgr->post(request, report.toUtf8()); const auto reply = window.mgr->post(request, report.toUtf8());
co_await reply; co_await reply;

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "utility.h" #include "utility.h"
#include "astra_http_log.h"
#include <QStandardPaths> #include <QStandardPaths>
@ -21,3 +22,8 @@ QString Utility::toWindowsPath(const QDir &dir)
{ {
return QStringLiteral("Z:") + dir.absolutePath().replace(QLatin1Char('/'), QLatin1Char('\\')); return QStringLiteral("Z:") + dir.absolutePath().replace(QLatin1Char('/'), QLatin1Char('\\'));
} }
void Utility::printRequest(const QString &type, const QNetworkRequest &request)
{
qDebug(ASTRA_HTTP) << type.toUtf8().constData() << request.url().toDisplayString();
}