Repository navigation
fix(apollo): run seat Apollo without a console so Restart and Quit are visible to the service (#124) - #128
Merged
Conversation
…e visible to the service (#124) Seat Apollo was launched with CREATE_NEW_CONSOLE. With a console, Apollo's Restart starts a copy of itself and exits 0, and Quit also exits 0. The service saw only that its tracked PID was gone, launched a second Apollo, and both ran on the seat's ports. Quit was undone within one health check. - Launch seat Apollo with CREATE_NO_WINDOW and keep the process handle (LaunchedProcess), so its exit code can be read. Apollo then sees GetConsoleWindow() == NULL, as under its own Windows service (tools/sunshinesvc.cpp): Restart exits 0 and starts nothing, Quit exits 1115 (ERROR_SHUTDOWN_IN_PROGRESS). - The health check classifies the exit (ApolloExit). 1115 leaves Apollo stopped, as the dashboard's Stop does, with a message on the seat and no restart attempt spent. 0 relaunches without spending an attempt, unless it came within the 30 s startup window. Anything else, or an unreadable code, is a crash and is handled as before. - Before every launch and at the end of every stop, kill any sunshine.exe whose command line passes exactly this seat's sunshine.conf (ApolloOwnership.CommandLineNamesConfig, WMI, confirmed again while a handle pins the PID). Other seats and the console Apollo never match. A WMI failure kills nothing. This reverses the #120/#121 choice to keep CREATE_NEW_CONSOLE, on purpose. The tests that asserted it are rewritten to assert the opposite. The SW_SHOWMINNOACTIVE request from #121 is kept: it is harmless, and it still applies if the exe is ever a GUI-subsystem build, for which CREATE_NO_WINDOW is ignored. Games, on-connect apps and the console-session Apollo launch are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ross a sleep (#124) After a Quit from Apollo's web UI or tray, or the dashboard's Stop, the health check's crash relaunch already left Apollo down. Its reconnect after a session drop did not: it called RestartAsync, which fell through to StartAsync and brought Apollo back after every sleep. - ApolloManager.StopByUser stops Apollo and marks the seat as stopped by the user. Quit (exit 1115) and SeatManager.StopApollo both use it. - The reconnect rescue reconnects the session but does not start Apollo while the seat is marked (RestartApolloAfterReconnectAsync). - The mark is cleared by an explicit Start or Restart, by a successful StartAsync, and by teardown. A reprovisioned seat is a new seat. - It is in memory only, like the seats. A service restart or reboot provisions auto-start seats and starts Apollo as usual. No API shape changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #124.
What was wrong. Seat Apollo was started with a new console. With a console, Apollo's Restart starts a copy of itself and then exits. The service only saw its tracked process exit, so it started a second Apollo. Both then ran on the seat's ports. Quit also exited with code 0, so the service started Apollo again within a few seconds.
What this changes.
CREATE_NO_WINDOW). The service keeps the process handle so it can read the exit code. With no console, Apollo acts as it does under its own Windows service. Restart exits with 0 and starts nothing. Quit exits with 1115.sunshine.exethat runs this seat's exact config file. Other seats' Apollo and the console Apollo never match.This reverses the #120 choice on purpose. #120 and #121 kept the console so Apollo's Restart and Quit would work as before. That console is the cause of this bug. The tests that asserted the console are rewritten. The minimized-window setting from #121 is kept. It does no harm, and it still helps if the Apollo exe is ever a GUI build.
Tested in the unit tests. They cover the launch flags, the exit-code rules, the health-check decision, the restart limit, the config-path match (including a near-miss seat name and the console Apollo), and the stopped-by-user marker. The new tests were checked by breaking the code on purpose: they failed, then passed again. The full suite passes: 1702 passed, 18 skipped, 0 failed.
Tested live on 2026-10-21, on a seat built from this branch on top of the 0.6.22 release (
0.6.22+321ea0fe2):Not tested live: a real sleep and wake after a Quit. That case is covered by the unit tests, and the dashboard's Stop uses the same code. The WMI step that ends stray Apollo processes was not exercised against a real stray, only its path matcher is unit-tested.
Changing the NVENC preset, resolution or scale restarts Apollo through the normal start path. On a seat whose Apollo was stopped by the user, that brings Apollo back and clears the stop.
🤖 Generated with Claude Code