Crash self-report + round-completed latch + PDB archiving

CRASH SELF-REPORT (the #35/#41 heisenbug hunt): every unhandled
exception now logs its code, address, and an EBP-walked stack as
module-relative offsets (btl4+0xNNNN) into BT_LOG before dying --
any field crash on any machine hands us a resolvable stack.  The
walker is per-dereference guarded (a corrupt chain truncates).
BT_CRASHTEST=1 exercises the path end-to-end (verified: forced AV
logs code/addr/5-frame stack).  mkdist archives each build's PDB
next to its zip so offsets resolve per distributed version.

Context: a flaky release-only crash in the mech build path surfaced
during #38 rigs -- it moves with build layout, spares neither peer,
and masks under a debugger heap (13 clean cdb runs vs 2/3 crashes
without).  A real latent memory bug; the self-report will catch it
wherever it strikes next.

ROUND-COMPLETED LATCH (issue #38, awaiting verification): relay-mode
clients refuse RunMission after a round has ended -- the lobby-loop
relaunch owns the next round (in-process round 2 rebuilt every
player/mech entity on stale globals; matchlog caught player=3:1->3:36
in one generation).  Latch sets ONLY when the stop ends a RUNNING
mission (a stray pre-mission StopMission must not poison round 1 --
the first cut ate a legit launch, caught + fixed in rig).  Regression
verification pending (contaminated by the heisenbug above).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-24 08:51:50 -05:00
co-authored by Claude Opus 4.8
parent ac7f0cb13a
commit f7daf03507
4 changed files with 113 additions and 0 deletions
+26
View File
@@ -571,6 +571,20 @@ Entity*
//
int gBTRunMissionPendCount = 0;
//
// Round-completed latch (issue #38, 2026-07-24): our port replaces the
// arcade's in-process next-round flow with a PROCESS RELAUNCH per round
// (reconstructed gBT* globals don't survive in-process re-init). A
// RunMission that lands after a round has ended (a stale pend, a relay
// resend racing the stop) used to start round 2 IN-PROCESS anyway --
// rebuilding every player/mech as new entities on stale globals (field
// signature: opponents' mechs re-created mid-session with the wrong
// paint; matchlog showed player=3:1->3:36 in one generation). Set by
// StopMissionMessageHandler; in relay mode any later RunMission is
// refused -- the relaunch owns the next round.
//
int gBTRoundCompleted = 0;
int BTTakePendingRunMissions(void)
{
int pended = gBTRunMissionPendCount;
@@ -593,6 +607,18 @@ void
Check(this);
Verify(message->messageID == RunMissionMessageID);
//
// Issue #38: after a completed round, relay mode NEVER runs another
// mission in-process -- the lobby-loop relaunch handles the next round
// with a fresh process (see gBTRoundCompleted above).
//
if (gBTRoundCompleted && getenv("BT_RELAY") != NULL)
{
DEBUG_STREAM << "[launch] RunMission after round end IGNORED -- the "
<< "relaunch owns the next round\n" << std::flush;
return;
}
//
//--------------------------------------------------------------------------
// If the application is already running then ignore this message