From 145bf368c82f9478cdca256c2183eae9a7ed647a Mon Sep 17 00:00:00 2001 From: arcattack Date: Sat, 25 Jul 2026 18:31:34 -0500 Subject: [PATCH] Launchers (players/, the shipped source): wait for the handed-off game process instead of announcing "The game has exited" mid-load Operator report while trying the build: the window said the game had exited "with an error", yet the game loaded and played fine. There was no error. The launchers were lying. The front end does NOT stay resident: btl4console.cpp CreateProcessW()es the mission generation, closes both handles and ExitProcess(0)s itself. So the bat's child exiting means "the menu handed off", not "the game closed". All four launchers then immediately printed [.. ] btl4.exe exited with code 0 The game has exited. If it closed unexpectedly, send the operator ... while the game was still loading in the handed-off process. Verified live on the operator's own session: first process pid 33352 = MENU profile, handed off to pid 12564 = GLASS profile, which was the one actually running; exit code 0 throughout. Harmless in itself, but it reads as a crash to a playtester and would have generated false bug reports on a test night. FIX: after the front-end child exits, poll until no btl4.exe remains, THEN report. Applied to play_solo / join / join_lan / play_steam. The #41 launch forensics are preserved: an exe that dies before it can run leaves no process, so the wait falls straight through and launch_report.txt still records timing, exit code and whether the log was ever created. Verified the wait condition both ways against the live session (read-only, no second instance spawned): btl4.exe present -> WOULD-WAIT; absent -> WOULD-EXIT. NOTE for future edits: players/ is the SOURCE OF TRUTH (mkdist packages from there, tools/mkdist.py:78); the root-level copies are gitignored local conveniences AND were LF-only, while players/ is correctly CRLF. Since this change adds a `goto` label -- the construct most likely to misbehave in an LF-only bat -- the root copies have been re-synced byte-identical from players/ so local runs match the shipped artifact. Co-Authored-By: Claude Opus 5 (1M context) --- players/join.bat | 17 +++++++++++++++++ players/join_lan.bat | 17 +++++++++++++++++ players/play_solo.bat | 17 +++++++++++++++++ players/play_steam.bat | 17 +++++++++++++++++ 4 files changed, 68 insertions(+) diff --git a/players/join.bat b/players/join.bat index 1779f9f..8129612 100644 --- a/players/join.bat +++ b/players/join.bat @@ -29,6 +29,23 @@ set BT_EXITCODE=%ERRORLEVEL% echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> launch_report.txt if not exist join.log echo join.log WAS NEVER CREATED: the exe was blocked before it could run (loader failure or antivirus) >> launch_report.txt if exist join.log for %%A in (join.log) do echo join.log size: %%~zA bytes >> launch_report.txt + +rem ------------------------------------------------------------------ +rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it +rem CreateProcess()es the mission generation and ExitProcess(0)s itself +rem (btl4console.cpp). So OUR child exiting means "the menu handed off", +rem NOT "the game closed" -- without this wait the window announced "The +rem game has exited" while the game was still loading, which reads as a +rem crash to a playtester. Wait for the handed-off generation(s) to go. +rem A genuinely dead exe leaves no process and falls straight through, so +rem the #41 launch forensics above still work. +rem ------------------------------------------------------------------ +:btwait +tasklist /FI "IMAGENAME eq btl4.exe" /NH 2>nul | find /I "btl4.exe" >nul +if not errorlevel 1 ( + timeout /t 2 /nobreak >nul + goto btwait +) echo. echo The game has exited. If you did NOT quit on purpose echo (crash, or it never connected), send the operator BOTH files: diff --git a/players/join_lan.bat b/players/join_lan.bat index f6fb9c1..7d36037 100644 --- a/players/join_lan.bat +++ b/players/join_lan.bat @@ -32,6 +32,23 @@ set BT_EXITCODE=%ERRORLEVEL% echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> launch_report.txt if not exist join.log echo join.log WAS NEVER CREATED: the exe was blocked before it could run (loader failure or antivirus) >> launch_report.txt if exist join.log for %%A in (join.log) do echo join.log size: %%~zA bytes >> launch_report.txt + +rem ------------------------------------------------------------------ +rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it +rem CreateProcess()es the mission generation and ExitProcess(0)s itself +rem (btl4console.cpp). So OUR child exiting means "the menu handed off", +rem NOT "the game closed" -- without this wait the window announced "The +rem game has exited" while the game was still loading, which reads as a +rem crash to a playtester. Wait for the handed-off generation(s) to go. +rem A genuinely dead exe leaves no process and falls straight through, so +rem the #41 launch forensics above still work. +rem ------------------------------------------------------------------ +:btwait +tasklist /FI "IMAGENAME eq btl4.exe" /NH 2>nul | find /I "btl4.exe" >nul +if not errorlevel 1 ( + timeout /t 2 /nobreak >nul + goto btwait +) echo. echo The game has exited. If you did NOT quit on purpose echo (crash, or it never connected), send the operator BOTH files: diff --git a/players/play_solo.bat b/players/play_solo.bat index 48f6bd1..57fda3d 100644 --- a/players/play_solo.bat +++ b/players/play_solo.bat @@ -28,6 +28,23 @@ set BT_EXITCODE=%ERRORLEVEL% echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> launch_report.txt if not exist join.log echo join.log WAS NEVER CREATED: the exe was blocked before it could run (loader failure or antivirus) >> launch_report.txt if exist join.log for %%A in (join.log) do echo join.log size: %%~zA bytes >> launch_report.txt + +rem ------------------------------------------------------------------ +rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it +rem CreateProcess()es the mission generation and ExitProcess(0)s itself +rem (btl4console.cpp). So OUR child exiting means "the menu handed off", +rem NOT "the game closed" -- without this wait the window announced "The +rem game has exited" while the game was still loading, which reads as a +rem crash to a playtester. Wait for the handed-off generation(s) to go. +rem A genuinely dead exe leaves no process and falls straight through, so +rem the #41 launch forensics above still work. +rem ------------------------------------------------------------------ +:btwait +tasklist /FI "IMAGENAME eq btl4.exe" /NH 2>nul | find /I "btl4.exe" >nul +if not errorlevel 1 ( + timeout /t 2 /nobreak >nul + goto btwait +) echo. echo The game has exited. If it closed unexpectedly, echo send the operator content\join.log. diff --git a/players/play_steam.bat b/players/play_steam.bat index 6223714..9898125 100644 --- a/players/play_steam.bat +++ b/players/play_steam.bat @@ -22,6 +22,23 @@ rem case this shell ran play_solo.bat first set BT_FE_SOLO= cd content ..\build\Release\btl4.exe + +rem ------------------------------------------------------------------ +rem HANDOFF WAIT (2026-07-25). The front end does NOT stay resident: it +rem CreateProcess()es the mission generation and ExitProcess(0)s itself +rem (btl4console.cpp). So OUR child exiting means "the menu handed off", +rem NOT "the game closed" -- without this wait the window announced "The +rem game has exited" while the game was still loading, which reads as a +rem crash to a playtester. Wait for the handed-off generation(s) to go. +rem A genuinely dead exe leaves no process and falls straight through, so +rem the #41 launch forensics above still work. +rem ------------------------------------------------------------------ +:btwait +tasklist /FI "IMAGENAME eq btl4.exe" /NH 2>nul | find /I "btl4.exe" >nul +if not errorlevel 1 ( + timeout /t 2 /nobreak >nul + goto btwait +) echo. echo The game has exited. If it closed unexpectedly, send the operator echo content\steam.log. Steam not running = the menu still works but