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

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.
This commit is contained in:
Joshua Goins 2024-07-30 19:15:37 -04:00
parent e32a61edad
commit 3b95982dcf
6 changed files with 83 additions and 8 deletions

View file

@ -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

View file

@ -10,7 +10,7 @@
class ProcessLogger : public QObject
{
public:
explicit ProcessLogger(QProcess *process);
explicit ProcessLogger(const QString &baseName, QProcess *process);
private:
QFile m_file;

View file

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QObject>
#include <QTimer>
/// 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;
};

View file

@ -7,9 +7,11 @@
#include <gamemode_client.h>
#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);

View file

@ -7,12 +7,12 @@
#include <QStandardPaths>
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] {

View file

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "processwatcher.h"
#include <KProcessList>
#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();
}