diff --git a/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.h b/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.h index a64ff9d..8e53f29 100644 --- a/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.h +++ b/src/steamnetworkingsockets/clientlib/csteamnetworkingsockets.h @@ -287,6 +287,18 @@ public: // Reset this utils instance for testing virtual void TEST_ResetSelf(); + // Post a connection update message to the OS diagnostics + #ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + virtual void PostConnectionStateUpdateForDiagnosticsUI( ESteamNetworkingConnectionState eOldState, CSteamNetworkConnectionBase *pConnection, SteamNetworkingMicroseconds usecNow ) = 0; + + // Desired update interval for connections, outside of state changes. + // The default is zero, which means no updates, not even for state changes. + // Derived classes that override PostConnectionStateUpdateForDiagnosticsUI must set this. + SteamNetworkingMicroseconds m_usecConnectionUpdateFrequency = 0; + #else + static constexpr SteamNetworkingMicroseconds m_usecConnectionUpdateFrequency = 0; + #endif + // Stubs if SDR not enabled #ifndef STEAMNETWORKINGSOCKETS_ENABLE_SDR virtual ESteamNetworkingAvailability GetRelayNetworkStatus( SteamRelayNetworkStatus_t *pDetails ) override diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp index a23eb8a..020d30e 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp @@ -9,6 +9,12 @@ #include "csteamnetworkingsockets.h" #include "crypto.h" +#include "tier0/memdbgoff.h" + +#ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + #include "../../common/steammessages_gamenetworkingui.pb.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -653,7 +659,11 @@ CSteamNetworkConnectionBase::CSteamNetworkConnectionBase( CSteamNetworkingSocket m_eConnectionState = k_ESteamNetworkingConnectionState_None; m_eConnectionWireState = k_ESteamNetworkingConnectionState_None; m_usecWhenEnteredConnectionState = 0; + #ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + m_usecWhenNextDiagnosticsUpdate = k_nThinkTime_Never; + #endif m_usecWhenSentConnectRequest = 0; + m_usecWhenCreated = 0; m_ulHandshakeRemoteTimestamp = 0; m_usecWhenReceivedHandshakeRemoteTimestamp = 0; m_eEndReason = k_ESteamNetConnectionEnd_Invalid; @@ -959,6 +969,7 @@ bool CSteamNetworkConnectionBase::BInitConnection( SteamNetworkingMicroseconds u m_eEndReason = k_ESteamNetConnectionEnd_Invalid; m_szEndDebug[0] = '\0'; m_statsEndToEnd.Init( usecNow, true ); // Until we go connected don't try to send acks, etc + m_usecWhenCreated = usecNow; // Select random connection ID, and make sure it passes certain sanity checks { @@ -1340,6 +1351,9 @@ void CSteamNetworkConnectionBase::FinalizeLocalCrypto() // Note: In certain circumstances, we may need to do this again, so don't wipte the key just yet //m_keyPrivate.Wipe(); + + // Probably a state change relevant to diagnostics + CheckScheduleDiagnosticsUpdateASAP(); } void CSteamNetworkConnectionBase::SetLocalCertUnsigned() @@ -1858,6 +1872,12 @@ void CConnectionTransport::GetDetailedConnectionStatus( SteamNetworkingDetailedC { } +#ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI +void CConnectionTransport::TransportPopulateDiagnostics( CGameNetworkingUI_ConnectionState &msgConnectionState, SteamNetworkingMicroseconds usecNow ) +{ +} +#endif + void CSteamNetworkConnectionBase::ConnectionPopulateInfo( SteamNetConnectionInfo_t &info ) const { m_pLock->AssertHeldByCurrentThread(); @@ -2445,17 +2465,13 @@ void CSteamNetworkConnectionBase::SetState( ESteamNetworkingConnectionState eNew Assert( m_nSupressStateChangeCallbacks >= 0 ); if ( m_nSupressStateChangeCallbacks == 0 ) { - // Check for posting callback, if connection state has changed from an API perspective - if ( eOldAPIState != eNewAPIState ) + if ( eOldState == k_ESteamNetworkingConnectionState_None && GetState() == k_ESteamNetworkingConnectionState_ProblemDetectedLocally ) { - if ( eOldState == k_ESteamNetworkingConnectionState_None && GetState() == k_ESteamNetworkingConnectionState_ProblemDetectedLocally ) - { - // Do not post callbacks for internal failures during connection creation - } - else - { - PostConnectionStateChangedCallback( eOldAPIState, eNewAPIState ); - } + // Do not post callbacks for internal failures during connection creation + } + else + { + PostConnectionStateChangedCallback( eOldAPIState, eNewAPIState ); } } @@ -2596,34 +2612,276 @@ void CSteamNetworkConnectionBase::ReceivedMessage( CSteamNetworkingMessage *pMsg void CSteamNetworkConnectionBase::PostConnectionStateChangedCallback( ESteamNetworkingConnectionState eOldAPIState, ESteamNetworkingConnectionState eNewAPIState ) { - SteamNetConnectionStatusChangedCallback_t c; - ConnectionPopulateInfo( c.m_info ); - c.m_eOldState = eOldAPIState; - c.m_hConn = m_hConnectionSelf; - - // !KLUDGE! For ISteamnetworkingMessages connections, we want to process the callback immediately. - void *fnCallback = m_connectionConfig.m_Callback_ConnectionStatusChanged.Get(); - if ( IsConnectionForMessagesSession() ) + // Send API callback for this state change? + // Do not post if connection state has not changed from an API perspective + if ( eOldAPIState != eNewAPIState ) { - if ( fnCallback ) + + SteamNetConnectionStatusChangedCallback_t c; + ConnectionPopulateInfo( c.m_info ); + c.m_eOldState = eOldAPIState; + c.m_hConn = m_hConnectionSelf; + + // !KLUDGE! For ISteamnetworkingMessages connections, we want to process the callback immediately. + void *fnCallback = m_connectionConfig.m_Callback_ConnectionStatusChanged.Get(); + if ( IsConnectionForMessagesSession() ) { - FnSteamNetConnectionStatusChanged fnConnectionStatusChanged = (FnSteamNetConnectionStatusChanged)( fnCallback ); - (*fnConnectionStatusChanged)( &c ); + if ( fnCallback ) + { + FnSteamNetConnectionStatusChanged fnConnectionStatusChanged = (FnSteamNetConnectionStatusChanged)( fnCallback ); + (*fnConnectionStatusChanged)( &c ); + } + else + { + // Currently there is no use case that does this. It's probably a bug. + Assert( false ); + } } else { - // Currently there is no use case that does this. It's probably a bug. - Assert( false ); + + // Typical codepath - post to a queue + m_pSteamNetworkingSocketsInterface->QueueCallback( c, fnCallback ); } } + + // Send diagnostics for this state change? + #ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + if ( + m_pSteamNetworkingSocketsInterface->m_pSteamNetworkingUtils->m_usecConnectionUpdateFrequency == 0 // Not enabled globally right now + || ( m_usecWhenNextDiagnosticsUpdate == k_nThinkTime_Never && !BStateIsActive() ) // We've sent a terminal state change. This transition isn't interesting from a diagnostic standpoint. + ) { + // Disabled, don't send any more + m_usecWhenNextDiagnosticsUpdate = k_nThinkTime_Never; + } + else + { + // Post an update. If more should be sent, we'll schedule it. + // NOTE: Here we are going to ask the connection to populate SteamNetConnectionInfo_t info + // *again*, even though we just called ConnectionPopulateInfo above. But this keeps the code + // simpler and connectoin state changes are infrequent, relatively speaking + SteamNetworkingMicroseconds usecNow = SteamNetworkingSockets_GetLocalTimestamp(); + m_pSteamNetworkingSocketsInterface->m_pSteamNetworkingUtils->PostConnectionStateUpdateForDiagnosticsUI( eOldAPIState, this, usecNow ); + Assert( m_usecWhenNextDiagnosticsUpdate > usecNow ); + + // If we might need to send another, schedule a wakeup call + EnsureMinThinkTime( m_usecWhenNextDiagnosticsUpdate ); + } + #endif +} + +#ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI +void CSteamNetworkConnectionBase::CheckScheduleDiagnosticsUpdateASAP() +{ + if ( m_pSteamNetworkingSocketsInterface->m_pSteamNetworkingUtils->m_usecConnectionUpdateFrequency <= 0 ) + { + m_usecWhenNextDiagnosticsUpdate = k_nThinkTime_Never; + } + else if ( m_usecWhenNextDiagnosticsUpdate == k_nThinkTime_Never ) + { + // We've sent out last update. Don't send any more + } else { - - // Typical codepath - post to a queue - m_pSteamNetworkingSocketsInterface->QueueCallback( c, fnCallback ); + m_usecWhenNextDiagnosticsUpdate = k_nThinkTime_ASAP; + SetNextThinkTimeASAP(); } } +// FIXME - Should we just move this into SteamNetConnectionInfo_t? +// Seems like maybe we should just provide a localized result directly +// there. +static const char *GetConnectionStateLocToken( ESteamNetworkingConnectionState eOldConnState, ESteamNetworkingConnectionState eState, int nEndReason ) +{ + if ( eState == k_ESteamNetworkingConnectionState_Connecting ) + return "#SteamNetSockets_Connecting"; + if ( eState == k_ESteamNetworkingConnectionState_FindingRoute ) + return "#SteamNetSockets_FindingRoute"; + if ( eState == k_ESteamNetworkingConnectionState_Connected ) + return "#SteamNetSockets_Connected"; + + if ( nEndReason == k_ESteamNetConnectionEnd_Misc_Timeout ) + { + if ( eOldConnState == k_ESteamNetworkingConnectionState_Connecting ) + return "#SteamNetSockets_Disconnect_ConnectionTimedout"; + return "#SteamNetSockets_Disconnect_TimedOut"; + } + if ( eState == k_ESteamNetworkingConnectionState_ProblemDetectedLocally ) + { + if ( nEndReason >= k_ESteamNetConnectionEnd_Local_Min && nEndReason <= k_ESteamNetConnectionEnd_Local_Max ) + { + if ( nEndReason == k_ESteamNetConnectionEnd_Local_ManyRelayConnectivity ) + return "#SteamNetSockets_Disconnect_LocalProblem_ManyRelays"; + if ( nEndReason == k_ESteamNetConnectionEnd_Local_HostedServerPrimaryRelay ) + return "#SteamNetSockets_Disconnect_LocalProblem_HostedServerPrimaryRelay"; + if ( nEndReason == k_ESteamNetConnectionEnd_Local_NetworkConfig ) + return "#SteamNetSockets_Disconnect_LocalProblem_NetworkConfig"; + return "#SteamNetSockets_Disconnect_LocalProblem_Other"; + } + + if ( nEndReason >= k_ESteamNetConnectionEnd_Remote_Min && nEndReason <= k_ESteamNetConnectionEnd_Remote_Max ) + { + if ( nEndReason == k_ESteamNetConnectionEnd_Remote_Timeout ) + { + if ( eOldConnState == k_ESteamNetworkingConnectionState_Connecting ) + return "#SteamNetSockets_Disconnect_RemoteProblem_TimeoutConnecting"; + return "#SteamNetSockets_Disconnect_RemoteProblem_Timeout"; + } + + if ( nEndReason == k_ESteamNetConnectionEnd_Remote_BadCrypt ) + return "#SteamNetSockets_Disconnect_RemoteProblem_BadCrypt"; + if ( nEndReason == k_ESteamNetConnectionEnd_Remote_BadCert ) + return "#SteamNetSockets_Disconnect_RemoteProblem_BadCert"; + } + + if ( nEndReason == k_ESteamNetConnectionEnd_Misc_P2P_Rendezvous ) + return "#SteamNetSockets_Disconnect_P2P_Rendezvous"; + + if ( nEndReason == k_ESteamNetConnectionEnd_Misc_SteamConnectivity ) + return "#SteamNetSockets_Disconnect_SteamConnectivity"; + + if ( nEndReason == k_ESteamNetConnectionEnd_Misc_InternalError ) + return "#SteamNetSockets_Disconnect_InternalError"; + + return "#SteamNetSockets_Disconnect_Unusual"; + } + if ( eState == k_ESteamNetworkingConnectionState_ClosedByPeer ) + { + if ( nEndReason >= k_ESteamNetConnectionEnd_Local_Min && nEndReason <= k_ESteamNetConnectionEnd_Local_Max ) + return "#SteamNetSockets_PeerClose_LocalProblem"; + + if ( nEndReason >= k_ESteamNetConnectionEnd_Remote_Min && nEndReason <= k_ESteamNetConnectionEnd_Remote_Max ) + { + if ( nEndReason == k_ESteamNetConnectionEnd_Remote_BadCrypt ) + return "#SteamNetSockets_PeerClose_RemoteProblem_BadCrypt"; + if ( nEndReason == k_ESteamNetConnectionEnd_Remote_BadCert ) + return "#SteamNetSockets_PeerClose_RemoteProblem_BadCert"; + // Peer closed the connection, and they think it's our fault somehow? + } + + if ( nEndReason >= k_ESteamNetConnectionEnd_App_Min && nEndReason <= k_ESteamNetConnectionEnd_App_Max ) + { + return "#SteamNetSockets_PeerClose_App_Normal"; + } + if ( nEndReason >= k_ESteamNetConnectionEnd_AppException_Min && nEndReason <= k_ESteamNetConnectionEnd_AppException_Max ) + { + return "#SteamNetSockets_PeerClose_App_Unusual"; + } + return "#SteamNetSockets_PeerClose_Ununusual"; + } + + if ( nEndReason >= k_ESteamNetConnectionEnd_App_Min && nEndReason <= k_ESteamNetConnectionEnd_App_Max ) + { + return "#SteamNetSockets_AppClose_Normal"; + } + if ( nEndReason >= k_ESteamNetConnectionEnd_AppException_Min && nEndReason <= k_ESteamNetConnectionEnd_AppException_Max ) + { + return "#SteamNetSockets_AppClose_Unusual"; + } + + return ""; +} + +void CSteamNetworkConnectionBase::ConnectionPopulateDiagnostics( ESteamNetworkingConnectionState eOldState, CGameNetworkingUI_ConnectionState &msgConnectionState, SteamNetworkingMicroseconds usecNow ) +{ + msgConnectionState.set_start_time( (usecNow - m_usecWhenCreated)/k_nMillion ); + + // Use the API function to populate the struct + SteamNetworkingDetailedConnectionStatus stats; + APIGetDetailedConnectionStatus( stats, usecNow ); + + // Fill in diagnostic fields that correspond to SteamNetConnectionInfo_t + if ( stats.m_info.m_eTransportKind != k_ESteamNetTransport_Unknown ) + msgConnectionState.set_transport_kind( stats.m_info.m_eTransportKind ); + if ( stats.m_info.m_idPOPRelay ) + msgConnectionState.set_sdrpopid_local( SteamNetworkingPOPIDRender( stats.m_info.m_idPOPRelay ).c_str() ); + if ( stats.m_info.m_idPOPRemote ) + msgConnectionState.set_sdrpopid_remote( SteamNetworkingPOPIDRender( stats.m_info.m_idPOPRemote ).c_str() ); + if ( !stats.m_info.m_addrRemote.IsIPv6AllZeros() ) + msgConnectionState.set_address_remote( SteamNetworkingIPAddrRender( stats.m_info.m_addrRemote ).c_str() ); + + msgConnectionState.set_connection_id_local( m_unConnectionIDLocal ); + msgConnectionState.set_identity_local( SteamNetworkingIdentityRender( m_identityLocal ).c_str() ); + if ( !m_identityRemote.IsInvalid() && m_identityRemote.m_eType != k_ESteamNetworkingIdentityType_IPAddress ) + msgConnectionState.set_identity_remote( SteamNetworkingIdentityRender( m_identityRemote ).c_str() ); + switch ( GetState() ) + { + default: + Assert( false ); + case k_ESteamNetworkingConnectionState_Connecting: + case k_ESteamNetworkingConnectionState_FindingRoute: + case k_ESteamNetworkingConnectionState_Connected: + case k_ESteamNetworkingConnectionState_ClosedByPeer: + case k_ESteamNetworkingConnectionState_ProblemDetectedLocally: + case k_ESteamNetworkingConnectionState_Linger: + msgConnectionState.set_connection_state( GetState() ); + break; + + case k_ESteamNetworkingConnectionState_None: + case k_ESteamNetworkingConnectionState_FinWait: + case k_ESteamNetworkingConnectionState_Dead: + // Use last public state + msgConnectionState.set_connection_state( GetWireState() ); + break; + } + + msgConnectionState.set_status_loc_token( GetConnectionStateLocToken( eOldState, stats.m_info.m_eState, stats.m_info.m_eEndReason ) ); + + // Check if we've been closed, and schedule the next diagnostics update + if ( m_eEndReason != k_ESteamNetConnectionEnd_Invalid ) + { + msgConnectionState.set_close_reason( GetConnectionEndReason() ); + msgConnectionState.set_close_message( GetConnectionEndDebugString() ); + } + if ( BStateIsActive() ) + { + m_usecWhenNextDiagnosticsUpdate = usecNow + m_pSteamNetworkingSocketsInterface->m_pSteamNetworkingUtils->m_usecConnectionUpdateFrequency; + } + else + { + m_usecWhenNextDiagnosticsUpdate = k_nThinkTime_Never; + } + + // end-to-end stats measured locally + LinkStatsInstantaneousStructToMsg( stats.m_statsEndToEnd.m_latest, *msgConnectionState.mutable_e2e_quality_local()->mutable_instantaneous() ); + LinkStatsLifetimeStructToMsg( stats.m_statsEndToEnd.m_lifetime, *msgConnectionState.mutable_e2e_quality_local()->mutable_lifetime() ); + + // end-to-end stats from remote host, if any + if ( stats.m_statsEndToEnd.m_flAgeLatestRemote >= 0.0f ) + { + msgConnectionState.set_e2e_quality_remote_instantaneous_time( uint64( stats.m_statsEndToEnd.m_flAgeLatestRemote * 1e6 ) ); + LinkStatsInstantaneousStructToMsg( stats.m_statsEndToEnd.m_latestRemote, *msgConnectionState.mutable_e2e_quality_remote()->mutable_instantaneous() ); + } + if ( stats.m_statsEndToEnd.m_flAgeLifetimeRemote >= 0.0f ) + { + msgConnectionState.set_e2e_quality_remote_lifetime_time( uint64( stats.m_statsEndToEnd.m_flAgeLifetimeRemote * 1e6 ) ); + LinkStatsLifetimeStructToMsg( stats.m_statsEndToEnd.m_lifetimeRemote, *msgConnectionState.mutable_e2e_quality_remote()->mutable_lifetime() ); + } + + if ( !stats.m_addrPrimaryRouter.IsIPv6AllZeros() && stats.m_statsPrimaryRouter.m_lifetime.m_nPktsRecvSequenced > 0 ) + { + LinkStatsInstantaneousStructToMsg( stats.m_statsPrimaryRouter.m_latest, *msgConnectionState.mutable_front_quality_local()->mutable_instantaneous() ); + LinkStatsLifetimeStructToMsg( stats.m_statsPrimaryRouter.m_lifetime, *msgConnectionState.mutable_front_quality_local()->mutable_lifetime() ); + + if ( stats.m_statsPrimaryRouter.m_flAgeLatestRemote >= 0.0f ) + { + msgConnectionState.set_front_quality_remote_instantaneous_time( uint64( stats.m_statsPrimaryRouter.m_flAgeLatestRemote * 1e6 ) ); + LinkStatsInstantaneousStructToMsg( stats.m_statsPrimaryRouter.m_latestRemote, *msgConnectionState.mutable_front_quality_remote()->mutable_instantaneous() ); + } + if ( stats.m_statsPrimaryRouter.m_flAgeLifetimeRemote >= 0.0f ) + { + msgConnectionState.set_front_quality_remote_lifetime_time( uint64( stats.m_statsPrimaryRouter.m_flAgeLifetimeRemote * 1e6 ) ); + LinkStatsLifetimeStructToMsg( stats.m_statsPrimaryRouter.m_lifetimeRemote, *msgConnectionState.mutable_front_quality_remote()->mutable_lifetime() ); + } + } + + // If any selected transport, give them a chance to fill in info + if ( m_pTransport ) + m_pTransport->TransportPopulateDiagnostics( msgConnectionState, usecNow ); +} + +#endif + void CSteamNetworkConnectionBase::ConnectionState_ProblemDetectedLocally( ESteamNetConnectionEnd eReason, const char *pszFmt, ... ) { AssertLocksHeldByCurrentThread(); @@ -3152,6 +3410,16 @@ void CSteamNetworkConnectionBase::CheckConnectionStateAndSetNextThinkTime( Steam // Hook for derived class to do its connection-type-specific stuff ThinkConnection( usecNow ); + // Check for sending diagnostics periodically + #ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + if ( m_usecWhenNextDiagnosticsUpdate <= usecNow ) + { + m_pSteamNetworkingSocketsInterface->m_pSteamNetworkingUtils->PostConnectionStateUpdateForDiagnosticsUI( CollapseConnectionStateToAPIState( GetState() ), this, usecNow ); + Assert( m_usecWhenNextDiagnosticsUpdate > usecNow ); + } + UpdateMinThinkTime( m_usecWhenNextDiagnosticsUpdate ); + #endif + // Schedule next time to think, if derived class didn't request an earlier // wakeup call. EnsureMinThinkTime( usecMinNextThinkTime ); diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h index 9eec03c..a5cc486 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h @@ -24,6 +24,7 @@ struct SteamNetConnectionStatusChangedCallback_t; class ISteamNetworkingSocketsSerialized; +class CGameNetworkingUI_ConnectionState; namespace SteamNetworkingSocketsLib { @@ -606,6 +607,25 @@ public: /// API could not do easily inline bool IsConnectionForMessagesSession() const { return m_connectionConfig.m_LocalVirtualPort.Get() == k_nVirtualPort_Messages; } + /// Time when we would like to send our next connection diagnostics + /// update. This is initialized the first time we enter the "connecting" + /// state and we are on a platform that wants those updates. + /// Once we wish to stop sending them, we set it to "never" + #ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + SteamNetworkingMicroseconds m_usecWhenNextDiagnosticsUpdate; + void CheckScheduleDiagnosticsUpdateASAP(); + + /// Fill out diagnostics message to send to steam client with current state of the + /// connection, and also schedule the next check, if the connection is active. + virtual void ConnectionPopulateDiagnostics( ESteamNetworkingConnectionState eOldState, CGameNetworkingUI_ConnectionState &msgConnectionState, SteamNetworkingMicroseconds usecNow ); + #else + inline void CheckScheduleDiagnosticsUpdateASAP() {} + static constexpr SteamNetworkingMicroseconds m_usecWhenNextDiagnosticsUpdate = k_nThinkTime_Never; + #endif + + /// Timestamp when we were created + SteamNetworkingMicroseconds m_usecWhenCreated; + protected: CSteamNetworkConnectionBase( CSteamNetworkingSockets *pSteamNetworkingSocketsInterface, ConnectionScopeLock &scopeLock ); virtual ~CSteamNetworkConnectionBase(); // hidden destructor, don't call directly. Use ConnectionQueueDestroy() @@ -857,6 +877,9 @@ public: virtual void SendEndToEndStatsMsg( EStatsReplyRequest eRequest, SteamNetworkingMicroseconds usecNow, const char *pszReason ) = 0; virtual void TransportPopulateConnectionInfo( SteamNetConnectionInfo_t &info ) const; virtual void GetDetailedConnectionStatus( SteamNetworkingDetailedConnectionStatus &stats, SteamNetworkingMicroseconds usecNow ); + #ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + virtual void TransportPopulateDiagnostics( CGameNetworkingUI_ConnectionState &msgConnectionState, SteamNetworkingMicroseconds usecNow ); + #endif /// Called when the connection state changes. Some transports need to do stuff virtual void TransportConnectionStateChanged( ESteamNetworkingConnectionState eOldState ); diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.cpp index 4169248..daf6d69 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.cpp @@ -30,6 +30,12 @@ #endif #endif +#include "tier0/memdbgoff.h" + +#ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + #include "../../common/steammessages_gamenetworkingui.pb.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -2052,6 +2058,39 @@ void CSteamNetworkConnectionP2P::PeerSelectedTransportChanged() SpewMsgGroup( LogLevel_P2PRendezvous(), "[%s] Peer appears to be using '%s' transport as primary\n", GetDescription(), m_pPeerSelectedTransport->m_pszP2PTransportDebugName ); } +#ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + +void CSteamNetworkConnectionP2P::ConnectionPopulateDiagnostics( ESteamNetworkingConnectionState eOldState, CGameNetworkingUI_ConnectionState &msgConnectionState, SteamNetworkingMicroseconds usecNow ) +{ + AssertLocksHeldByCurrentThread(); + CSteamNetworkConnectionBase::ConnectionPopulateDiagnostics( eOldState, msgConnectionState, usecNow ); + + CMsgSteamDatagramP2PRoutingSummary &p2p_routing = *msgConnectionState.mutable_p2p_routing(); + PopulateP2PRoutingSummary( p2p_routing ); + + #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE + if ( m_pTransportICE ) + { + if ( m_pTransportICE->m_pingEndToEnd.m_nSmoothedPing >= 0 ) + { + msgConnectionState.set_ping_default_internet_route( m_pTransportICE->m_pingEndToEnd.m_nSmoothedPing ); + } + } + else + { + if ( p2p_routing.has_ice() ) + { + const CMsgSteamNetworkingICESessionSummary &ice = p2p_routing.ice(); + if ( ice.has_initial_ping() ) + msgConnectionState.set_ping_default_internet_route( ice.initial_ping() ); + } + } + #endif + +} + +#endif + ///////////////////////////////////////////////////////////////////////////// // // CSteamNetworkingSockets CConnectionTransportP2PBase diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.h b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.h index 46bc3aa..d347e97 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.h +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_p2p.h @@ -212,6 +212,9 @@ public: virtual void ProcessSNPPing( int msPing, RecvPacketContext_t &ctx ) override; virtual bool BSupportsSymmetricMode() override; ESteamNetConnectionEnd CheckRemoteCert( const CertAuthScope *pCACertAuthScope, SteamNetworkingErrMsg &errMsg ) override; + #ifdef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + virtual void ConnectionPopulateDiagnostics( ESteamNetworkingConnectionState eOldState, CGameNetworkingUI_ConnectionState &msgConnectionState, SteamNetworkingMicroseconds usecNow ) override; + #endif void SendConnectOKSignal( SteamNetworkingMicroseconds usecNow ); void SendConnectionClosedSignal( SteamNetworkingMicroseconds usecNow ); @@ -271,6 +274,8 @@ public: // Peer to peer, over SDR CConnectionTransportP2PSDR *m_pTransportP2PSDR; CMsgSteamNetworkingP2PSDRRoutingSummary m_msgSDRRoutingSummary; + + void PopulateP2PRoutingSummary( CMsgSteamDatagramP2PRoutingSummary &msg ); #endif // Client connecting to hosted dedicated server over SDR. These are not really diff --git a/src/steamnetworkingsockets/steamnetworkingsockets_internal.h b/src/steamnetworkingsockets/steamnetworkingsockets_internal.h index ebfcc18..3989cd0 100644 --- a/src/steamnetworkingsockets/steamnetworkingsockets_internal.h +++ b/src/steamnetworkingsockets/steamnetworkingsockets_internal.h @@ -63,6 +63,13 @@ #endif #endif +// Enable diagnostics UI if there is a Steam client to display them +#ifndef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + #ifdef STEAMNETWORKINGSOCKETS_STEAM + #define STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI + #endif +#endif + // Redefine the macros for byte-swapping, to sure the correct // argument size. We probably should move this into platform.h, // but I suspect we'd find a bunch of "bugs" which currently don't