@@ -731,6 +731,16 @@ NetworkMesh::NetworkMesh()
731731 return ;
732732 }
733733
734+ // When the anti-cheat plugin owns the transport, no Steam connection is ever
735+ // created or accepted by this mesh - StartConnectionSignalling() goes
736+ // straight to the plugin. Taking a Steam listen socket we never use only
737+ // risks colliding with a previous mesh on P2P vport 0, so skip it.
738+ if (AnticheatPlugInterface::DoesACPluginProvideSecureGameTransport ())
739+ {
740+ m_bInitialized = true ;
741+ return ;
742+ }
743+
734744 std::vector<SteamNetworkingConfigValue_t> vecListenOpts;
735745 SteamNetworkingConfigValue_t opt;
736746 opt.SetInt32 (k_ESteamNetworkingConfig_SymmetricConnect, 1 );
@@ -1124,15 +1134,18 @@ void NetworkMesh::Disconnect()
11241134 {
11251135 AnticheatPlugInterface::DisconnectAll ();
11261136 }
1127- else
1128- {
1129- if (SteamNetworkingSockets () && m_hListenSock != k_HSteamListenSocket_Invalid)
1130- {
1131- SteamNetworkingSockets ()->CloseListenSocket (m_hListenSock);
1132- }
11331137
1134- m_hListenSock = k_HSteamListenSocket_Invalid;
1138+ // The listen socket is created unconditionally in the constructor, so it must
1139+ // be released unconditionally too. Skipping this when the anti-cheat plugin
1140+ // owns the transport leaks the socket, and the next NetworkMesh then fails
1141+ // with "Already have a listen socket on P2P vport 0" - which makes
1142+ // IsInitialized() false and aborts the second lobby join.
1143+ if (SteamNetworkingSockets () && m_hListenSock != k_HSteamListenSocket_Invalid)
1144+ {
1145+ SteamNetworkingSockets ()->CloseListenSocket (m_hListenSock);
11351146 }
1147+
1148+ m_hListenSock = k_HSteamListenSocket_Invalid;
11361149}
11371150
11381151void NetworkMesh::Tick ()
0 commit comments