fix(linux): Play from a desktop launcher no longer has systemd stop the game #2540
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2540
Loading…
Reference in a new issue
No description provided.
Delete branch "linux/play-own-scope"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
On Linux desktops that start each launcher as a systemd service (KDE/SteamOS, GNOME, COSMIC), pressing Play in the launcher window starts the game and then systemd kills it about 90 seconds later, which players see as a silent crash.
Fixes #2529
What changed
builder/linux/hookshot_builder.py: newown_scope(). When the Builder is running inside a.servicecgroup andsystemd-runis available, Play starts the game throughsystemd-run --user --scope --collect --quiet --, so it lives in a scope of its own and the launcher's service can stop without it. A trialsystemd-run ... trueruns first; if the user manager can't be reached (or there is no systemd, or the launcher is in a scope or a terminal), Play starts the game exactly as before.tests/test_linux_play_scope.py(new, game-free, runs on Linux and Windows): when the scope is used and when it isn't (scope / cgroup v1 / no/proc/ nosystemd-run/ failing trial / timeout), and thatplay()puts the prefix in front of both the diagnostics script and the direct-exe fallback.tests/test_linux_play_diagnostics.py: stubsown_scopein the namespace it buildsplay()in.The game not exiting on SIGTERM in a windowed run (also noted in the issue) is left for a separate PR.
How it was tested
python -I -B tests/run_all.py play builderon Windows: all pass (the Linux-only checks skip there).systemd-run --user --unit=... python3 launcher.py, the reporter's setup) that starts a long-lived child withstart_new_session=Trueand exits:app.slice/run-p<pid>-i<n>.scope.Assisted-by: Claude Code
🤖 Generated with Claude Code
Tested on COSMIC (Pop!_OS 24.04, systemd 255, NVIDIA 595.104.02, RTX 3070 Ti). Before this PR, Play from the launcher run as a user service (
systemd-run --user, same as the desktop'sapp-hookshot@….service) made the unit go tostop-sigtermas soon as the Builder exited, and the game was SIGKILLed 90 s later (#2529).With this PR's
hookshot_builder.py(run with the v2.1.0 AppImage's Python/Tk), same setup, Play clicked: the unit went toinactive/deadcleanly, the game ran in its ownrun-r….scope, and it was still alive 151 s later, past the 90 s stop timeout. No SIGTERM/SIGKILL in the journal.Caveat: this ran the PR's builder from source, not a packaged AppImage built from it.
Thanks for the fix, and @toniosim for the analysis. I tested the merged change on Arch (Hyprland, systemd 262.1) with a transient user service shaped like the issue's launcher service and a stand-in for the game: on its base the child was killed 90.1 s after Play; with the fix it was still alive in its own scope after 96 s. So it fixes the reported case here.
Two cases are left, both shown by injection (not an estimate of how often they happen):
truesucceeds but the realsystemd-runthen fails: Play starts nothing and does not fall back;systemd-runis 250 ms slow: the window closes first, the launcher's service ends, and the game is never started.#2643 is a small follow-up for both: Play waits (at most two seconds, usually no time at all) until the real runner has left the launcher's control group before the window closes, and starts the game the plain way exactly once if that does not happen. Ten ordinary launches behave as before on both. Not tested: a real desktop's Play click, GNOME, KDE or COSMIC, and a real log-out.
github-sync referenced this pull request2026-11-12 06:22:54 +00:00