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:
co-authored by
Claude Fable 5
parent
c029df6e6b
commit
dbb9af2dfa
@@ -350,6 +350,22 @@ private:
|
||||
RelayGameDown(const char *why);
|
||||
Logical
|
||||
CheckRelay(NetworkPacket *network_packet);
|
||||
//
|
||||
// UDP unreliable channel (restores the 1995 NETNUB reliable/unreliable
|
||||
// split): the ~60Hz update records ride UDP so a lost/late datagram is
|
||||
// dropped (dead reckoning absorbs the gap) instead of head-of-line-
|
||||
// blocking the reliable stream. Reliable traffic + control stay on TCP.
|
||||
//
|
||||
void
|
||||
ConnectRelayUdp(HostID local_host_ID);
|
||||
Logical
|
||||
RelayUdpSendFrame(int route, const NetworkPacket *packet, int packet_size);
|
||||
void
|
||||
RelayUdpKeepalive(HostID local_host_ID);
|
||||
Logical
|
||||
RelayShouldUseUdp(const Message *message) const;
|
||||
Logical
|
||||
CheckRelayUdp(NetworkPacket *network_packet);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Private Data
|
||||
@@ -390,7 +406,9 @@ private:
|
||||
int relayPadTail;
|
||||
Logical relayConsoleDialedOnce; // first dial gets the long timeout
|
||||
Logical udpUp; // UDP HELLO acked by the relay
|
||||
HostID relayLocalHostID; // our hostID (for UDP HELLO/keepalive/tx)
|
||||
unsigned long lastUdpKeepaliveTick;
|
||||
unsigned long lastUdpHelloTick; // HELLO retry until acked
|
||||
unsigned long udpTxSeq;
|
||||
unsigned long udpRxSeq[64]; // per-fromHost last-seen sequence
|
||||
NetworkMode currentNetworkMode; // Reliable/Unreliable (Mode() stores)
|
||||
|
||||
Reference in New Issue
Block a user