the X button means QUIT: a user-closed window exits for real instead of relaunching

Operator report (00:30): "my local instance auto-relaunches whenever I stop and
start a session, even if I close my window."  Root cause is the pod's AUTHENTIC
immortality: an arcade cabinet's join loop never exits, so every RunMissions
return relaunches into the join wait -- and a human closing the window was
indistinguishable from a round ending.  On a desktop that made the client
unkillable outside Task Manager, and explains every orphaned-plasma zombie
hunted this week (my own test teardowns included), plus tonight's "eventually
all I could find was my plasma display".

FIX, one flag + one gate:
  * btl4main WndProc WM_CLOSE stamps gBTUserRequestedExit before DestroyWindow
    (covers X, Alt-F4, taskbar close, Task Manager's polite phase).
  * BTFE_RelaunchSelfAndExit -- the single choke point every relaunch path
    funnels through (round end, console-pad lost, round abort, FE loop) --
    exits for real when the flag is set, logging 'window closed by the user
    -- exiting for real (no relaunch)' to the marshal log.

Round-end auto-rejoin is UNTOUCHED: the flag only sets on WM_CLOSE.

VERIFIED both directions on the built exe (4.11.591):
  * WM_CLOSE posted to a live solo instance -> the whole process tree is GONE
    8s later: no relaunched generation, no plasma orphan.  [was: immortal]
  * Offset-port rig, full launch -> StopMission -> both pods AUTO-REJOINED and
    re-ACKed for the next round (2/2 ready, WAITING FOR OPERATOR); zero
    exiting-for-real lines fired (nobody closed a window).

Requires the next zip for players; the operator's running instance keeps the
old behavior until restarted onto this build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-27 00:38:05 -05:00
co-authored by Claude Opus 5
parent 33487ab846
commit ecb9b338bd
2 changed files with 33 additions and 0 deletions
+11
View File
@@ -97,6 +97,11 @@ struct BTDriveInput { float throttle; float turn; int forced; int fire; int fire
int keyFwd; int keyBack; int keyLeft; int keyRight; int allStop; };
BTDriveInput gBTDrive = { 0.0f, 0.0f, 0, 0, 0, 1.0f, 0, 0, 0, 0, 0 };
// The human clicked X (WM_CLOSE). Read by BTFE_RelaunchSelfAndExit: a
// user-closed window exits for REAL instead of relaunching into the join
// wait (the pod's authentic immortality, wrong for a desktop window).
volatile int gBTUserRequestedExit = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
@@ -157,6 +162,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
}
return 0;
case WM_CLOSE:
// The human clicked X / pressed Alt-F4. Without this stamp the
// relaunch chain treats the resulting RunMissions return like a
// round ending and RESURRECTS the client (the pod was immortal by
// design; a desktop window must not be) -- see the gate in
// BTFE_RelaunchSelfAndExit.
gBTUserRequestedExit = 1;
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
+22
View File
@@ -137,6 +137,28 @@ static unsigned long
void
BTFE_RelaunchSelfAndExit(const char *arguments)
{
//
// THE X BUTTON MEANS QUIT (2026-07-27, operator report: "my local
// instance auto-relaunches whenever I stop and start a session, even if
// I close my window"). The 1995 pod was IMMORTAL by design -- an arcade
// cabinet's join loop never exits -- so every RunMissions return
// relaunches, and a human closing the window was indistinguishable from
// a round ending. On a desktop that made the client unkillable outside
// Task Manager (the orphaned-plasma zombies). btl4main's WM_CLOSE now
// stamps gBTUserRequestedExit; every relaunch path funnels through here,
// so this single gate turns a user-closed window into a REAL exit while
// round-end / console-loss / abort relaunches stay exactly as they were.
//
{
extern volatile int gBTUserRequestedExit;
if (gBTUserRequestedExit)
{
MarshalLog("window closed by the user -- exiting for real "
"(no relaunch)");
ExitProcess(0);
}
}
//
// RELAUNCH-STORM DAMPER (issue #33, night-2 capture): a client whose
// generations die young (stale identity -> HELLO reject -> drop ->