From 7ad53e03b5e8eaab13375f512ce4592eb346bf0b Mon Sep 17 00:00:00 2001 From: Cyd Date: Sun, 12 Jul 2026 23:19:03 -0500 Subject: [PATCH] Steam transport: identity-resolved accepts + load-proof timeout Round four reached one step from the race: eggs delivered, both members ACKed with complete meshes, one member entered LoadingMission. Two failures remained, both now fixed. One: Steam reports incoming callers under locally-allocated ALIAS FakeIPs, not their global ones - the owner accepted both mesh legs but its identity check compared the alias against the egg address and never counted the connections (no Connected to GameMachineHost on the owner). The peer table now carries each member SteamID (lobby go roster gained a field) and Accept resolves the caller identity back to the global FakeIP the egg promised. Two: mission load stalls the game thread for 10-30s with nothing pumping, and Steam default 10s connected-timeout sheared every connection mid-load (end reason 4001, rx ages 11.5-20.5s - right at load duration). Connected timeout is now 90s; TCP never timed out an idle arcade link and races pump every frame once running. Self-test still green (ping, survives listener close). Co-Authored-By: Claude Fable 5 --- MUNGA_L4/L4STEAMTRANSPORT.cpp | 57 +++++++++++++++++++++++++++++------ MUNGA_L4/L4STEAMTRANSPORT.h | 10 ++++-- RP_L4/RPL4LOBBY.cpp | 21 +++++++++---- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/MUNGA_L4/L4STEAMTRANSPORT.cpp b/MUNGA_L4/L4STEAMTRANSPORT.cpp index a718140..0d19d3b 100644 --- a/MUNGA_L4/L4STEAMTRANSPORT.cpp +++ b/MUNGA_L4/L4STEAMTRANSPORT.cpp @@ -65,6 +65,7 @@ namespace unsigned long fakeIP; // host order unsigned short fakeConsolePort; // host order unsigned short fakeGamePort; // host order + unsigned __int64 steamID; // identity -> global FakeIP on accept }; // engine-side port convention (lobby default; game port is +1) @@ -500,14 +501,38 @@ namespace } --listener->pendingCount; - // the remote engine port is the same engine port we accept - // on: all pods share the -net convention under Steam + // + // The remote engine port is the same engine port we accept + // on (all pods share the -net convention under Steam). The + // caller's address: Steam reports incoming connections + // under a locally-allocated ALIAS FakeIP, so resolve the + // caller's identity through the peer table to the GLOBAL + // FakeIP the egg's [pilots] list promised - the engine's + // mesh identity checks compare against that. + // unsigned long remote_ip_net = 0; - SteamNetworkingIPAddr remote_fake; - if (SteamNetworkingSockets()->GetRemoteFakeIPForConnection( - handle, &remote_fake) == k_EResultOK) + SteamNetConnectionInfo_t info; + if (SteamNetworkingSockets()->GetConnectionInfo(handle, &info)) { - remote_ip_net = htonl(remote_fake.GetIPv4()); + unsigned __int64 caller_id = info.m_identityRemote.GetSteamID64(); + for (int p = 0; p < gPeerCount; ++p) + { + if (gPeers[p].steamID != 0 && gPeers[p].steamID == caller_id) + { + remote_ip_net = htonl(gPeers[p].fakeIP); + break; + } + } + } + if (remote_ip_net == 0) + { + // unregistered caller: fall back to the alias address + SteamNetworkingIPAddr remote_fake; + if (SteamNetworkingSockets()->GetRemoteFakeIPForConnection( + handle, &remote_fake) == k_EResultOK) + { + remote_ip_net = htonl(remote_fake.GetIPv4()); + } } ConnectionRecord *record = AddConnection( handle, remote_ip_net, htons(listener->enginePort)); @@ -676,6 +701,16 @@ Logical SteamNetworkingUtils()->InitRelayNetworkAccess(); + // + // Mission load stalls the game thread for 10-30s with nothing + // pumping - Steam's default 10s connected-timeout would shear + // every connection mid-load (seen live: end reason 4001 with rx + // ages right at load duration). TCP never timed out idle arcade + // links; 90s keeps that spirit. + // + SteamNetworkingUtils()->SetGlobalConfigValueInt32( + k_ESteamNetworkingConfig_TimeoutConnected, 90 * 1000); + if (!SteamNetworkingSockets()->BeginAsyncRequestFakeIP(2)) { DEBUG_STREAM << "SteamNetTransport: BeginAsyncRequestFakeIP failed - staying on TCP\n" << std::flush; @@ -734,7 +769,8 @@ Logical { DEBUG_STREAM << "SteamNetTransport: SELF TEST starting\n" << std::flush; SteamNetTransport_RegisterPeer( - gLocalFakeAddressString, gLocalFakePorts[0], gLocalFakePorts[1]); + gLocalFakeAddressString, gLocalFakePorts[0], gLocalFakePorts[1], + SteamUser()->GetSteamID().ConvertToUint64()); NetTransport *transport = &gSteamTransport; NetTransport::Connection listener = transport->Listen(1501, 1); @@ -833,7 +869,8 @@ void SteamNetTransport_RegisterPeer( const char *fake_ip, int fake_console_port, - int fake_game_port + int fake_game_port, + unsigned __int64 steam_id ) { SteamNetworkingIPAddr parsed; @@ -847,6 +884,7 @@ void { gPeers[i].fakeConsolePort = (unsigned short) fake_console_port; gPeers[i].fakeGamePort = (unsigned short) fake_game_port; + gPeers[i].steamID = steam_id; return; } } @@ -857,10 +895,11 @@ void gPeers[gPeerCount].fakeIP = parsed.GetIPv4(); gPeers[gPeerCount].fakeConsolePort = (unsigned short) fake_console_port; gPeers[gPeerCount].fakeGamePort = (unsigned short) fake_game_port; + gPeers[gPeerCount].steamID = steam_id; ++gPeerCount; DEBUG_STREAM << "SteamNetTransport: peer registered: " << fake_ip << " (console " << fake_console_port << ", game " - << fake_game_port << ")\n" << std::flush; + << fake_game_port << ", id " << steam_id << ")\n" << std::flush; } #endif // RP412_STEAM diff --git a/MUNGA_L4/L4STEAMTRANSPORT.h b/MUNGA_L4/L4STEAMTRANSPORT.h index f6f3b8a..0a333bc 100644 --- a/MUNGA_L4/L4STEAMTRANSPORT.h +++ b/MUNGA_L4/L4STEAMTRANSPORT.h @@ -81,13 +81,17 @@ int void SteamNetTransport_SetEnginePorts(int console_port); -// The lobby feeds every member's FakeIP + fake ports here before -// anything dials out. Registering is idempotent per IP. +// The lobby feeds every member's FakeIP + fake ports + SteamID here +// before anything dials out. The SteamID matters on accept: Steam +// reports incoming callers under locally-allocated alias addresses, +// so the transport maps the caller's identity back to the global +// FakeIP the egg promised. Registering is idempotent per IP. void SteamNetTransport_RegisterPeer( const char *fake_ip, int fake_console_port, - int fake_game_port + int fake_game_port, + unsigned __int64 steam_id ); #endif // RP412_STEAM diff --git a/RP_L4/RPL4LOBBY.cpp b/RP_L4/RPL4LOBBY.cpp index cbbc42b..929e871 100644 --- a/RP_L4/RPL4LOBBY.cpp +++ b/RP_L4/RPL4LOBBY.cpp @@ -205,7 +205,8 @@ namespace if (members[i].published) { SteamNetTransport_RegisterPeer( - members[i].ip, members[i].consolePort, members[i].gamePort); + members[i].ip, members[i].consolePort, members[i].gamePort, + members[i].id.ConvertToUint64()); } } } @@ -513,13 +514,14 @@ namespace if (all_published && room.memberCount >= 1) { ++gLastGoNonce; - char go[600]; + char go[800]; sprintf(go, "%d:", gLastGoNonce); for (int i = 0; i < room.memberCount; ++i) { - char entry[64]; - sprintf(entry, "%s|%d|%d;", room.members[i].ip, - room.members[i].consolePort, room.members[i].gamePort); + char entry[96]; + sprintf(entry, "%s|%d|%d|%I64u;", room.members[i].ip, + room.members[i].consolePort, room.members[i].gamePort, + room.members[i].id.ConvertToUint64()); if (strlen(go) + strlen(entry) < sizeof(go)) { strcat(go, entry); @@ -554,6 +556,7 @@ namespace { char ip[32]; int console_port = 0, game_port = 0; + unsigned __int64 steam_id = 0; int n = 0; while (cursor[n] != '\0' && cursor[n] != '|' && n < (int) sizeof(ip) - 1) @@ -571,13 +574,19 @@ namespace if (*cursor == '|') { game_port = atoi(++cursor); + while (*cursor != '\0' && *cursor != '|' && *cursor != ';') ++cursor; + } + if (*cursor == '|') + { + steam_id = _strtoui64(++cursor, NULL, 10); } while (*cursor != '\0' && *cursor != ';') ++cursor; if (*cursor == ';') ++cursor; if (ip[0] != '\0' && console_port > 0 && game_port > 0) { - SteamNetTransport_RegisterPeer(ip, console_port, game_port); + SteamNetTransport_RegisterPeer( + ip, console_port, game_port, steam_id); } } outcome = LobbyLaunchMember;