diff --git a/src/common/steamnetworkingsockets_messages.proto b/src/common/steamnetworkingsockets_messages.proto index 6d6f5d4..ef0218f 100644 --- a/src/common/steamnetworkingsockets_messages.proto +++ b/src/common/steamnetworkingsockets_messages.proto @@ -285,19 +285,25 @@ message CMsgSteamNetworkingP2PRendezvous }; // A summary of what happened in an ICE session, for analytics -message CMsgSteamNetworkingSocketsICESessionSummary +message CMsgSteamNetworkingICESessionSummary { + // What was the overall outcome of ICE? + // Should always be set. Will be zero if + // ICE succeeded + optional uint32 failure_reason_code = 7; // ESteamNetConnectionEnd (or special internal value), if we failed in some way + // What types of candidates were we able to gather? - // Should always be present - optional uint32 local_candidate_types = 1; + // Should always be present if this message is sent + optional uint32 local_candidate_types = 1; // k_EICECandidate_xxx // What types of candidates did we receive from peer? - optional uint32 remote_candidate_types = 2; + optional uint32 remote_candidate_types = 2; // k_EICECandidate_xxx // Initial route type, and ping, when when we first pierced NAT. // Will not be present if we never pierced NAT optional uint32 initial_route_kind = 3; // ESteamNetTransportKind optional uint32 initial_ping = 4; + optional uint32 nat_traversal_ms = 5; // How long did it take to get a NAT traversal result (either success or failure) }; // Do not remove this comment due to a bug on the Mac OS X protobuf compiler diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp index 4160727..e776896 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp @@ -2805,10 +2805,32 @@ void CSteamNetworkConnectionBase::ConnectionTimedOut( SteamNetworkingMicrosecond ESteamNetConnectionEnd nReasonCode; ConnectionEndDebugMsg msg; - // Set some generic defaults using our base class version, so - // this function will work even if the derived class forgets to - // call the base class. - CSteamNetworkConnectionBase::GuessTimeoutReason( nReasonCode, msg, usecNow ); + // Set some generic defaults. + nReasonCode = k_ESteamNetConnectionEnd_Misc_Timeout; + switch ( GetState() ) + { + case k_ESteamNetworkingConnectionState_Connecting: + // Should use this more specific reason code at somepoint, when I add an API to get localized error messages + //if ( !m_bConnectionInitiatedRemotely && m_statsEndToEnd.m_usecTimeLastRecv == 0 ) + //{ + // nReasonCode = k_ESteamNetConnectionEnd_Misc_ServerNeverReplied; + // V_strcpy_safe( msg, "" ); + //} + //else + //{ + V_strcpy_safe( msg, "Timed out attempting to connect" ); + //} + break; + + case k_ESteamNetworkingConnectionState_FindingRoute: + nReasonCode = k_ESteamNetConnectionEnd_Misc_P2P_Rendezvous; + V_strcpy_safe( msg, "Timed out attempting to negotiate rendezvous" ); + break; + + default: + V_strcpy_safe( msg, "Connection dropped" ); + break; + } // Check if connection has a more enlightened understanding of what's wrong GuessTimeoutReason( nReasonCode, msg, usecNow ); @@ -2819,23 +2841,7 @@ void CSteamNetworkConnectionBase::ConnectionTimedOut( SteamNetworkingMicrosecond void CSteamNetworkConnectionBase::GuessTimeoutReason( ESteamNetConnectionEnd &nReasonCode, ConnectionEndDebugMsg &msg, SteamNetworkingMicroseconds usecNow ) { - NOTE_UNUSED( usecNow ); - - nReasonCode = k_ESteamNetConnectionEnd_Misc_Timeout; - switch ( GetState() ) - { - case k_ESteamNetworkingConnectionState_Connecting: - V_strcpy_safe( msg, "Timed out attempting to connect" ); - break; - - case k_ESteamNetworkingConnectionState_FindingRoute: - V_strcpy_safe( msg, "Timed out attempting to negotiate rendezvous" ); - break; - - default: - V_strcpy_safe( msg, "Connection dropped" ); - break; - } + // No enlightenments at base class } void CSteamNetworkConnectionBase::UpdateSpeeds( int nTXSpeed, int nRXSpeed ) diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.cpp index 73c562e..d2b0d7f 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.cpp @@ -95,6 +95,7 @@ CSteamNetworkConnectionP2P::CSteamNetworkConnectionP2P( CSteamNetworkingSockets m_nRemoteVirtualPort = -1; m_idxMapIncomingP2PConnections = -1; m_pSignaling = nullptr; + m_usecWhenStartedFindingRoute = 0; m_usecNextEvaluateTransport = k_nThinkTime_ASAP; m_bTransportSticky = false; @@ -111,7 +112,6 @@ CSteamNetworkConnectionP2P::CSteamNetworkConnectionP2P( CSteamNetworkingSockets #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE m_pTransportICE = nullptr; m_pTransportICEPendingDelete = nullptr; - m_nICECloseCode = 0; m_szICECloseMsg[ 0 ] = '\0'; #endif } @@ -249,7 +249,7 @@ void CSteamNetworkConnectionP2P::CheckInitICE() Assert( !m_pTransportICEPendingDelete ); // Did we already fail? - if ( m_nICECloseCode != 0 ) + if ( GetICEFailureCode() != 0 ) return; int P2P_Transport_ICE_Enable = m_connectionConfig.m_P2P_Transport_ICE_Enable.Get(); @@ -301,7 +301,7 @@ void CSteamNetworkConnectionP2P::CheckInitICE() #endif if ( !g_SteamNetworkingSockets_CreateICESessionFunc ) { - ICEFailed( k_nICECloseCode_Local_FailedInit, "No ICE session factory" ); + ICEFailed( k_ESteamNetConnectionEnd_Misc_InternalError, "No ICE session factory" ); return; } } @@ -321,10 +321,144 @@ void CSteamNetworkConnectionP2P::CheckInitICE() // If we're still all good, then add it to the list of options if ( m_pTransportICE ) + { m_vecAvailableTransports.push_back( m_pTransportICE ); + + // Set a field in the ice session summary message, + // which is how we will remember that we did attempt to use ICE + Assert( !m_msgICESessionSummary.has_local_candidate_types() ); + m_msgICESessionSummary.set_local_candidate_types( 0 ); + } #endif } + +void CSteamNetworkConnectionP2P::EnsureICEFailureReasonSet( SteamNetworkingMicroseconds usecNow ) +{ +#ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE + + // Already have a reason? + if ( m_msgICESessionSummary.has_failure_reason_code() ) + return; + + // If we never tried ICE, then there's no "failure"! + if ( !m_msgICESessionSummary.has_local_candidate_types() ) + return; + + // Classify failure, and make it permanent + ESteamNetConnectionEnd nReasonCode; + GuessICEFailureReason( nReasonCode, m_szICECloseMsg, usecNow ); + m_msgICESessionSummary.set_failure_reason_code( nReasonCode ); + int nSeverity = ( nReasonCode != 0 && nReasonCode != k_nICECloseCode_Aborted ) ? k_ESteamNetworkingSocketsDebugOutputType_Msg : k_ESteamNetworkingSocketsDebugOutputType_Verbose; + SpewTypeGroup( nSeverity, LogLevel_P2PRendezvous(), "[%s] Guessed ICE failure to be %d: %s\n", + GetDescription(), nReasonCode, m_szICECloseMsg ); + +#endif +} + +#ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE +void CSteamNetworkConnectionP2P::GuessICEFailureReason( ESteamNetConnectionEnd &nReasonCode, ConnectionEndDebugMsg &msg, SteamNetworkingMicroseconds usecNow ) +{ + // Already have a reason? + if ( m_msgICESessionSummary.has_failure_reason_code() ) + { + nReasonCode = ESteamNetConnectionEnd( m_msgICESessionSummary.failure_reason_code() ); + V_strcpy_safe( msg, m_szICECloseMsg ); + return; + } + + // This should not be called if we never even tried + Assert( m_msgICESessionSummary.has_local_candidate_types() ); + + // This ought to be called before we cleanup and destroy the info we need + Assert( m_pTransportICE ); + + // If we are connected right now, then there is no problem! + if ( m_pTransportICE && !m_pTransportICE->m_bNeedToConfirmEndToEndConnectivity ) + { + nReasonCode = k_ESteamNetConnectionEnd_Invalid; + V_strcpy_safe( msg, "OK" ); + return; + } + + // Did we ever pierce NAT? If so, then we just dropped connection. + if ( m_msgICESessionSummary.has_nat_traversal_ms() ) + { + nReasonCode = k_ESteamNetConnectionEnd_Misc_Timeout; + V_strcpy_safe( msg, "ICE connection dropped after successful negotiation" ); + return; + } + + // OK, looks like we never pierced NAT. Try to figure out why. + const int nAllowedTypes = m_pTransportICE ? m_pTransportICE->m_nAllowedCandidateTypes : 0; + const int nGatheredTypes = m_msgICESessionSummary.local_candidate_types(); + const int nFailedToGatherTypes = nAllowedTypes & ~nGatheredTypes; + const int nRemoteTypes = m_msgICESessionSummary.remote_candidate_types(); + + // Terminated prematurely? Presumably the higher level code hs a reason, + // and so this will only be used for analytics. + if ( m_usecWhenStartedFindingRoute == 0 || m_usecWhenStartedFindingRoute+5*k_nMillion > usecNow ) + { + nReasonCode = ESteamNetConnectionEnd( k_nICECloseCode_Aborted ); + V_strcpy_safe( msg, "NAT traversal aborted" ); + return; + } + + // Problem on our end? + if ( nFailedToGatherTypes & (k_EICECandidate_Any_HostPrivate|k_EICECandidate_Any_HostPublic) ) + { + // We should always be able to collect these sorts of candidates! + nReasonCode = k_ESteamNetConnectionEnd_Misc_InternalError; + V_strcpy_safe( msg, "Never gathered *any* host candidates?" ); + return; + } + + // Never received *any* candidates from them? + if ( nRemoteTypes == 0 ) + { + nReasonCode = k_ESteamNetConnectionEnd_Misc_Generic; + V_strcpy_safe( msg, "Never received any remote candidates" ); + return; + } + + // We failed to STUN? + if ( !( nGatheredTypes & k_EICECandidate_Any_Reflexive ) ) + { + if ( m_connectionConfig.m_P2P_STUN_ServerList.Get().empty() ) + { + nReasonCode = k_ESteamNetConnectionEnd_Misc_InternalError; + V_strcpy_safe( msg, "No configured STUN servers" ); + return; + } + nReasonCode = k_ESteamNetConnectionEnd_Local_P2P_ICE_NoPublicAddresses; + V_strcpy_safe( msg, "Failed to determine our public address via STUN" ); + return; + } + + // FIXME - we should probably handle this as a special case. TURN candidates + // should basically always work + //if ( (nAllowedTypes|nGatheredTypes) | k_EICECandidate_Any_Relay ) + //{ + //} + + // Any candidates from remote host that we really ought to have been able to talk to? + if ( !(nRemoteTypes & ( k_EICECandidate_IPv4_HostPublic|k_EICECandidate_Any_Reflexive|k_EICECandidate_Any_Relay) ) ) + { + nReasonCode = k_ESteamNetConnectionEnd_Remote_P2P_ICE_NoPublicAddresses; + V_strcpy_safe( msg, "No public or relay candidates from remote host" ); + return; + } + + // NOTE: in theory, we could haveIPv4 vs IPv6 capabilities mismatch. In practice + // does that ever happen? + + // OK, both sides shared reflexive candidates, but we still failed? This is probably + // a firewall thing + nReasonCode = k_ESteamNetConnectionEnd_Misc_P2P_NAT_Firewall; + V_strcpy_safe( msg, "NAT traversal failed" ); +} +#endif + void CSteamNetworkConnectionP2P::CheckCleanupICE() { #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE @@ -364,39 +498,29 @@ void CSteamNetworkConnectionP2P::DestroyICENow() } #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE - void CSteamNetworkConnectionP2P::ICEFailed( int nReasonCode, const char *pszReason ) { SteamDatagramTransportLock::AssertHeldByCurrentThread(); // Remember reason code, if we didn't already set one - if ( m_nICECloseCode == 0 ) + if ( GetICEFailureCode() == 0 ) { SpewMsgGroup( LogLevel_P2PRendezvous(), "[%s] ICE failed %d %s\n", GetDescription(), nReasonCode, pszReason ); - m_nICECloseCode = nReasonCode; + m_msgICESessionSummary.set_failure_reason_code( nReasonCode ); V_strcpy_safe( m_szICECloseMsg, pszReason ); } - QueueDestroyICE(); -} - -void CSteamNetworkConnectionP2P::QueueDestroyICE() -{ - if ( !m_pTransportICE ) - return; - // Queue for deletion if ( !m_pTransportICEPendingDelete ) { m_pTransportICEPendingDelete = m_pTransportICE; m_pTransportICE = nullptr; + + // Make sure we clean ourselves up as soon as it is safe to do so + SetNextThinkTimeASAP(); } - - // Make sure we clean ourselves up as soon as it is safe to do so - SetNextThinkTimeASAP(); } - -#endif // #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE +#endif void CSteamNetworkConnectionP2P::FreeResources() { @@ -490,19 +614,32 @@ void CSteamNetworkConnectionP2P::TransportEndToEndConnectivityChanged( CConnecti // Schedule us to wake up immediately and deal with it. m_usecNextEvaluateTransport = k_nThinkTime_ASAP; SetNextThinkTimeASAP(); + + // Check if this is the first time ICE was successful + #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE + if ( !pTransport->m_bNeedToConfirmEndToEndConnectivity && pTransport == m_pTransportICE && !m_msgICESessionSummary.has_nat_traversal_ms() ) + { + if ( m_usecWhenStartedFindingRoute == 0 ) + { + AssertMsg1( false, "[%s] We are confirming end-to-end connectivity, but usecWhenStartedFindingRoute == 0", GetDescription() ); + } + else + { + m_msgICESessionSummary.set_nat_traversal_ms( ( usecNow - m_usecWhenStartedFindingRoute + 500 ) / 1000 ); + } + } + #endif } void CSteamNetworkConnectionP2P::ConnectionStateChanged( ESteamNetworkingConnectionState eOldState ) { + SteamNetworkingMicroseconds usecNow = SteamNetworkingSockets_GetLocalTimestamp(); + // NOTE: Do not call base class, because it it going to // call TransportConnectionStateChanged on whatever transport is active. // We don't want that here. - // Inform transports - for ( CConnectionTransportP2PBase *pTransportP2P: m_vecAvailableTransports ) - pTransportP2P->m_pSelfAsConnectionTransport->TransportConnectionStateChanged( eOldState ); - - // Reset timer to evaluate transport at certain times + // Take action at certain transitions switch ( GetState() ) { case k_ESteamNetworkingConnectionState_Dead: @@ -513,17 +650,26 @@ void CSteamNetworkConnectionP2P::ConnectionStateChanged( ESteamNetworkingConnect case k_ESteamNetworkingConnectionState_ClosedByPeer: case k_ESteamNetworkingConnectionState_FinWait: + EnsureICEFailureReasonSet( usecNow ); + break; + case k_ESteamNetworkingConnectionState_Linger: break; case k_ESteamNetworkingConnectionState_ProblemDetectedLocally: + EnsureICEFailureReasonSet( usecNow ); // If we fail during these states, send a signal to Steam, for analytics if ( eOldState == k_ESteamNetworkingConnectionState_Connecting || eOldState == k_ESteamNetworkingConnectionState_FindingRoute ) - SendConnectionClosedSignal( SteamNetworkingSockets_GetLocalTimestamp() ); + SendConnectionClosedSignal( usecNow ); break; case k_ESteamNetworkingConnectionState_FindingRoute: + m_usecWhenStartedFindingRoute = usecNow; + // | + // | + // V + // FALLTHROUGH case k_ESteamNetworkingConnectionState_Connecting: m_bTransportSticky = false; // Not sure how we could have set this flag, but make sure and clear it // | @@ -540,6 +686,10 @@ void CSteamNetworkConnectionP2P::ConnectionStateChanged( ESteamNetworkingConnect break; } + + // Inform transports + for ( CConnectionTransportP2PBase *pTransportP2P: m_vecAvailableTransports ) + pTransportP2P->m_pSelfAsConnectionTransport->TransportConnectionStateChanged( eOldState ); } void CSteamNetworkConnectionP2P::ThinkConnection( SteamNetworkingMicroseconds usecNow ) @@ -1080,15 +1230,8 @@ bool CSteamNetworkConnectionP2P::ProcessSignal( const CMsgSteamNetworkingP2PRend #endif #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE - if ( m_pTransportICE && !msg.ice_enabled() ) - { - // The lack of any message at all (even an empty one) means that they - // will not support ICE, so we can destroy our transport - SpewMsgGroup( LogLevel_P2PRendezvous(), "[%s] Destroying ICE transport, peer rendezvous indicates they will not use it\n", GetDescription() ); - m_nICECloseCode = k_nICECloseCode_Remote_NotEnabled; - V_strcpy_safe( m_szICECloseMsg, "Peer sent signal without ice_enabled set" ); - DestroyICENow(); - } + if ( !msg.ice_enabled() ) + ICEFailed( k_nICECloseCode_Remote_NotEnabled, "Peer sent signal without ice_enabled set" ); #endif // Check for acking reliable messages @@ -1140,7 +1283,7 @@ bool CSteamNetworkConnectionP2P::ProcessSignal( const CMsgSteamNetworkingP2PRend { m_pTransportICE->RecvRendezvous( reliable_msg.ice(), usecNow ); } - else if ( GetState() == k_ESteamNetworkingConnectionState_Connecting && m_nICECloseCode == 0 ) + else if ( GetState() == k_ESteamNetworkingConnectionState_Connecting && GetICEFailureCode() == 0 ) { m_vecPendingICEMessages.push_back( reliable_msg.ice() ); } @@ -1767,15 +1910,6 @@ bool CSteamNetworkingSockets::ReceivedP2PCustomSignal( const void *pMsg, int cbM // They accepted the request already. break; } - - // Remember if peer has ICE enabled - #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE - if ( !msg.ice_enabled() ) - { - pConn->m_nICECloseCode = k_nICECloseCode_Remote_NotEnabled; - V_strcpy_safe( pConn->m_szICECloseMsg, "Peer did not enable ICE" ); - } - #endif } // Stop suppressing state change notifications diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.h b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.h index 6f6f975..0ed923e 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.h +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.h @@ -30,7 +30,7 @@ constexpr int k_nP2P_TransportOverride_ICE = 2; constexpr int k_nICECloseCode_Local_NotCompiled = k_ESteamNetConnectionEnd_Local_Max; constexpr int k_nICECloseCode_Local_UserNotEnabled = k_ESteamNetConnectionEnd_Local_Max-1; -constexpr int k_nICECloseCode_Local_FailedInit = k_ESteamNetConnectionEnd_Local_Max-2; +constexpr int k_nICECloseCode_Aborted = k_ESteamNetConnectionEnd_Local_Max-2; constexpr int k_nICECloseCode_Remote_NotEnabled = k_ESteamNetConnectionEnd_Remote_Max; // A really terrible ping score, but one that we can do some math with without overflowing @@ -212,20 +212,22 @@ public: // m_pTransportICE. Then it will be deleted at a safe time. CConnectionTransportP2PICE *m_pTransportICEPendingDelete; - // Failure reason for ICE, if any. (0 if no failure yet.) - int m_nICECloseCode; - char m_szICECloseMsg[ k_cchSteamNetworkingMaxConnectionCloseReason ]; - // When we receive a connection from peer, we need to wait for the app // to accept it. During that time we may need to pend any ICE messages std::vector m_vecPendingICEMessages; - CMsgSteamNetworkingSocketsICESessionSummary m_msgICESessionSummary; + // Summary of connection. Note in particular that the failure reason (if any) + // is here. + CMsgSteamNetworkingICESessionSummary m_msgICESessionSummary; + + // Detailed failure reason string. + ConnectionEndDebugMsg m_szICECloseMsg; void ICEFailed( int nReasonCode, const char *pszReason ); - void QueueDestroyICE(); + inline int GetICEFailureCode() const { return m_msgICESessionSummary.failure_reason_code(); } + void GuessICEFailureReason( ESteamNetConnectionEnd &nReasonCode, ConnectionEndDebugMsg &msg, SteamNetworkingMicroseconds usecNow ); #else - static constexpr int m_nICECloseCode = k_nICECloseCode_Local_NotCompiled; + inline int GetICEFailureCode() const { return k_nICECloseCode_Local_NotCompiled; } #endif /// Sometimes it's nice to have all existing options in a list @@ -254,10 +256,15 @@ public: // Check if we pended ICE deletion, then do so now void CheckCleanupICE(); + // If we don't already have a failure code for ice, set one now. + void EnsureICEFailureReasonSet( SteamNetworkingMicroseconds usecNow ); + // // Transport evaluation and selection // + SteamNetworkingMicroseconds m_usecWhenStartedFindingRoute; + SteamNetworkingMicroseconds m_usecNextEvaluateTransport; /// True if we should be "sticky" to the current transport. diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p_ice.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p_ice.cpp index f1988d4..10244de 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p_ice.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p_ice.cpp @@ -26,8 +26,7 @@ CConnectionTransportP2PICE::CConnectionTransportP2PICE( CSteamNetworkConnectionP , CConnectionTransportP2PBase( "ICE", this, this ) , m_pICESession( nullptr ) { - //m_eCurrentRouteLocalCandidateType = k_EICECandidate_Invalid; - //m_eCurrentRouteRemoteCandidateType = k_EICECandidate_Invalid; + m_nAllowedCandidateTypes = 0; m_eCurrentRouteKind = k_ESteamNetTransport_Unknown; m_currentRouteRemoteAddress.Clear(); } @@ -114,11 +113,19 @@ void CConnectionTransportP2PICE::Init() // Set role cfg.m_eRole = Connection().IsControllingAgent() ? k_EICERole_Controlling : k_EICERole_Controlled; - // Get the stun server list + const int P2P_Transport_ICE_Enable = m_connection.m_connectionConfig.m_P2P_Transport_ICE_Enable.Get(); + + m_nAllowedCandidateTypes = 0; + if ( P2P_Transport_ICE_Enable & k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Private ) + m_nAllowedCandidateTypes |= k_EICECandidate_Any_HostPrivate; + + // Get the STUN server list std::vector vecStunServers; std::vector vecStunServersPsz; - if ( m_connection.m_connectionConfig.m_P2P_Transport_ICE_Enable.Get() & k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Public ) + if ( P2P_Transport_ICE_Enable & k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Public ) { + m_nAllowedCandidateTypes |= k_EICECandidate_Any_HostPublic; + SpewVerboseGroup( LogLevel_P2PRendezvous(), "[%s] Using STUN server list: %s\n", ConnectionDescription(), m_connection.m_connectionConfig.m_P2P_STUN_ServerList.Get().c_str() ); { CUtlVectorAutoPurge tempStunServers; @@ -144,6 +151,25 @@ void CConnectionTransportP2PICE::Init() cfg.m_nStunServers = len( vecStunServersPsz ); cfg.m_pStunServers = vecStunServersPsz.data(); + // Get the TURN server list + if ( P2P_Transport_ICE_Enable & k_nSteamNetworkingConfig_P2P_Transport_ICE_Enable_Relay ) + { + // FIXME + } + + if ( cfg.m_nStunServers > 0 ) + m_nAllowedCandidateTypes |= k_EICECandidate_Any_Reflexive; + if ( cfg.m_nTurnServers > 0 ) + m_nAllowedCandidateTypes |= k_EICECandidate_Any_Relay; + cfg.m_nCandidateTypes = m_nAllowedCandidateTypes; + + // No candidates possible? + if ( m_nAllowedCandidateTypes == 0 ) + { + Connection().ICEFailed( k_nICECloseCode_Local_UserNotEnabled, "No local candidate types are allowed by user settings and configured servers" ); + return; + } + // Create the session m_pICESession = (*g_SteamNetworkingSockets_CreateICESessionFunc)( cfg, this, ICESESSION_INTERFACE_VERSION ); if ( !m_pICESession ) @@ -339,8 +365,7 @@ bool CConnectionTransportP2PICE::SendPacketGather( int nChunks, const iovec *pCh if ( nChunks == 1 ) { Assert( (int)pChunks->iov_len == cbSendTotal ); - SendPacket( pChunks->iov_base, pChunks->iov_len ); - return false; + return SendPacket( pChunks->iov_base, pChunks->iov_len ); } if ( cbSendTotal > k_cbSteamNetworkingSocketsMaxUDPMsgLen ) { @@ -391,7 +416,7 @@ void CConnectionTransportP2PICE::RecvValidUDPDataPacket( UDPRecvPacketContext_t if ( !ctx.m_pStatsIn || !( ctx.m_pStatsIn->flags() & ctx.m_pStatsIn->NOT_PRIMARY_TRANSPORT_E2E ) ) Connection().SetPeerSelectedTransport( this ); P2PTransportTrackRecvEndToEndPacket( ctx.m_usecNow ); - if ( m_bNeedToConfirmEndToEndConnectivity ) + if ( m_bNeedToConfirmEndToEndConnectivity && BCanSendEndToEndData() ) P2PTransportEndToEndConnectivityConfirmed( ctx.m_usecNow ); } @@ -465,7 +490,7 @@ void CConnectionTransportP2PICE::RouteOrWritableStateChanged() Connection().TransportEndToEndConnectivityChanged( this, usecNow ); - CMsgSteamNetworkingSocketsICESessionSummary &ice_summary = Connection().m_msgICESessionSummary; + CMsgSteamNetworkingICESessionSummary &ice_summary = Connection().m_msgICESessionSummary; if ( ConnectionState() == k_ESteamNetworkingConnectionState_FindingRoute || !ice_summary.has_initial_ping() @@ -555,7 +580,7 @@ void CConnectionTransportP2PICE::OnLocalCandidateGathered( EICECandidateType eTy virtual void RunTransportP2PICE( CConnectionTransportP2PICE *pTransport ) { CSteamNetworkConnectionP2P &conn = pTransport->Connection(); - CMsgSteamNetworkingSocketsICESessionSummary &sum = conn.m_msgICESessionSummary; + CMsgSteamNetworkingICESessionSummary &sum = conn.m_msgICESessionSummary; sum.set_local_candidate_types( sum.local_candidate_types() | eType ); pTransport->Connection().QueueSignalReliableMessage( std::move(msg), "LocalCandidateAdded" ); } diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p_ice.h b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p_ice.h index a0fd213..2594e11 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p_ice.h +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p_ice.h @@ -63,6 +63,7 @@ public: //EICECandidateType m_eCurrentRouteRemoteCandidateType; SteamNetworkingIPAddr m_currentRouteRemoteAddress; ESteamNetTransportKind m_eCurrentRouteKind; + int m_nAllowedCandidateTypes; // k_EICECandidate_xxx private: IICESession *m_pICESession;