D1 phase 5+6: UDP unreliable channel (restores the 1995 reliable/unreliable split)

The ~60Hz entity update records now ride UDP in relay mode, so a lost/late
datagram is dropped (dead reckoning absorbs the gap) instead of head-of-line-
blocking the reliable TCP stream -- the fix for internet rubber-banding.
Reliable traffic (make/damage/death/control/egg/launch) stays on TCP.

This RESTORES the authentic 1995 NETNUB split, it doesn't invent one:
Receiver::Message defaults messageFlags=ReliableFlag; the update path clears
it (flags=0, ENTITY.cpp:590); Mode(UnreliableMode) fires at LoadingMission
(APP.cpp:704 -- the 2007 port's Mode() was a no-op, now it STORES the mode).
Routing gate = (mode==UnreliableMode && !(flags & ReliableFlag)) -- flag AND
mode, so the map-stream creation messages (also flags=0 but flowing during the
still-Reliable CreatingMission window) ride TCP for free.

Client (L4NET):
- Mode() stores currentNetworkMode (was ignored).
- ConnectRelayUdp: UDP socket connect()ed to the relay game port; HELLO
  (outbound punch-through, relay learns our endpoint); udpUp on HELLO-ACK.
  BT_RELAY_TCP_ONLY=1 disables the channel (UDP-blocked nets degrade to the
  pure-TCP checkpoint automatically -- no ACK => everything stays on TCP).
- RelayUdpSendFrame: {route, fromHost, seq} envelope + frame, best-effort.
- RelayUdpKeepalive: ~15s NAT-hold + ~1s HELLO retry until acked.
- CheckRelayUdp: HELLO-ACK sets udpUp; per-sender seq gate drops stale/
  out-of-order datagrams; frames handed up envelope-stripped like TCP.
- Send / ExclusiveBroadcast: route unreliable-window traffic to UDP, else TCP.
- CheckBuffers polls the UDP channel first, then TCP; keepalive tick.
(The relay-side UDP forwarder + endpoint learning + --udp-drop hook landed in
phase 1.)

Verified 2-node localhost relay: during a drive the update stream flows on UDP
(relay udp tx 2->190+, TCP frozen at 53) and the peer's replicant tracks the
master to sub-2u; 15% forced drop (--udp-drop 15) stays coherent (tracked to
0.1u); TCP-only fallback confirmed (udp-known drops the TCP-only pod); no
crashes; mesh mode un-regressed.  KB: context/multiplayer.md gains the D1
RELAY MODE section.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-18 10:00:26 -05:00
co-authored by Claude Fable 5
parent c029df6e6b
commit dbb9af2dfa
3 changed files with 293 additions and 5 deletions
+47
View File
@@ -261,6 +261,53 @@ Base fns in every subsystem vtable: slot 6 = 41bd34 (Read/apply), slot 7 = 41c50
the same read; the twist clamp contains it (on the fixed-torso BLH it pins to ±0.01° —
invisible). First real twist record overwrites it.
## D1 RELAY MODE — internet play without rearchitecting (2026-07-18) [T2 live]
Opt-in relay/dispatcher topology so pods play over the internet behind NAT with no port
forwarding, WHILE the authentic distributed-authority simulation is UNTOUCHED (each pod still
masters its own mech; peers stay replicants). Env-gated: **`BT_RELAY=<host>:<consolePort>`**
(host may be a DNS name; game port = +1; UDP = same number) + **`BT_SELF=<exact [pilots]
entry>`** (NIC matching can't identify a pod across NAT). BT_RELAY unset ⇒ every relay branch
is dead ⇒ the classic mesh is byte-for-byte unchanged (mesh smoke-tested un-regressed each
phase). Plan: `~/.claude/plans/partitioned-snuggling-piglet.md`.
- **Topology**: every pod makes ONE outbound TCP connection to the relay (an extension of
`tools/btconsole.py --relay`) carrying both the console protocol (egg/launch — direction
flipped, the pod dials OUT; the relay TERMINATES the legacy protocol) and all reliable game
traffic, multiplexed by an 8-byte envelope `{int32 route; uint32 length}` (route ≥2 =
unicast hostID, 1 = broadcast-except-sender, 2 HELLO/3 PEER_UP/4 PEER_DOWN). Plus one UDP
socket for the unreliable channel.
- **The relay is DUMB**: routes envelopes by hostID (validated against the egg `[pilots]`
roster), holds no game state. PEER_UP/DOWN control frames on the client synthesize the SAME
`HostConnected/DisconnectedMessage` the mesh accept path routes, so the "All connections
completed!" gate + the whole app ladder run UNMODIFIED. Peers become VIRTUAL L4Hosts
(INVALID_SOCKET, flipped online by PEER_UP).
- **O(N) upload fix**: `ExclusiveBroadcast` builds ONCE and sends ONCE with the broadcast route
— the relay fans out server-side (kills the mesh's N1× duplication at the seam where
`EntityBroadcastToReplicants` already serialized once). Verified: relay `tcp rx==tx`, one
copy per broadcast.
- **UDP channel = the 1995 NETNUB reliable/unreliable split RESTORED (not invented)**:
`Receiver::Message` defaults `messageFlags=ReliableFlag` (RECEIVER.h:195); the ~60Hz update
path sets flags=0 (ENTITY.cpp:590); `Mode(UnreliableMode)` at LoadingMission (APP.cpp:704,
the 2007 port's `Mode()` was a no-op — now STORES the mode). The routing gate is
**`(mode==UnreliableMode && !(flags & ReliableFlag))`** — flag AND mode, so the map-stream
creation messages (also flags=0 but flowing during the still-Reliable CreatingMission window)
ride TCP for free. UDP envelope `{route, fromHost, seq}` + frame; relay learns each pod's
endpoint per-datagram (NAT-rebind tolerant), forwards verbatim, TCP-wraps to a peer whose UDP
endpoint is unknown (asymmetric-block fallback); per-sender seq gate drops stale/out-of-order
(dead reckoning absorbs the gaps — that's what it's for). `BT_RELAY_TCP_ONLY=1` = escape
hatch (UDP-blocked networks degrade to the pure-TCP checkpoint automatically).
- **VERIFIED 2-node localhost relay**: full ladder to RunningMission via PEER_UP; bidirectional
replication (both mechs' MakeMessages cross the relay, paint ×2 each); during a drive the
update stream flows on UDP (relay `udp tx` climbs 2→190+, TCP frozen at 53) and the peer's
replicant tracks the master to sub-2u; 15% forced UDP drop (`--udp-drop 15`) stays coherent
(replicant tracked to 0.1u); TCP-only fallback confirmed (`udp-known` drops the TCP-only pod);
no crashes; mesh mode un-regressed. Recipe: relay `python tools/btconsole.py --relay 1500
MP_RELAY.EGG --bind 127.0.0.1`; pods `BT_RELAY=127.0.0.1:1500 BT_SELF=10.99.0.N:1502 btl4
-egg MP_RELAY.EGG -net 1501` (egg `[pilots]` = arbitrary unique tags). Diag: relay
`[relay-stats]` per-transport census; client `BT_NET_TRACE`.
- **STILL UNTESTED (config, not code)**: a real two-physical-machine / internet run (only the
relay's console+game ports need to be reachable inbound — one forwarded port total).
Optional future: LAN `BT_RELAY=auto` UDP-broadcast discovery.
## Remaining (P6 phase 4 / Phase 7)
Pod-LAN config: **real-IP path VALIDATED (2026-07-15)** — an egg with the machine's real LAN IP in
`[pilots]` (not `127.0.0.1`) connected + replicated + moved end-to-end (`Connected to