Files
lutris/tests/runners
rawrmonster17 a2cc28bd44 steam: track game running state via Steam's per-game reaper process
Steam games launch via non-blocking URI handlers (steam://rungameid/ or
steam -applaunch) that exit almost immediately, and the real game runs
under Steam's PID tree without inheriting LUTRIS_GAME_UUID. The default PID
tracking therefore sees no processes and Game.beat() calls on_game_quit()
within a couple of seconds, firing post-exit scripts while the game is
still launching (#2926).

Detect Steam's per-game "reaper" process instead, whose command line
contains "SteamLaunch AppId=<appid>". The reaper exists for the entire
lifetime of the game (through shader pre-caching and play) and exits when it
stops, for native, Proton/Wine and Flatpak Steam alike. An earlier revision
read the appmanifest "AppRunning" StateFlag, but that flag is not updated
for Flatpak Steam (verified live: it stays "Fully Installed" through a whole
session), nor for native Steam, so it was inert on both.

Changes:
- get_steam_game_pids(appid): find the reaper PID(s) by scanning /proc
  cmdlines. The appid match is anchored (followed by a space or end of
  string) so 440 does not also match 4400 / 44012.
- steam.filter_game_pids(): track the reaper PID (primary), keeping the
  appmanifest AppRunning flag as a secondary signal where Steam updates it.
- Runner.keep_game_alive() hook (default False): lets a runner tell
  Game.beat() to keep monitoring even though Lutris's launch thread has
  exited. Steam overrides it to bridge the launch window (grace period, so a
  failed launch still ends) and the whole game lifetime. Without this, beat()
  treated the live game's PIDs as orphans and SIGTERM'd them ~2s in.
- steam.force_stop_game(): SIGTERM the game's reaper (stops just this game),
  never the Steam client.
- Per-beat caching of the reaper scan so filter_game_pids() and
  keep_game_alive() share one /proc walk per heartbeat; plus per-appid
  caching of the resolved appmanifest path.
- The launch grace period defaults to 120s (DEFAULT_LAUNCH_GRACE_SECONDS)
  and is overridable per game via the advanced "launch_grace_seconds" option.
- get_steam_pid() now narrows system.get_pid()'s str | list | None return to
  Optional[str] at the source, so callers don't each re-handle the union.
- STEAM_DATA_DIRS: add the current Flatpak Steam layout so Flatpak libraries
  are found.
- Tests (built through the real runner __init__) for the reaper helper
  (including the prefix-collision guard), filter_game_pids, keep_game_alive
  (launcher-alive / reaper-alive / launch grace / configured grace / post-
  quit), force_stop_game sparing Steam, get_steam_pid narrowing and the
  appmanifest caching.

Verified end-to-end through the Lutris GUI with Team Fortress 2 on both
Flatpak Steam (~4 min session, clean quit) and native Steam (multiple
sessions, e.g. "has run for 66 seconds", clean quit), replacing the previous
"ran for 2 seconds, did it crash?".

Fixes: #2926

Warn consistently on any invalid launch_grace_seconds: a non-positive
numeric value now logs the same warning as a non-numeric one instead of
silently falling back to the default.
2026-06-14 13:34:01 -05:00
..
2020-06-26 16:28:25 -07:00