diff --git a/tools/btconsole.py b/tools/btconsole.py index 17ae82f..c606b74 100644 --- a/tools/btconsole.py +++ b/tools/btconsole.py @@ -729,6 +729,32 @@ class Relay: return if time.time() < self.launch_at: return + # ALL-READY GATE (2026-07-22, live finding round 3): firing the + # mission while a pod is still LOADING drowns that pod's load in the + # running mission's update streams (same event queue) -- the operator, + # who always joins last, loaded 5+ minutes or crashed while testers + # took ~20s. Hold the RunMission pair until every REGISTERED pod has + # sent READY (route -10): everyone enters together, 1995-style. + # Escape hatches: a pod that never readies is announced every 10s + # (the operator sees who), and a 3-minute cap force-fires so one + # wedged pod cannot hold the night hostage. + if self.launches_sent == 0: + not_ready = [h for h, c in sorted(self.by_host.items()) + if not getattr(c, "ready", False)] + if not_ready and time.time() < self.launch_at + 180.0: + now = time.time() + if now - getattr(self, "_hold_notice_at", 0) >= 10.0: + self._hold_notice_at = now + names = ", ".join( + "PLAYER %d" % (h - FIRST_GAME_HOST_ID + 1) + for h in not_ready) + print(f"[relay] launch HELD -- still loading: {names} " + f"({len(self.by_host) - len(not_ready)}/" + f"{len(self.by_host)} ready)", flush=True) + return + if not_ready: + print(f"[relay] launch FORCED after 180s hold -- " + f"{len(not_ready)} pod(s) never readied", flush=True) pkt = run_mission_packet() for conn in list(self.console_conns): try: