diff --git a/context/multiplayer.md b/context/multiplayer.md index b797b63..03bc709 100644 --- a/context/multiplayer.md +++ b/context/multiplayer.md @@ -412,6 +412,19 @@ phase). Plan: `~/.claude/plans/partitioned-snuggling-piglet.md`. survivors registered under remapped ids and ran the mission. Auto (CLI) mode keeps the full-roster behavior. Caveat: `sys.stdin` is None under some spawn shapes (pyenv .bat shim pipelines) -- the stdin reader guards it; the GUI's QProcess pipe is the supported channel. +- **LAUNCH/LOAD RACE -- the silent mission-start crash (2026-07-22, FIXED) [T2]**: the relay + sends the RunMission pair LAUNCH_SETTLE_SECONDS (20s) after the egg ACK, but the ACK happens at + egg RECEIPT -- a long mission load (glass + dev gauges on a busy box took ~50s live) is still + in LoadingMission when RunMission lands, and `L4Application::RunMissionMessageHandler`'s + default case `Fail()`ed = abort = a SILENT crash (c0000409, no WER record; cdb stack: + ucrtbase!abort <- RunMissionMessageHandler <- RoutePacket; 3/3 reproducible in the + console-shaped run, and the field report's 464 crash died mid-LOD-load with the same + signature). The 1995 console never hit this state (operator-paced launches). FIX + (L4APP.cpp): the default case now DEFERS -- re-posts the message to itself at +1s (the + VehicleDead re-post pattern) until the ladder reaches WaitingForLaunch; bounded at 120 + deferrals so a wedged load still surfaces the original Fail. `[launch] ... deferred +1s` + log per deferral. Verified: the crashing shape deferred 104x through a ~50s load, then + launched and ran clean. - **PATIENT WALK-UP (2026-07-18) [T2]**: a pod dialed at a relay that isn't up yet no longer aborts (release `Fail()` is a bare `abort()` = looked like a "crash", field report). Both the seat request (`RelayRequestSeat`) and LAN discovery (`BT_RELAY=auto`) now RETRY (2s dials, 30 diff --git a/engine/MUNGA_L4/L4APP.cpp b/engine/MUNGA_L4/L4APP.cpp index e54e4f1..b450f6b 100644 --- a/engine/MUNGA_L4/L4APP.cpp +++ b/engine/MUNGA_L4/L4APP.cpp @@ -610,6 +610,30 @@ void break; default: + // + // LAUNCH/LOAD RACE (2026-07-22): the relay sends the RunMission pair + // a fixed settle after the egg ACK, but a long mission load (glass + + // dev gauges on a busy box) can still be in LoadingMission when it + // lands -- the 1995 console never hit this state (the operator + // launched much later), so it Fail()ed = the "silent crash at + // mission launch" (c0000409 abort; 3/3 reproduced under cdb). + // DEFER instead: re-post the message to ourselves at +1s until the + // ladder reaches WaitingForLaunch (the VehicleDead re-post pattern). + // Bounded so a truly wedged load still surfaces the original Fail. + { + static int s_launch_deferrals = 0; + if (++s_launch_deferrals <= 120) + { + DEBUG_STREAM << "[launch] RunMission arrived in state " + << (int)GetApplicationState() + << " (still loading) -- deferred +1s (" + << s_launch_deferrals << ")" << std::endl << std::flush; + Time when = Now(); + when += 1.0f; + Post(HighEventPriority, this, message, when); + return; // base handler runs when ready + } + } Fail("Application::RunMissionMessageHandler - Not ready to run!\n"); break; }