From 3b95982dcf0d870c84cfa9f40b5b7114c7e264cc Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Tue, 30 Jul 2024 19:15:37 -0400 Subject: [PATCH] Fix Astra considering the game exited when Dalamud exits This is a design limitation of the Dalamud Injector (at least from how we use it) because we depend on the child process exiting to be when the game exists. For vanilla games, this is true but not for Dalamud. Now we track the PID given to us by Dalamud and use that as an indicator to when the game exits. This is needed to properly keep tracked of when the game exists for the future sync feature. --- launcher/CMakeLists.txt | 2 ++ launcher/include/processlogger.h | 2 +- launcher/include/processwatcher.h | 21 +++++++++++++++++ launcher/src/gamerunner.cpp | 38 +++++++++++++++++++++++++++---- launcher/src/processlogger.cpp | 4 ++-- launcher/src/processwatcher.cpp | 24 +++++++++++++++++++ 6 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 launcher/include/processwatcher.h create mode 100644 launcher/src/processwatcher.cpp diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 2b17f71..ce7db77 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -56,6 +56,7 @@ target_sources(astra PRIVATE include/logger.h include/patcher.h include/processlogger.h + include/processwatcher.h include/profile.h include/profilemanager.h include/sapphirelogin.h @@ -79,6 +80,7 @@ target_sources(astra PRIVATE src/main.cpp src/patcher.cpp src/processlogger.cpp + src/processwatcher.cpp src/profile.cpp src/profilemanager.cpp src/sapphirelogin.cpp diff --git a/launcher/include/processlogger.h b/launcher/include/processlogger.h index 8542936..5fc8708 100644 --- a/launcher/include/processlogger.h +++ b/launcher/include/processlogger.h @@ -10,7 +10,7 @@ class ProcessLogger : public QObject { public: - explicit ProcessLogger(QProcess *process); + explicit ProcessLogger(const QString &baseName, QProcess *process); private: QFile m_file; diff --git a/launcher/include/processwatcher.h b/launcher/include/processwatcher.h new file mode 100644 index 0000000..5f71790 --- /dev/null +++ b/launcher/include/processwatcher.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2024 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +/// Listens and waits for a process to finish. +class ProcessWatcher : public QObject +{ + Q_OBJECT +public: + explicit ProcessWatcher(const int PID); + +Q_SIGNALS: + void finished(); + +private: + QTimer *m_timer = nullptr; +}; \ No newline at end of file diff --git a/launcher/src/gamerunner.cpp b/launcher/src/gamerunner.cpp index 98e45d6..942e41f 100644 --- a/launcher/src/gamerunner.cpp +++ b/launcher/src/gamerunner.cpp @@ -7,9 +7,11 @@ #include #endif +#include "astra_log.h" #include "encryptedarg.h" #include "launchercore.h" #include "processlogger.h" +#include "processwatcher.h" #include "utility.h" using namespace Qt::StringLiterals; @@ -52,7 +54,7 @@ void GameRunner::beginVanillaGame(const QString &gameExecutablePath, Profile &pr auto args = getGameArgs(profile, auth); - new ProcessLogger(gameProcess); + new ProcessLogger(QStringLiteral("ffxiv"), gameProcess); launchExecutable(profile, gameProcess, {gameExecutablePath, args}, true, true); } @@ -75,7 +77,7 @@ void GameRunner::beginDalamudGame(const QString &gameExecutablePath, Profile &pr // so we need to match typical XIVQuickLauncher behavior here. Why? I have no clue. const QDir dalamudPluginDir = dalamudUserPluginDir.absoluteFilePath(QStringLiteral("installedPlugins")); - const QString logDir = dataDir.absoluteFilePath(QStringLiteral("log")); + const QDir logDir = dataDir.absoluteFilePath(QStringLiteral("log")); Utility::createPathIfNeeded(logDir); const QDir dalamudRuntimeDir = dalamudDir.absoluteFilePath(QStringLiteral("runtime")); @@ -86,9 +88,35 @@ void GameRunner::beginDalamudGame(const QString &gameExecutablePath, Profile &pr const QString dalamudInjector = dalamudInstallDir.absoluteFilePath(QStringLiteral("Dalamud.Injector.exe")); const auto dalamudProcess = new QProcess(this); - connect(dalamudProcess, &QProcess::finished, this, [this, &profile](const int exitCode) { - profile.setLoggedIn(false); + connect(dalamudProcess, &QProcess::finished, this, [this, &profile, logDir](const int exitCode) { Q_UNUSED(exitCode) + + // So here's the kicker, we can't depend on Dalamud to give us an accurate finished signal for the game. + // finished() is called when the injector exits. + + // so what we'll do instead is get the game PID from Dalamud first. + QFile logFile(logDir.absoluteFilePath(QStringLiteral("dalamud-initial-injection.log"))); + logFile.open(QIODevice::ReadOnly); + + const static QRegularExpression pidRegex(QStringLiteral("{\"pid\": (\\d*),")); + const QString log = QString::fromUtf8(logFile.readAll()); + + const auto match = pidRegex.match(log); + if (match.hasCaptured(1)) { + const int PID = match.captured(1).toInt(); + if (PID > 0) { + qCInfo(ASTRA_LOG) << "Recieved PID from Dalamud:" << PID; + auto watcher = new ProcessWatcher(PID); + connect(watcher, &ProcessWatcher::finished, this, [this, &profile] { + profile.setLoggedIn(false); + Q_EMIT m_launcher.gameClosed(); + }); + return; + } + } + + // If Dalamud didn't give a valid PID, OK. Let's just do our previous status quo and inidcate we did log out. + profile.setLoggedIn(false); Q_EMIT m_launcher.gameClosed(); }); @@ -100,7 +128,7 @@ void GameRunner::beginDalamudGame(const QString &gameExecutablePath, Profile &pr #endif dalamudProcess->setProcessEnvironment(env); - new ProcessLogger(dalamudProcess); + new ProcessLogger(QStringLiteral("dalamud-initial-injection"), dalamudProcess); const auto args = getGameArgs(profile, auth); diff --git a/launcher/src/processlogger.cpp b/launcher/src/processlogger.cpp index 073360c..6330837 100644 --- a/launcher/src/processlogger.cpp +++ b/launcher/src/processlogger.cpp @@ -7,12 +7,12 @@ #include -ProcessLogger::ProcessLogger(QProcess *process) +ProcessLogger::ProcessLogger(const QString &baseName, QProcess *process) { const QDir dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); const QDir logDirectory = dataDir.absoluteFilePath(QStringLiteral("log")); - m_file.setFileName(logDirectory.absoluteFilePath(QStringLiteral("ffxiv.log"))); + m_file.setFileName(logDirectory.absoluteFilePath(QStringLiteral("%1.log").arg(baseName))); m_file.open(QIODevice::WriteOnly | QIODevice::Unbuffered); connect(process, &QProcess::readyReadStandardOutput, this, [this, process] { diff --git a/launcher/src/processwatcher.cpp b/launcher/src/processwatcher.cpp new file mode 100644 index 0000000..69339f8 --- /dev/null +++ b/launcher/src/processwatcher.cpp @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2024 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "processwatcher.h" + +#include + +#include "moc_processwatcher.cpp" + +ProcessWatcher::ProcessWatcher(const int PID) +{ + m_timer = new QTimer(); + connect(m_timer, &QTimer::timeout, this, [this, PID] { + const auto info = KProcessList::processInfo(PID); + // If we can't find the PID, bail! + if (!info.isValid()) { + m_timer->stop(); + deleteLater(); + Q_EMIT finished(); + } + }); + m_timer->setInterval(std::chrono::seconds(30)); + m_timer->start(); +} \ No newline at end of file