From aaa54dc58d48567875549567aee7a56b6874bfdf Mon Sep 17 00:00:00 2001 From: Fletcher Dunn Date: Fri, 28 May 2021 15:46:27 -0700 Subject: [PATCH] Add plumbing for Diagnostics UI. This is only compiled in if STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI is defined, which is only the case when running with Steam. So no functional change (is intended) for the opensource version of the code, just syncing up with Steam. P4:6570679,6570685,6570693,6570802,6570807,6570812 --- .../clientlib/csteamnetworkingsockets.h | 12 + .../steamnetworkingsockets_connections.cpp | 320 ++++++++++++++++-- .../steamnetworkingsockets_connections.h | 23 ++ .../clientlib/steamnetworkingsockets_p2p.cpp | 39 +++ .../clientlib/steamnetworkingsockets_p2p.h | 5 + .../steamnetworkingsockets_internal.h | 7 + 6 files changed, 380 insertions(+), 26 deletions(-) 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