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 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-12 23:19:03 -05:00
co-authored by Claude Fable 5
parent 974cc6e120
commit 7ad53e03b5
3 changed files with 70 additions and 18 deletions
+48 -9
View File
@@ -65,6 +65,7 @@ namespace
unsigned long fakeIP; // host order unsigned long fakeIP; // host order
unsigned short fakeConsolePort; // host order unsigned short fakeConsolePort; // host order
unsigned short fakeGamePort; // 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) // engine-side port convention (lobby default; game port is +1)
@@ -500,14 +501,38 @@ namespace
} }
--listener->pendingCount; --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; unsigned long remote_ip_net = 0;
SteamNetworkingIPAddr remote_fake; SteamNetConnectionInfo_t info;
if (SteamNetworkingSockets()->GetRemoteFakeIPForConnection( if (SteamNetworkingSockets()->GetConnectionInfo(handle, &info))
handle, &remote_fake) == k_EResultOK)
{ {
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( ConnectionRecord *record = AddConnection(
handle, remote_ip_net, htons(listener->enginePort)); handle, remote_ip_net, htons(listener->enginePort));
@@ -676,6 +701,16 @@ Logical
SteamNetworkingUtils()->InitRelayNetworkAccess(); 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)) if (!SteamNetworkingSockets()->BeginAsyncRequestFakeIP(2))
{ {
DEBUG_STREAM << "SteamNetTransport: BeginAsyncRequestFakeIP failed - staying on TCP\n" << std::flush; 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; DEBUG_STREAM << "SteamNetTransport: SELF TEST starting\n" << std::flush;
SteamNetTransport_RegisterPeer( SteamNetTransport_RegisterPeer(
gLocalFakeAddressString, gLocalFakePorts[0], gLocalFakePorts[1]); gLocalFakeAddressString, gLocalFakePorts[0], gLocalFakePorts[1],
SteamUser()->GetSteamID().ConvertToUint64());
NetTransport *transport = &gSteamTransport; NetTransport *transport = &gSteamTransport;
NetTransport::Connection listener = transport->Listen(1501, 1); NetTransport::Connection listener = transport->Listen(1501, 1);
@@ -833,7 +869,8 @@ void
SteamNetTransport_RegisterPeer( SteamNetTransport_RegisterPeer(
const char *fake_ip, const char *fake_ip,
int fake_console_port, int fake_console_port,
int fake_game_port int fake_game_port,
unsigned __int64 steam_id
) )
{ {
SteamNetworkingIPAddr parsed; SteamNetworkingIPAddr parsed;
@@ -847,6 +884,7 @@ void
{ {
gPeers[i].fakeConsolePort = (unsigned short) fake_console_port; gPeers[i].fakeConsolePort = (unsigned short) fake_console_port;
gPeers[i].fakeGamePort = (unsigned short) fake_game_port; gPeers[i].fakeGamePort = (unsigned short) fake_game_port;
gPeers[i].steamID = steam_id;
return; return;
} }
} }
@@ -857,10 +895,11 @@ void
gPeers[gPeerCount].fakeIP = parsed.GetIPv4(); gPeers[gPeerCount].fakeIP = parsed.GetIPv4();
gPeers[gPeerCount].fakeConsolePort = (unsigned short) fake_console_port; gPeers[gPeerCount].fakeConsolePort = (unsigned short) fake_console_port;
gPeers[gPeerCount].fakeGamePort = (unsigned short) fake_game_port; gPeers[gPeerCount].fakeGamePort = (unsigned short) fake_game_port;
gPeers[gPeerCount].steamID = steam_id;
++gPeerCount; ++gPeerCount;
DEBUG_STREAM << "SteamNetTransport: peer registered: " << fake_ip DEBUG_STREAM << "SteamNetTransport: peer registered: " << fake_ip
<< " (console " << fake_console_port << ", game " << " (console " << fake_console_port << ", game "
<< fake_game_port << ")\n" << std::flush; << fake_game_port << ", id " << steam_id << ")\n" << std::flush;
} }
#endif // RP412_STEAM #endif // RP412_STEAM
+7 -3
View File
@@ -81,13 +81,17 @@ int
void void
SteamNetTransport_SetEnginePorts(int console_port); SteamNetTransport_SetEnginePorts(int console_port);
// The lobby feeds every member's FakeIP + fake ports here before // The lobby feeds every member's FakeIP + fake ports + SteamID here
// anything dials out. Registering is idempotent per IP. // 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 void
SteamNetTransport_RegisterPeer( SteamNetTransport_RegisterPeer(
const char *fake_ip, const char *fake_ip,
int fake_console_port, int fake_console_port,
int fake_game_port int fake_game_port,
unsigned __int64 steam_id
); );
#endif // RP412_STEAM #endif // RP412_STEAM
+15 -6
View File
@@ -205,7 +205,8 @@ namespace
if (members[i].published) if (members[i].published)
{ {
SteamNetTransport_RegisterPeer( 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) if (all_published && room.memberCount >= 1)
{ {
++gLastGoNonce; ++gLastGoNonce;
char go[600]; char go[800];
sprintf(go, "%d:", gLastGoNonce); sprintf(go, "%d:", gLastGoNonce);
for (int i = 0; i < room.memberCount; ++i) for (int i = 0; i < room.memberCount; ++i)
{ {
char entry[64]; char entry[96];
sprintf(entry, "%s|%d|%d;", room.members[i].ip, sprintf(entry, "%s|%d|%d|%I64u;", room.members[i].ip,
room.members[i].consolePort, room.members[i].gamePort); room.members[i].consolePort, room.members[i].gamePort,
room.members[i].id.ConvertToUint64());
if (strlen(go) + strlen(entry) < sizeof(go)) if (strlen(go) + strlen(entry) < sizeof(go))
{ {
strcat(go, entry); strcat(go, entry);
@@ -554,6 +556,7 @@ namespace
{ {
char ip[32]; char ip[32];
int console_port = 0, game_port = 0; int console_port = 0, game_port = 0;
unsigned __int64 steam_id = 0;
int n = 0; int n = 0;
while (cursor[n] != '\0' && cursor[n] != '|' && while (cursor[n] != '\0' && cursor[n] != '|' &&
n < (int) sizeof(ip) - 1) n < (int) sizeof(ip) - 1)
@@ -571,13 +574,19 @@ namespace
if (*cursor == '|') if (*cursor == '|')
{ {
game_port = atoi(++cursor); game_port = atoi(++cursor);
while (*cursor != '\0' && *cursor != '|' && *cursor != ';') ++cursor;
}
if (*cursor == '|')
{
steam_id = _strtoui64(++cursor, NULL, 10);
} }
while (*cursor != '\0' && *cursor != ';') ++cursor; while (*cursor != '\0' && *cursor != ';') ++cursor;
if (*cursor == ';') ++cursor; if (*cursor == ';') ++cursor;
if (ip[0] != '\0' && console_port > 0 && game_port > 0) 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; outcome = LobbyLaunchMember;