Conn Man crashed twice in an Owens on 2026-07-27, kept playing, and by the time
we asked for his logs they no longer existed. Not a mystery: every launcher did
if exist content\X.old.log del content\X.old.log
if exist content\X.log ren content\X.log X.old.log
which keeps TWO generations. He relaunched more than twice, so his own bat
deleted both crash sessions. The player who crashes is exactly the player who
relaunches immediately, so the retention window was aimed at the wrong case.
THE LOG. BT_LOG set still means "use this path verbatim" -- that contract is
load-bearing (mp_a.log/mp_b.log for 2-node runs from one cwd, btoperator's
operator_N.log, every scratchpad/mp_*.sh that greps the file it named) and is
untouched. Only when BT_LOG is UNSET does the exe now name the file itself:
content\<stem>_YYYYMMDD.log, appended, one per day, no rotation window to fall
out of. The stem (solo/join/steam/joyconfig) comes from the gates the bat
already sets, so solo still cannot overwrite the MULTIPLAYER log.
Self-named files append UNCONDITIONALLY. The default open mode is ios::out --
TRUNCATE -- and the operator GUI's exported bats never set BT_LOG_APPEND, so
they were truncating already; leaving append implicit would have let the second
launch of the day erase the morning.
IDENTITY, because these arrive from many players at once and Discord renames
half of them to message.txt (most of the night-5 evidence had to be
re-identified by hand):
===== BT411 SESSION build=4.11.603 (d64d75f+) machine=... user=...
callsign=Conn Man mode=solo pid=13192 local=... log=... =====
Also the session separator inside a per-day file, and it puts build+hash ABOVE
any [crash] block so btl4+0xNNNN offsets stay matchable to a PDB across builds.
SIZE. Never delete, but stay sendable: past 8 MB the next launch rolls to
<stem>_YYYYMMDD.1.log. Checked only at open, so a live session is never split.
CONSOLIDATION. launch_report.txt is gone, folded into lastrun_<stem>.txt (one
launch record: the exe appends its block at first breath, the bat appends the
exit line). Per-stem because one shared lastrun.txt let a second launcher's
`del` destroy the first launch's block. Its ABSENCE after a run is now the
#41 "never reached WinMain" probe -- the old "if not exist X.log" test cannot
work against a per-day file, which survives earlier launches, and would have
reported every healthy run as blocked by antivirus. marshal.log folded into
the day log too; it keeps its own handle and gained a CRITICAL_SECTION because
it runs on a worker thread while the engine log stream is single-threaded.
play_steam.bat gained a launch bracket it never had -- which is why a Steam
player killed before WinMain previously left no evidence at all.
_putenv_s, not SetEnvironmentVariableA, to pin the resolved name: MSVC's CRT
keeps its own environment copy, so getenv() in-process does NOT see a
SetEnvironmentVariableA write (measured). btl4console/btl4lobby resolve the
day log via getenv, so with the Win32-only call they silently fell back to
marshal.log and the fold never happened.
logfile.open() is now checked -- a failed open used to rebind cout to a dead
buffer, silently dropping the header, [boot] and any crash stack while
lastrun still claimed log=<name>.
Verified: append across sessions with distinct pids, crash block landing under
its own header, BT_LOG verbatim unmangled, an inherited BT_LOG cleared by the
bats, roll-over at 8 MB, and both forensic verdicts (exe ran / exe blocked).
Console auto-retrieval is unaffected -- it uploads the matchlog, a different
file, and a crashed round never reaches the upload call anyway.
Known gaps, deliberately not in this commit: the setlocal hoist for gate
leakage when two bats share one console (dev-only), and btoperator's exported
bats still lack the lastrun bracket -- do not press Export on a shipped zip.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
109 lines
5.6 KiB
Batchfile
109 lines
5.6 KiB
Batchfile
@echo off
|
|
rem BT411 -- STEAM play (EXPERIMENTAL, dev testers). Launches the glass
|
|
rem front-end menu: HOST a Steam lobby or JOIN one from the menu -- there
|
|
rem is no address to configure; sessions form through the Steam lobby.
|
|
rem Requires: the Steam client RUNNING and logged in, and the steam build
|
|
rem extracted next to content\.
|
|
cd /d %~dp0
|
|
if not exist "build\Release\btl4.exe" goto badpath
|
|
if not exist "content\BTL4.RES" goto badpath
|
|
set BT_PLATFORM=glass
|
|
set BT_STEAM_NET=1
|
|
rem Cockpit view + MFD gauges -- the same client flags the join bats set
|
|
rem (field report: players booted 3rd-person without them). Inherited by
|
|
rem the menu's mission relaunch.
|
|
set BT_START_INSIDE=1
|
|
set BT_DEV_GAUGES=1
|
|
rem full menu here (all modes + steam buttons); clear the solo trim in
|
|
rem case this shell ran play_solo.bat first
|
|
set BT_FE_SOLO=
|
|
rem LOGGING (2026-07-28): the bat no longer names or rotates the log. With
|
|
rem BT_LOG unset the exe names a PER-DAY file itself -- content\steam_YYYYMMDD.log
|
|
rem -- and always appends, so every session of the day lands in ONE file, each
|
|
rem starting with a "===== BT411 SESSION ... =====" header naming the build,
|
|
rem machine and pid (so it identifies itself even if the filename is changed
|
|
rem in transit). Rotation used to keep only TWO generations: on 2026-07-27 a
|
|
rem player crashed twice, kept playing, and his own relaunches deleted both
|
|
rem crash sessions before anyone could ask for them.
|
|
rem Clear any INHERITED BT_LOG: with it set the exe uses that path verbatim
|
|
rem and, with no BT_LOG_APPEND, TRUNCATES it -- the exact evidence loss this
|
|
rem change removes. Empty forces the per-day self-named file + append.
|
|
set BT_LOG=
|
|
cd content
|
|
rem ------------------------------------------------------------------
|
|
rem OUR GENERATION ONLY (2026-07-27). The wait loop below used to hold
|
|
rem this window open for ANY btl4.exe on the machine -- a second client,
|
|
rem the operator's own pod, or an orphan left by a crash. The window
|
|
rem then never printed the sign-off and looked hung: the reported "it
|
|
rem leaves the terminal open". Snapshot the PIDs alive BEFORE we launch;
|
|
rem those belong to somebody else and must not hold us here.
|
|
rem ------------------------------------------------------------------
|
|
setlocal EnableDelayedExpansion
|
|
set "BT_PRE=,"
|
|
for /f "tokens=1,2" %%P in ('%SystemRoot%\System32\tasklist.exe /FI "IMAGENAME eq btl4.exe" /NH 2^>nul') do (
|
|
if /I "%%P"=="btl4.exe" set "BT_PRE=!BT_PRE!%%Q,"
|
|
)
|
|
rem LAUNCH RECORD (#41 forensics -- NEW on the Steam path, 2026-07-28). This
|
|
rem bat never had the outside bracket the join/solo bats carry, which is why a
|
|
rem Steam player whose exe was killed before WinMain left no evidence at all.
|
|
rem The exe appends its own block to lastrun_steam.txt at first breath; delete it here
|
|
rem so its ABSENCE afterwards means the exe never started.
|
|
if exist lastrun_steam.txt del lastrun_steam.txt
|
|
..\build\Release\btl4.exe
|
|
set BT_EXITCODE=%ERRORLEVEL%
|
|
if not exist lastrun_steam.txt echo THE EXE NEVER REACHED WinMain: blocked before it could run (loader failure or antivirus) > lastrun_steam.txt
|
|
echo [%DATE% %TIME%] btl4.exe exited with code %BT_EXITCODE% >> lastrun_steam.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
|
|
rem NO EXTERNAL TOOLS: `find`, `findstr` and `timeout` are all shadowed by
|
|
rem Git-Bash / MSYS / GnuWin32 builds of the same names when those are on
|
|
rem PATH (a dev box, or a player who installed git). The MSYS `find` is
|
|
rem the Unix one and dies with "find: '/I': No such file or directory",
|
|
rem and GNU `timeout` takes different arguments. So: parse tasklist with
|
|
rem a pure-cmd for/f, and call both helpers by ABSOLUTE System32 path.
|
|
rem A genuinely dead exe leaves no process and falls straight through, so
|
|
rem the #41 launch forensics above still work. If tasklist is missing
|
|
rem entirely the loop degrades to reporting immediately -- the old
|
|
rem behaviour, never a hang.
|
|
rem ------------------------------------------------------------------
|
|
:btwait
|
|
set "BT_STILL_RUNNING="
|
|
for /f "tokens=1,2" %%P in ('%SystemRoot%\System32\tasklist.exe /FI "IMAGENAME eq btl4.exe" /NH 2^>nul') do (
|
|
if /I "%%P"=="btl4.exe" (
|
|
rem a PID that was NOT in the pre-launch snapshot is ours
|
|
set "BT_STRIP=!BT_PRE:,%%Q,=!"
|
|
if "!BT_STRIP!"=="!BT_PRE!" set "BT_STILL_RUNNING=1"
|
|
)
|
|
)
|
|
if defined BT_STILL_RUNNING (
|
|
%SystemRoot%\System32\timeout.exe /t 2 /nobreak >nul 2>nul
|
|
goto btwait
|
|
)
|
|
echo.
|
|
echo The game has exited. If it closed unexpectedly, send the operator BOTH
|
|
echo content\lastrun_steam.txt (tiny -- send it EVEN IF there is no log
|
|
echo dated today: that is how we spot a
|
|
echo launch that was blocked before it ran)
|
|
echo content\steam_YYYYMMDD.log (today's, newest)
|
|
echo Steam not running = the menu still works but lobbies will not appear.
|
|
pause
|
|
exit /b
|
|
:badpath
|
|
echo.
|
|
echo ==================================================
|
|
echo ERROR: this file is not in the right place, or
|
|
echo the zip was not fully extracted.
|
|
echo.
|
|
echo It must sit in the EXTRACTED game folder, next to
|
|
echo the 'content' and 'build' folders.
|
|
echo ==================================================
|
|
echo.
|
|
pause
|