//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "tf_ping_panel.h" #include "vgui_controls/ProgressBar.h" #include "vgui_controls/AnimationController.h" #include "tf_gc_client.h" #include "clientmode_tf.h" #include "tf_matchmaking_shared.h" static void OnConVarChangeCustomPingTolerance( IConVar *pConVar, const char *pOldString, float flOldValue ) { if ( GTFGCClientSystem() ) { // Otherwise the client system will do this when it starts GTFGCClientSystem()->UpdateCustomPingTolerance(); } } ConVar tf_custom_ping_enabled( "tf_custom_ping_enabled", "0", FCVAR_ARCHIVE, "", false, 0.f, false, 0.f, OnConVarChangeCustomPingTolerance ); ConVar tf_custom_ping( "tf_custom_ping", "100", FCVAR_ARCHIVE, "", true, (float)CUSTOM_PING_TOLERANCE_MIN, true, (float)CUSTOM_PING_TOLERANCE_MAX, OnConVarChangeCustomPingTolerance ); #ifdef STAGING_ONLY ConVar tf_custom_ping_add_random_datacenters( "tf_custom_ping_add_random_datacenters", "0" ); #endif // STAGING_ONLY CTFPingPanel::CTFPingPanel( Panel* pPanel, const char *pszName, EMatchGroup eMatchGroup ) : EditablePanel( pPanel, pszName ), m_eMatchGroup( eMatchGroup ) { SetProportional( true ); m_pMainContainer = new EditablePanel( this, "MainContainer" ); m_pCheckButton = new CheckButton( m_pMainContainer, "CheckButton", "" ); m_pCurrentPingLabel = new Label( m_pMainContainer, "CurrentPingLabel", "" ); m_pPingSlider = new CCvarSlider( m_pMainContainer, "PingSlider" ); ListenForGameEvent( "ping_updated" ); } CTFPingPanel::~CTFPingPanel() { CleanupPingPanels(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPingPanel::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "resource/ui/MatchMakingPingPanel.res" ); CleanupPingPanels(); m_pCheckButton->AddActionSignalTarget( this ); m_pPingSlider->AddActionSignalTarget( this ); CScrollableList *pDataCenterList = FindControl< CScrollableList >( "DataCenterList", true ); static const wchar_t *s_pwszPingFormat = L"%ls (%d ms)"; if ( pDataCenterList && GTFGCClientSystem()->BHavePingData() ) { auto pingData = GTFGCClientSystem()->GetPingData(); const auto& dictDataCenterPopulations = GTFGCClientSystem()->GetDataCenterPopulationRatioDict( m_eMatchGroup ); bool bTesting = false; #ifdef STAGING_ONLY if ( tf_custom_ping_add_random_datacenters.GetBool() ) { bTesting = true; const char* pszDataCenterNames[] = { "eat", "lax","iad","atl","gru","scl","lim","lux","vie","sto", "mad","sgp","hkg","tyo","syd","dxb","bom","maa","ord","waw","jhb" }; CUniformRandomStream randomstream; randomstream.SetSeed( tf_custom_ping_add_random_datacenters.GetInt() ); for( int i=0; i < ARRAYSIZE( pszDataCenterNames ); ++i ) { auto pNewPingData = pingData.add_pingdata(); pNewPingData->set_name( pszDataCenterNames[ i ] ); pNewPingData->set_ping( randomstream.RandomInt( 0, 250 ) ); pNewPingData->set_ping_status( CMsgGCDataCenterPing_Update_Status_Normal ); } } #endif // for each ping data, check for intersection with data center population from MMStats for ( int iPing=0; iPingLoadControlSettings( "resource/ui/MatchMakingDataCenterPopulationPanel.res" ); pDataCenterPopulationPanel->SetAutoDelete( false ); pDataCenterList->ResetScrollAmount(); pDataCenterList->InvalidateLayout(); // Update label wchar_t wszDataCenterName[ 128 ]; wchar_t* pwszLocalizedDataCenterName = g_pVGuiLocalize->Find( CFmtStr( "#TF_DataCenter_%s", pszPingFromDataCenterName ) ); if ( pwszLocalizedDataCenterName ) { V_wcsncpy( wszDataCenterName, pwszLocalizedDataCenterName, sizeof( wszDataCenterName ) ); } else { // Fallback is no token. If you hit this, go add a string for this data center in tf_english! Assert( false ); g_pVGuiLocalize->ConvertANSIToUnicode( pszPingFromDataCenterName, wszDataCenterName, sizeof( wszDataCenterName ) ); } wchar_t wszLabelText[ 128 ]; V_snwprintf( wszLabelText, sizeof( wszLabelText ), s_pwszPingFormat, wszDataCenterName, pingEntry.ping() ); pDataCenterPopulationPanel->SetDialogVariable( "datacenter_name", wszLabelText ); PingPanelInfo panelInfo; panelInfo.m_pPanel = pDataCenterPopulationPanel; panelInfo.m_flPopulationRatio = bTesting ? RandomFloat( 0.f, 1.f ) : dictDataCenterPopulations[dictIndex]; panelInfo.m_nPing = pingEntry.ping(); m_vecDataCenterPingPanels.AddToTail( panelInfo ); } } } struct PingPanelInfoSorter { static int SortPingPanelInfo( const PingPanelInfo* a, const PingPanelInfo* b ) { return a->m_nPing - b->m_nPing; } }; m_vecDataCenterPingPanels.Sort( &PingPanelInfoSorter::SortPingPanelInfo ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPingPanel::PerformLayout() { BaseClass::PerformLayout(); m_pCheckButton->SetSelected( tf_custom_ping_enabled.GetBool() ); FOR_EACH_VEC( m_vecDataCenterPingPanels, i ) { const PingPanelInfo& info = m_vecDataCenterPingPanels[i]; int iTall = info.m_pPanel->GetTall(); int iYGap = i > 0 ? m_iDataCenterYSpace : 0; int iXPos = info.m_pPanel->GetXPos(); info.m_pPanel->SetPos( iXPos, m_iDataCenterY + iYGap + i * iTall ); // Update bars with latest health data ProgressBar* pProgress = info.m_pPanel->FindControl< ProgressBar >( "HealthProgressBar", true ); if ( pProgress ) { auto healthData = GTFGCClientSystem()->GetHealthBracketForRatio( info.m_flPopulationRatio ); pProgress->MakeReadyForUse(); pProgress->SetProgress( healthData.m_flRatio ); pProgress->SetFgColor( healthData.m_colorBar ); } } UpdateCurrentPing(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPingPanel::OnCommand( const char *command ) { if ( FStrEq( command, "close" ) ) { MarkForDeletion(); return; } BaseClass::OnCommand( command ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPingPanel::FireGameEvent( IGameEvent *event ) { const char *pszEventName = event->GetName(); if ( FStrEq( pszEventName, "ping_updated" ) ) { InvalidateLayout( true, true ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPingPanel::CleanupPingPanels() { FOR_EACH_VEC( m_vecDataCenterPingPanels, i ) { m_vecDataCenterPingPanels[i].m_pPanel->MarkForDeletion(); } m_vecDataCenterPingPanels.Purge(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPingPanel::UpdateCurrentPing() { bool bUsePingLimit = m_pCheckButton->IsSelected(); int nLowestDataCenterPing = m_vecDataCenterPingPanels.Count() ? m_vecDataCenterPingPanels[0].m_nPing : 0; int nCurrentPingLimit = MAX( (int)m_pPingSlider->GetSliderValue(), nLowestDataCenterPing ); m_pCurrentPingLabel->SetText( bUsePingLimit ? CFmtStr( "Ping Limit: %d", nCurrentPingLimit ) : "Ping Limit: AUTO" ); FOR_EACH_VEC( m_vecDataCenterPingPanels, i ) { const PingPanelInfo& info = m_vecDataCenterPingPanels[i]; int bHighLight = !bUsePingLimit || info.m_nPing <= nCurrentPingLimit; if ( bHighLight ) { g_pClientMode->GetViewportAnimationController()->StopAnimationSequence( info.m_pPanel, "HealthProgressBar_NotSelected" ); g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( info.m_pPanel, "HealthProgressBar_Selected" ); } else { g_pClientMode->GetViewportAnimationController()->StopAnimationSequence( info.m_pPanel, "HealthProgressBar_Selected" ); g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( info.m_pPanel, "HealthProgressBar_NotSelected" ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPingPanel::OnCheckButtonChecked( vgui::Panel *panel ) { if ( m_pCheckButton == panel ) { tf_custom_ping_enabled.SetValue( m_pCheckButton->IsSelected() ); } m_pPingSlider->SetVisible( tf_custom_ping_enabled.GetBool() ); UpdateCurrentPing(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPingPanel::OnSliderMoved() { UpdateCurrentPing(); }