diff options
Diffstat (limited to 'engine/matchmakingqos.cpp')
| -rw-r--r-- | engine/matchmakingqos.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/engine/matchmakingqos.cpp b/engine/matchmakingqos.cpp new file mode 100644 index 0000000..9148a8e --- /dev/null +++ b/engine/matchmakingqos.cpp @@ -0,0 +1,262 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "matchmakingqos.h" +#include "threadtools.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +#if _X360 + +//----------------------------------------------------------------------------- +// Quality of Service detection routines +//----------------------------------------------------------------------------- + +class CQosDetector +{ +public: + CQosDetector(); + ~CQosDetector(); + +public: + bool InitiateLookup(); + bool WaitForResults(); + +public: + MM_QOS_t GetResults() const { return m_Results; } + +private: + void Release(); + XNQOS *m_pQos; + + void ProcessResults(); + MM_QOS_t m_Results; +}; + +CQosDetector::CQosDetector() : m_pQos( NULL ) +{ + m_Results.nPingMsMin = 50; // 50 ms default ping + m_Results.nPingMsMed = 50; // 50 ms default ping + m_Results.flBwUpKbs = 32.0f; // 32 KB/s = 256 kbps + m_Results.flBwDnKbs = 32.0f; // 32 KB/s = 256 kbps + m_Results.flLoss = 0.0f; // 0% packet loss +} + +CQosDetector::~CQosDetector() +{ + Release(); +} + +void CQosDetector::Release() +{ + if ( m_pQos ) + { + XNetQosRelease( m_pQos ); + m_pQos = NULL; + } +} + +bool CQosDetector::InitiateLookup() +{ + Release(); + + // + // Issue the asynchronous QOS service lookup query + // + XNQOS *pQos = NULL; + INT err = XNetQosServiceLookup( NULL, NULL, &pQos ); + if ( err ) + { + Msg( "CQosDetector::InitiateLookup failed, err = %d\n", err ); + return false; + } + if ( !pQos ) + { + Msg( "CQosDetector::InitiateLookup failed, qos is null.\n" ); + return false; + } + m_pQos = pQos; + return true; +} + +bool CQosDetector::WaitForResults() +{ + if ( !m_pQos ) + return false; + + // + // Spin-sleep here while waiting for the probes to come back + // + const int numSecQosTimeout = 30; // Consider QOS query timed out if 30 seconds elapsed + float flTimeStart = Plat_FloatTime(), flTimeEndTimeout = flTimeStart + numSecQosTimeout; + for ( ; ; ) + { + if ( Plat_FloatTime() > flTimeEndTimeout ) + { + Msg( "CQosDetector::WaitForResults timed out after %d sec.\n", numSecQosTimeout ); + Release(); + return false; // Timed out + } + + if ( m_pQos->axnqosinfo->bFlags & XNET_XNQOSINFO_COMPLETE ) + break; // QOS query has completed + + ThreadSleep( 10 ); + } + + if ( ! ( m_pQos->axnqosinfo->bFlags & XNET_XNQOSINFO_TARGET_CONTACTED ) || + ( m_pQos->axnqosinfo->bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) ) + { + // Failed to contact host or target disabled + Msg( "CQosDetector::WaitForResults host unavailable.\n" ); + Release(); + return false; + } + + ProcessResults(); + Release(); + return true; +} + +void CQosDetector::ProcessResults() +{ + MM_QOS_t Results; + + Results.nPingMsMin = m_pQos->axnqosinfo->wRttMinInMsecs; + Results.nPingMsMed = m_pQos->axnqosinfo->wRttMedInMsecs; + + Results.flBwUpKbs = m_pQos->axnqosinfo->dwUpBitsPerSec / 8192.0f; + if ( Results.flBwUpKbs < 1.f ) + { + Results.flBwUpKbs = 1.f; + } + Results.flBwDnKbs = m_pQos->axnqosinfo->dwDnBitsPerSec / 8192.0f; + if ( Results.flBwDnKbs < 1.f ) + { + Results.flBwDnKbs = 1.f; + } + + Results.flLoss = m_pQos->axnqosinfo->cProbesXmit ? ( float( m_pQos->axnqosinfo->cProbesXmit - m_pQos->axnqosinfo->cProbesRecv ) * 100.f / m_pQos->axnqosinfo->cProbesXmit ) : 0.f; + + m_Results = Results; + + // Dump the results + Msg( + "[QOS] Fresh QOS results available:\n" + "[QOS] ping %d min, %d med\n" + "[QOS] bandwidth %.1f kB/s upstream, %.1f kB/s downstream\n" + "[QOS] avg packet loss %.0f percents\n", + Results.nPingMsMin, Results.nPingMsMed, + Results.flBwUpKbs, Results.flBwDnKbs, + Results.flLoss + ); +} + +// +// Global QOS detector thread +// + +static class CQosThread +{ +public: + CQosThread(); + + MM_QOS_t GetResults(); + + static unsigned ThreadProc( void *pThis ) { return ( (CQosThread *) pThis )->Run(), 0; } + void Run(); + +private: + ThreadHandle_t m_hHandle; + CThreadEvent m_hRequestResultsEvent; // Auto reset event to trigger next query + CQosDetector m_QosDetector; +} +s_QosThread; + +CQosThread::CQosThread() : m_hHandle( NULL ) +{ +} + +void CQosThread::Run() +{ + // + // Sit here and fetch fresh QOS results whenever somebody needs them. + // Every request of QOS data is instantaneous and returns the last successful QOS query result. + // + + for ( unsigned int numQosRequestsMade = 0; ; ++ numQosRequestsMade ) + { + m_QosDetector.InitiateLookup(); + m_QosDetector.WaitForResults(); + + if ( numQosRequestsMade ) + { + m_hRequestResultsEvent.Wait(); + } + } + + ReleaseThreadHandle( m_hHandle ); + m_hHandle = NULL; +} + +MM_QOS_t CQosThread::GetResults() +{ + // Launch the thread if this is the first time QOS is needed + if ( !m_hHandle ) + { + m_hHandle = CreateSimpleThread( ThreadProc, this ); + + if( m_hHandle ) + { + ThreadSetAffinity( m_hHandle, XBOX_PROCESSOR_3 ); + } + } + + // Signal the event that we can make a fresh QOS query + m_hRequestResultsEvent.Set(); + return m_QosDetector.GetResults(); +} + + +// +// Function to retrieve the result of the last QOS query +// + +MM_QOS_t MM_GetQos() +{ + return s_QosThread.GetResults(); +} + + +#else + +// +// Default implementation of QOS +// + +static struct Default_MM_QOS_t : public MM_QOS_t +{ + Default_MM_QOS_t() + { + nPingMsMin = 50; // 50 ms default ping + nPingMsMed = 50; // 50 ms default ping + flBwUpKbs = 32.0f; // 32 KB/s = 256 kbps + flBwDnKbs = 32.0f; // 32 KB/s = 256 kbps + flLoss = 0.0f; // 0% packet loss + } +} +s_DefaultQos; + +MM_QOS_t MM_GetQos() +{ + return s_DefaultQos; +} + +#endif |