Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

286 lines
9.6 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "tf_ping_panel.h"
  8. #include "vgui_controls/ProgressBar.h"
  9. #include "vgui_controls/AnimationController.h"
  10. #include "tf_gc_client.h"
  11. #include "clientmode_tf.h"
  12. #include "tf_matchmaking_shared.h"
  13. static void OnConVarChangeCustomPingTolerance( IConVar *pConVar, const char *pOldString, float flOldValue )
  14. {
  15. if ( GTFGCClientSystem() )
  16. {
  17. // Otherwise the client system will do this when it starts
  18. GTFGCClientSystem()->UpdateCustomPingTolerance();
  19. }
  20. }
  21. ConVar tf_custom_ping_enabled( "tf_custom_ping_enabled", "0", FCVAR_ARCHIVE, "",
  22. false, 0.f, false, 0.f, OnConVarChangeCustomPingTolerance );
  23. ConVar tf_custom_ping( "tf_custom_ping", "100", FCVAR_ARCHIVE, "",
  24. true, (float)CUSTOM_PING_TOLERANCE_MIN, true, (float)CUSTOM_PING_TOLERANCE_MAX,
  25. OnConVarChangeCustomPingTolerance );
  26. #ifdef STAGING_ONLY
  27. ConVar tf_custom_ping_add_random_datacenters( "tf_custom_ping_add_random_datacenters", "0" );
  28. #endif // STAGING_ONLY
  29. CTFPingPanel::CTFPingPanel( Panel* pPanel, const char *pszName, EMatchGroup eMatchGroup )
  30. : EditablePanel( pPanel, pszName ),
  31. m_eMatchGroup( eMatchGroup )
  32. {
  33. SetProportional( true );
  34. m_pMainContainer = new EditablePanel( this, "MainContainer" );
  35. m_pCheckButton = new CheckButton( m_pMainContainer, "CheckButton", "" );
  36. m_pCurrentPingLabel = new Label( m_pMainContainer, "CurrentPingLabel", "" );
  37. m_pPingSlider = new CCvarSlider( m_pMainContainer, "PingSlider" );
  38. ListenForGameEvent( "ping_updated" );
  39. }
  40. CTFPingPanel::~CTFPingPanel()
  41. {
  42. CleanupPingPanels();
  43. }
  44. //-----------------------------------------------------------------------------
  45. // Purpose:
  46. //-----------------------------------------------------------------------------
  47. void CTFPingPanel::ApplySchemeSettings( IScheme *pScheme )
  48. {
  49. BaseClass::ApplySchemeSettings( pScheme );
  50. LoadControlSettings( "resource/ui/MatchMakingPingPanel.res" );
  51. CleanupPingPanels();
  52. m_pCheckButton->AddActionSignalTarget( this );
  53. m_pPingSlider->AddActionSignalTarget( this );
  54. CScrollableList *pDataCenterList = FindControl< CScrollableList >( "DataCenterList", true );
  55. static const wchar_t *s_pwszPingFormat = L"%ls (%d ms)";
  56. if ( pDataCenterList && GTFGCClientSystem()->BHavePingData() )
  57. {
  58. auto pingData = GTFGCClientSystem()->GetPingData();
  59. const auto& dictDataCenterPopulations = GTFGCClientSystem()->GetDataCenterPopulationRatioDict( m_eMatchGroup );
  60. bool bTesting = false;
  61. #ifdef STAGING_ONLY
  62. if ( tf_custom_ping_add_random_datacenters.GetBool() )
  63. {
  64. bTesting = true;
  65. const char* pszDataCenterNames[] = { "eat", "lax","iad","atl","gru","scl","lim","lux","vie","sto",
  66. "mad","sgp","hkg","tyo","syd","dxb","bom","maa","ord","waw","jhb" };
  67. CUniformRandomStream randomstream;
  68. randomstream.SetSeed( tf_custom_ping_add_random_datacenters.GetInt() );
  69. for( int i=0; i < ARRAYSIZE( pszDataCenterNames ); ++i )
  70. {
  71. auto pNewPingData = pingData.add_pingdata();
  72. pNewPingData->set_name( pszDataCenterNames[ i ] );
  73. pNewPingData->set_ping( randomstream.RandomInt( 0, 250 ) );
  74. pNewPingData->set_ping_status( CMsgGCDataCenterPing_Update_Status_Normal );
  75. }
  76. }
  77. #endif
  78. // for each ping data, check for intersection with data center population from MMStats
  79. for ( int iPing=0; iPing<pingData.pingdata_size(); ++iPing )
  80. {
  81. auto pingEntry = pingData.pingdata( iPing );
  82. if ( pingEntry.ping_status() != CMsgGCDataCenterPing_Update_Status_Normal )
  83. continue;
  84. const char *pszPingFromDataCenterName = pingEntry.name().c_str();
  85. auto dictIndex = dictDataCenterPopulations.Find( pszPingFromDataCenterName );
  86. // found intersection. add a population health panel
  87. if ( dictIndex != dictDataCenterPopulations.InvalidIndex() || bTesting)
  88. {
  89. // Load control settings
  90. EditablePanel* pDataCenterPopulationPanel = new EditablePanel( pDataCenterList, "DataCenterPopulationPanel" );
  91. pDataCenterPopulationPanel->LoadControlSettings( "resource/ui/MatchMakingDataCenterPopulationPanel.res" );
  92. pDataCenterPopulationPanel->SetAutoDelete( false );
  93. pDataCenterList->ResetScrollAmount();
  94. pDataCenterList->InvalidateLayout();
  95. // Update label
  96. wchar_t wszDataCenterName[ 128 ];
  97. wchar_t* pwszLocalizedDataCenterName = g_pVGuiLocalize->Find( CFmtStr( "#TF_DataCenter_%s", pszPingFromDataCenterName ) );
  98. if ( pwszLocalizedDataCenterName )
  99. {
  100. V_wcsncpy( wszDataCenterName, pwszLocalizedDataCenterName, sizeof( wszDataCenterName ) );
  101. }
  102. else
  103. {
  104. // Fallback is no token. If you hit this, go add a string for this data center in tf_english!
  105. Assert( false );
  106. g_pVGuiLocalize->ConvertANSIToUnicode( pszPingFromDataCenterName, wszDataCenterName, sizeof( wszDataCenterName ) );
  107. }
  108. wchar_t wszLabelText[ 128 ];
  109. V_snwprintf( wszLabelText, sizeof( wszLabelText ), s_pwszPingFormat, wszDataCenterName, pingEntry.ping() );
  110. pDataCenterPopulationPanel->SetDialogVariable( "datacenter_name", wszLabelText );
  111. PingPanelInfo panelInfo;
  112. panelInfo.m_pPanel = pDataCenterPopulationPanel;
  113. panelInfo.m_flPopulationRatio = bTesting ? RandomFloat( 0.f, 1.f ) : dictDataCenterPopulations[dictIndex];
  114. panelInfo.m_nPing = pingEntry.ping();
  115. m_vecDataCenterPingPanels.AddToTail( panelInfo );
  116. }
  117. }
  118. }
  119. struct PingPanelInfoSorter
  120. {
  121. static int SortPingPanelInfo( const PingPanelInfo* a, const PingPanelInfo* b )
  122. {
  123. return a->m_nPing - b->m_nPing;
  124. }
  125. };
  126. m_vecDataCenterPingPanels.Sort( &PingPanelInfoSorter::SortPingPanelInfo );
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. //-----------------------------------------------------------------------------
  131. void CTFPingPanel::PerformLayout()
  132. {
  133. BaseClass::PerformLayout();
  134. m_pCheckButton->SetSelected( tf_custom_ping_enabled.GetBool() );
  135. FOR_EACH_VEC( m_vecDataCenterPingPanels, i )
  136. {
  137. const PingPanelInfo& info = m_vecDataCenterPingPanels[i];
  138. int iTall = info.m_pPanel->GetTall();
  139. int iYGap = i > 0 ? m_iDataCenterYSpace : 0;
  140. int iXPos = info.m_pPanel->GetXPos();
  141. info.m_pPanel->SetPos( iXPos, m_iDataCenterY + iYGap + i * iTall );
  142. // Update bars with latest health data
  143. ProgressBar* pProgress = info.m_pPanel->FindControl< ProgressBar >( "HealthProgressBar", true );
  144. if ( pProgress )
  145. {
  146. auto healthData = GTFGCClientSystem()->GetHealthBracketForRatio( info.m_flPopulationRatio );
  147. pProgress->MakeReadyForUse();
  148. pProgress->SetProgress( healthData.m_flRatio );
  149. pProgress->SetFgColor( healthData.m_colorBar );
  150. }
  151. }
  152. UpdateCurrentPing();
  153. }
  154. //-----------------------------------------------------------------------------
  155. // Purpose:
  156. //-----------------------------------------------------------------------------
  157. void CTFPingPanel::OnCommand( const char *command )
  158. {
  159. if ( FStrEq( command, "close" ) )
  160. {
  161. MarkForDeletion();
  162. return;
  163. }
  164. BaseClass::OnCommand( command );
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose:
  168. //-----------------------------------------------------------------------------
  169. void CTFPingPanel::FireGameEvent( IGameEvent *event )
  170. {
  171. const char *pszEventName = event->GetName();
  172. if ( FStrEq( pszEventName, "ping_updated" ) )
  173. {
  174. InvalidateLayout( true, true );
  175. }
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose:
  179. //-----------------------------------------------------------------------------
  180. void CTFPingPanel::CleanupPingPanels()
  181. {
  182. FOR_EACH_VEC( m_vecDataCenterPingPanels, i )
  183. {
  184. m_vecDataCenterPingPanels[i].m_pPanel->MarkForDeletion();
  185. }
  186. m_vecDataCenterPingPanels.Purge();
  187. }
  188. //-----------------------------------------------------------------------------
  189. // Purpose:
  190. //-----------------------------------------------------------------------------
  191. void CTFPingPanel::UpdateCurrentPing()
  192. {
  193. bool bUsePingLimit = m_pCheckButton->IsSelected();
  194. int nLowestDataCenterPing = m_vecDataCenterPingPanels.Count() ? m_vecDataCenterPingPanels[0].m_nPing : 0;
  195. int nCurrentPingLimit = MAX( (int)m_pPingSlider->GetSliderValue(), nLowestDataCenterPing );
  196. m_pCurrentPingLabel->SetText( bUsePingLimit ? CFmtStr( "Ping Limit: %d", nCurrentPingLimit ) : "Ping Limit: AUTO" );
  197. FOR_EACH_VEC( m_vecDataCenterPingPanels, i )
  198. {
  199. const PingPanelInfo& info = m_vecDataCenterPingPanels[i];
  200. int bHighLight = !bUsePingLimit || info.m_nPing <= nCurrentPingLimit;
  201. if ( bHighLight )
  202. {
  203. g_pClientMode->GetViewportAnimationController()->StopAnimationSequence( info.m_pPanel, "HealthProgressBar_NotSelected" );
  204. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( info.m_pPanel, "HealthProgressBar_Selected" );
  205. }
  206. else
  207. {
  208. g_pClientMode->GetViewportAnimationController()->StopAnimationSequence( info.m_pPanel, "HealthProgressBar_Selected" );
  209. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( info.m_pPanel, "HealthProgressBar_NotSelected" );
  210. }
  211. }
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Purpose:
  215. //-----------------------------------------------------------------------------
  216. void CTFPingPanel::OnCheckButtonChecked( vgui::Panel *panel )
  217. {
  218. if ( m_pCheckButton == panel )
  219. {
  220. tf_custom_ping_enabled.SetValue( m_pCheckButton->IsSelected() );
  221. }
  222. m_pPingSlider->SetVisible( tf_custom_ping_enabled.GetBool() );
  223. UpdateCurrentPing();
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose:
  227. //-----------------------------------------------------------------------------
  228. void CTFPingPanel::OnSliderMoved()
  229. {
  230. UpdateCurrentPing();
  231. }