aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-06-10 23:56:47 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-06-10 23:56:47 -0500
commit30d2c801a01eeb140ddd5adf8676f2626ecff3fa (patch)
tree93d5fad39ec3c5b1420cc0498922deb78b6db413
parentce4aca6f32ad3a593fc722a12306711e4e5efa63 (diff)
Improve Proton launch context
-rw-r--r--src/src/processrunner.cpp50
-rw-r--r--src/src/protonlauncher.cpp32
-rw-r--r--src/src/protonlauncher.h2
-rw-r--r--src/src/spawn.cpp62
-rw-r--r--src/src/spawn.h1
5 files changed, 130 insertions, 17 deletions
diff --git a/src/src/processrunner.cpp b/src/src/processrunner.cpp
index 63ecf83..a0deae2 100644
--- a/src/src/processrunner.cpp
+++ b/src/src/processrunner.cpp
@@ -1,8 +1,10 @@
#include "processrunner.h"
#include "env.h"
#include "envmodule.h"
+#include "fluorineconfig.h"
#include "instancemanager.h"
#include "iuserinterface.h"
+#include "knowngames.h"
#include "organizercore.h"
#include <iplugingame.h>
@@ -17,6 +19,7 @@
#include <QMetaObject>
#include <QPointer>
#include <QProcess>
+#include <QSettings>
#include <QThread>
#include <cerrno>
@@ -31,6 +34,31 @@
using namespace MOBase;
+static QString steamAppIdFromFluorineConfig()
+{
+ if (auto cfg = FluorineConfig::load(); cfg.has_value() && cfg->app_id != 0) {
+ return QString::number(cfg->app_id);
+ }
+
+ return {};
+}
+
+static QString steamAppIdFromKnownGame(const QString& gameName)
+{
+ if (const auto* knownGame = findKnownGameByTitle(gameName);
+ knownGame != nullptr && knownGame->steam_app_id != nullptr) {
+ return QString::fromLatin1(knownGame->steam_app_id);
+ }
+
+ return {};
+}
+
+static bool steamOverlayEnabled(const Settings& settings)
+{
+ const QSettings instanceIni(settings.filename(), QSettings::IniFormat);
+ return instanceIni.value("fluorine/steam_overlay", false).toBool();
+}
+
void adjustForVirtualized(const IPluginGame* game, spawn::SpawnParameters& sp,
const Settings& settings)
{
@@ -1173,6 +1201,7 @@ std::optional<ProcessRunner::Results> ProcessRunner::runBinary()
const auto* game = m_core.managedGame();
auto& settings = m_core.settings();
+ m_sp.gameDirectory = game->gameDirectory();
if (m_sp.steamAppID.trimmed().isEmpty()) {
const QString gameSteamId = game->steamAPPId().trimmed();
@@ -1183,6 +1212,27 @@ std::optional<ProcessRunner::Results> ProcessRunner::runBinary()
}
}
+ const bool resolveOverlaySteamAppId = m_sp.useProton && steamOverlayEnabled(settings);
+
+ if (resolveOverlaySteamAppId && m_sp.steamAppID.trimmed().isEmpty()) {
+ const QString knownSteamId = steamAppIdFromKnownGame(game->gameName());
+ if (!knownSteamId.isEmpty()) {
+ m_sp.steamAppID = knownSteamId;
+ log::debug("process runner: using known-game steam app id '{}' for '{}'",
+ m_sp.steamAppID, game->gameName());
+ }
+ }
+
+ if (resolveOverlaySteamAppId && m_sp.steamAppID.trimmed().isEmpty()) {
+ const QString configSteamId = steamAppIdFromFluorineConfig();
+ if (!configSteamId.isEmpty()) {
+ m_sp.steamAppID = configSteamId;
+ log::debug("process runner: using Fluorine config steam app id '{}' "
+ "for launch",
+ m_sp.steamAppID);
+ }
+ }
+
if (!checkSteam(parent, m_sp, game->gameDirectory(), m_sp.steamAppID, settings)) {
return Error;
}
diff --git a/src/src/protonlauncher.cpp b/src/src/protonlauncher.cpp
index 800d837..e7da03a 100644
--- a/src/src/protonlauncher.cpp
+++ b/src/src/protonlauncher.cpp
@@ -581,6 +581,12 @@ ProtonLauncher& ProtonLauncher::setWorkingDir(const QString& dir)
return *this;
}
+ProtonLauncher& ProtonLauncher::setGameDirectory(const QString& dir)
+{
+ m_gameDirectory = dir.trimmed();
+ return *this;
+}
+
ProtonLauncher& ProtonLauncher::setProtonPath(const QString& path)
{
m_protonPath = path.trimmed();
@@ -723,8 +729,9 @@ bool ProtonLauncher::launchWithProton(qint64& pid) const
// before starting a new one.
const QStringList protonArgs = QStringList() << "waitforexitandrun" << m_binary << m_arguments;
QStringList pressureVesselImportantPaths;
- pressureVesselImportantPaths << m_binary << m_workingDir << m_prefixPath
- << m_bindMountSource << m_bindMountTarget;
+ pressureVesselImportantPaths << m_binary << m_workingDir << m_gameDirectory
+ << m_prefixPath << m_bindMountSource
+ << m_bindMountTarget;
// If SLR is enabled, wrap the whole proton invocation inside the
// pressure-vessel container provided by SteamLinuxRuntime_sniper.
@@ -739,20 +746,13 @@ bool ProtonLauncher::launchWithProton(qint64& pid) const
// Build: [wrappers] run_script [--filesystem=...] -- proton_script protonArgs
QStringList slrArgs;
- // Expose host directories to the pressure-vessel container.
- // Without --filesystem= flags, the container's mount namespace
- // may not see FUSE mounts or system-installed Proton paths.
- if (!m_binary.isEmpty()) {
- QString gameDir = QFileInfo(m_binary).absolutePath();
- const QFileInfo gameDirInfo(gameDir);
- if (gameDirInfo.fileName().compare(QStringLiteral("bin"),
- Qt::CaseInsensitive) == 0) {
- const QString gameRoot = gameDirInfo.dir().absolutePath();
- if (QFileInfo(gameRoot).exists()) {
- gameDir = gameRoot;
- }
- }
- slrArgs << QStringLiteral("--filesystem=%1").arg(gameDir);
+ // Expose the managed game root. Extenders frequently live below a
+ // nested bin directory but load assets/modules from the root.
+ if (!m_gameDirectory.isEmpty() && QFileInfo::exists(m_gameDirectory)) {
+ slrArgs << QStringLiteral("--filesystem=%1").arg(m_gameDirectory);
+ } else if (!m_binary.isEmpty()) {
+ slrArgs << QStringLiteral("--filesystem=%1")
+ .arg(QFileInfo(m_binary).absolutePath());
}
if (!m_prefixPath.isEmpty()) {
slrArgs << QStringLiteral("--filesystem=%1").arg(m_prefixPath);
diff --git a/src/src/protonlauncher.h b/src/src/protonlauncher.h
index 25c4b16..f0ce09b 100644
--- a/src/src/protonlauncher.h
+++ b/src/src/protonlauncher.h
@@ -16,6 +16,7 @@ public:
ProtonLauncher& setBinary(const QString& path);
ProtonLauncher& setArguments(const QStringList& args);
ProtonLauncher& setWorkingDir(const QString& dir);
+ ProtonLauncher& setGameDirectory(const QString& dir);
ProtonLauncher& setProtonPath(const QString& path);
ProtonLauncher& setPrefix(const QString& path);
ProtonLauncher& setSteamAppId(uint32_t id);
@@ -50,6 +51,7 @@ private:
QString m_binary;
QStringList m_arguments;
QString m_workingDir;
+ QString m_gameDirectory;
QString m_protonPath;
QString m_prefixPath;
uint32_t m_steamAppId{0};
diff --git a/src/src/spawn.cpp b/src/src/spawn.cpp
index ec41221..701304b 100644
--- a/src/src/spawn.cpp
+++ b/src/src/spawn.cpp
@@ -22,6 +22,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "env.h"
#include "envmodule.h"
#include "fluorineconfig.h"
+#include "knowngames.h"
#include "protonlauncher.h"
#include "settings.h"
#include "shared/appconfig.h"
@@ -192,6 +193,38 @@ uint32_t parseSteamAppId(const QString& steamAppId)
return (ok ? n : 0u);
}
+static uint32_t steamAppIdFromFluorineConfig()
+{
+ if (auto cfg = FluorineConfig::load(); cfg.has_value()) {
+ return cfg->app_id;
+ }
+
+ return 0;
+}
+
+static uint32_t steamAppIdFromKnownGame(const QString& gameName)
+{
+ if (const auto* knownGame = findKnownGameByTitle(gameName);
+ knownGame != nullptr && knownGame->steam_app_id != nullptr) {
+ bool ok = false;
+ const auto n = QString::fromLatin1(knownGame->steam_app_id).toUInt(&ok);
+ return (ok ? n : 0u);
+ }
+
+ return 0;
+}
+
+static bool steamOverlayEnabledForCurrentInstance()
+{
+ const Settings* instanceForLaunch = Settings::maybeInstance();
+ if (!instanceForLaunch) {
+ return false;
+ }
+
+ const QSettings instanceIni(instanceForLaunch->filename(), QSettings::IniFormat);
+ return instanceIni.value("fluorine/steam_overlay", false).toBool();
+}
+
QString firstExistingSetting(const QSettings& settings, const QStringList& keys)
{
for (const QString& key : keys) {
@@ -406,11 +439,38 @@ int spawn(const SpawnParameters& sp, pid_t& processId)
logSpawning(sp, bin + " " + sp.arguments);
+ uint32_t steamAppId = parseSteamAppId(sp.steamAppID);
+ const bool resolveOverlaySteamAppId =
+ sp.useProton && steamOverlayEnabledForCurrentInstance();
+ if (resolveOverlaySteamAppId && steamAppId == 0) {
+ const Settings* instanceForLaunch = Settings::maybeInstance();
+ if (instanceForLaunch) {
+ const auto gameName = instanceForLaunch->game().name();
+ if (gameName && !gameName->trimmed().isEmpty()) {
+ steamAppId = steamAppIdFromKnownGame(*gameName);
+ if (steamAppId != 0) {
+ MOBase::log::debug("spawn: using known-game steam app id '{}' "
+ "for '{}'",
+ steamAppId, *gameName);
+ }
+ }
+ }
+ }
+ if (resolveOverlaySteamAppId && steamAppId == 0) {
+ steamAppId = steamAppIdFromFluorineConfig();
+ if (steamAppId != 0) {
+ MOBase::log::debug("spawn: using Fluorine config steam app id '{}' "
+ "for Proton launch",
+ steamAppId);
+ }
+ }
+
ProtonLauncher launcher;
launcher.setBinary(bin)
.setArguments(argList)
.setWorkingDir(cwd)
- .setSteamAppId(parseSteamAppId(sp.steamAppID));
+ .setGameDirectory(MOBase::normalizePathForHost(sp.gameDirectory.absolutePath()))
+ .setSteamAppId(steamAppId);
if (sp.useProton) {
// Read per-instance settings from the instance INI (not the global QSettings).
diff --git a/src/src/spawn.h b/src/src/spawn.h
index 40207c3..c871fcc 100644
--- a/src/src/spawn.h
+++ b/src/src/spawn.h
@@ -49,6 +49,7 @@ struct SpawnParameters
QFileInfo binary;
QString arguments;
QDir currentDirectory;
+ QDir gameDirectory;
QString steamAppID;
bool hooked = false;
bool useProton = true;