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.

2323 lines
71 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "pch_serverbrowser.h"
  8. #if defined( _X360 )
  9. #include "xbox/xbox_win32stubs.h"
  10. #endif
  11. using namespace vgui;
  12. #define FILTER_ALLSERVERS 0
  13. #define FILTER_SECURESERVERSONLY 1
  14. #define FILTER_INSECURESERVERSONLY 2
  15. #define UNIVERSE_OFFICIAL 0
  16. #define UNIVERSE_CUSTOMGAMES 1
  17. #define QUICKLIST_FILTER_MIN_PING 0
  18. #define MAX_MAP_NAME 128
  19. const char *COM_GetModDirectory();
  20. #undef wcscat
  21. ConVar sb_mod_suggested_maxplayers( "sb_mod_suggested_maxplayers", "0", FCVAR_HIDDEN );
  22. ConVar sb_filter_incompatible_versions( "sb_filter_incompatible_versions",
  23. #ifdef STAGING_ONLY
  24. "0",
  25. #else
  26. "1",
  27. #endif
  28. 0, "Hides servers running incompatible versions from the server browser. (Internet tab only.)" );
  29. bool GameSupportsReplay()
  30. {
  31. extern IEngineReplay *g_pEngineReplay;
  32. return g_pEngineReplay && g_pEngineReplay->IsSupportedModAndPlatform();
  33. }
  34. #ifdef STAGING_ONLY
  35. ConVar sb_fake_app_id( "sb_fake_app_id", "0", 0, "If nonzero, then server browser requests will use this App ID instead" );
  36. #endif
  37. //--------------------------------------------------------------------------------------------------------
  38. bool IsReplayServer( gameserveritem_t &server )
  39. {
  40. bool bReplay = false;
  41. if ( GameSupportsReplay() )
  42. {
  43. if ( server.m_szGameTags && server.m_szGameTags[0] )
  44. {
  45. CUtlVector<char*> TagList;
  46. V_SplitString( server.m_szGameTags, ",", TagList );
  47. for ( int i = 0; i < TagList.Count(); i++ )
  48. {
  49. if ( Q_stricmp( TagList[i], "replays" ) == 0 )
  50. {
  51. bReplay = true;
  52. }
  53. }
  54. }
  55. }
  56. return bReplay;
  57. }
  58. //--------------------------------------------------------------------------------------------------------
  59. inline char *CloneString( const char *str )
  60. {
  61. char *cloneStr = new char [ strlen(str)+1 ];
  62. strcpy( cloneStr, str );
  63. return cloneStr;
  64. }
  65. const char *COM_GetModDirectory()
  66. {
  67. static char modDir[MAX_PATH];
  68. if ( Q_strlen( modDir ) == 0 )
  69. {
  70. const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) );
  71. Q_strncpy( modDir, gamedir, sizeof(modDir) );
  72. if ( strchr( modDir, '/' ) || strchr( modDir, '\\' ) )
  73. {
  74. Q_StripLastDir( modDir, sizeof(modDir) );
  75. int dirlen = Q_strlen( modDir );
  76. Q_strncpy( modDir, gamedir + dirlen, sizeof(modDir) - dirlen );
  77. }
  78. }
  79. return modDir;
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Purpose: Constructor
  83. //-----------------------------------------------------------------------------
  84. CGameListPanel::CGameListPanel( CBaseGamesPage *pOuter, const char *pName ) :
  85. BaseClass( pOuter, pName )
  86. {
  87. m_pOuter = pOuter;
  88. }
  89. //-----------------------------------------------------------------------------
  90. // Purpose: Forward KEY_ENTER to the CBaseGamesPage.
  91. //-----------------------------------------------------------------------------
  92. void CGameListPanel::OnKeyCodePressed(vgui::KeyCode code)
  93. {
  94. // Let the outer class handle it.
  95. if ( code == KEY_ENTER && m_pOuter->OnGameListEnterPressed() )
  96. return;
  97. BaseClass::OnKeyCodePressed( code );
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Purpose: Constructor
  101. //-----------------------------------------------------------------------------
  102. CBaseGamesPage::CBaseGamesPage( vgui::Panel *parent, const char *name, EPageType eType, const char *pCustomResFilename)
  103. : PropertyPage(parent, name),
  104. m_CallbackFavoritesMsg( this, &CBaseGamesPage::OnFavoritesMsg ),
  105. m_hRequest( NULL ),
  106. m_pCustomResFilename( pCustomResFilename )
  107. {
  108. SetSize( 624, 278 );
  109. m_szGameFilter[0] = 0;
  110. m_szMapFilter[0] = 0;
  111. m_iMaxPlayerFilter = 0;
  112. m_iPingFilter = 0;
  113. m_iServerRefreshCount = 0;
  114. m_bFilterNoFullServers = false;
  115. m_bFilterNoEmptyServers = false;
  116. m_bFilterNoPasswordedServers = false;
  117. m_iSecureFilter = FILTER_ALLSERVERS;
  118. m_hFont = NULL;
  119. m_eMatchMakingType = eType;
  120. m_bFilterReplayServers = false;
  121. SetDefLessFunc( m_mapServers );
  122. SetDefLessFunc( m_mapServerIP );
  123. SetDefLessFunc( m_mapGamesFilterItem );
  124. // Not always loaded
  125. m_pWorkshopFilter = NULL;
  126. bool bRunningTF2 = GameSupportsReplay();
  127. // get the 'all' text
  128. wchar_t *all = g_pVGuiLocalize->Find("ServerBrowser_All");
  129. Q_UnicodeToUTF8(all, m_szComboAllText, sizeof(m_szComboAllText));
  130. // Init UI
  131. m_pConnect = new Button(this, "ConnectButton", "#ServerBrowser_Connect");
  132. m_pConnect->SetEnabled(false);
  133. m_pRefreshAll = new Button(this, "RefreshButton", "#ServerBrowser_Refresh");
  134. m_pRefreshQuick = new Button(this, "RefreshQuickButton", "#ServerBrowser_RefreshQuick");
  135. m_pAddServer = new Button(this, "AddServerButton", "#ServerBrowser_AddServer");
  136. m_pAddCurrentServer = new Button(this, "AddCurrentServerButton", "#ServerBrowser_AddCurrentServer");
  137. m_pGameList = new CGameListPanel(this, "gamelist");
  138. m_pGameList->SetAllowUserModificationOfColumns(true);
  139. m_pQuickList = new PanelListPanel(this, "quicklist");
  140. m_pQuickList->SetFirstColumnWidth( 0 );
  141. m_pAddToFavoritesButton = new vgui::Button( this, "AddToFavoritesButton", "" );
  142. m_pAddToFavoritesButton->SetEnabled( false );
  143. m_pAddToFavoritesButton->SetVisible( false );
  144. // Increment this number if columns are added / removed or some other change is done that requires
  145. // tossing out old saved user configs.
  146. m_pGameList->m_nUserConfigFileVersion = 2;
  147. // Add the column headers
  148. m_pGameList->AddColumnHeader( k_nColumn_Password, "Password", "#ServerBrowser_Password", 16, ListPanel::COLUMN_FIXEDSIZE | ListPanel::COLUMN_IMAGE);
  149. m_pGameList->AddColumnHeader( k_nColumn_Secure, "Secure", "#ServerBrowser_Secure", 16, ListPanel::COLUMN_FIXEDSIZE | ListPanel::COLUMN_IMAGE);
  150. int nReplayWidth = 16;
  151. if ( !bRunningTF2 )
  152. {
  153. nReplayWidth = 0;
  154. }
  155. m_pGameList->AddColumnHeader( k_nColumn_Replay, "Replay", "#ServerBrowser_Replay", nReplayWidth, ListPanel::COLUMN_FIXEDSIZE | ListPanel::COLUMN_IMAGE);
  156. m_pGameList->AddColumnHeader( k_nColumn_Name, "Name", "#ServerBrowser_Servers", 50, ListPanel::COLUMN_RESIZEWITHWINDOW | ListPanel::COLUMN_UNHIDABLE);
  157. m_pGameList->AddColumnHeader( k_nColumn_IPAddr, "IPAddr", "#ServerBrowser_IPAddress", 64, ListPanel::COLUMN_HIDDEN);
  158. m_pGameList->AddColumnHeader( k_nColumn_GameDesc, "GameDesc", "#ServerBrowser_Game", 112,
  159. 112, // minwidth
  160. 300, // maxwidth
  161. 0 // flags
  162. );
  163. m_pGameList->AddColumnHeader( k_nColumn_Players, "Players", "#ServerBrowser_Players", 55, ListPanel::COLUMN_FIXEDSIZE);
  164. m_pGameList->AddColumnHeader( k_nColumn_Bots, "Bots", "#ServerBrowser_Bots", 40, ListPanel::COLUMN_FIXEDSIZE);
  165. m_pGameList->AddColumnHeader( k_nColumn_Map, "Map", "#ServerBrowser_Map", 90,
  166. 90, // minwidth
  167. 300, // maxwidth
  168. 0 // flags
  169. );
  170. m_pGameList->AddColumnHeader( k_nColumn_Ping, "Ping", "#ServerBrowser_Latency", 55, ListPanel::COLUMN_FIXEDSIZE);
  171. m_pGameList->SetColumnHeaderTooltip( k_nColumn_Password, "#ServerBrowser_PasswordColumn_Tooltip");
  172. m_pGameList->SetColumnHeaderTooltip( k_nColumn_Bots, "#ServerBrowser_BotColumn_Tooltip");
  173. m_pGameList->SetColumnHeaderTooltip( k_nColumn_Secure, "#ServerBrowser_SecureColumn_Tooltip");
  174. if ( bRunningTF2 )
  175. {
  176. m_pGameList->SetColumnHeaderTooltip( k_nColumn_Replay, "#ServerBrowser_ReplayColumn_Tooltip");
  177. }
  178. // setup fast sort functions
  179. m_pGameList->SetSortFunc( k_nColumn_Password, PasswordCompare);
  180. m_pGameList->SetSortFunc( k_nColumn_Bots, BotsCompare);
  181. m_pGameList->SetSortFunc( k_nColumn_Secure, SecureCompare);
  182. if ( bRunningTF2 )
  183. {
  184. m_pGameList->SetSortFunc( k_nColumn_Replay, ReplayCompare);
  185. }
  186. m_pGameList->SetSortFunc( k_nColumn_Name, ServerNameCompare);
  187. m_pGameList->SetSortFunc( k_nColumn_IPAddr, IPAddressCompare);
  188. m_pGameList->SetSortFunc( k_nColumn_GameDesc, GameCompare);
  189. m_pGameList->SetSortFunc( k_nColumn_Players, PlayersCompare);
  190. m_pGameList->SetSortFunc( k_nColumn_Map, MapCompare);
  191. m_pGameList->SetSortFunc( k_nColumn_Ping, PingCompare);
  192. // Sort by ping time by default
  193. m_pGameList->SetSortColumn( k_nColumn_Ping );
  194. CreateFilters();
  195. LoadFilterSettings();
  196. m_bAutoSelectFirstItemInGameList = false;
  197. // In TF2, fill out the max player count so that we sort all >24 player servers below the rest.
  198. if ( bRunningTF2 )
  199. {
  200. sb_mod_suggested_maxplayers.SetValue( 24 );
  201. }
  202. }
  203. //-----------------------------------------------------------------------------
  204. // Purpose: Destructor
  205. //-----------------------------------------------------------------------------
  206. CBaseGamesPage::~CBaseGamesPage()
  207. {
  208. if ( m_hRequest )
  209. {
  210. steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
  211. m_hRequest = NULL;
  212. }
  213. }
  214. //-----------------------------------------------------------------------------
  215. // Purpose:
  216. //-----------------------------------------------------------------------------
  217. int CBaseGamesPage::GetInvalidServerListID()
  218. {
  219. return m_pGameList->InvalidItemID();
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Purpose:
  223. //-----------------------------------------------------------------------------
  224. void CBaseGamesPage::PerformLayout()
  225. {
  226. BaseClass::PerformLayout();
  227. if ( GetSelectedServerID() == -1 )
  228. {
  229. m_pConnect->SetEnabled(false);
  230. }
  231. else
  232. {
  233. m_pConnect->SetEnabled(true);
  234. }
  235. if (SupportsItem(IGameList::GETNEWLIST))
  236. {
  237. m_pRefreshQuick->SetVisible(true);
  238. m_pRefreshAll->SetText("#ServerBrowser_RefreshAll");
  239. }
  240. else
  241. {
  242. m_pRefreshQuick->SetVisible(false);
  243. m_pRefreshAll->SetText("#ServerBrowser_Refresh");
  244. }
  245. if ( SupportsItem(IGameList::ADDSERVER) )
  246. {
  247. // m_pFilterString->SetWide( 90 ); // shrink the filter label to fix the add current server button
  248. m_pAddServer->SetVisible(true);
  249. }
  250. else
  251. {
  252. m_pAddServer->SetVisible(false);
  253. }
  254. if ( SupportsItem(IGameList::ADDCURRENTSERVER) )
  255. {
  256. m_pAddCurrentServer->SetVisible(true);
  257. }
  258. else
  259. {
  260. m_pAddCurrentServer->SetVisible(false);
  261. }
  262. if ( IsRefreshing() )
  263. {
  264. m_pRefreshAll->SetText( "#ServerBrowser_StopRefreshingList" );
  265. }
  266. if (m_pGameList->GetItemCount() > 0)
  267. {
  268. m_pRefreshQuick->SetEnabled(true);
  269. }
  270. else
  271. {
  272. m_pRefreshQuick->SetEnabled(false);
  273. }
  274. if ( !steamapicontext->SteamMatchmakingServers() || !steamapicontext->SteamMatchmaking() )
  275. {
  276. m_pAddCurrentServer->SetVisible( false );
  277. m_pRefreshQuick->SetEnabled( false );
  278. m_pAddServer->SetEnabled( false );
  279. m_pConnect->SetEnabled( false );
  280. m_pRefreshAll->SetEnabled( false );
  281. m_pAddToFavoritesButton->SetEnabled( false );
  282. m_pGameList->SetEmptyListText( "#ServerBrowser_SteamRunning" );
  283. }
  284. Repaint();
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose:
  288. //-----------------------------------------------------------------------------
  289. void CBaseGamesPage::ApplySchemeSettings(IScheme *pScheme)
  290. {
  291. BaseClass::ApplySchemeSettings(pScheme);
  292. // load the password icon
  293. ImageList *imageList = new ImageList(false);
  294. m_nImageIndexPassword = imageList->AddImage(scheme()->GetImage("servers/icon_password", false));
  295. //imageList->AddImage(scheme()->GetImage("servers/icon_bots", false));
  296. m_nImageIndexSecure = imageList->AddImage(scheme()->GetImage("servers/icon_robotron", false));
  297. m_nImageIndexSecureVacBanned = imageList->AddImage(scheme()->GetImage("servers/icon_secure_deny", false));
  298. m_nImageIndexReplay = imageList->AddImage(scheme()->GetImage("servers/icon_replay", false));
  299. int passwordColumnImage = imageList->AddImage(scheme()->GetImage("servers/icon_password_column", false));
  300. //int botColumnImage = imageList->AddImage(scheme()->GetImage("servers/icon_bots_column", false));
  301. int secureColumnImage = imageList->AddImage(scheme()->GetImage("servers/icon_robotron_column", false));
  302. int replayColumnImage = imageList->AddImage(scheme()->GetImage("servers/icon_replay_column", false));
  303. m_pGameList->SetImageList(imageList, true);
  304. m_hFont = pScheme->GetFont( "ListSmall", IsProportional() );
  305. if ( !m_hFont )
  306. m_hFont = pScheme->GetFont( "DefaultSmall", IsProportional() );
  307. m_pGameList->SetFont( m_hFont );
  308. m_pGameList->SetColumnHeaderImage( k_nColumn_Password, passwordColumnImage);
  309. //m_pGameList->SetColumnHeaderImage( k_nColumn_Bots, botColumnImage);
  310. m_pGameList->SetColumnHeaderImage( k_nColumn_Secure, secureColumnImage);
  311. m_pGameList->SetColumnHeaderImage( k_nColumn_Replay, replayColumnImage);
  312. }
  313. struct serverqualitysort_t
  314. {
  315. int iIndex;
  316. int iPing;
  317. int iPlayerCount;
  318. int iMaxPlayerCount;
  319. };
  320. int ServerQualitySort( const serverqualitysort_t *pSQ1, const serverqualitysort_t *pSQ2 )
  321. {
  322. int iMaxP = sb_mod_suggested_maxplayers.GetInt();
  323. if ( iMaxP && pSQ1->iMaxPlayerCount != pSQ2->iMaxPlayerCount )
  324. {
  325. if ( pSQ1->iMaxPlayerCount > iMaxP )
  326. return 1;
  327. if ( pSQ2->iMaxPlayerCount > iMaxP )
  328. return -1;
  329. }
  330. if ( pSQ1->iPing <= 100 && pSQ2->iPing <= 100 && pSQ1->iPlayerCount != pSQ2->iPlayerCount )
  331. {
  332. return pSQ2->iPlayerCount - pSQ1->iPlayerCount;
  333. }
  334. return pSQ1->iPing - pSQ2->iPing;
  335. }
  336. //-----------------------------------------------------------------------------
  337. // Purpose:
  338. //-----------------------------------------------------------------------------
  339. void CBaseGamesPage::SelectQuickListServers( void )
  340. {
  341. int iIndex = m_pQuickList->FirstItem();
  342. while ( iIndex != m_pQuickList->InvalidItemID() )
  343. {
  344. CQuickListPanel *pQuickListPanel = dynamic_cast< CQuickListPanel *> ( m_pQuickList->GetItemPanel( iIndex ) );
  345. if ( pQuickListPanel )
  346. {
  347. CUtlVector< serverqualitysort_t > vecServerQuality;
  348. int iElement = m_quicklistserverlist.Find( pQuickListPanel->GetName() );
  349. if ( iElement != m_quicklistserverlist.InvalidIndex() )
  350. {
  351. CQuickListMapServerList *vecMapServers = &m_quicklistserverlist[iElement];
  352. if ( vecMapServers )
  353. {
  354. for ( int i =0; i < vecMapServers->Count(); i++ )
  355. {
  356. int iListID = vecMapServers->Element( i );
  357. serverqualitysort_t serverquality;
  358. serverquality.iIndex = iListID;
  359. KeyValues *kv = NULL;
  360. if ( m_pGameList->IsValidItemID( iListID ) )
  361. {
  362. kv = m_pGameList->GetItem( iListID );
  363. }
  364. if ( kv )
  365. {
  366. serverquality.iPing = kv->GetInt( "ping", 0 );
  367. serverquality.iPlayerCount = kv->GetInt( "PlayerCount", 0 );
  368. serverquality.iMaxPlayerCount = kv->GetInt( "MaxPlayerCount", 0 );
  369. }
  370. vecServerQuality.AddToTail( serverquality );
  371. }
  372. vecServerQuality.Sort( ServerQualitySort );
  373. serverqualitysort_t bestserver = vecServerQuality.Head();
  374. if ( m_pGameList->IsValidItemID( bestserver.iIndex ) )
  375. {
  376. pQuickListPanel->SetServerInfo( m_pGameList->GetItem( bestserver.iIndex ), bestserver.iIndex, vecServerQuality.Count() );
  377. }
  378. }
  379. }
  380. }
  381. iIndex = m_pQuickList->NextItem( iIndex );
  382. }
  383. //Force the connect button to recalculate its state.
  384. OnItemSelected();
  385. }
  386. int ServerMapnameSortFunc( const servermaps_t *p1, const servermaps_t *p2 )
  387. {
  388. //If they're both on disc OR both missing then sort them alphabetically
  389. if ( (p1->bOnDisk && p2->bOnDisk) || (!p1->bOnDisk && !p2->bOnDisk ) )
  390. return Q_strcmp( p1->pFriendlyName, p2->pFriendlyName );
  391. //Otherwise maps you have show up first
  392. return p2->bOnDisk - p1->bOnDisk;
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Purpose: prepares all the QuickListPanel map panels...
  396. //-----------------------------------------------------------------------------
  397. void CBaseGamesPage::PrepareQuickListMap( const char *pMapName, int iListID )
  398. {
  399. char szMapName[ 512 ];
  400. Q_snprintf( szMapName, sizeof( szMapName ), "%s", pMapName );
  401. Q_strlower( szMapName );
  402. char path[ 512 ];
  403. Q_snprintf( path, sizeof( path ), "maps/%s.bsp", szMapName );
  404. int iIndex = m_quicklistserverlist.Find( szMapName );
  405. if ( m_quicklistserverlist.IsValidIndex( iIndex ) == false )
  406. {
  407. CQuickListMapServerList vecMapServers;
  408. iIndex = m_quicklistserverlist.Insert( szMapName, vecMapServers );
  409. char szFriendlyName[MAX_MAP_NAME];
  410. const char *pszFriendlyGameTypeName = ServerBrowser().GetMapFriendlyNameAndGameType( szMapName, szFriendlyName, sizeof(szFriendlyName) );
  411. //Add the map to our list of panels.
  412. if ( m_pQuickList )
  413. {
  414. servermaps_t servermap;
  415. servermap.pFriendlyName = CloneString( szFriendlyName );
  416. servermap.pOriginalName = CloneString( szMapName );
  417. char path[ 512 ];
  418. Q_snprintf( path, sizeof( path ), "maps/%s.bsp", szMapName );
  419. servermap.bOnDisk = g_pFullFileSystem->FileExists( path, "MOD" );
  420. CQuickListPanel *pQuickListPanel = new CQuickListPanel( m_pQuickList, "QuickListPanel");
  421. if ( pQuickListPanel )
  422. {
  423. pQuickListPanel->InvalidateLayout();
  424. pQuickListPanel->SetName( servermap.pOriginalName );
  425. pQuickListPanel->SetMapName( servermap.pFriendlyName );
  426. pQuickListPanel->SetImage( servermap.pOriginalName );
  427. pQuickListPanel->SetGameType( pszFriendlyGameTypeName );
  428. pQuickListPanel->SetVisible( true );
  429. pQuickListPanel->SetRefreshing();
  430. servermap.iPanelIndex = m_pQuickList->AddItem( NULL, pQuickListPanel );
  431. }
  432. m_vecMapNamesFound.AddToTail( servermap );
  433. m_vecMapNamesFound.Sort( ServerMapnameSortFunc );
  434. }
  435. //Now make sure that list is sorted.
  436. CUtlVector<int> *pPanelSort = m_pQuickList->GetSortedVector();
  437. if ( pPanelSort )
  438. {
  439. pPanelSort->RemoveAll();
  440. for ( int i = 0; i < m_vecMapNamesFound.Count(); i++ )
  441. {
  442. pPanelSort->AddToTail( m_vecMapNamesFound[i].iPanelIndex );
  443. }
  444. }
  445. }
  446. if ( iIndex != m_quicklistserverlist.InvalidIndex() )
  447. {
  448. CQuickListMapServerList *vecMapServers = &m_quicklistserverlist[iIndex];
  449. if ( vecMapServers )
  450. {
  451. if ( vecMapServers->Find( iListID ) == vecMapServers->InvalidIndex() )
  452. {
  453. vecMapServers->AddToTail( iListID );
  454. }
  455. }
  456. }
  457. }
  458. //-----------------------------------------------------------------------------
  459. // Purpose: gets information about specified server
  460. //-----------------------------------------------------------------------------
  461. gameserveritem_t *CBaseGamesPage::GetServer( unsigned int serverID )
  462. {
  463. if ( !steamapicontext->SteamMatchmakingServers() )
  464. return NULL;
  465. // No point checking for >= 0 when serverID is unsigned.
  466. //if ( serverID >= 0 )
  467. {
  468. return steamapicontext->SteamMatchmakingServers()->GetServerDetails( m_hRequest, serverID );
  469. }
  470. //else
  471. //{
  472. // Assert( !"Unable to return a useful entry" );
  473. // return NULL; // bugbug Alfred: temp Favorites/History objects won't return a good value here...
  474. //}
  475. }
  476. //-----------------------------------------------------------------------------
  477. // Purpose:
  478. //-----------------------------------------------------------------------------
  479. bool CBaseGamesPage::TagsExclude( void )
  480. {
  481. if ( m_pTagsIncludeFilter == NULL )
  482. return false;
  483. return m_pTagsIncludeFilter->GetActiveItem();
  484. }
  485. //-----------------------------------------------------------------------------
  486. // Purpose: What mode the workshop selection is in for pages that use it
  487. //-----------------------------------------------------------------------------
  488. CBaseGamesPage::eWorkshopMode CBaseGamesPage::WorkshopMode()
  489. {
  490. if ( !m_pWorkshopFilter || !ServerBrowser().IsWorkshopEnabled() )
  491. {
  492. return eWorkshop_None;
  493. }
  494. return (eWorkshopMode)m_pWorkshopFilter->GetActiveItem();
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Purpose:
  498. //-----------------------------------------------------------------------------
  499. void CBaseGamesPage::HideReplayFilter( void )
  500. {
  501. if ( m_pReplayFilterCheck && m_pReplayFilterCheck->IsVisible() )
  502. {
  503. m_pReplayFilterCheck->SetVisible( false );
  504. }
  505. }
  506. //-----------------------------------------------------------------------------
  507. // Purpose:
  508. //-----------------------------------------------------------------------------
  509. void CBaseGamesPage::CreateFilters()
  510. {
  511. m_pFilter = new ToggleButton(this, "Filter", "#ServerBrowser_Filters");
  512. m_pFilterString = new Label(this, "FilterString", "");
  513. if ( Q_stricmp( COM_GetModDirectory(), "cstrike" ) == 0 )
  514. {
  515. m_pFilter->SetSelected( false );
  516. m_bFiltersVisible = false;
  517. }
  518. else
  519. {
  520. m_pFilter->SetSelected( true );
  521. m_bFiltersVisible = true;
  522. }
  523. // filter controls
  524. m_pGameFilter = new ComboBox(this, "GameFilter", 6, false);
  525. m_pLocationFilter = new ComboBox(this, "LocationFilter", 6, false);
  526. m_pLocationFilter->AddItem("", NULL);
  527. m_pMapFilter = new TextEntry(this, "MapFilter");
  528. m_pMaxPlayerFilter = new TextEntry(this, "MaxPlayerFilter");
  529. m_pPingFilter = new ComboBox(this, "PingFilter", 6, false);
  530. m_pPingFilter->AddItem("#ServerBrowser_All", NULL);
  531. m_pPingFilter->AddItem("#ServerBrowser_LessThan50", NULL);
  532. m_pPingFilter->AddItem("#ServerBrowser_LessThan100", NULL);
  533. m_pPingFilter->AddItem("#ServerBrowser_LessThan150", NULL);
  534. m_pPingFilter->AddItem("#ServerBrowser_LessThan250", NULL);
  535. m_pPingFilter->AddItem("#ServerBrowser_LessThan350", NULL);
  536. m_pPingFilter->AddItem("#ServerBrowser_LessThan600", NULL);
  537. m_pSecureFilter = new ComboBox(this, "SecureFilter", 3, false);
  538. m_pSecureFilter->AddItem("#ServerBrowser_All", NULL);
  539. m_pSecureFilter->AddItem("#ServerBrowser_SecureOnly", NULL);
  540. m_pSecureFilter->AddItem("#ServerBrowser_InsecureOnly", NULL);
  541. m_pTagsIncludeFilter = new ComboBox(this, "TagsInclude", 2, false);
  542. m_pTagsIncludeFilter->AddItem("#ServerBrowser_TagsInclude", NULL);
  543. m_pTagsIncludeFilter->AddItem("#ServerBrowser_TagsDoNotInclude", NULL);
  544. m_pTagsIncludeFilter->SetVisible( false );
  545. if ( ServerBrowser().IsWorkshopEnabled() )
  546. {
  547. m_pWorkshopFilter = new ComboBox(this, "WorkshopFilter", 3, false);
  548. m_pWorkshopFilter->AddItem("#ServerBrowser_All", NULL);
  549. m_pWorkshopFilter->AddItem("#ServerBrowser_WorkshopFilterWorkshopOnly", NULL);
  550. m_pWorkshopFilter->AddItem("#ServerBrowser_WorkshopFilterSubscribed", NULL);
  551. m_pWorkshopFilter->SetVisible( false );
  552. }
  553. m_pNoEmptyServersFilterCheck = new CheckButton(this, "ServerEmptyFilterCheck", "");
  554. m_pNoFullServersFilterCheck = new CheckButton(this, "ServerFullFilterCheck", "");
  555. m_pNoPasswordFilterCheck = new CheckButton(this, "NoPasswordFilterCheck", "");
  556. m_pQuickListCheckButton = new CCheckBoxWithStatus(this, "QuickListCheck", "");
  557. m_pReplayFilterCheck = new CheckButton(this, "ReplayFilterCheck", "");
  558. KeyValues *pkv = new KeyValues("mod", "gamedir", "", "appid", NULL );
  559. m_pGameFilter->AddItem("#ServerBrowser_All", pkv);
  560. for (int i = 0; i < ModList().ModCount(); i++)
  561. {
  562. pkv->SetString("gamedir", ModList().GetModDir(i));
  563. pkv->SetUint64("appid", ModList().GetAppID(i).ToUint64() );
  564. int iItemID = m_pGameFilter->AddItem(ModList().GetModName(i), pkv);
  565. m_mapGamesFilterItem.Insert( ModList().GetAppID(i).ToUint64(), iItemID );
  566. }
  567. pkv->deleteThis();
  568. }
  569. //-----------------------------------------------------------------------------
  570. // Purpose: loads filter settings from the keyvalues
  571. //-----------------------------------------------------------------------------
  572. void CBaseGamesPage::LoadFilterSettings()
  573. {
  574. KeyValues *filter = ServerBrowserDialog().GetFilterSaveData(GetName());
  575. if (ServerBrowserDialog().GetActiveModName())
  576. {
  577. Q_strncpy(m_szGameFilter, ServerBrowserDialog().GetActiveModName(), sizeof(m_szGameFilter));
  578. m_iLimitToAppID = ServerBrowserDialog().GetActiveAppID();
  579. }
  580. else
  581. {
  582. Q_strncpy(m_szGameFilter, filter->GetString("game"), sizeof(m_szGameFilter));
  583. m_iLimitToAppID = CGameID( filter->GetUint64( "appid", 0 ) );
  584. }
  585. Q_strncpy(m_szMapFilter, filter->GetString("map"), sizeof(m_szMapFilter));
  586. m_iMaxPlayerFilter = filter->GetInt("MaxPlayerCount");
  587. m_iPingFilter = filter->GetInt("ping");
  588. m_bFilterNoFullServers = filter->GetInt("NoFull");
  589. m_bFilterNoEmptyServers = filter->GetInt("NoEmpty");
  590. m_bFilterNoPasswordedServers = filter->GetInt("NoPassword");
  591. m_bFilterReplayServers = filter->GetInt("Replay");
  592. m_pQuickListCheckButton->SetSelected( filter->GetInt( "QuickList", 0 ) );
  593. int secureFilter = filter->GetInt("Secure");
  594. m_pSecureFilter->ActivateItem(secureFilter);
  595. int tagsinclude = filter->GetInt("tagsinclude");
  596. m_pTagsIncludeFilter->ActivateItem( tagsinclude );
  597. if ( m_pWorkshopFilter )
  598. {
  599. int workshopFilter = filter->GetInt("workshopfilter");
  600. m_pWorkshopFilter->ActivateItem( workshopFilter );
  601. }
  602. // apply to the controls
  603. UpdateGameFilter();
  604. m_pMapFilter->SetText(m_szMapFilter);
  605. m_pLocationFilter->ActivateItem(filter->GetInt("location"));
  606. if (m_iMaxPlayerFilter)
  607. {
  608. char buf[32];
  609. Q_snprintf(buf, sizeof(buf), "%d", m_iMaxPlayerFilter);
  610. m_pMaxPlayerFilter->SetText(buf);
  611. }
  612. if (m_iPingFilter)
  613. {
  614. char buf[32];
  615. Q_snprintf(buf, sizeof(buf), "< %d", m_iPingFilter);
  616. m_pPingFilter->SetText(buf);
  617. }
  618. m_pNoFullServersFilterCheck->SetSelected(m_bFilterNoFullServers);
  619. m_pNoEmptyServersFilterCheck->SetSelected(m_bFilterNoEmptyServers);
  620. m_pNoPasswordFilterCheck->SetSelected(m_bFilterNoPasswordedServers);
  621. m_pReplayFilterCheck->SetSelected(m_bFilterReplayServers);
  622. OnLoadFilter( filter );
  623. UpdateFilterSettings();
  624. UpdateFilterAndQuickListVisibility();
  625. }
  626. //-----------------------------------------------------------------------------
  627. // Purpose: Sets the game filter combo box to be the saved setting
  628. //-----------------------------------------------------------------------------
  629. void CBaseGamesPage::UpdateGameFilter()
  630. {
  631. bool bFound = false;
  632. for (int i = 0; i < m_pGameFilter->GetItemCount(); i++)
  633. {
  634. KeyValues *kv = m_pGameFilter->GetItemUserData(i);
  635. CGameID gameID( kv->GetUint64( "appID", 0 ) );
  636. const char *pchGameDir = kv->GetString( "gamedir" );
  637. if ( ( gameID == m_iLimitToAppID || m_iLimitToAppID.AppID() == 0 ) && ( !m_szGameFilter[0] ||
  638. ( pchGameDir && pchGameDir[0] && !Q_strncmp( pchGameDir, m_szGameFilter, Q_strlen( pchGameDir ) ) ) ) )
  639. {
  640. if ( i != m_pGameFilter->GetActiveItem() )
  641. {
  642. m_pGameFilter->ActivateItem(i);
  643. }
  644. bFound = true;
  645. break;
  646. }
  647. }
  648. if (!bFound)
  649. {
  650. // default to empty
  651. if ( 0 != m_pGameFilter->GetActiveItem() )
  652. {
  653. m_pGameFilter->ActivateItem(0);
  654. }
  655. }
  656. // only one mod is allowed in the game
  657. if ( ServerBrowserDialog().GetActiveModName() )
  658. {
  659. m_pGameFilter->SetEnabled( false );
  660. m_pGameFilter->SetText( ServerBrowserDialog().GetActiveGameName() );
  661. }
  662. }
  663. //-----------------------------------------------------------------------------
  664. // Purpose: Handles incoming server refresh data
  665. // updates the server browser with the refreshed information from the server itself
  666. //-----------------------------------------------------------------------------
  667. void CBaseGamesPage::ServerResponded( gameserveritem_t &server )
  668. {
  669. int nIndex = -1; // start at -1 and work backwards to find the next free slot for this adhoc query
  670. while ( m_mapServers.Find( nIndex ) != m_mapServers.InvalidIndex() )
  671. nIndex--;
  672. ServerResponded( nIndex, &server );
  673. }
  674. //-----------------------------------------------------------------------------
  675. // Purpose: Callback for ISteamMatchmakingServerListResponse
  676. //-----------------------------------------------------------------------------
  677. void CBaseGamesPage::ServerResponded( HServerListRequest hReq, int iServer )
  678. {
  679. gameserveritem_t *pServerItem = steamapicontext->SteamMatchmakingServers()->GetServerDetails( hReq, iServer );
  680. if ( !pServerItem )
  681. {
  682. Assert( !"Missing server response" );
  683. return;
  684. }
  685. // FIXME(johns): This is a workaround for a steam bug, where it inproperly reads signed bytes out of the
  686. // message. Once the upstream fix makes it into our SteamSDK, this block can be removed.
  687. pServerItem->m_nPlayers = (uint8)(int8)pServerItem->m_nPlayers;
  688. pServerItem->m_nBotPlayers = (uint8)(int8)pServerItem->m_nBotPlayers;
  689. pServerItem->m_nMaxPlayers = (uint8)(int8)pServerItem->m_nMaxPlayers;
  690. ServerResponded( iServer, pServerItem );
  691. }
  692. //-----------------------------------------------------------------------------
  693. // Purpose: Handles incoming server refresh data
  694. // updates the server browser with the refreshed information from the server itself
  695. //-----------------------------------------------------------------------------
  696. void CBaseGamesPage::ServerResponded( int iServer, gameserveritem_t *pServerItem )
  697. {
  698. int iServerMap = m_mapServers.Find( iServer );
  699. if ( iServerMap == m_mapServers.InvalidIndex() )
  700. {
  701. netadr_t netAdr( pServerItem->m_NetAdr.GetIP(), pServerItem->m_NetAdr.GetConnectionPort() );
  702. int iServerIP = m_mapServerIP.Find( netAdr );
  703. if ( iServerIP != m_mapServerIP.InvalidIndex() )
  704. {
  705. // if we already had this entry under another index remove the old entry
  706. int iServerMap = m_mapServers.Find( m_mapServerIP[ iServerIP ] );
  707. if ( iServerMap != m_mapServers.InvalidIndex() )
  708. {
  709. serverdisplay_t &server = m_mapServers[ iServerMap ];
  710. if ( m_pGameList->IsValidItemID( server.m_iListID ) )
  711. m_pGameList->RemoveItem( server.m_iListID );
  712. m_mapServers.RemoveAt( iServerMap );
  713. }
  714. m_mapServerIP.RemoveAt( iServerIP );
  715. }
  716. serverdisplay_t serverFind;
  717. serverFind.m_iListID = -1;
  718. serverFind.m_bDoNotRefresh = false;
  719. iServerMap = m_mapServers.Insert( iServer, serverFind );
  720. m_mapServerIP.Insert( netAdr, iServer );
  721. }
  722. serverdisplay_t *pServer = &m_mapServers[ iServerMap ];
  723. pServer->m_iServerID = iServer;
  724. Assert( pServerItem->m_NetAdr.GetIP() != 0 );
  725. // check filters
  726. bool removeItem = false;
  727. if ( !CheckPrimaryFilters( *pServerItem ) )
  728. {
  729. // server has been filtered at a primary level
  730. // remove from lists
  731. pServer->m_bDoNotRefresh = true;
  732. // remove from UI list
  733. removeItem = true;
  734. if ( m_pGameList->IsValidItemID( pServer->m_iListID ) )
  735. {
  736. m_pGameList->RemoveItem( pServer->m_iListID );
  737. pServer->m_iListID = GetInvalidServerListID();
  738. }
  739. return;
  740. }
  741. else if (!CheckSecondaryFilters( *pServerItem ))
  742. {
  743. // we still ping this server in the future; however it is removed from UI list
  744. removeItem = true;
  745. }
  746. // update UI
  747. KeyValues *kv;
  748. if ( m_pGameList->IsValidItemID( pServer->m_iListID ) )
  749. {
  750. // we're updating an existing entry
  751. kv = m_pGameList->GetItem( pServer->m_iListID );
  752. m_pGameList->SetUserData( pServer->m_iListID, pServer->m_iServerID );
  753. }
  754. else
  755. {
  756. // new entry
  757. kv = new KeyValues("Server");
  758. }
  759. kv->SetString("name", pServerItem->GetName());
  760. kv->SetString("map", pServerItem->m_szMap);
  761. kv->SetString("GameDir", pServerItem->m_szGameDir);
  762. kv->SetString("GameDesc", pServerItem->m_szGameDescription);
  763. kv->SetInt("password", pServerItem->m_bPassword ? m_nImageIndexPassword : 0);
  764. if ( pServerItem->m_nBotPlayers > 0 )
  765. kv->SetInt("bots", pServerItem->m_nBotPlayers);
  766. else
  767. kv->SetString("bots", "");
  768. if ( pServerItem->m_bSecure )
  769. {
  770. // show the denied icon if banned from secure servers, the secure icon otherwise
  771. kv->SetInt("secure", ServerBrowser().IsVACBannedFromGame( pServerItem->m_nAppID ) ? m_nImageIndexSecureVacBanned : m_nImageIndexSecure );
  772. }
  773. else
  774. {
  775. kv->SetInt("secure", 0);
  776. }
  777. kv->SetString( "IPAddr", pServerItem->m_NetAdr.GetConnectionAddressString() );
  778. int nAdjustedForBotsPlayers = max( 0, pServerItem->m_nPlayers - pServerItem->m_nBotPlayers );
  779. char buf[32];
  780. Q_snprintf(buf, sizeof(buf), "%d / %d", nAdjustedForBotsPlayers, pServerItem->m_nMaxPlayers );
  781. kv->SetString("Players", buf);
  782. kv->SetInt("PlayerCount", nAdjustedForBotsPlayers );
  783. kv->SetInt("MaxPlayerCount", pServerItem->m_nMaxPlayers );
  784. kv->SetInt("Ping", pServerItem->m_nPing);
  785. kv->SetString("Tags", pServerItem->m_szGameTags );
  786. kv->SetInt("Replay", IsReplayServer( *pServerItem ) ? m_nImageIndexReplay : 0);
  787. if ( pServerItem->m_ulTimeLastPlayed )
  788. {
  789. // construct a time string for last played time
  790. struct tm *now;
  791. now = localtime( (time_t*)&pServerItem->m_ulTimeLastPlayed );
  792. if ( now )
  793. {
  794. char buf[64];
  795. strftime(buf, sizeof(buf), "%a %d %b %I:%M%p", now);
  796. Q_strlower(buf + strlen(buf) - 4);
  797. kv->SetString("LastPlayed", buf);
  798. }
  799. }
  800. if ( pServer->m_bDoNotRefresh )
  801. {
  802. // clear out the vars
  803. kv->SetString("Ping", "");
  804. kv->SetWString("GameDesc", g_pVGuiLocalize->Find("#ServerBrowser_NotResponding"));
  805. kv->SetString("Players", "");
  806. kv->SetString("map", "");
  807. }
  808. if ( !m_pGameList->IsValidItemID( pServer->m_iListID ) )
  809. {
  810. // new server, add to list
  811. pServer->m_iListID = m_pGameList->AddItem(kv, pServer->m_iServerID, false, false);
  812. if ( m_bAutoSelectFirstItemInGameList && m_pGameList->GetItemCount() == 1 )
  813. {
  814. m_pGameList->AddSelectedItem( pServer->m_iListID );
  815. }
  816. m_pGameList->SetItemVisible( pServer->m_iListID, !removeItem );
  817. kv->deleteThis();
  818. }
  819. else
  820. {
  821. // tell the list that we've changed the data
  822. m_pGameList->ApplyItemChanges( pServer->m_iListID );
  823. m_pGameList->SetItemVisible( pServer->m_iListID, !removeItem );
  824. }
  825. PrepareQuickListMap( pServerItem->m_szMap, pServer->m_iListID );
  826. UpdateStatus();
  827. m_iServerRefreshCount++;
  828. }
  829. //-----------------------------------------------------------------------------
  830. // Purpose:
  831. //-----------------------------------------------------------------------------
  832. void CBaseGamesPage::UpdateFilterAndQuickListVisibility()
  833. {
  834. bool showQuickList = m_pQuickListCheckButton->IsSelected();
  835. bool showFilter = m_pFilter->IsSelected();
  836. m_bFiltersVisible = !showQuickList && !m_pCustomResFilename && showFilter;
  837. int wide, tall;
  838. GetSize( wide, tall );
  839. SetSize( 624, 278 );
  840. UpdateDerivedLayouts();
  841. UpdateGameFilter();
  842. if ( m_hFont )
  843. {
  844. SETUP_PANEL( m_pGameList );
  845. m_pGameList->SetFont( m_hFont );
  846. }
  847. SetSize( wide, tall );
  848. m_pQuickList->SetVisible( showQuickList );
  849. m_pGameList->SetVisible( !showQuickList );
  850. m_pFilter->SetVisible( !showQuickList );
  851. m_pFilterString->SetVisible ( !showQuickList );
  852. InvalidateLayout();
  853. UpdateFilterSettings();
  854. ApplyGameFilters();
  855. }
  856. //-----------------------------------------------------------------------------
  857. // Purpose:
  858. //-----------------------------------------------------------------------------
  859. void CBaseGamesPage::SetQuickListEnabled( bool bEnabled )
  860. {
  861. m_pQuickListCheckButton->SetSelected( bEnabled );
  862. m_pQuickList->SetVisible( m_pQuickListCheckButton->IsSelected() );
  863. m_pGameList->SetVisible( !m_pQuickListCheckButton->IsSelected() );
  864. m_pFilter->SetVisible( !m_pQuickListCheckButton->IsSelected() );
  865. m_pFilterString->SetVisible( !m_pQuickListCheckButton->IsSelected() );
  866. }
  867. //-----------------------------------------------------------------------------
  868. // Purpose:
  869. //-----------------------------------------------------------------------------
  870. void CBaseGamesPage::SetFiltersVisible( bool bVisible )
  871. {
  872. if ( bVisible == m_pFilter->IsSelected() )
  873. return;
  874. m_pFilter->SetSelected( bVisible );
  875. OnButtonToggled( m_pFilter, bVisible );
  876. }
  877. //-----------------------------------------------------------------------------
  878. // Purpose: Handles filter dropdown being toggled
  879. //-----------------------------------------------------------------------------
  880. void CBaseGamesPage::OnButtonToggled( Panel *panel, int state )
  881. {
  882. UpdateFilterAndQuickListVisibility();
  883. if (panel == m_pNoFullServersFilterCheck || panel == m_pNoEmptyServersFilterCheck || panel == m_pNoPasswordFilterCheck || panel == m_pReplayFilterCheck)
  884. {
  885. // treat changing these buttons like any other filter has changed
  886. OnTextChanged(panel, "");
  887. }
  888. }
  889. //-----------------------------------------------------------------------------
  890. // Purpose:
  891. //-----------------------------------------------------------------------------
  892. void CBaseGamesPage::UpdateDerivedLayouts( void )
  893. {
  894. char rgchControlSettings[MAX_PATH];
  895. if ( m_pCustomResFilename )
  896. {
  897. Q_snprintf( rgchControlSettings, sizeof( rgchControlSettings ), "%s", m_pCustomResFilename );
  898. }
  899. else
  900. {
  901. if ( m_pFilter->IsSelected() && !m_pQuickListCheckButton->IsSelected() )
  902. {
  903. // drop down
  904. V_strncpy( rgchControlSettings, "servers/InternetGamesPage_Filters.res", sizeof( rgchControlSettings ) );
  905. }
  906. else
  907. {
  908. // hide filter area
  909. V_strncpy( rgchControlSettings, "servers/InternetGamesPage.res", sizeof( rgchControlSettings ) );
  910. }
  911. }
  912. const char *pPathID = "PLATFORM";
  913. if ( g_pFullFileSystem->FileExists( rgchControlSettings, "MOD" ) )
  914. {
  915. pPathID = "MOD";
  916. }
  917. LoadControlSettings( rgchControlSettings, pPathID );
  918. if ( !GameSupportsReplay() )
  919. {
  920. HideReplayFilter();
  921. }
  922. }
  923. //-----------------------------------------------------------------------------
  924. // Purpose: Called when the game dir combo box is changed
  925. //-----------------------------------------------------------------------------
  926. void CBaseGamesPage::OnTextChanged(Panel *panel, const char *text)
  927. {
  928. if (!Q_stricmp(text, m_szComboAllText))
  929. {
  930. ComboBox *box = dynamic_cast<ComboBox *>(panel);
  931. if (box)
  932. {
  933. box->SetText("");
  934. text = "";
  935. }
  936. }
  937. // get filter settings from controls
  938. UpdateFilterSettings();
  939. // apply settings
  940. ApplyGameFilters();
  941. if ( m_bFiltersVisible && ( panel == m_pGameFilter || panel == m_pLocationFilter ) && ServerBrowserDialog().IsVisible() )
  942. {
  943. // if they changed games and/or region then cancel the refresh because the old list they are getting
  944. // will be for the wrong game, so stop and start a refresh
  945. StopRefresh();
  946. GetNewServerList();
  947. }
  948. }
  949. //-----------------------------------------------------------------------------
  950. // Purpose: applies only the game filter to the current list
  951. //-----------------------------------------------------------------------------
  952. void CBaseGamesPage::ApplyGameFilters()
  953. {
  954. if ( !steamapicontext->SteamMatchmakingServers() )
  955. return;
  956. m_iServersBlacklisted = 0;
  957. // loop through all the servers checking filters
  958. FOR_EACH_MAP_FAST( m_mapServers, i )
  959. {
  960. serverdisplay_t &server = m_mapServers[ i ];
  961. gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()->GetServerDetails( m_hRequest, server.m_iServerID );
  962. if ( !pServer )
  963. continue;
  964. if (!CheckPrimaryFilters( *pServer ) || !CheckSecondaryFilters( *pServer ))
  965. {
  966. // server failed filtering, remove it
  967. server.m_bDoNotRefresh = true;
  968. if ( m_pGameList->IsValidItemID( server.m_iListID) )
  969. {
  970. // don't remove the server from list, just hide since this is a lot faster
  971. m_pGameList->SetItemVisible( server.m_iListID, false );
  972. }
  973. }
  974. else if ( BShowServer( server ) )
  975. {
  976. // server passed filters, so it can be refreshed again
  977. server.m_bDoNotRefresh = false;
  978. // re-add item to list
  979. if ( !m_pGameList->IsValidItemID( server.m_iListID ) )
  980. {
  981. KeyValues *kv = new KeyValues("Server");
  982. kv->SetString("name", pServer->GetName());
  983. kv->SetString("map", pServer->m_szMap);
  984. kv->SetString("GameDir", pServer->m_szGameDir);
  985. kv->SetString( "GameTags", pServer->m_szGameTags );
  986. if ( pServer->m_szGameDescription[0] )
  987. {
  988. kv->SetString("GameDesc", pServer->m_szGameDescription );
  989. }
  990. else
  991. {
  992. kv->SetWString("GameDesc", g_pVGuiLocalize->Find("#ServerBrowser_PendingPing"));
  993. }
  994. int nAdjustedForBotsPlayers = max( 0, pServer->m_nPlayers - pServer->m_nBotPlayers );
  995. char buf[256];
  996. Q_snprintf(buf, sizeof(buf), "%d / %d", nAdjustedForBotsPlayers, pServer->m_nMaxPlayers );
  997. kv->SetString( "Players", buf);
  998. kv->SetInt( "Ping", pServer->m_nPing );
  999. kv->SetInt( "password", pServer->m_bPassword ? m_nImageIndexPassword : 0);
  1000. if ( pServer->m_nBotPlayers > 0 )
  1001. kv->SetInt("bots", pServer->m_nBotPlayers);
  1002. else
  1003. kv->SetString("bots", "");
  1004. kv->SetInt("Replay", IsReplayServer( *pServer ) ? m_nImageIndexReplay : 0);
  1005. server.m_iListID = m_pGameList->AddItem(kv, server.m_iServerID, false, false);
  1006. kv->deleteThis();
  1007. }
  1008. // make sure the server is visible
  1009. m_pGameList->SetItemVisible( server.m_iListID, true );
  1010. }
  1011. }
  1012. UpdateStatus();
  1013. m_pGameList->SortList();
  1014. InvalidateLayout();
  1015. Repaint();
  1016. }
  1017. //-----------------------------------------------------------------------------
  1018. // Purpose: Resets UI server count
  1019. //-----------------------------------------------------------------------------
  1020. void CBaseGamesPage::UpdateStatus()
  1021. {
  1022. if (m_pGameList->GetItemCount() > 1)
  1023. {
  1024. wchar_t header[256];
  1025. wchar_t count[128];
  1026. wchar_t blacklistcount[128];
  1027. _snwprintf( count, Q_ARRAYSIZE(count), L"%d", m_pGameList->GetItemCount() );
  1028. _snwprintf( blacklistcount, Q_ARRAYSIZE(blacklistcount), L"%d", m_iServersBlacklisted );
  1029. g_pVGuiLocalize->ConstructString( header, sizeof( header ), g_pVGuiLocalize->Find( "#ServerBrowser_ServersCountWithBlacklist"), 2, count, blacklistcount );
  1030. m_pGameList->SetColumnHeaderText( k_nColumn_Name, header);
  1031. }
  1032. else
  1033. {
  1034. m_pGameList->SetColumnHeaderText( k_nColumn_Name, g_pVGuiLocalize->Find("#ServerBrowser_Servers"));
  1035. }
  1036. }
  1037. //-----------------------------------------------------------------------------
  1038. // Purpose: gets filter settings from controls
  1039. //-----------------------------------------------------------------------------
  1040. void CBaseGamesPage::UpdateFilterSettings()
  1041. {
  1042. // game
  1043. if ( ServerBrowserDialog().GetActiveModName() )
  1044. {
  1045. // overriding the game filter
  1046. Q_strncpy(m_szGameFilter, ServerBrowserDialog().GetActiveModName(), sizeof(m_szGameFilter));
  1047. m_iLimitToAppID = ServerBrowserDialog().GetActiveAppID();
  1048. #ifdef STAGING_ONLY
  1049. if ( sb_fake_app_id.GetInt() != 0 )
  1050. m_iLimitToAppID = CGameID( sb_fake_app_id.GetInt() );
  1051. #endif
  1052. RecalculateFilterString();
  1053. UpdateGameFilter();
  1054. }
  1055. else
  1056. {
  1057. KeyValues *data = m_pGameFilter->GetActiveItemUserData();
  1058. if (data && Q_strlen( data->GetString( "gamedir" ) ) > 0 )
  1059. {
  1060. Q_strncpy( m_szGameFilter, data->GetString( "gamedir" ), sizeof( m_szGameFilter ) );
  1061. if ( Q_strlen( m_szGameFilter ) > 0 ) // if there is a gamedir
  1062. {
  1063. m_iLimitToAppID = CGameID( data->GetUint64( "appid", 0 ) );
  1064. }
  1065. else
  1066. {
  1067. m_iLimitToAppID.Reset();
  1068. }
  1069. }
  1070. else
  1071. {
  1072. m_iLimitToAppID.Reset();
  1073. m_szGameFilter[0] = 0;
  1074. }
  1075. m_pGameFilter->SetEnabled(true);
  1076. }
  1077. Q_strlower(m_szGameFilter);
  1078. // map
  1079. m_pMapFilter->GetText(m_szMapFilter, sizeof(m_szMapFilter) - 1);
  1080. Q_strlower(m_szMapFilter);
  1081. // max player
  1082. char buf[256];
  1083. m_pMaxPlayerFilter->GetText(buf, sizeof(buf));
  1084. if (buf[0])
  1085. {
  1086. m_iMaxPlayerFilter = atoi(buf);
  1087. }
  1088. else
  1089. {
  1090. m_iMaxPlayerFilter = 0;
  1091. }
  1092. // ping
  1093. m_pPingFilter->GetText(buf, sizeof(buf));
  1094. if (buf[0])
  1095. {
  1096. m_iPingFilter = atoi(buf + 2);
  1097. }
  1098. else
  1099. {
  1100. m_iPingFilter = 0;
  1101. }
  1102. // players
  1103. m_bFilterNoFullServers = m_pNoFullServersFilterCheck->IsSelected();
  1104. m_bFilterNoEmptyServers = m_pNoEmptyServersFilterCheck->IsSelected();
  1105. m_bFilterNoPasswordedServers = m_pNoPasswordFilterCheck->IsSelected();
  1106. m_iSecureFilter = m_pSecureFilter->GetActiveItem();
  1107. if ( GameSupportsReplay() )
  1108. {
  1109. m_bFilterReplayServers = m_pReplayFilterCheck->IsSelected();
  1110. }
  1111. else
  1112. {
  1113. m_bFilterReplayServers = false;
  1114. }
  1115. m_vecServerFilters.RemoveAll();
  1116. bool bFilterNoEmpty = m_bFilterNoEmptyServers;
  1117. bool bFilterNoFull = m_bFilterNoFullServers;
  1118. bool bFilterNoPassword = m_bFilterNoPasswordedServers;
  1119. int iFilterSecure = m_iSecureFilter;
  1120. if ( m_pQuickList->IsVisible() == true )
  1121. {
  1122. bFilterNoEmpty = true;
  1123. bFilterNoFull = true;
  1124. bFilterNoPassword = true;
  1125. iFilterSecure = FILTER_SECURESERVERSONLY;
  1126. }
  1127. extern IRunGameEngine *g_pRunGameEngine;
  1128. if ( sb_filter_incompatible_versions.GetBool() && g_pRunGameEngine != NULL )
  1129. {
  1130. const char *pszVersion = g_pRunGameEngine->GetProductVersionString();
  1131. const char k_VersionFromP4[] = "2000"; // magic version string we use when we're running from P4
  1132. if ( pszVersion && *pszVersion && ( V_strcmp( pszVersion, k_VersionFromP4 ) != 0 ) )
  1133. {
  1134. m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "version_match", pszVersion ) );
  1135. }
  1136. }
  1137. // update master filter string text
  1138. if (m_szGameFilter[0] && m_iLimitToAppID.AppID() != 1002 ) // HACKHACK: Alfred - don't use a dir filter for RDKF
  1139. {
  1140. m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "gamedir", m_szGameFilter ) );
  1141. }
  1142. if (bFilterNoEmpty)
  1143. {
  1144. m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "empty", "1" ) );
  1145. }
  1146. if (bFilterNoFull)
  1147. {
  1148. m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "full", "1" ) );
  1149. }
  1150. if (iFilterSecure == FILTER_SECURESERVERSONLY)
  1151. {
  1152. m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "secure", "1" ) );
  1153. }
  1154. int regCode = GetRegionCodeToFilter();
  1155. if ( ( regCode >= 0 ) && ( regCode < 255 ) )
  1156. {
  1157. char szRegCode[ 32 ];
  1158. Q_snprintf( szRegCode, sizeof(szRegCode), "%i", regCode );
  1159. m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "region", szRegCode ) );
  1160. }
  1161. // copy filter settings into filter file
  1162. KeyValues *filter = ServerBrowserDialog().GetFilterSaveData(GetName());
  1163. // only save the game filter if we're not overriding it
  1164. if (!ServerBrowserDialog().GetActiveModName())
  1165. {
  1166. filter->SetString("game", m_szGameFilter);
  1167. filter->SetUint64( "appid", m_iLimitToAppID.ToUint64() );
  1168. }
  1169. filter->SetString("map", m_szMapFilter);
  1170. filter->SetInt("MaxPlayerCount", m_iMaxPlayerFilter);
  1171. filter->SetInt("ping", m_iPingFilter);
  1172. if ( m_pLocationFilter->GetItemCount() > 1 )
  1173. {
  1174. // only save this if there are options to choose from
  1175. filter->SetInt("location", m_pLocationFilter->GetActiveItem());
  1176. }
  1177. filter->SetInt("NoFull", m_bFilterNoFullServers);
  1178. filter->SetInt("NoEmpty", m_bFilterNoEmptyServers);
  1179. filter->SetInt("NoPassword", m_bFilterNoPasswordedServers);
  1180. filter->SetInt("Secure", m_iSecureFilter);
  1181. filter->SetInt("QuickList", m_pQuickListCheckButton->IsSelected() );
  1182. filter->SetInt("tagsinclude", m_pTagsIncludeFilter->GetActiveItem() );
  1183. if ( m_pWorkshopFilter )
  1184. {
  1185. filter->SetInt("workshopfilter", m_pWorkshopFilter->GetActiveItem() );
  1186. }
  1187. filter->SetInt("Replay", m_bFilterReplayServers);
  1188. OnSaveFilter(filter);
  1189. RecalculateFilterString();
  1190. }
  1191. //-----------------------------------------------------------------------------
  1192. // Purpose: allow derived classes access to the saved filter string
  1193. //-----------------------------------------------------------------------------
  1194. void CBaseGamesPage::OnSaveFilter(KeyValues *filter)
  1195. {
  1196. }
  1197. //-----------------------------------------------------------------------------
  1198. // Purpose: allow derived classes access to the saved filter string
  1199. //-----------------------------------------------------------------------------
  1200. void CBaseGamesPage::OnLoadFilter(KeyValues *filter)
  1201. {
  1202. }
  1203. //-----------------------------------------------------------------------------
  1204. // Purpose: reconstructs the filter description string from the current filter settings
  1205. //-----------------------------------------------------------------------------
  1206. void CBaseGamesPage::RecalculateFilterString()
  1207. {
  1208. wchar_t unicode[2048], tempUnicode[128], spacerUnicode[8];
  1209. unicode[0] = 0;
  1210. int iTempUnicodeSize = sizeof( tempUnicode );
  1211. Q_UTF8ToUnicode( "; ", spacerUnicode, sizeof( spacerUnicode ) );
  1212. if (m_szGameFilter[0])
  1213. {
  1214. Q_UTF8ToUnicode( ModList().GetModNameForModDir( m_iLimitToAppID ), tempUnicode, iTempUnicodeSize );
  1215. wcscat( unicode, tempUnicode );
  1216. wcscat( unicode, spacerUnicode );
  1217. }
  1218. if (m_iSecureFilter == FILTER_SECURESERVERSONLY)
  1219. {
  1220. wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescSecureOnly" ) );
  1221. wcscat( unicode, spacerUnicode );
  1222. }
  1223. else if (m_iSecureFilter == FILTER_INSECURESERVERSONLY)
  1224. {
  1225. wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescInsecureOnly" ) );
  1226. wcscat( unicode, spacerUnicode );
  1227. }
  1228. if (m_pLocationFilter->GetActiveItem() > 0)
  1229. {
  1230. m_pLocationFilter->GetText(tempUnicode, sizeof(tempUnicode));
  1231. wcscat( unicode, tempUnicode );
  1232. wcscat( unicode, spacerUnicode );
  1233. }
  1234. if (m_iPingFilter)
  1235. {
  1236. char tmpBuf[16];
  1237. _snprintf( tmpBuf, sizeof(tmpBuf), "%d", m_iPingFilter );
  1238. wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescLatency" ) );
  1239. Q_UTF8ToUnicode( " < ", tempUnicode, iTempUnicodeSize );
  1240. wcscat( unicode, tempUnicode );
  1241. Q_UTF8ToUnicode(tmpBuf, tempUnicode, iTempUnicodeSize );
  1242. wcscat( unicode, tempUnicode );
  1243. wcscat( unicode, spacerUnicode );
  1244. }
  1245. if ( m_iMaxPlayerFilter )
  1246. {
  1247. char tmpBuf[16];
  1248. _snprintf( tmpBuf, sizeof(tmpBuf), "%d", m_iMaxPlayerFilter );
  1249. wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescMaxPlayers" ) );
  1250. Q_UTF8ToUnicode( " <= ", tempUnicode, iTempUnicodeSize );
  1251. wcscat( unicode, tempUnicode );
  1252. Q_UTF8ToUnicode(tmpBuf, tempUnicode, iTempUnicodeSize );
  1253. wcscat( unicode, tempUnicode );
  1254. wcscat( unicode, spacerUnicode );
  1255. }
  1256. if (m_bFilterNoFullServers)
  1257. {
  1258. wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescNotFull" ) );
  1259. wcscat( unicode, spacerUnicode );
  1260. }
  1261. if (m_bFilterNoEmptyServers)
  1262. {
  1263. wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescNotEmpty" ) );
  1264. wcscat( unicode, spacerUnicode );
  1265. }
  1266. if (m_bFilterNoPasswordedServers)
  1267. {
  1268. wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescNoPassword" ) );
  1269. wcscat( unicode, spacerUnicode );
  1270. }
  1271. if (m_bFilterReplayServers)
  1272. {
  1273. wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescReplays" ) );
  1274. wcscat( unicode, spacerUnicode );
  1275. }
  1276. if (m_szMapFilter[0])
  1277. {
  1278. Q_UTF8ToUnicode( m_szMapFilter, tempUnicode, iTempUnicodeSize );
  1279. wcscat( unicode, tempUnicode );
  1280. }
  1281. m_pFilterString->SetText(unicode);
  1282. }
  1283. //-----------------------------------------------------------------------------
  1284. // Purpose: Checks to see if the server passes the primary filters
  1285. // if the server fails the filters, it will not be refreshed again
  1286. //-----------------------------------------------------------------------------
  1287. bool CBaseGamesPage::CheckPrimaryFilters( gameserveritem_t &server )
  1288. {
  1289. if (m_szGameFilter[0] && ( server.m_szGameDir[0] || server.m_nPing ) && Q_stricmp(m_szGameFilter, server.m_szGameDir ) )
  1290. {
  1291. return false;
  1292. }
  1293. // If it's blacklisted, we ignore it too
  1294. if ( ServerBrowserDialog().IsServerBlacklisted( server ) )
  1295. {
  1296. m_iServersBlacklisted++;
  1297. return false;
  1298. }
  1299. return true;
  1300. }
  1301. //-----------------------------------------------------------------------------
  1302. // Purpose: Checks to see if a server passes the secondary set of filters
  1303. // server will be continued to be pinged if it fails the filter, since
  1304. // the relvent server data is dynamic
  1305. //-----------------------------------------------------------------------------
  1306. bool CBaseGamesPage::CheckSecondaryFilters( gameserveritem_t &server )
  1307. {
  1308. bool bFilterNoEmpty = m_bFilterNoEmptyServers;
  1309. bool bFilterNoFull = m_bFilterNoFullServers;
  1310. int iFilterPing = m_iPingFilter;
  1311. int iFilterMaxPlayerCount = m_iMaxPlayerFilter;
  1312. bool bFilterNoPassword = m_bFilterNoPasswordedServers;
  1313. int iFilterSecure = m_iSecureFilter;
  1314. if ( m_pQuickList->IsVisible() == true )
  1315. {
  1316. bFilterNoEmpty = true;
  1317. bFilterNoFull = true;
  1318. iFilterPing = QUICKLIST_FILTER_MIN_PING;
  1319. bFilterNoPassword = true;
  1320. iFilterSecure = FILTER_SECURESERVERSONLY;
  1321. iFilterMaxPlayerCount = sb_mod_suggested_maxplayers.GetInt();
  1322. }
  1323. if ( bFilterNoEmpty && (server.m_nPlayers - server.m_nBotPlayers) < 1 )
  1324. {
  1325. return false;
  1326. }
  1327. if ( bFilterNoFull && server.m_nPlayers >= server.m_nMaxPlayers )
  1328. {
  1329. return false;
  1330. }
  1331. if ( iFilterPing && server.m_nPing > iFilterPing )
  1332. {
  1333. return false;
  1334. }
  1335. if ( iFilterMaxPlayerCount && server.m_nMaxPlayers > iFilterMaxPlayerCount )
  1336. {
  1337. return false;
  1338. }
  1339. if ( bFilterNoPassword && server.m_bPassword )
  1340. {
  1341. return false;
  1342. }
  1343. if ( iFilterSecure == FILTER_SECURESERVERSONLY && !server.m_bSecure )
  1344. {
  1345. return false;
  1346. }
  1347. if ( iFilterSecure == FILTER_INSECURESERVERSONLY && server.m_bSecure )
  1348. {
  1349. return false;
  1350. }
  1351. if ( m_bFilterReplayServers && !IsReplayServer( server ) )
  1352. {
  1353. return false;
  1354. }
  1355. if ( m_pQuickList->IsVisible() == false )
  1356. {
  1357. // compare the first few characters of the filter name
  1358. int count = Q_strlen( m_szMapFilter );
  1359. if ( count && Q_strnicmp( server.m_szMap, m_szMapFilter, count ) )
  1360. {
  1361. return false;
  1362. }
  1363. }
  1364. return CheckTagFilter( server ) && CheckWorkshopFilter( server );
  1365. }
  1366. //-----------------------------------------------------------------------------
  1367. // Purpose:
  1368. //-----------------------------------------------------------------------------
  1369. uint32 CBaseGamesPage::GetServerFilters( MatchMakingKeyValuePair_t **pFilters )
  1370. {
  1371. *pFilters = m_vecServerFilters.Base();
  1372. return m_vecServerFilters.Count();
  1373. }
  1374. //-----------------------------------------------------------------------------
  1375. // Purpose: call to let the UI now whether the game list is currently refreshing
  1376. //-----------------------------------------------------------------------------
  1377. void CBaseGamesPage::SetRefreshing(bool state)
  1378. {
  1379. if (state)
  1380. {
  1381. ServerBrowserDialog().UpdateStatusText("#ServerBrowser_RefreshingServerList");
  1382. // clear message in panel
  1383. m_pGameList->SetEmptyListText("");
  1384. m_pRefreshAll->SetText("#ServerBrowser_StopRefreshingList");
  1385. m_pRefreshAll->SetCommand("stoprefresh");
  1386. m_pRefreshQuick->SetEnabled(false);
  1387. }
  1388. else
  1389. {
  1390. ServerBrowserDialog().UpdateStatusText("");
  1391. if (SupportsItem(IGameList::GETNEWLIST))
  1392. {
  1393. m_pRefreshAll->SetText("#ServerBrowser_RefreshAll");
  1394. }
  1395. else
  1396. {
  1397. m_pRefreshAll->SetText("#ServerBrowser_Refresh");
  1398. }
  1399. m_pRefreshAll->SetCommand("GetNewList");
  1400. // 'refresh quick' button is only enabled if there are servers in the list
  1401. if (m_pGameList->GetItemCount() > 0)
  1402. {
  1403. m_pRefreshQuick->SetEnabled(true);
  1404. }
  1405. else
  1406. {
  1407. m_pRefreshQuick->SetEnabled(false);
  1408. }
  1409. }
  1410. }
  1411. //-----------------------------------------------------------------------------
  1412. // Purpose:
  1413. //-----------------------------------------------------------------------------
  1414. void CBaseGamesPage::OnCommand(const char *command)
  1415. {
  1416. if (!Q_stricmp(command, "Connect"))
  1417. {
  1418. OnBeginConnect();
  1419. }
  1420. else if (!Q_stricmp(command, "stoprefresh"))
  1421. {
  1422. // cancel the existing refresh
  1423. StopRefresh();
  1424. }
  1425. else if ( !Q_stricmp(command, "refresh") )
  1426. {
  1427. if ( steamapicontext->SteamMatchmakingServers() )
  1428. steamapicontext->SteamMatchmakingServers()->RefreshQuery( m_hRequest );
  1429. SetRefreshing( true );
  1430. m_iServerRefreshCount = 0;
  1431. ClearQuickList();
  1432. }
  1433. else if (!Q_stricmp(command, "GetNewList"))
  1434. {
  1435. GetNewServerList();
  1436. }
  1437. else
  1438. {
  1439. BaseClass::OnCommand(command);
  1440. }
  1441. }
  1442. //-----------------------------------------------------------------------------
  1443. // Purpose: called when a row gets selected in the list
  1444. //-----------------------------------------------------------------------------
  1445. void CBaseGamesPage::OnItemSelected()
  1446. {
  1447. if ( GetSelectedServerID() == -1 )
  1448. {
  1449. m_pConnect->SetEnabled(false);
  1450. }
  1451. else
  1452. {
  1453. m_pConnect->SetEnabled(true);
  1454. }
  1455. }
  1456. //-----------------------------------------------------------------------------
  1457. // Purpose: refreshes server list on F5
  1458. //-----------------------------------------------------------------------------
  1459. void CBaseGamesPage::OnKeyCodePressed(vgui::KeyCode code)
  1460. {
  1461. if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A )
  1462. {
  1463. m_pConnect->DoClick();
  1464. }
  1465. else if ( code == KEY_F5 || code == KEY_XBUTTON_X || code == STEAMCONTROLLER_X )
  1466. {
  1467. StartRefresh();
  1468. }
  1469. else if ( m_pGameList->GetItemCount() > 0 &&
  1470. ( code == KEY_XBUTTON_UP || code == KEY_XSTICK1_UP || code == KEY_XSTICK2_UP || code == STEAMCONTROLLER_DPAD_UP ||
  1471. code == KEY_XBUTTON_DOWN || code == KEY_XSTICK1_DOWN || code == KEY_XSTICK2_DOWN || code == STEAMCONTROLLER_DPAD_DOWN ) )
  1472. {
  1473. m_pGameList->RequestFocus();
  1474. }
  1475. else
  1476. {
  1477. BaseClass::OnKeyCodePressed(code);
  1478. }
  1479. }
  1480. //-----------------------------------------------------------------------------
  1481. // Purpose: Handle enter pressed in the games list page. Return true
  1482. // to intercept the message instead of passing it on through vgui.
  1483. //-----------------------------------------------------------------------------
  1484. bool CBaseGamesPage::OnGameListEnterPressed()
  1485. {
  1486. return false;
  1487. }
  1488. //-----------------------------------------------------------------------------
  1489. // Purpose: Get the # items selected in the game list.
  1490. //-----------------------------------------------------------------------------
  1491. int CBaseGamesPage::GetSelectedItemsCount()
  1492. {
  1493. return m_pGameList->GetSelectedItemsCount();
  1494. }
  1495. //-----------------------------------------------------------------------------
  1496. // Purpose: adds a server to the favorites
  1497. //-----------------------------------------------------------------------------
  1498. void CBaseGamesPage::OnAddToFavorites()
  1499. {
  1500. if ( !steamapicontext->SteamMatchmakingServers() )
  1501. return;
  1502. // loop through all the selected favorites
  1503. for (int i = 0; i < m_pGameList->GetSelectedItemsCount(); i++)
  1504. {
  1505. int serverID = m_pGameList->GetItemUserData(m_pGameList->GetSelectedItem(i));
  1506. gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()->GetServerDetails( m_hRequest, serverID );
  1507. if ( pServer )
  1508. {
  1509. // add to favorites list
  1510. ServerBrowserDialog().AddServerToFavorites(*pServer);
  1511. }
  1512. }
  1513. }
  1514. //-----------------------------------------------------------------------------
  1515. // Purpose: adds a server to the blacklist
  1516. //-----------------------------------------------------------------------------
  1517. void CBaseGamesPage::OnAddToBlacklist()
  1518. {
  1519. if ( !steamapicontext->SteamMatchmakingServers() )
  1520. return;
  1521. // loop through all the selected favorites
  1522. for (int i = 0; i < m_pGameList->GetSelectedItemsCount(); i++)
  1523. {
  1524. int serverID = m_pGameList->GetItemUserData(m_pGameList->GetSelectedItem(i));
  1525. gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()->GetServerDetails( m_hRequest, serverID );
  1526. if ( pServer )
  1527. {
  1528. ServerBrowserDialog().AddServerToBlacklist(*pServer);
  1529. }
  1530. }
  1531. ServerBrowserDialog().BlacklistsChanged();
  1532. }
  1533. //-----------------------------------------------------------------------------
  1534. // Purpose:
  1535. //-----------------------------------------------------------------------------
  1536. void CBaseGamesPage::ServerFailedToRespond( HServerListRequest hReq, int iServer )
  1537. {
  1538. ServerResponded( hReq, iServer );
  1539. }
  1540. //-----------------------------------------------------------------------------
  1541. // Purpose: removes the server from the UI list
  1542. //-----------------------------------------------------------------------------
  1543. void CBaseGamesPage::RemoveServer( serverdisplay_t &server )
  1544. {
  1545. if ( m_pGameList->IsValidItemID( server.m_iListID ) )
  1546. {
  1547. // don't remove the server from list, just hide since this is a lot faster
  1548. m_pGameList->SetItemVisible( server.m_iListID, false );
  1549. // find the row in the list and kill
  1550. // m_pGameList->RemoveItem(server.listEntryID);
  1551. // server.listEntryID = GetInvalidServerListID();
  1552. }
  1553. UpdateStatus();
  1554. }
  1555. //-----------------------------------------------------------------------------
  1556. // Purpose: refreshes a single server
  1557. //-----------------------------------------------------------------------------
  1558. void CBaseGamesPage::OnRefreshServer( int serverID )
  1559. {
  1560. if ( !steamapicontext->SteamMatchmakingServers() )
  1561. return;
  1562. // walk the list of selected servers refreshing them
  1563. for (int i = 0; i < m_pGameList->GetSelectedItemsCount(); i++)
  1564. {
  1565. int serverID = m_pGameList->GetItemUserData(m_pGameList->GetSelectedItem(i));
  1566. // refresh this server
  1567. steamapicontext->SteamMatchmakingServers()->RefreshServer( m_hRequest, serverID );
  1568. }
  1569. SetRefreshing(IsRefreshing());
  1570. }
  1571. //-----------------------------------------------------------------------------
  1572. // Purpose: starts the servers refreshing
  1573. //-----------------------------------------------------------------------------
  1574. void CBaseGamesPage::StartRefresh()
  1575. {
  1576. if ( !steamapicontext->SteamMatchmakingServers() )
  1577. return;
  1578. ClearServerList();
  1579. MatchMakingKeyValuePair_t *pFilters;
  1580. int nFilters = GetServerFilters( &pFilters );
  1581. if ( m_hRequest )
  1582. {
  1583. steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
  1584. m_hRequest = NULL;
  1585. }
  1586. switch ( m_eMatchMakingType )
  1587. {
  1588. case eFavoritesServer:
  1589. m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestFavoritesServerList( GetFilterAppID().AppID(), &pFilters, nFilters, this );
  1590. break;
  1591. case eHistoryServer:
  1592. m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestHistoryServerList( GetFilterAppID().AppID(), &pFilters, nFilters, this );
  1593. break;
  1594. case eInternetServer:
  1595. m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestInternetServerList( GetFilterAppID().AppID(), &pFilters, nFilters, this );
  1596. break;
  1597. case eSpectatorServer:
  1598. m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestSpectatorServerList( GetFilterAppID().AppID(), &pFilters, nFilters, this );
  1599. break;
  1600. case eFriendsServer:
  1601. m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestFriendsServerList( GetFilterAppID().AppID(), &pFilters, nFilters, this );
  1602. break;
  1603. case eLANServer:
  1604. m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestLANServerList( GetFilterAppID().AppID(), this );
  1605. break;
  1606. default:
  1607. Assert( !"Unknown server type" );
  1608. break;
  1609. }
  1610. SetRefreshing( true );
  1611. m_iServerRefreshCount = 0;
  1612. }
  1613. //-----------------------------------------------------------------------------
  1614. // Purpose:
  1615. //-----------------------------------------------------------------------------
  1616. void CBaseGamesPage::ClearQuickList( void )
  1617. {
  1618. m_pQuickList->DeleteAllItems();
  1619. m_vecMapNamesFound.RemoveAll();
  1620. int iIndex = m_quicklistserverlist.First();
  1621. while ( iIndex != m_quicklistserverlist.InvalidIndex() )
  1622. {
  1623. CQuickListMapServerList *vecMapServers = &m_quicklistserverlist[iIndex];
  1624. vecMapServers->RemoveAll();
  1625. iIndex = m_quicklistserverlist.Next( iIndex );
  1626. }
  1627. m_quicklistserverlist.RemoveAll();
  1628. }
  1629. //-----------------------------------------------------------------------------
  1630. // Purpose: Remove all the servers we currently have
  1631. //-----------------------------------------------------------------------------
  1632. void CBaseGamesPage::ClearServerList()
  1633. {
  1634. m_mapServers.RemoveAll();
  1635. m_mapServerIP.RemoveAll();
  1636. m_pGameList->RemoveAll();
  1637. m_iServersBlacklisted = 0;
  1638. ClearQuickList();
  1639. }
  1640. //-----------------------------------------------------------------------------
  1641. // Purpose: get a new list of servers from the backend
  1642. //-----------------------------------------------------------------------------
  1643. void CBaseGamesPage::GetNewServerList()
  1644. {
  1645. StartRefresh();
  1646. }
  1647. //-----------------------------------------------------------------------------
  1648. // Purpose: stops current refresh/GetNewServerList()
  1649. //-----------------------------------------------------------------------------
  1650. void CBaseGamesPage::StopRefresh()
  1651. {
  1652. // clear update states
  1653. m_iServerRefreshCount = 0;
  1654. // Stop the server list refreshing
  1655. if ( steamapicontext->SteamMatchmakingServers() )
  1656. steamapicontext->SteamMatchmakingServers()->CancelQuery( m_hRequest );
  1657. // update UI
  1658. RefreshComplete( m_hRequest, eServerResponded );
  1659. // apply settings
  1660. ApplyGameFilters();
  1661. }
  1662. //-----------------------------------------------------------------------------
  1663. // Purpose:
  1664. //-----------------------------------------------------------------------------
  1665. void CBaseGamesPage::RefreshComplete( HServerListRequest hRequest, EMatchMakingServerResponse response )
  1666. {
  1667. SelectQuickListServers();
  1668. }
  1669. //-----------------------------------------------------------------------------
  1670. // Purpose: returns true if the list is currently refreshing servers
  1671. //-----------------------------------------------------------------------------
  1672. bool CBaseGamesPage::IsRefreshing()
  1673. {
  1674. return steamapicontext->SteamMatchmakingServers() && steamapicontext->SteamMatchmakingServers()->IsRefreshing( m_hRequest );
  1675. }
  1676. //-----------------------------------------------------------------------------
  1677. // Purpose: Activates the page, starts refresh
  1678. //-----------------------------------------------------------------------------
  1679. void CBaseGamesPage::OnPageShow()
  1680. {
  1681. StartRefresh();
  1682. }
  1683. //-----------------------------------------------------------------------------
  1684. // Purpose: Called on page hide, stops any refresh
  1685. //-----------------------------------------------------------------------------
  1686. void CBaseGamesPage::OnPageHide()
  1687. {
  1688. StopRefresh();
  1689. }
  1690. //-----------------------------------------------------------------------------
  1691. // Purpose:
  1692. //-----------------------------------------------------------------------------
  1693. vgui::Panel *CBaseGamesPage::GetActiveList( void )
  1694. {
  1695. if ( m_pQuickList->IsVisible() )
  1696. return m_pQuickList;
  1697. return m_pGameList;
  1698. }
  1699. //-----------------------------------------------------------------------------
  1700. // Purpose:
  1701. //-----------------------------------------------------------------------------
  1702. int CBaseGamesPage::GetSelectedServerID( KeyValues **pKV )
  1703. {
  1704. int serverID = -1;
  1705. if ( pKV )
  1706. {
  1707. *pKV = NULL;
  1708. }
  1709. if ( m_pQuickList->IsVisible() == true )
  1710. {
  1711. if ( IsRefreshing() == true )
  1712. return -1;
  1713. if ( m_pQuickList->GetSelectedPanel() )
  1714. {
  1715. CQuickListPanel *pQuickPanel = dynamic_cast<CQuickListPanel*>( m_pQuickList->GetSelectedPanel() );
  1716. if ( pQuickPanel )
  1717. {
  1718. serverID = m_pGameList->GetItemUserData( pQuickPanel->GetListID() );
  1719. if ( pKV )
  1720. {
  1721. *pKV = m_pGameList->GetItem( pQuickPanel->GetListID() );
  1722. }
  1723. }
  1724. }
  1725. }
  1726. else
  1727. {
  1728. if (!m_pGameList->GetSelectedItemsCount())
  1729. return -1;
  1730. // get the server
  1731. serverID = m_pGameList->GetItemUserData( m_pGameList->GetSelectedItem(0) );
  1732. if ( pKV )
  1733. {
  1734. *pKV = m_pGameList->GetItem( m_pGameList->GetSelectedItem(0) );
  1735. }
  1736. }
  1737. return serverID;
  1738. }
  1739. //-----------------------------------------------------------------------------
  1740. // Purpose: Dialog which warns the user about the server they're joining
  1741. //-----------------------------------------------------------------------------
  1742. class CDialogServerWarning : public vgui::Frame
  1743. {
  1744. DECLARE_CLASS_SIMPLE( CDialogServerWarning, vgui::Frame );
  1745. public:
  1746. CDialogServerWarning(vgui::Panel *parent, IGameList *gameList, int serverID );
  1747. virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
  1748. virtual void OnCommand(const char *command);
  1749. MESSAGE_FUNC_PTR_INT( OnButtonToggled, "ButtonToggled", panel, state );
  1750. private:
  1751. IGameList *m_pGameList;
  1752. int m_iServerID;
  1753. vgui::CheckButton *m_pDontShowThisAgainCheckButton;
  1754. };
  1755. //-----------------------------------------------------------------------------
  1756. // Purpose: Constructor
  1757. // Input : *gameList - game list to add specified server to
  1758. //-----------------------------------------------------------------------------
  1759. CDialogServerWarning::CDialogServerWarning(vgui::Panel *parent, IGameList *gameList, int serverID ) : Frame(parent, "DialogServerWarning")
  1760. {
  1761. m_pGameList = gameList;
  1762. m_iServerID = serverID;
  1763. m_pDontShowThisAgainCheckButton = new CheckButton(this, "DontShowThisAgainCheckbutton", "");
  1764. SetDeleteSelfOnClose(true);
  1765. SetSizeable( false );
  1766. }
  1767. //-----------------------------------------------------------------------------
  1768. // Purpose:
  1769. //-----------------------------------------------------------------------------
  1770. void CDialogServerWarning::ApplySchemeSettings( IScheme *pScheme )
  1771. {
  1772. BaseClass::ApplySchemeSettings( pScheme );
  1773. LoadControlSettings("Servers/DialogServerWarning.res");
  1774. }
  1775. //-----------------------------------------------------------------------------
  1776. // Purpose: button command handler
  1777. //-----------------------------------------------------------------------------
  1778. void CDialogServerWarning::OnCommand(const char *command)
  1779. {
  1780. if ( Q_stricmp(command, "OK") == 0 )
  1781. {
  1782. // mark ourselves to be closed
  1783. PostMessage(this, new KeyValues("Close"));
  1784. // join the game
  1785. ServerBrowserDialog().JoinGame( m_pGameList, m_iServerID );
  1786. }
  1787. else
  1788. {
  1789. BaseClass::OnCommand(command);
  1790. }
  1791. }
  1792. //-----------------------------------------------------------------------------
  1793. // Purpose: Handles filter dropdown being toggled
  1794. //-----------------------------------------------------------------------------
  1795. void CDialogServerWarning::OnButtonToggled(Panel *panel, int state)
  1796. {
  1797. ConVarRef sb_dontshow_maxplayer_warning( "sb_dontshow_maxplayer_warning", true );
  1798. if ( sb_dontshow_maxplayer_warning.IsValid() )
  1799. {
  1800. sb_dontshow_maxplayer_warning.SetValue( state );
  1801. }
  1802. }
  1803. //-----------------------------------------------------------------------------
  1804. // Purpose: initiates server connection
  1805. //-----------------------------------------------------------------------------
  1806. void CBaseGamesPage::OnBeginConnect()
  1807. {
  1808. KeyValues *pKV = NULL;
  1809. int serverID = GetSelectedServerID( &pKV );
  1810. if ( serverID == -1 )
  1811. return;
  1812. // Stop the current refresh
  1813. StopRefresh();
  1814. ConVarRef sb_dontshow_maxplayer_warning( "sb_dontshow_maxplayer_warning", true );
  1815. if ( sb_dontshow_maxplayer_warning.IsValid() )
  1816. {
  1817. // If the server is above the suggested maxplayers, warn the player
  1818. int iMaxP = sb_mod_suggested_maxplayers.GetInt();
  1819. if ( iMaxP && pKV && !sb_dontshow_maxplayer_warning.GetBool() )
  1820. {
  1821. int iMaxCount = pKV->GetInt( "MaxPlayerCount", 0 );
  1822. if ( iMaxCount > iMaxP )
  1823. {
  1824. CDialogServerWarning *dlg = vgui::SETUP_PANEL( new CDialogServerWarning( this, this, serverID ) );
  1825. dlg->MoveToCenterOfScreen();
  1826. dlg->DoModal();
  1827. wchar_t wszWarning[512];
  1828. wchar_t wszServerMaxPlayers[12];
  1829. wchar_t wszDesignedMaxPlayers[12];
  1830. wchar_t wszGameName[256];
  1831. _snwprintf( wszServerMaxPlayers, Q_ARRAYSIZE(wszServerMaxPlayers), L"%d", iMaxCount );
  1832. _snwprintf( wszDesignedMaxPlayers, Q_ARRAYSIZE(wszDesignedMaxPlayers), L"%d", iMaxP );
  1833. Q_UTF8ToUnicode( ModList().GetModNameForModDir( m_iLimitToAppID ), wszGameName, Q_ARRAYSIZE(wszGameName) );
  1834. g_pVGuiLocalize->ConstructString( wszWarning, sizeof( wszWarning ), g_pVGuiLocalize->Find( "#ServerBrowser_ServerWarning_MaxPlayers"), 4, wszServerMaxPlayers, wszGameName, wszDesignedMaxPlayers, wszDesignedMaxPlayers );
  1835. dlg->SetDialogVariable( "warning", wszWarning );
  1836. return;
  1837. }
  1838. }
  1839. }
  1840. // join the game
  1841. ServerBrowserDialog().JoinGame(this, serverID);
  1842. }
  1843. //-----------------------------------------------------------------------------
  1844. // Purpose: Displays the current game info without connecting
  1845. //-----------------------------------------------------------------------------
  1846. void CBaseGamesPage::OnViewGameInfo()
  1847. {
  1848. int serverID = GetSelectedServerID();
  1849. if ( serverID == -1 )
  1850. return;
  1851. // Stop the current refresh
  1852. StopRefresh();
  1853. // join the game
  1854. ServerBrowserDialog().OpenGameInfoDialog(this, serverID);
  1855. }
  1856. //-----------------------------------------------------------------------------
  1857. // Purpose: Return code to use for tracking how people are connecting to servers
  1858. //-----------------------------------------------------------------------------
  1859. const char *CBaseGamesPage::GetConnectCode()
  1860. {
  1861. // Determine code to use, for the "connect" command.
  1862. //
  1863. // E.g.: "connect serverbrowser" (This command primarily exists so i can grep the code....)
  1864. const char *pszConnectCode = "serverbrowser";
  1865. switch ( m_eMatchMakingType )
  1866. {
  1867. default:
  1868. AssertMsg1( false, "Unknown matchmaking type %d", m_eMatchMakingType );
  1869. break;
  1870. case eInternetServer:
  1871. pszConnectCode = "serverbrowser_internet";
  1872. break;
  1873. case eLANServer:
  1874. pszConnectCode = "serverbrowser_lan";
  1875. break;
  1876. case eFriendsServer:
  1877. pszConnectCode = "serverbrowser_friends";
  1878. break;
  1879. case eFavoritesServer:
  1880. pszConnectCode = "serverbrowser_favorites";
  1881. break;
  1882. case eHistoryServer:
  1883. pszConnectCode = "serverbrowser_history";
  1884. break;
  1885. case eSpectatorServer:
  1886. pszConnectCode = "serverbrowser_spectator";
  1887. break;
  1888. };
  1889. return pszConnectCode;
  1890. }
  1891. //-----------------------------------------------------------------------------
  1892. // Purpose: Refresh if our favorites list changed
  1893. //-----------------------------------------------------------------------------
  1894. void CBaseGamesPage::OnFavoritesMsg( FavoritesListChanged_t *pFavListChanged )
  1895. {
  1896. if ( !pFavListChanged->m_nIP ) // a zero for IP means the whole list was reloaded and we need to reload
  1897. {
  1898. switch ( m_eMatchMakingType )
  1899. {
  1900. case eInternetServer:
  1901. case eLANServer:
  1902. case eSpectatorServer:
  1903. case eFriendsServer:
  1904. return;
  1905. case eFavoritesServer:
  1906. case eHistoryServer:
  1907. // check containing property sheet to see if the page is visible.
  1908. // if not, don't bother initiating a server list grab right now -
  1909. // it will happen when the dialog is activated later.
  1910. if ( reinterpret_cast< PropertySheet* >( GetParent() )->GetActivePage() == this &&
  1911. GetParent()->IsVisible() && ServerBrowserDialog().IsVisible() )
  1912. {
  1913. GetNewServerList();
  1914. }
  1915. return;
  1916. default:
  1917. Assert( !"unknown matchmaking type" );
  1918. }
  1919. return;
  1920. }
  1921. switch ( m_eMatchMakingType )
  1922. {
  1923. case eInternetServer:
  1924. case eLANServer:
  1925. case eSpectatorServer:
  1926. case eFriendsServer:
  1927. break;
  1928. case eFavoritesServer:
  1929. case eHistoryServer:
  1930. {
  1931. int iIPServer = m_mapServerIP.Find( netadr_t( pFavListChanged->m_nIP, pFavListChanged->m_nConnPort ) );
  1932. if ( iIPServer == m_mapServerIP.InvalidIndex() )
  1933. {
  1934. if ( pFavListChanged->m_bAdd )
  1935. {
  1936. if ( steamapicontext->SteamMatchmakingServers() )
  1937. steamapicontext->SteamMatchmakingServers()->PingServer( pFavListChanged->m_nIP, pFavListChanged->m_nQueryPort, this );
  1938. }
  1939. // ignore deletes of fav's we didn't have
  1940. }
  1941. else
  1942. {
  1943. if ( pFavListChanged->m_bAdd )
  1944. {
  1945. if ( m_mapServerIP[ iIPServer ] > 0 )
  1946. ServerResponded( m_hRequest, m_mapServerIP[ iIPServer ] );
  1947. }
  1948. else
  1949. {
  1950. int iServer = m_mapServers.Find( m_mapServerIP[ iIPServer ] );
  1951. serverdisplay_t &server = m_mapServers[ iServer ];
  1952. RemoveServer( server );
  1953. }
  1954. }
  1955. }
  1956. break;
  1957. default:
  1958. Assert( !"unknown matchmaking type" );
  1959. };
  1960. }
  1961. void CCheckBoxWithStatus::OnCursorEntered()
  1962. {
  1963. ServerBrowserDialog().UpdateStatusText("#ServerBrowser_QuickListExplanation");
  1964. }
  1965. void CCheckBoxWithStatus::OnCursorExited()
  1966. {
  1967. ServerBrowserDialog().UpdateStatusText("");
  1968. }