diff --git a/MUNGA_L4/L4STEAMTRANSPORT.cpp b/MUNGA_L4/L4STEAMTRANSPORT.cpp index ba4c55a..a718140 100644 --- a/MUNGA_L4/L4STEAMTRANSPORT.cpp +++ b/MUNGA_L4/L4STEAMTRANSPORT.cpp @@ -42,6 +42,11 @@ namespace unsigned short enginePort; // host order HSteamNetConnection pending[maxPending]; int pendingCount; + // The engine closes listeners with TCP semantics (accepted + // connections survive). Steam's CloseListenSocket kills the + // accepted connections ungracefully - so an engine close only + // marks the listener here; the real close waits for Cleanup. + Logical closed; }; struct ConnectionRecord @@ -171,6 +176,18 @@ namespace case k_ESteamNetworkingConnectionState_Connecting: if (status->m_info.m_hListenSocket != k_HSteamListenSocket_Invalid) { + // no more callers once the engine closed the listener + ListenerRecord *listener = FindListener(status->m_info.m_hListenSocket); + if (listener == NULL || listener->closed) + { + DEBUG_STREAM << "SteamNetTransport: incoming [" + << status->m_info.m_szConnectionDescription + << "] on a closed listener - rejecting\n" << std::flush; + SteamNetworkingSockets()->CloseConnection( + status->m_hConn, 0, "listener closed", false); + break; + } + // incoming: accept immediately, queue it when Connected DEBUG_STREAM << "SteamNetTransport: incoming [" << status->m_info.m_szConnectionDescription @@ -411,6 +428,20 @@ namespace return InvalidConnection; } + // a logically-closed listener for this engine port reopens + // (the Steam socket outlives engine closes; see Close) + for (int existing = 0; existing < gListenerCount; ++existing) + { + if (gListeners[existing].enginePort == (unsigned short) local_port) + { + gListeners[existing].closed = False; + gListeners[existing].pendingCount = 0; + DEBUG_STREAM << "SteamNetTransport: reopened listener on engine port " + << local_port << "\n" << std::flush; + return (Connection) gListeners[existing].handle; + } + } + // engine port -> fake port index, in order of appearance: // the console channel always listens first, the mesh second int index = -1; @@ -457,7 +488,7 @@ namespace SteamAPI_RunCallbacks(); ListenerRecord *listener = FindListener((HSteamListenSocket) listener_handle); - if (listener == NULL || listener->pendingCount == 0) + if (listener == NULL || listener->closed || listener->pendingCount == 0) { return InvalidConnection; } @@ -493,9 +524,19 @@ namespace ListenerRecord *listener = FindListener((HSteamListenSocket) connection); if (listener != NULL) { - SteamNetworkingSockets()->CloseListenSocket(listener->handle); - *listener = gListeners[gListenerCount - 1]; - --gListenerCount; + // + // TCP semantics: accepted connections must survive a + // listener close. Steam's CloseListenSocket kills them + // ungracefully, so only mark it closed (rejecting any + // new callers) and destroy it for real in Cleanup. + // + listener->closed = True; + for (int i = 0; i < listener->pendingCount; ++i) + { + SteamNetworkingSockets()->CloseConnection( + listener->pending[i], 0, "listener closed", false); + } + listener->pendingCount = 0; return; } SteamNetworkingSockets()->CloseConnection( @@ -713,6 +754,7 @@ Logical } Logical ok = False; + Logical survives_close = False; if (outgoing != NetTransport::InvalidConnection && incoming != NetTransport::InvalidConnection) { @@ -728,11 +770,32 @@ Logical } Sleep(25); } + + // + // The arcade engine closes a listener right after + // accepting - the accepted connection MUST survive + // (Steam kills children on CloseListenSocket; the + // transport defers the real close to Cleanup) + // + transport->Close(listener); + transport->Send(incoming, "PONG", 4); + recv_deadline = GetTickCount() + 5 * 1000; + while ((LONG)(GetTickCount() - recv_deadline) < 0) + { + if (transport->Receive(outgoing, buffer, sizeof(buffer)) == 4) + { + survives_close = (memcmp(buffer, "PONG", 4) == 0); + break; + } + Sleep(25); + } } DEBUG_STREAM << "SteamNetTransport: SELF TEST " - << (ok ? "PASSED" : "FAILED") + << ((ok && survives_close) ? "PASSED" : "FAILED") << " (out " << (outgoing != NetTransport::InvalidConnection) << ", in " << (incoming != NetTransport::InvalidConnection) + << ", ping " << ok + << ", survives listener close " << survives_close << ")\n" << std::flush; // leave the session pristine for the real race