Improvements to ICE error handling

Change how CSteamNetworkConnectionBase::GuessTimeoutReason defaults are set.
Just set them directly in CSteamNetworkConnectionBase::ConnectionTimedOut.
What we were doing previously was a bit convoluted.

Add logic to actually attempt to classify ICE failure.
This commit is contained in:
Fletcher Dunn
2020-05-28 16:41:44 -07:00
parent 15fbda1403
commit aa3ef9765d
6 changed files with 265 additions and 86 deletions
@@ -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
@@ -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 )
@@ -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
@@ -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<CMsgICERendezvous> 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.
@@ -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<std::string> vecStunServers;
std::vector<const char *> 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<char *> 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" );
}
@@ -63,6 +63,7 @@ public:
//EICECandidateType m_eCurrentRouteRemoteCandidateType;
SteamNetworkingIPAddr m_currentRouteRemoteAddress;
ESteamNetTransportKind m_eCurrentRouteKind;
int m_nAllowedCandidateTypes; // k_EICECandidate_xxx
private:
IICESession *m_pICESession;