From 7eb765696d5a9170821b61a519dea1d8ef251881 Mon Sep 17 00:00:00 2001 From: Cyd Date: Sun, 12 Jul 2026 22:45:45 -0500 Subject: [PATCH] Steam transport: status callbacks now dispatch (CCallback, not config ptr) Root cause of the three-machine failure found and fixed. The connection-status callback was registered as a per-connection config value pointer - which the steam_api flavor of the library never dispatches (only the standalone lib does). So members never saw the incoming connection request, never accepted, and the owner timed out after 120s of state-5 retries. Valve''s own SpaceWar example registers SteamNetConnectionStatusChangedCallback_t with STEAM_CALLBACK; the transport now does the same through a CCallback listener constructed after SteamAPI_Init. Proven with the new RP412STEAMSELFTEST=1 loopback: one machine listens on its fake console port and dials its own FakeIP - the log now shows incoming/accept/connected on both ends and a verified data round trip, connect succeeded on attempt 1. Co-Authored-By: Claude Fable 5 --- MUNGA_L4/L4STEAMTRANSPORT.cpp | 105 +++++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 15 deletions(-) diff --git a/MUNGA_L4/L4STEAMTRANSPORT.cpp b/MUNGA_L4/L4STEAMTRANSPORT.cpp index 6e38734..ba4c55a 100644 --- a/MUNGA_L4/L4STEAMTRANSPORT.cpp +++ b/MUNGA_L4/L4STEAMTRANSPORT.cpp @@ -157,10 +157,13 @@ namespace } //--------------------------------------------------------------- - // Status-changed callback (fires inside SteamAPI_RunCallbacks on - // the game thread): accept incoming, queue connected, mark drops + // Status-changed handler (fires inside SteamAPI_RunCallbacks on + // the game thread): accept incoming, queue connected, mark drops. + // Registered through the CCallback dispatcher below - the + // config-value function pointer is NOT dispatched by the steam_api + // flavor of the library (Valve's own example uses STEAM_CALLBACK). //--------------------------------------------------------------- - void __cdecl + void OnConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t *status) { switch (status->m_info.m_eState) @@ -219,6 +222,25 @@ namespace } } + //--------------------------------------------------------------- + // The callback listener: constructed after SteamAPI_Init so the + // CCallback registration lands in a live callback manager + //--------------------------------------------------------------- + class SteamTransportCallbacks + { + public: + STEAM_CALLBACK(SteamTransportCallbacks, OnStatusChanged, + SteamNetConnectionStatusChangedCallback_t); + }; + + void SteamTransportCallbacks::OnStatusChanged( + SteamNetConnectionStatusChangedCallback_t *status) + { + OnConnectionStatusChanged(status); + } + + SteamTransportCallbacks *gCallbacks = NULL; + //--------------------------------------------------------------- // The transport //--------------------------------------------------------------- @@ -306,11 +328,6 @@ namespace << inet_ntoa(remote->sin_addr) << ":" << ntohs(remote->sin_port) << " (fake port " << fake_port << ")...\n" << std::flush; - SteamNetworkingConfigValue_t option; - option.SetPtr( - k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged, - (void *) &OnConnectionStatusChanged); - SteamNetworkingIPAddr target; target.Clear(); target.SetIPv4(remote_fake_ip, fake_port); @@ -323,7 +340,7 @@ namespace { ++attempt; HSteamNetConnection handle = - SteamNetworkingSockets()->ConnectByIPAddress(target, 1, &option); + SteamNetworkingSockets()->ConnectByIPAddress(target, 0, NULL); if (handle == k_HSteamNetConnection_Invalid) { DEBUG_STREAM << "SteamNetTransport: ConnectByIPAddress refused the call\n" << std::flush; @@ -419,13 +436,8 @@ namespace << local_port << " (fake port " << gLocalFakePorts[index] << ")...\n" << std::flush; - SteamNetworkingConfigValue_t option; - option.SetPtr( - k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged, - (void *) &OnConnectionStatusChanged); - HSteamListenSocket handle = - SteamNetworkingSockets()->CreateListenSocketP2PFakeIP(index, 1, &option); + SteamNetworkingSockets()->CreateListenSocketP2PFakeIP(index, 0, NULL); if (handle == k_HSteamListenSocket_Invalid) { return InvalidConnection; @@ -615,6 +627,12 @@ Logical return False; } + // connection status arrives through the CCallback dispatcher + if (gCallbacks == NULL) + { + gCallbacks = new SteamTransportCallbacks; + } + SteamNetworkingUtils()->InitRelayNetworkAccess(); if (!SteamNetworkingSockets()->BeginAsyncRequestFakeIP(2)) @@ -664,6 +682,63 @@ Logical gSteamReady = True; NetTransport_Set(&gSteamTransport); + + // + // dev: RP412STEAMSELFTEST=1 loops a connection back to ourselves, + // proving the listen/accept/connect/send/receive machinery (and + // the status-callback dispatch) without a second machine + // + const char *self_test = getenv("RP412STEAMSELFTEST"); + if (self_test != NULL && atoi(self_test) != 0) + { + DEBUG_STREAM << "SteamNetTransport: SELF TEST starting\n" << std::flush; + SteamNetTransport_RegisterPeer( + gLocalFakeAddressString, gLocalFakePorts[0], gLocalFakePorts[1]); + + NetTransport *transport = &gSteamTransport; + NetTransport::Connection listener = transport->Listen(1501, 1); + + SOCKADDR_IN self_address; + transport->Resolve(gLocalFakeAddressString, &self_address); + self_address.sin_port = htons(1501); + NetTransport::Connection outgoing = transport->Connect(&self_address, 0); + + NetTransport::Connection incoming = NetTransport::InvalidConnection; + DWORD accept_deadline = GetTickCount() + 10 * 1000; + while (incoming == NetTransport::InvalidConnection && + (LONG)(GetTickCount() - accept_deadline) < 0) + { + incoming = transport->Accept(listener); + Sleep(50); + } + + Logical ok = False; + if (outgoing != NetTransport::InvalidConnection && + incoming != NetTransport::InvalidConnection) + { + transport->Send(outgoing, "PING", 4); + char buffer[16]; + DWORD recv_deadline = GetTickCount() + 5 * 1000; + while ((LONG)(GetTickCount() - recv_deadline) < 0) + { + if (transport->Receive(incoming, buffer, sizeof(buffer)) == 4) + { + ok = (memcmp(buffer, "PING", 4) == 0); + break; + } + Sleep(25); + } + } + DEBUG_STREAM << "SteamNetTransport: SELF TEST " + << (ok ? "PASSED" : "FAILED") + << " (out " << (outgoing != NetTransport::InvalidConnection) + << ", in " << (incoming != NetTransport::InvalidConnection) + << ")\n" << std::flush; + + // leave the session pristine for the real race + gSteamTransport.Cleanup(); + gPeerCount = 0; + } return True; }