Counter Strike : Global Offensive Source Code
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.

2234 lines
68 KiB

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