aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-03-17 17:26:05 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-03-17 17:26:05 -0500
commit2efc7e6124ef814ac9abd874bff0dc6b3e6768d0 (patch)
tree6167ebc6b89b1799506f426a58661c9e1e26400d
parente41a79a9033761916fa133260ff1849288db7df1 (diff)
Fix Steam game mode: clear LD_LIBRARY_PATH, deploy native basic_games, add env var detection
Steam game mode injects its scout/soldier runtime into LD_LIBRARY_PATH, which breaks Python extension modules and Qt internals that resolve dependencies via LD_LIBRARY_PATH rather than RPATH. This caused all Python-based basic_games plugins (Witcher 3, Oblivion Remastered, BG3, etc.) to fail to load when launched through Steam. Changes: - Launcher script: set LD_LIBRARY_PATH to only bundled lib/ dir instead of leaving Steam's runtime in the path - AppRun: same fix — replace rather than append to LD_LIBRARY_PATH - Deploy libbasic_games_native.so in tarball (was built but never copied) so basic_games entries work without Python dependency - Add STEAM_COMPAT_CLIENT_INSTALL_PATH env var check to Python steam_utils.py (mirrors C++ fix from e41a79a) for game mode detection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
-rwxr-xr-xdocker/build-inner.sh15
-rw-r--r--libs/basic_games/games/game_arkhamcity.py2
-rw-r--r--libs/basic_games/games/game_vampirebloodlines.py1
-rw-r--r--libs/basic_games/steam_utils.py11
4 files changed, 25 insertions, 4 deletions
diff --git a/docker/build-inner.sh b/docker/build-inner.sh
index 62232d9..484412b 100755
--- a/docker/build-inner.sh
+++ b/docker/build-inner.sh
@@ -68,6 +68,7 @@ find build/libs -type f \( \
-name "libinibakery.so" -o \
-name "libbsa_extractor.so" -o \
-name "libbsa_packer.so" -o \
+ -name "libbasic_games_native.so" -o \
-name "libproxy.so" \
\) -exec cp -f {} "${OUT_DIR}/plugins/" \;
@@ -103,6 +104,8 @@ if [ -d "libs/basic_games" ]; then
-o -name "*.txt" -o -name "*.md" -o -name "LICENSE" \
-o -name "CMakeLists.txt" -o -name "CMakePresets.json" \) \
-delete 2>/dev/null || true
+ # Remove __pycache__ directories to prevent stale bytecode
+ find "${OUT_DIR}/plugins/basic_games" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
fi
# data/ dir (DDS headers etc., used by DDSPreview.py via plugins/data/ in sys.path).
# Stage DDS package from source directly (cmake staging is OFF by default).
@@ -406,8 +409,12 @@ fi
RUN="${BIN_DST}"
export PATH="${RUN}:${PATH}"
-# NOTE: Do NOT set LD_LIBRARY_PATH here. The binary uses DT_RPATH
-# ($ORIGIN/lib) to find its libraries.
+# Steam game mode injects its scout/soldier runtime into LD_LIBRARY_PATH.
+# Those old libraries (libssl, libz, etc.) break Python extension modules
+# and Qt internals that don't have RPATH pointing to our bundled libs.
+# Clear it — the binary uses DT_RPATH ($ORIGIN/lib) for its own deps,
+# and we prepend our lib/ for anything loaded via dlopen (Python, Qt plugins).
+export LD_LIBRARY_PATH="${RUN}/lib"
export MO2_BASE_DIR="${RUN}"
export MO2_PLUGINS_DIR="${RUN}/plugins"
export MO2_DLLS_DIR="${RUN}/dlls"
@@ -628,7 +635,9 @@ export FLUORINE_ORIG_XDG_DATA_DIRS="${XDG_DATA_DIRS:-/usr/local/share:/usr/share
export FLUORINE_ORIG_QT_PLUGIN_PATH="${QT_PLUGIN_PATH:-}"
export PATH="${BIN}:${PATH}"
-export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH:-}"
+# Replace (not append) LD_LIBRARY_PATH — Steam game mode injects its runtime
+# libs which break Python/Qt. RPATH handles the binary's own deps.
+export LD_LIBRARY_PATH="${HERE}/usr/lib"
export MO2_BASE_DIR="${APPIMAGE_DIR}"
export MO2_PLUGINS_DIR="${BIN}/plugins"
diff --git a/libs/basic_games/games/game_arkhamcity.py b/libs/basic_games/games/game_arkhamcity.py
index 0b8269f..adcf6d4 100644
--- a/libs/basic_games/games/game_arkhamcity.py
+++ b/libs/basic_games/games/game_arkhamcity.py
@@ -53,7 +53,7 @@ class ArkhamCityGame(BasicGame):
# This will only detect saves from the earliest-created Steam profile on the user's PC.
def savesDirectory(self) -> QDir:
- docSaves = QDir(self.documentsDirectory().cleanPath("../../SaveData"))
+ docSaves = QDir(self.documentsDirectory().absoluteFilePath("../../SaveData"))
if self.is_steam():
if (steamDir := find_steam_path()) is None:
return docSaves
diff --git a/libs/basic_games/games/game_vampirebloodlines.py b/libs/basic_games/games/game_vampirebloodlines.py
index a63b308..41d63cc 100644
--- a/libs/basic_games/games/game_vampirebloodlines.py
+++ b/libs/basic_games/games/game_vampirebloodlines.py
@@ -83,6 +83,7 @@ class VampireTheMasqueradeBloodlinesGame(BasicGame):
for iniFile in self.iniFiles():
iniPath = Path(self.documentsDirectory().absoluteFilePath(iniFile))
if not iniPath.exists():
+ iniPath.parent.mkdir(parents=True, exist_ok=True)
with open(iniPath, "w") as _:
pass
diff --git a/libs/basic_games/steam_utils.py b/libs/basic_games/steam_utils.py
index a3daeaf..f61f392 100644
--- a/libs/basic_games/steam_utils.py
+++ b/libs/basic_games/steam_utils.py
@@ -1,5 +1,6 @@
# Code greatly inspired by https://github.com/LostDragonist/steam-library-setup-tool
+import os
import sys
import winreg
from pathlib import Path
@@ -150,6 +151,16 @@ def find_steam_path() -> Path | None:
except FileNotFoundError:
pass
+ # When Steam launches an app (e.g. in game mode), it sets
+ # STEAM_COMPAT_CLIENT_INSTALL_PATH to its own root. Check it first so we
+ # find the right installation even if $HOME resolves differently inside
+ # the gamescope session (e.g. /var/home vs /home on Bazzite).
+ steam_env = os.environ.get("STEAM_COMPAT_CLIENT_INSTALL_PATH", "")
+ if steam_env:
+ p = Path(steam_env)
+ if p.is_dir() and p.joinpath("steamapps", "libraryfolders.vdf").exists():
+ return p
+
# Linux: check common Steam install locations.
for candidate in (
Path.home() / ".local" / "share" / "Steam",