D1 phase 2+3: client TCP relay mode (L4NET) + 2-node verification
The pod can now run entirely over the relay: one outbound TCP connection
carries both the console protocol (egg/launch) and all game traffic
(envelope-multiplexed), so internet play works behind NAT with no port
forwarding. All new code is behind 'if (relayMode)' (BT_RELAY unset => mesh
mode byte-for-byte untouched; verified un-regressed).
Env gates (parsed once in the ctor, after WSAStartup so gethostbyname works):
- BT_RELAY=<host>:<consolePort> (host may be a DNS name; game port = +1)
- BT_SELF=<exact [pilots] entry> (NIC matching can't identify us across NAT)
L4NET changes:
- CreateConsoleHost: relay branch dials OUT to the relay console port
(bounded retry; console-less continue on a re-dial) instead of listening.
- StartConnecting: relay self-match by BT_SELF string; peers become VIRTUAL
hosts (INVALID_SOCKET, NoNetworkConnectionStatus) flipped online by the
relay's PEER_UP; then ConnectRelayGame dials the game port + HELLOs. HostID
assignment / remoteHostCount / the connection gate / app ladder unchanged.
- CheckRelay (new): the receive seam -- drains {route,length} envelopes,
synthesizes HostConnected/Disconnected from PEER_UP/DOWN (same messages the
mesh accept path routes), returns game frames envelope-stripped (they
self-identify via NetworkPacketHeader.fromHost; consumers route by payload).
- Send: game-host traffic -> relay unicast envelope (console host excluded --
its legacy protocol is relay-TERMINATED, not routed).
- ExclusiveBroadcast: build ONCE, send ONCE with the broadcast route -- the
relay fans out, killing the mesh's (N-1)x upload duplication.
- RelaySendAll (new): partial-send-safe transmit (required on the multiplexed
socket -- a partial write would desync framing for all peers).
- CheckBuffers: polls CheckRelay first; skips recv on virtual game hosts.
- RelayGameDown: relay-loss synthesizes all-peers-disconnected (match
continues peer-less; pod never exits mid-match).
- Mode(): now STORES the reliable/unreliable mode (was ignored) for the UDP
phase's authentic mode+flag routing.
Verified 2-node localhost relay (MP_RELAY.EGG, tagged [pilots]):
- both pods reach 'All connections completed!' via PEER_UP, then
RunningMission; both mechs' MakeMessages cross the relay (paint x2 each) and
bidirectional 148-byte update records flow (net-tx/net-rx traces; relay
stats tcp rx==tx, registered [2,3]); cockpit/HUD render; no crashes.
- driving one node transmits pose/damage/death frames through the relay.
- mesh smoke (no BT_RELAY): 2-node session still forms + simulates unchanged.
Plan: ~/.claude/plans/partitioned-snuggling-piglet.md. Next: UDP channel.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
bf8c2ffaae
commit
c029df6e6b
@@ -327,6 +327,30 @@ private:
|
||||
DroppedMessageHostSocket *dropped_message_host_socket
|
||||
);
|
||||
|
||||
//
|
||||
// D1 RELAY MODE (BT_RELAY=host:port + BT_SELF=<[pilots] entry>): outbound-
|
||||
// only topology -- the pod dials the relay (extended btconsole.py) for BOTH
|
||||
// the console protocol (egg/launch; legacy raw frames, direction flipped)
|
||||
// and the game traffic (envelope-framed, one TCP connection multiplexing
|
||||
// every peer; UDP for the unreliable channel in a later phase). BT_RELAY
|
||||
// unset => relayMode False => every relay branch is dead and the classic
|
||||
// mesh is untouched.
|
||||
//
|
||||
SOCKET
|
||||
RelayDialTcp(const SOCKADDR_IN &endpoint, int timeout_seconds);
|
||||
Logical
|
||||
RelaySendAll(SOCKET sock, const char *buffer, int length);
|
||||
NetworkPacket *
|
||||
BuildRelayPacket(Message *message, ClientID client, int *packet_size);
|
||||
Logical
|
||||
RelaySendFrame(int route, const NetworkPacket *packet, int packet_size);
|
||||
void
|
||||
ConnectRelayGame(HostID local_host_ID);
|
||||
void
|
||||
RelayGameDown(const char *why);
|
||||
Logical
|
||||
CheckRelay(NetworkPacket *network_packet);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Private Data
|
||||
//
|
||||
@@ -353,6 +377,23 @@ private:
|
||||
addrinfo* thisHost;
|
||||
SOCKET gameListenerSocket;
|
||||
SOCKET consoleListenerSocket;
|
||||
|
||||
// D1 relay mode state (see the private-methods block above). All inert
|
||||
// unless BT_RELAY is set.
|
||||
Logical relayMode;
|
||||
char relaySelf[64]; // BT_SELF -- our [pilots] entry string
|
||||
SOCKADDR_IN relayConsoleAddress; // relay console endpoint (BT_RELAY port)
|
||||
SOCKADDR_IN relayGameAddress; // relay game endpoint (port + 1, tcp+udp)
|
||||
SOCKET relayGameSocket; // the ONE multiplexed game connection
|
||||
SOCKET relayUdpSocket; // unreliable channel (later phase)
|
||||
char relayPad[16384]; // relay-connection reassembly buffer
|
||||
int relayPadTail;
|
||||
Logical relayConsoleDialedOnce; // first dial gets the long timeout
|
||||
Logical udpUp; // UDP HELLO acked by the relay
|
||||
unsigned long lastUdpKeepaliveTick;
|
||||
unsigned long udpTxSeq;
|
||||
unsigned long udpRxSeq[64]; // per-fromHost last-seen sequence
|
||||
NetworkMode currentNetworkMode; // Reliable/Unreliable (Mode() stores)
|
||||
};
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~ L4NetworkManager inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Reference in New Issue
Block a user