summaryrefslogtreecommitdiff
path: root/serverbrowser/BaseGamesPage.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /serverbrowser/BaseGamesPage.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'serverbrowser/BaseGamesPage.cpp')
-rw-r--r--serverbrowser/BaseGamesPage.cpp2323
1 files changed, 2323 insertions, 0 deletions
diff --git a/serverbrowser/BaseGamesPage.cpp b/serverbrowser/BaseGamesPage.cpp
new file mode 100644
index 0000000..6cef882
--- /dev/null
+++ b/serverbrowser/BaseGamesPage.cpp
@@ -0,0 +1,2323 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+using namespace vgui;
+
+#define FILTER_ALLSERVERS 0
+#define FILTER_SECURESERVERSONLY 1
+#define FILTER_INSECURESERVERSONLY 2
+
+#define UNIVERSE_OFFICIAL 0
+#define UNIVERSE_CUSTOMGAMES 1
+#define QUICKLIST_FILTER_MIN_PING 0
+
+#define MAX_MAP_NAME 128
+const char *COM_GetModDirectory();
+
+#undef wcscat
+
+ConVar sb_mod_suggested_maxplayers( "sb_mod_suggested_maxplayers", "0", FCVAR_HIDDEN );
+ConVar sb_filter_incompatible_versions( "sb_filter_incompatible_versions",
+ #ifdef STAGING_ONLY
+ "0",
+ #else
+ "1",
+ #endif
+ 0, "Hides servers running incompatible versions from the server browser. (Internet tab only.)" );
+
+bool GameSupportsReplay()
+{
+ extern IEngineReplay *g_pEngineReplay;
+ return g_pEngineReplay && g_pEngineReplay->IsSupportedModAndPlatform();
+}
+
+#ifdef STAGING_ONLY
+ ConVar sb_fake_app_id( "sb_fake_app_id", "0", 0, "If nonzero, then server browser requests will use this App ID instead" );
+#endif
+
+//--------------------------------------------------------------------------------------------------------
+bool IsReplayServer( gameserveritem_t &server )
+{
+ bool bReplay = false;
+
+ if ( GameSupportsReplay() )
+ {
+ if ( server.m_szGameTags && server.m_szGameTags[0] )
+ {
+ CUtlVector<char*> TagList;
+ V_SplitString( server.m_szGameTags, ",", TagList );
+ for ( int i = 0; i < TagList.Count(); i++ )
+ {
+ if ( Q_stricmp( TagList[i], "replays" ) == 0 )
+ {
+ bReplay = true;
+ }
+ }
+ }
+ }
+
+ return bReplay;
+}
+
+//--------------------------------------------------------------------------------------------------------
+inline char *CloneString( const char *str )
+{
+ char *cloneStr = new char [ strlen(str)+1 ];
+ strcpy( cloneStr, str );
+ return cloneStr;
+}
+
+const char *COM_GetModDirectory()
+{
+ static char modDir[MAX_PATH];
+ if ( Q_strlen( modDir ) == 0 )
+ {
+ const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) );
+ Q_strncpy( modDir, gamedir, sizeof(modDir) );
+ if ( strchr( modDir, '/' ) || strchr( modDir, '\\' ) )
+ {
+ Q_StripLastDir( modDir, sizeof(modDir) );
+ int dirlen = Q_strlen( modDir );
+ Q_strncpy( modDir, gamedir + dirlen, sizeof(modDir) - dirlen );
+ }
+ }
+
+ return modDir;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CGameListPanel::CGameListPanel( CBaseGamesPage *pOuter, const char *pName ) :
+ BaseClass( pOuter, pName )
+{
+ m_pOuter = pOuter;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forward KEY_ENTER to the CBaseGamesPage.
+//-----------------------------------------------------------------------------
+void CGameListPanel::OnKeyCodePressed(vgui::KeyCode code)
+{
+ // Let the outer class handle it.
+ if ( code == KEY_ENTER && m_pOuter->OnGameListEnterPressed() )
+ return;
+
+ BaseClass::OnKeyCodePressed( code );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CBaseGamesPage::CBaseGamesPage( vgui::Panel *parent, const char *name, EPageType eType, const char *pCustomResFilename)
+ : PropertyPage(parent, name),
+ m_CallbackFavoritesMsg( this, &CBaseGamesPage::OnFavoritesMsg ),
+ m_hRequest( NULL ),
+ m_pCustomResFilename( pCustomResFilename )
+{
+ SetSize( 624, 278 );
+ m_szGameFilter[0] = 0;
+ m_szMapFilter[0] = 0;
+ m_iMaxPlayerFilter = 0;
+ m_iPingFilter = 0;
+ m_iServerRefreshCount = 0;
+ m_bFilterNoFullServers = false;
+ m_bFilterNoEmptyServers = false;
+ m_bFilterNoPasswordedServers = false;
+ m_iSecureFilter = FILTER_ALLSERVERS;
+ m_hFont = NULL;
+ m_eMatchMakingType = eType;
+ m_bFilterReplayServers = false;
+ SetDefLessFunc( m_mapServers );
+ SetDefLessFunc( m_mapServerIP );
+ SetDefLessFunc( m_mapGamesFilterItem );
+
+ // Not always loaded
+ m_pWorkshopFilter = NULL;
+
+ bool bRunningTF2 = GameSupportsReplay();
+
+ // get the 'all' text
+ wchar_t *all = g_pVGuiLocalize->Find("ServerBrowser_All");
+ Q_UnicodeToUTF8(all, m_szComboAllText, sizeof(m_szComboAllText));
+
+ // Init UI
+ m_pConnect = new Button(this, "ConnectButton", "#ServerBrowser_Connect");
+ m_pConnect->SetEnabled(false);
+ m_pRefreshAll = new Button(this, "RefreshButton", "#ServerBrowser_Refresh");
+ m_pRefreshQuick = new Button(this, "RefreshQuickButton", "#ServerBrowser_RefreshQuick");
+ m_pAddServer = new Button(this, "AddServerButton", "#ServerBrowser_AddServer");
+ m_pAddCurrentServer = new Button(this, "AddCurrentServerButton", "#ServerBrowser_AddCurrentServer");
+ m_pGameList = new CGameListPanel(this, "gamelist");
+ m_pGameList->SetAllowUserModificationOfColumns(true);
+
+ m_pQuickList = new PanelListPanel(this, "quicklist");
+ m_pQuickList->SetFirstColumnWidth( 0 );
+
+ m_pAddToFavoritesButton = new vgui::Button( this, "AddToFavoritesButton", "" );
+ m_pAddToFavoritesButton->SetEnabled( false );
+ m_pAddToFavoritesButton->SetVisible( false );
+
+ // Increment this number if columns are added / removed or some other change is done that requires
+ // tossing out old saved user configs.
+ m_pGameList->m_nUserConfigFileVersion = 2;
+
+ // Add the column headers
+ m_pGameList->AddColumnHeader( k_nColumn_Password, "Password", "#ServerBrowser_Password", 16, ListPanel::COLUMN_FIXEDSIZE | ListPanel::COLUMN_IMAGE);
+ m_pGameList->AddColumnHeader( k_nColumn_Secure, "Secure", "#ServerBrowser_Secure", 16, ListPanel::COLUMN_FIXEDSIZE | ListPanel::COLUMN_IMAGE);
+
+ int nReplayWidth = 16;
+ if ( !bRunningTF2 )
+ {
+ nReplayWidth = 0;
+ }
+
+ m_pGameList->AddColumnHeader( k_nColumn_Replay, "Replay", "#ServerBrowser_Replay", nReplayWidth, ListPanel::COLUMN_FIXEDSIZE | ListPanel::COLUMN_IMAGE);
+ m_pGameList->AddColumnHeader( k_nColumn_Name, "Name", "#ServerBrowser_Servers", 50, ListPanel::COLUMN_RESIZEWITHWINDOW | ListPanel::COLUMN_UNHIDABLE);
+ m_pGameList->AddColumnHeader( k_nColumn_IPAddr, "IPAddr", "#ServerBrowser_IPAddress", 64, ListPanel::COLUMN_HIDDEN);
+ m_pGameList->AddColumnHeader( k_nColumn_GameDesc, "GameDesc", "#ServerBrowser_Game", 112,
+ 112, // minwidth
+ 300, // maxwidth
+ 0 // flags
+ );
+ m_pGameList->AddColumnHeader( k_nColumn_Players, "Players", "#ServerBrowser_Players", 55, ListPanel::COLUMN_FIXEDSIZE);
+ m_pGameList->AddColumnHeader( k_nColumn_Bots, "Bots", "#ServerBrowser_Bots", 40, ListPanel::COLUMN_FIXEDSIZE);
+ m_pGameList->AddColumnHeader( k_nColumn_Map, "Map", "#ServerBrowser_Map", 90,
+ 90, // minwidth
+ 300, // maxwidth
+ 0 // flags
+ );
+ m_pGameList->AddColumnHeader( k_nColumn_Ping, "Ping", "#ServerBrowser_Latency", 55, ListPanel::COLUMN_FIXEDSIZE);
+
+ m_pGameList->SetColumnHeaderTooltip( k_nColumn_Password, "#ServerBrowser_PasswordColumn_Tooltip");
+ m_pGameList->SetColumnHeaderTooltip( k_nColumn_Bots, "#ServerBrowser_BotColumn_Tooltip");
+ m_pGameList->SetColumnHeaderTooltip( k_nColumn_Secure, "#ServerBrowser_SecureColumn_Tooltip");
+
+ if ( bRunningTF2 )
+ {
+ m_pGameList->SetColumnHeaderTooltip( k_nColumn_Replay, "#ServerBrowser_ReplayColumn_Tooltip");
+ }
+
+ // setup fast sort functions
+ m_pGameList->SetSortFunc( k_nColumn_Password, PasswordCompare);
+ m_pGameList->SetSortFunc( k_nColumn_Bots, BotsCompare);
+ m_pGameList->SetSortFunc( k_nColumn_Secure, SecureCompare);
+
+ if ( bRunningTF2 )
+ {
+ m_pGameList->SetSortFunc( k_nColumn_Replay, ReplayCompare);
+ }
+
+ m_pGameList->SetSortFunc( k_nColumn_Name, ServerNameCompare);
+ m_pGameList->SetSortFunc( k_nColumn_IPAddr, IPAddressCompare);
+ m_pGameList->SetSortFunc( k_nColumn_GameDesc, GameCompare);
+ m_pGameList->SetSortFunc( k_nColumn_Players, PlayersCompare);
+ m_pGameList->SetSortFunc( k_nColumn_Map, MapCompare);
+ m_pGameList->SetSortFunc( k_nColumn_Ping, PingCompare);
+
+ // Sort by ping time by default
+ m_pGameList->SetSortColumn( k_nColumn_Ping );
+
+ CreateFilters();
+ LoadFilterSettings();
+
+ m_bAutoSelectFirstItemInGameList = false;
+
+ // In TF2, fill out the max player count so that we sort all >24 player servers below the rest.
+ if ( bRunningTF2 )
+ {
+ sb_mod_suggested_maxplayers.SetValue( 24 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CBaseGamesPage::~CBaseGamesPage()
+{
+ if ( m_hRequest )
+ {
+ steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
+ m_hRequest = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseGamesPage::GetInvalidServerListID()
+{
+ return m_pGameList->InvalidItemID();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ if ( GetSelectedServerID() == -1 )
+ {
+ m_pConnect->SetEnabled(false);
+ }
+ else
+ {
+ m_pConnect->SetEnabled(true);
+ }
+
+
+ if (SupportsItem(IGameList::GETNEWLIST))
+ {
+ m_pRefreshQuick->SetVisible(true);
+ m_pRefreshAll->SetText("#ServerBrowser_RefreshAll");
+ }
+ else
+ {
+ m_pRefreshQuick->SetVisible(false);
+ m_pRefreshAll->SetText("#ServerBrowser_Refresh");
+ }
+
+ if ( SupportsItem(IGameList::ADDSERVER) )
+ {
+// m_pFilterString->SetWide( 90 ); // shrink the filter label to fix the add current server button
+ m_pAddServer->SetVisible(true);
+ }
+ else
+ {
+ m_pAddServer->SetVisible(false);
+ }
+
+ if ( SupportsItem(IGameList::ADDCURRENTSERVER) )
+ {
+ m_pAddCurrentServer->SetVisible(true);
+ }
+ else
+ {
+ m_pAddCurrentServer->SetVisible(false);
+ }
+
+ if ( IsRefreshing() )
+ {
+ m_pRefreshAll->SetText( "#ServerBrowser_StopRefreshingList" );
+ }
+
+ if (m_pGameList->GetItemCount() > 0)
+ {
+ m_pRefreshQuick->SetEnabled(true);
+ }
+ else
+ {
+ m_pRefreshQuick->SetEnabled(false);
+ }
+
+ if ( !steamapicontext->SteamMatchmakingServers() || !steamapicontext->SteamMatchmaking() )
+ {
+ m_pAddCurrentServer->SetVisible( false );
+ m_pRefreshQuick->SetEnabled( false );
+ m_pAddServer->SetEnabled( false );
+ m_pConnect->SetEnabled( false );
+ m_pRefreshAll->SetEnabled( false );
+ m_pAddToFavoritesButton->SetEnabled( false );
+ m_pGameList->SetEmptyListText( "#ServerBrowser_SteamRunning" );
+ }
+
+ Repaint();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ // load the password icon
+ ImageList *imageList = new ImageList(false);
+ m_nImageIndexPassword = imageList->AddImage(scheme()->GetImage("servers/icon_password", false));
+ //imageList->AddImage(scheme()->GetImage("servers/icon_bots", false));
+ m_nImageIndexSecure = imageList->AddImage(scheme()->GetImage("servers/icon_robotron", false));
+ m_nImageIndexSecureVacBanned = imageList->AddImage(scheme()->GetImage("servers/icon_secure_deny", false));
+ m_nImageIndexReplay = imageList->AddImage(scheme()->GetImage("servers/icon_replay", false));
+ int passwordColumnImage = imageList->AddImage(scheme()->GetImage("servers/icon_password_column", false));
+ //int botColumnImage = imageList->AddImage(scheme()->GetImage("servers/icon_bots_column", false));
+ int secureColumnImage = imageList->AddImage(scheme()->GetImage("servers/icon_robotron_column", false));
+ int replayColumnImage = imageList->AddImage(scheme()->GetImage("servers/icon_replay_column", false));
+
+ m_pGameList->SetImageList(imageList, true);
+ m_hFont = pScheme->GetFont( "ListSmall", IsProportional() );
+ if ( !m_hFont )
+ m_hFont = pScheme->GetFont( "DefaultSmall", IsProportional() );
+
+ m_pGameList->SetFont( m_hFont );
+ m_pGameList->SetColumnHeaderImage( k_nColumn_Password, passwordColumnImage);
+ //m_pGameList->SetColumnHeaderImage( k_nColumn_Bots, botColumnImage);
+ m_pGameList->SetColumnHeaderImage( k_nColumn_Secure, secureColumnImage);
+ m_pGameList->SetColumnHeaderImage( k_nColumn_Replay, replayColumnImage);
+}
+
+struct serverqualitysort_t
+{
+ int iIndex;
+ int iPing;
+ int iPlayerCount;
+ int iMaxPlayerCount;
+};
+
+int ServerQualitySort( const serverqualitysort_t *pSQ1, const serverqualitysort_t *pSQ2 )
+{
+ int iMaxP = sb_mod_suggested_maxplayers.GetInt();
+ if ( iMaxP && pSQ1->iMaxPlayerCount != pSQ2->iMaxPlayerCount )
+ {
+ if ( pSQ1->iMaxPlayerCount > iMaxP )
+ return 1;
+ if ( pSQ2->iMaxPlayerCount > iMaxP )
+ return -1;
+ }
+
+ if ( pSQ1->iPing <= 100 && pSQ2->iPing <= 100 && pSQ1->iPlayerCount != pSQ2->iPlayerCount )
+ {
+ return pSQ2->iPlayerCount - pSQ1->iPlayerCount;
+ }
+
+ return pSQ1->iPing - pSQ2->iPing;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::SelectQuickListServers( void )
+{
+ int iIndex = m_pQuickList->FirstItem();
+
+ while ( iIndex != m_pQuickList->InvalidItemID() )
+ {
+ CQuickListPanel *pQuickListPanel = dynamic_cast< CQuickListPanel *> ( m_pQuickList->GetItemPanel( iIndex ) );
+
+ if ( pQuickListPanel )
+ {
+ CUtlVector< serverqualitysort_t > vecServerQuality;
+
+ int iElement = m_quicklistserverlist.Find( pQuickListPanel->GetName() );
+
+ if ( iElement != m_quicklistserverlist.InvalidIndex() )
+ {
+ CQuickListMapServerList *vecMapServers = &m_quicklistserverlist[iElement];
+
+ if ( vecMapServers )
+ {
+ for ( int i =0; i < vecMapServers->Count(); i++ )
+ {
+ int iListID = vecMapServers->Element( i );
+
+ serverqualitysort_t serverquality;
+
+ serverquality.iIndex = iListID;
+
+ KeyValues *kv = NULL;
+ if ( m_pGameList->IsValidItemID( iListID ) )
+ {
+ kv = m_pGameList->GetItem( iListID );
+ }
+
+ if ( kv )
+ {
+ serverquality.iPing = kv->GetInt( "ping", 0 );
+ serverquality.iPlayerCount = kv->GetInt( "PlayerCount", 0 );
+ serverquality.iMaxPlayerCount = kv->GetInt( "MaxPlayerCount", 0 );
+ }
+
+ vecServerQuality.AddToTail( serverquality );
+ }
+
+ vecServerQuality.Sort( ServerQualitySort );
+
+ serverqualitysort_t bestserver = vecServerQuality.Head();
+
+ if ( m_pGameList->IsValidItemID( bestserver.iIndex ) )
+ {
+ pQuickListPanel->SetServerInfo( m_pGameList->GetItem( bestserver.iIndex ), bestserver.iIndex, vecServerQuality.Count() );
+ }
+ }
+ }
+ }
+
+ iIndex = m_pQuickList->NextItem( iIndex );
+ }
+
+ //Force the connect button to recalculate its state.
+ OnItemSelected();
+}
+
+int ServerMapnameSortFunc( const servermaps_t *p1, const servermaps_t *p2 )
+{
+ //If they're both on disc OR both missing then sort them alphabetically
+ if ( (p1->bOnDisk && p2->bOnDisk) || (!p1->bOnDisk && !p2->bOnDisk ) )
+ return Q_strcmp( p1->pFriendlyName, p2->pFriendlyName );
+
+ //Otherwise maps you have show up first
+ return p2->bOnDisk - p1->bOnDisk;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: prepares all the QuickListPanel map panels...
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::PrepareQuickListMap( const char *pMapName, int iListID )
+{
+ char szMapName[ 512 ];
+ Q_snprintf( szMapName, sizeof( szMapName ), "%s", pMapName );
+
+ Q_strlower( szMapName );
+
+ char path[ 512 ];
+ Q_snprintf( path, sizeof( path ), "maps/%s.bsp", szMapName );
+
+ int iIndex = m_quicklistserverlist.Find( szMapName );
+
+ if ( m_quicklistserverlist.IsValidIndex( iIndex ) == false )
+ {
+ CQuickListMapServerList vecMapServers;
+ iIndex = m_quicklistserverlist.Insert( szMapName, vecMapServers );
+
+ char szFriendlyName[MAX_MAP_NAME];
+ const char *pszFriendlyGameTypeName = ServerBrowser().GetMapFriendlyNameAndGameType( szMapName, szFriendlyName, sizeof(szFriendlyName) );
+
+ //Add the map to our list of panels.
+ if ( m_pQuickList )
+ {
+ servermaps_t servermap;
+
+ servermap.pFriendlyName = CloneString( szFriendlyName );
+ servermap.pOriginalName = CloneString( szMapName );
+
+ char path[ 512 ];
+ Q_snprintf( path, sizeof( path ), "maps/%s.bsp", szMapName );
+
+ servermap.bOnDisk = g_pFullFileSystem->FileExists( path, "MOD" );
+
+ CQuickListPanel *pQuickListPanel = new CQuickListPanel( m_pQuickList, "QuickListPanel");
+
+ if ( pQuickListPanel )
+ {
+ pQuickListPanel->InvalidateLayout();
+ pQuickListPanel->SetName( servermap.pOriginalName );
+ pQuickListPanel->SetMapName( servermap.pFriendlyName );
+ pQuickListPanel->SetImage( servermap.pOriginalName );
+ pQuickListPanel->SetGameType( pszFriendlyGameTypeName );
+ pQuickListPanel->SetVisible( true );
+ pQuickListPanel->SetRefreshing();
+
+ servermap.iPanelIndex = m_pQuickList->AddItem( NULL, pQuickListPanel );
+ }
+
+ m_vecMapNamesFound.AddToTail( servermap );
+ m_vecMapNamesFound.Sort( ServerMapnameSortFunc );
+ }
+
+ //Now make sure that list is sorted.
+ CUtlVector<int> *pPanelSort = m_pQuickList->GetSortedVector();
+
+ if ( pPanelSort )
+ {
+ pPanelSort->RemoveAll();
+
+ for ( int i = 0; i < m_vecMapNamesFound.Count(); i++ )
+ {
+ pPanelSort->AddToTail( m_vecMapNamesFound[i].iPanelIndex );
+ }
+ }
+ }
+
+ if ( iIndex != m_quicklistserverlist.InvalidIndex() )
+ {
+ CQuickListMapServerList *vecMapServers = &m_quicklistserverlist[iIndex];
+
+ if ( vecMapServers )
+ {
+ if ( vecMapServers->Find( iListID ) == vecMapServers->InvalidIndex() )
+ {
+ vecMapServers->AddToTail( iListID );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets information about specified server
+//-----------------------------------------------------------------------------
+gameserveritem_t *CBaseGamesPage::GetServer( unsigned int serverID )
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return NULL;
+
+ // No point checking for >= 0 when serverID is unsigned.
+ //if ( serverID >= 0 )
+ {
+ return steamapicontext->SteamMatchmakingServers()->GetServerDetails( m_hRequest, serverID );
+ }
+ //else
+ //{
+ // Assert( !"Unable to return a useful entry" );
+ // return NULL; // bugbug Alfred: temp Favorites/History objects won't return a good value here...
+ //}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseGamesPage::TagsExclude( void )
+{
+ if ( m_pTagsIncludeFilter == NULL )
+ return false;
+
+ return m_pTagsIncludeFilter->GetActiveItem();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: What mode the workshop selection is in for pages that use it
+//-----------------------------------------------------------------------------
+CBaseGamesPage::eWorkshopMode CBaseGamesPage::WorkshopMode()
+{
+ if ( !m_pWorkshopFilter || !ServerBrowser().IsWorkshopEnabled() )
+ {
+ return eWorkshop_None;
+ }
+
+ return (eWorkshopMode)m_pWorkshopFilter->GetActiveItem();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::HideReplayFilter( void )
+{
+ if ( m_pReplayFilterCheck && m_pReplayFilterCheck->IsVisible() )
+ {
+ m_pReplayFilterCheck->SetVisible( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::CreateFilters()
+{
+ m_pFilter = new ToggleButton(this, "Filter", "#ServerBrowser_Filters");
+ m_pFilterString = new Label(this, "FilterString", "");
+
+ if ( Q_stricmp( COM_GetModDirectory(), "cstrike" ) == 0 )
+ {
+ m_pFilter->SetSelected( false );
+ m_bFiltersVisible = false;
+ }
+ else
+ {
+ m_pFilter->SetSelected( true );
+ m_bFiltersVisible = true;
+ }
+
+ // filter controls
+ m_pGameFilter = new ComboBox(this, "GameFilter", 6, false);
+
+ m_pLocationFilter = new ComboBox(this, "LocationFilter", 6, false);
+ m_pLocationFilter->AddItem("", NULL);
+
+ m_pMapFilter = new TextEntry(this, "MapFilter");
+ m_pMaxPlayerFilter = new TextEntry(this, "MaxPlayerFilter");
+ m_pPingFilter = new ComboBox(this, "PingFilter", 6, false);
+ m_pPingFilter->AddItem("#ServerBrowser_All", NULL);
+ m_pPingFilter->AddItem("#ServerBrowser_LessThan50", NULL);
+ m_pPingFilter->AddItem("#ServerBrowser_LessThan100", NULL);
+ m_pPingFilter->AddItem("#ServerBrowser_LessThan150", NULL);
+ m_pPingFilter->AddItem("#ServerBrowser_LessThan250", NULL);
+ m_pPingFilter->AddItem("#ServerBrowser_LessThan350", NULL);
+ m_pPingFilter->AddItem("#ServerBrowser_LessThan600", NULL);
+
+ m_pSecureFilter = new ComboBox(this, "SecureFilter", 3, false);
+ m_pSecureFilter->AddItem("#ServerBrowser_All", NULL);
+ m_pSecureFilter->AddItem("#ServerBrowser_SecureOnly", NULL);
+ m_pSecureFilter->AddItem("#ServerBrowser_InsecureOnly", NULL);
+
+ m_pTagsIncludeFilter = new ComboBox(this, "TagsInclude", 2, false);
+ m_pTagsIncludeFilter->AddItem("#ServerBrowser_TagsInclude", NULL);
+ m_pTagsIncludeFilter->AddItem("#ServerBrowser_TagsDoNotInclude", NULL);
+ m_pTagsIncludeFilter->SetVisible( false );
+
+ if ( ServerBrowser().IsWorkshopEnabled() )
+ {
+ m_pWorkshopFilter = new ComboBox(this, "WorkshopFilter", 3, false);
+ m_pWorkshopFilter->AddItem("#ServerBrowser_All", NULL);
+ m_pWorkshopFilter->AddItem("#ServerBrowser_WorkshopFilterWorkshopOnly", NULL);
+ m_pWorkshopFilter->AddItem("#ServerBrowser_WorkshopFilterSubscribed", NULL);
+ m_pWorkshopFilter->SetVisible( false );
+ }
+
+ m_pNoEmptyServersFilterCheck = new CheckButton(this, "ServerEmptyFilterCheck", "");
+ m_pNoFullServersFilterCheck = new CheckButton(this, "ServerFullFilterCheck", "");
+ m_pNoPasswordFilterCheck = new CheckButton(this, "NoPasswordFilterCheck", "");
+ m_pQuickListCheckButton = new CCheckBoxWithStatus(this, "QuickListCheck", "");
+ m_pReplayFilterCheck = new CheckButton(this, "ReplayFilterCheck", "");
+
+ KeyValues *pkv = new KeyValues("mod", "gamedir", "", "appid", NULL );
+ m_pGameFilter->AddItem("#ServerBrowser_All", pkv);
+
+ for (int i = 0; i < ModList().ModCount(); i++)
+ {
+ pkv->SetString("gamedir", ModList().GetModDir(i));
+ pkv->SetUint64("appid", ModList().GetAppID(i).ToUint64() );
+ int iItemID = m_pGameFilter->AddItem(ModList().GetModName(i), pkv);
+ m_mapGamesFilterItem.Insert( ModList().GetAppID(i).ToUint64(), iItemID );
+ }
+ pkv->deleteThis();
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: loads filter settings from the keyvalues
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::LoadFilterSettings()
+{
+ KeyValues *filter = ServerBrowserDialog().GetFilterSaveData(GetName());
+
+ if (ServerBrowserDialog().GetActiveModName())
+ {
+ Q_strncpy(m_szGameFilter, ServerBrowserDialog().GetActiveModName(), sizeof(m_szGameFilter));
+ m_iLimitToAppID = ServerBrowserDialog().GetActiveAppID();
+ }
+ else
+ {
+ Q_strncpy(m_szGameFilter, filter->GetString("game"), sizeof(m_szGameFilter));
+ m_iLimitToAppID = CGameID( filter->GetUint64( "appid", 0 ) );
+ }
+
+ Q_strncpy(m_szMapFilter, filter->GetString("map"), sizeof(m_szMapFilter));
+ m_iMaxPlayerFilter = filter->GetInt("MaxPlayerCount");
+ m_iPingFilter = filter->GetInt("ping");
+ m_bFilterNoFullServers = filter->GetInt("NoFull");
+ m_bFilterNoEmptyServers = filter->GetInt("NoEmpty");
+ m_bFilterNoPasswordedServers = filter->GetInt("NoPassword");
+ m_bFilterReplayServers = filter->GetInt("Replay");
+ m_pQuickListCheckButton->SetSelected( filter->GetInt( "QuickList", 0 ) );
+
+ int secureFilter = filter->GetInt("Secure");
+ m_pSecureFilter->ActivateItem(secureFilter);
+
+ int tagsinclude = filter->GetInt("tagsinclude");
+ m_pTagsIncludeFilter->ActivateItem( tagsinclude );
+
+ if ( m_pWorkshopFilter )
+ {
+ int workshopFilter = filter->GetInt("workshopfilter");
+ m_pWorkshopFilter->ActivateItem( workshopFilter );
+ }
+
+ // apply to the controls
+ UpdateGameFilter();
+ m_pMapFilter->SetText(m_szMapFilter);
+ m_pLocationFilter->ActivateItem(filter->GetInt("location"));
+
+ if (m_iMaxPlayerFilter)
+ {
+ char buf[32];
+ Q_snprintf(buf, sizeof(buf), "%d", m_iMaxPlayerFilter);
+ m_pMaxPlayerFilter->SetText(buf);
+ }
+
+ if (m_iPingFilter)
+ {
+ char buf[32];
+ Q_snprintf(buf, sizeof(buf), "< %d", m_iPingFilter);
+ m_pPingFilter->SetText(buf);
+ }
+
+ m_pNoFullServersFilterCheck->SetSelected(m_bFilterNoFullServers);
+ m_pNoEmptyServersFilterCheck->SetSelected(m_bFilterNoEmptyServers);
+ m_pNoPasswordFilterCheck->SetSelected(m_bFilterNoPasswordedServers);
+ m_pReplayFilterCheck->SetSelected(m_bFilterReplayServers);
+
+ OnLoadFilter( filter );
+ UpdateFilterSettings();
+
+ UpdateFilterAndQuickListVisibility();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the game filter combo box to be the saved setting
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::UpdateGameFilter()
+{
+ bool bFound = false;
+ for (int i = 0; i < m_pGameFilter->GetItemCount(); i++)
+ {
+ KeyValues *kv = m_pGameFilter->GetItemUserData(i);
+ CGameID gameID( kv->GetUint64( "appID", 0 ) );
+ const char *pchGameDir = kv->GetString( "gamedir" );
+ if ( ( gameID == m_iLimitToAppID || m_iLimitToAppID.AppID() == 0 ) && ( !m_szGameFilter[0] ||
+ ( pchGameDir && pchGameDir[0] && !Q_strncmp( pchGameDir, m_szGameFilter, Q_strlen( pchGameDir ) ) ) ) )
+ {
+ if ( i != m_pGameFilter->GetActiveItem() )
+ {
+ m_pGameFilter->ActivateItem(i);
+ }
+ bFound = true;
+ break;
+ }
+ }
+ if (!bFound)
+ {
+ // default to empty
+ if ( 0 != m_pGameFilter->GetActiveItem() )
+ {
+ m_pGameFilter->ActivateItem(0);
+ }
+ }
+
+ // only one mod is allowed in the game
+ if ( ServerBrowserDialog().GetActiveModName() )
+ {
+ m_pGameFilter->SetEnabled( false );
+ m_pGameFilter->SetText( ServerBrowserDialog().GetActiveGameName() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles incoming server refresh data
+// updates the server browser with the refreshed information from the server itself
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::ServerResponded( gameserveritem_t &server )
+{
+ int nIndex = -1; // start at -1 and work backwards to find the next free slot for this adhoc query
+ while ( m_mapServers.Find( nIndex ) != m_mapServers.InvalidIndex() )
+ nIndex--;
+ ServerResponded( nIndex, &server );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Callback for ISteamMatchmakingServerListResponse
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::ServerResponded( HServerListRequest hReq, int iServer )
+{
+ gameserveritem_t *pServerItem = steamapicontext->SteamMatchmakingServers()->GetServerDetails( hReq, iServer );
+ if ( !pServerItem )
+ {
+ Assert( !"Missing server response" );
+ return;
+ }
+
+ // FIXME(johns): This is a workaround for a steam bug, where it inproperly reads signed bytes out of the
+ // message. Once the upstream fix makes it into our SteamSDK, this block can be removed.
+ pServerItem->m_nPlayers = (uint8)(int8)pServerItem->m_nPlayers;
+ pServerItem->m_nBotPlayers = (uint8)(int8)pServerItem->m_nBotPlayers;
+ pServerItem->m_nMaxPlayers = (uint8)(int8)pServerItem->m_nMaxPlayers;
+
+ ServerResponded( iServer, pServerItem );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles incoming server refresh data
+// updates the server browser with the refreshed information from the server itself
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::ServerResponded( int iServer, gameserveritem_t *pServerItem )
+{
+ int iServerMap = m_mapServers.Find( iServer );
+ if ( iServerMap == m_mapServers.InvalidIndex() )
+ {
+ netadr_t netAdr( pServerItem->m_NetAdr.GetIP(), pServerItem->m_NetAdr.GetConnectionPort() );
+ int iServerIP = m_mapServerIP.Find( netAdr );
+ if ( iServerIP != m_mapServerIP.InvalidIndex() )
+ {
+ // if we already had this entry under another index remove the old entry
+ int iServerMap = m_mapServers.Find( m_mapServerIP[ iServerIP ] );
+ if ( iServerMap != m_mapServers.InvalidIndex() )
+ {
+ serverdisplay_t &server = m_mapServers[ iServerMap ];
+ if ( m_pGameList->IsValidItemID( server.m_iListID ) )
+ m_pGameList->RemoveItem( server.m_iListID );
+ m_mapServers.RemoveAt( iServerMap );
+ }
+ m_mapServerIP.RemoveAt( iServerIP );
+ }
+
+ serverdisplay_t serverFind;
+ serverFind.m_iListID = -1;
+ serverFind.m_bDoNotRefresh = false;
+ iServerMap = m_mapServers.Insert( iServer, serverFind );
+ m_mapServerIP.Insert( netAdr, iServer );
+ }
+
+ serverdisplay_t *pServer = &m_mapServers[ iServerMap ];
+ pServer->m_iServerID = iServer;
+ Assert( pServerItem->m_NetAdr.GetIP() != 0 );
+
+ // check filters
+ bool removeItem = false;
+ if ( !CheckPrimaryFilters( *pServerItem ) )
+ {
+ // server has been filtered at a primary level
+ // remove from lists
+ pServer->m_bDoNotRefresh = true;
+
+ // remove from UI list
+ removeItem = true;
+
+ if ( m_pGameList->IsValidItemID( pServer->m_iListID ) )
+ {
+ m_pGameList->RemoveItem( pServer->m_iListID );
+ pServer->m_iListID = GetInvalidServerListID();
+ }
+
+ return;
+ }
+ else if (!CheckSecondaryFilters( *pServerItem ))
+ {
+ // we still ping this server in the future; however it is removed from UI list
+ removeItem = true;
+ }
+
+ // update UI
+ KeyValues *kv;
+ if ( m_pGameList->IsValidItemID( pServer->m_iListID ) )
+ {
+ // we're updating an existing entry
+ kv = m_pGameList->GetItem( pServer->m_iListID );
+ m_pGameList->SetUserData( pServer->m_iListID, pServer->m_iServerID );
+ }
+ else
+ {
+ // new entry
+ kv = new KeyValues("Server");
+ }
+
+ kv->SetString("name", pServerItem->GetName());
+ kv->SetString("map", pServerItem->m_szMap);
+ kv->SetString("GameDir", pServerItem->m_szGameDir);
+ kv->SetString("GameDesc", pServerItem->m_szGameDescription);
+ kv->SetInt("password", pServerItem->m_bPassword ? m_nImageIndexPassword : 0);
+
+ if ( pServerItem->m_nBotPlayers > 0 )
+ kv->SetInt("bots", pServerItem->m_nBotPlayers);
+ else
+ kv->SetString("bots", "");
+
+ if ( pServerItem->m_bSecure )
+ {
+ // show the denied icon if banned from secure servers, the secure icon otherwise
+ kv->SetInt("secure", ServerBrowser().IsVACBannedFromGame( pServerItem->m_nAppID ) ? m_nImageIndexSecureVacBanned : m_nImageIndexSecure );
+ }
+ else
+ {
+ kv->SetInt("secure", 0);
+ }
+
+ kv->SetString( "IPAddr", pServerItem->m_NetAdr.GetConnectionAddressString() );
+
+ int nAdjustedForBotsPlayers = max( 0, pServerItem->m_nPlayers - pServerItem->m_nBotPlayers );
+
+ char buf[32];
+ Q_snprintf(buf, sizeof(buf), "%d / %d", nAdjustedForBotsPlayers, pServerItem->m_nMaxPlayers );
+ kv->SetString("Players", buf);
+
+ kv->SetInt("PlayerCount", nAdjustedForBotsPlayers );
+ kv->SetInt("MaxPlayerCount", pServerItem->m_nMaxPlayers );
+
+ kv->SetInt("Ping", pServerItem->m_nPing);
+
+ kv->SetString("Tags", pServerItem->m_szGameTags );
+
+ kv->SetInt("Replay", IsReplayServer( *pServerItem ) ? m_nImageIndexReplay : 0);
+
+ if ( pServerItem->m_ulTimeLastPlayed )
+ {
+ // construct a time string for last played time
+ struct tm *now;
+ now = localtime( (time_t*)&pServerItem->m_ulTimeLastPlayed );
+
+ if ( now )
+ {
+ char buf[64];
+ strftime(buf, sizeof(buf), "%a %d %b %I:%M%p", now);
+ Q_strlower(buf + strlen(buf) - 4);
+ kv->SetString("LastPlayed", buf);
+ }
+ }
+
+ if ( pServer->m_bDoNotRefresh )
+ {
+ // clear out the vars
+ kv->SetString("Ping", "");
+ kv->SetWString("GameDesc", g_pVGuiLocalize->Find("#ServerBrowser_NotResponding"));
+ kv->SetString("Players", "");
+ kv->SetString("map", "");
+ }
+
+ if ( !m_pGameList->IsValidItemID( pServer->m_iListID ) )
+ {
+ // new server, add to list
+ pServer->m_iListID = m_pGameList->AddItem(kv, pServer->m_iServerID, false, false);
+ if ( m_bAutoSelectFirstItemInGameList && m_pGameList->GetItemCount() == 1 )
+ {
+ m_pGameList->AddSelectedItem( pServer->m_iListID );
+ }
+
+ m_pGameList->SetItemVisible( pServer->m_iListID, !removeItem );
+
+ kv->deleteThis();
+ }
+ else
+ {
+ // tell the list that we've changed the data
+ m_pGameList->ApplyItemChanges( pServer->m_iListID );
+ m_pGameList->SetItemVisible( pServer->m_iListID, !removeItem );
+ }
+
+ PrepareQuickListMap( pServerItem->m_szMap, pServer->m_iListID );
+ UpdateStatus();
+ m_iServerRefreshCount++;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::UpdateFilterAndQuickListVisibility()
+{
+ bool showQuickList = m_pQuickListCheckButton->IsSelected();
+ bool showFilter = m_pFilter->IsSelected();
+
+ m_bFiltersVisible = !showQuickList && !m_pCustomResFilename && showFilter;
+
+ int wide, tall;
+ GetSize( wide, tall );
+ SetSize( 624, 278 );
+
+ UpdateDerivedLayouts();
+ UpdateGameFilter();
+
+ if ( m_hFont )
+ {
+ SETUP_PANEL( m_pGameList );
+ m_pGameList->SetFont( m_hFont );
+ }
+
+ SetSize( wide, tall );
+
+
+ m_pQuickList->SetVisible( showQuickList );
+ m_pGameList->SetVisible( !showQuickList );
+ m_pFilter->SetVisible( !showQuickList );
+ m_pFilterString->SetVisible ( !showQuickList );
+
+
+ InvalidateLayout();
+
+ UpdateFilterSettings();
+ ApplyGameFilters();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::SetQuickListEnabled( bool bEnabled )
+{
+ m_pQuickListCheckButton->SetSelected( bEnabled );
+
+ m_pQuickList->SetVisible( m_pQuickListCheckButton->IsSelected() );
+ m_pGameList->SetVisible( !m_pQuickListCheckButton->IsSelected() );
+
+ m_pFilter->SetVisible( !m_pQuickListCheckButton->IsSelected() );
+ m_pFilterString->SetVisible( !m_pQuickListCheckButton->IsSelected() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::SetFiltersVisible( bool bVisible )
+{
+ if ( bVisible == m_pFilter->IsSelected() )
+ return;
+
+ m_pFilter->SetSelected( bVisible );
+ OnButtonToggled( m_pFilter, bVisible );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles filter dropdown being toggled
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnButtonToggled( Panel *panel, int state )
+{
+ UpdateFilterAndQuickListVisibility();
+
+
+ if (panel == m_pNoFullServersFilterCheck || panel == m_pNoEmptyServersFilterCheck || panel == m_pNoPasswordFilterCheck || panel == m_pReplayFilterCheck)
+ {
+ // treat changing these buttons like any other filter has changed
+ OnTextChanged(panel, "");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::UpdateDerivedLayouts( void )
+{
+ char rgchControlSettings[MAX_PATH];
+ if ( m_pCustomResFilename )
+ {
+ Q_snprintf( rgchControlSettings, sizeof( rgchControlSettings ), "%s", m_pCustomResFilename );
+ }
+ else
+ {
+ if ( m_pFilter->IsSelected() && !m_pQuickListCheckButton->IsSelected() )
+ {
+ // drop down
+ V_strncpy( rgchControlSettings, "servers/InternetGamesPage_Filters.res", sizeof( rgchControlSettings ) );
+ }
+ else
+ {
+ // hide filter area
+ V_strncpy( rgchControlSettings, "servers/InternetGamesPage.res", sizeof( rgchControlSettings ) );
+ }
+ }
+
+ const char *pPathID = "PLATFORM";
+
+ if ( g_pFullFileSystem->FileExists( rgchControlSettings, "MOD" ) )
+ {
+ pPathID = "MOD";
+ }
+
+ LoadControlSettings( rgchControlSettings, pPathID );
+
+ if ( !GameSupportsReplay() )
+ {
+ HideReplayFilter();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the game dir combo box is changed
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnTextChanged(Panel *panel, const char *text)
+{
+ if (!Q_stricmp(text, m_szComboAllText))
+ {
+ ComboBox *box = dynamic_cast<ComboBox *>(panel);
+ if (box)
+ {
+ box->SetText("");
+ text = "";
+ }
+ }
+
+ // get filter settings from controls
+ UpdateFilterSettings();
+
+ // apply settings
+ ApplyGameFilters();
+
+ if ( m_bFiltersVisible && ( panel == m_pGameFilter || panel == m_pLocationFilter ) && ServerBrowserDialog().IsVisible() )
+ {
+ // if they changed games and/or region then cancel the refresh because the old list they are getting
+ // will be for the wrong game, so stop and start a refresh
+ StopRefresh();
+ GetNewServerList();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: applies only the game filter to the current list
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::ApplyGameFilters()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ m_iServersBlacklisted = 0;
+
+ // loop through all the servers checking filters
+ FOR_EACH_MAP_FAST( m_mapServers, i )
+ {
+ serverdisplay_t &server = m_mapServers[ i ];
+ gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()->GetServerDetails( m_hRequest, server.m_iServerID );
+ if ( !pServer )
+ continue;
+
+ if (!CheckPrimaryFilters( *pServer ) || !CheckSecondaryFilters( *pServer ))
+ {
+ // server failed filtering, remove it
+ server.m_bDoNotRefresh = true;
+ if ( m_pGameList->IsValidItemID( server.m_iListID) )
+ {
+ // don't remove the server from list, just hide since this is a lot faster
+ m_pGameList->SetItemVisible( server.m_iListID, false );
+ }
+ }
+ else if ( BShowServer( server ) )
+ {
+ // server passed filters, so it can be refreshed again
+ server.m_bDoNotRefresh = false;
+
+ // re-add item to list
+ if ( !m_pGameList->IsValidItemID( server.m_iListID ) )
+ {
+ KeyValues *kv = new KeyValues("Server");
+ kv->SetString("name", pServer->GetName());
+ kv->SetString("map", pServer->m_szMap);
+ kv->SetString("GameDir", pServer->m_szGameDir);
+ kv->SetString( "GameTags", pServer->m_szGameTags );
+ if ( pServer->m_szGameDescription[0] )
+ {
+ kv->SetString("GameDesc", pServer->m_szGameDescription );
+ }
+ else
+ {
+ kv->SetWString("GameDesc", g_pVGuiLocalize->Find("#ServerBrowser_PendingPing"));
+ }
+
+ int nAdjustedForBotsPlayers = max( 0, pServer->m_nPlayers - pServer->m_nBotPlayers );
+
+ char buf[256];
+ Q_snprintf(buf, sizeof(buf), "%d / %d", nAdjustedForBotsPlayers, pServer->m_nMaxPlayers );
+ kv->SetString( "Players", buf);
+ kv->SetInt( "Ping", pServer->m_nPing );
+ kv->SetInt( "password", pServer->m_bPassword ? m_nImageIndexPassword : 0);
+ if ( pServer->m_nBotPlayers > 0 )
+ kv->SetInt("bots", pServer->m_nBotPlayers);
+ else
+ kv->SetString("bots", "");
+
+ kv->SetInt("Replay", IsReplayServer( *pServer ) ? m_nImageIndexReplay : 0);
+
+ server.m_iListID = m_pGameList->AddItem(kv, server.m_iServerID, false, false);
+ kv->deleteThis();
+ }
+
+ // make sure the server is visible
+ m_pGameList->SetItemVisible( server.m_iListID, true );
+ }
+ }
+
+ UpdateStatus();
+ m_pGameList->SortList();
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resets UI server count
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::UpdateStatus()
+{
+ if (m_pGameList->GetItemCount() > 1)
+ {
+ wchar_t header[256];
+ wchar_t count[128];
+ wchar_t blacklistcount[128];
+
+ _snwprintf( count, Q_ARRAYSIZE(count), L"%d", m_pGameList->GetItemCount() );
+ _snwprintf( blacklistcount, Q_ARRAYSIZE(blacklistcount), L"%d", m_iServersBlacklisted );
+ g_pVGuiLocalize->ConstructString( header, sizeof( header ), g_pVGuiLocalize->Find( "#ServerBrowser_ServersCountWithBlacklist"), 2, count, blacklistcount );
+ m_pGameList->SetColumnHeaderText( k_nColumn_Name, header);
+ }
+ else
+ {
+ m_pGameList->SetColumnHeaderText( k_nColumn_Name, g_pVGuiLocalize->Find("#ServerBrowser_Servers"));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets filter settings from controls
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::UpdateFilterSettings()
+{
+ // game
+ if ( ServerBrowserDialog().GetActiveModName() )
+ {
+ // overriding the game filter
+ Q_strncpy(m_szGameFilter, ServerBrowserDialog().GetActiveModName(), sizeof(m_szGameFilter));
+ m_iLimitToAppID = ServerBrowserDialog().GetActiveAppID();
+
+
+ #ifdef STAGING_ONLY
+ if ( sb_fake_app_id.GetInt() != 0 )
+ m_iLimitToAppID = CGameID( sb_fake_app_id.GetInt() );
+ #endif
+
+
+ RecalculateFilterString();
+ UpdateGameFilter();
+ }
+ else
+ {
+ KeyValues *data = m_pGameFilter->GetActiveItemUserData();
+ if (data && Q_strlen( data->GetString( "gamedir" ) ) > 0 )
+ {
+ Q_strncpy( m_szGameFilter, data->GetString( "gamedir" ), sizeof( m_szGameFilter ) );
+ if ( Q_strlen( m_szGameFilter ) > 0 ) // if there is a gamedir
+ {
+ m_iLimitToAppID = CGameID( data->GetUint64( "appid", 0 ) );
+ }
+ else
+ {
+ m_iLimitToAppID.Reset();
+ }
+ }
+ else
+ {
+ m_iLimitToAppID.Reset();
+ m_szGameFilter[0] = 0;
+ }
+ m_pGameFilter->SetEnabled(true);
+ }
+ Q_strlower(m_szGameFilter);
+
+ // map
+ m_pMapFilter->GetText(m_szMapFilter, sizeof(m_szMapFilter) - 1);
+ Q_strlower(m_szMapFilter);
+
+ // max player
+ char buf[256];
+ m_pMaxPlayerFilter->GetText(buf, sizeof(buf));
+ if (buf[0])
+ {
+ m_iMaxPlayerFilter = atoi(buf);
+ }
+ else
+ {
+ m_iMaxPlayerFilter = 0;
+ }
+
+ // ping
+ m_pPingFilter->GetText(buf, sizeof(buf));
+ if (buf[0])
+ {
+ m_iPingFilter = atoi(buf + 2);
+ }
+ else
+ {
+ m_iPingFilter = 0;
+ }
+
+ // players
+ m_bFilterNoFullServers = m_pNoFullServersFilterCheck->IsSelected();
+ m_bFilterNoEmptyServers = m_pNoEmptyServersFilterCheck->IsSelected();
+ m_bFilterNoPasswordedServers = m_pNoPasswordFilterCheck->IsSelected();
+ m_iSecureFilter = m_pSecureFilter->GetActiveItem();
+
+ if ( GameSupportsReplay() )
+ {
+ m_bFilterReplayServers = m_pReplayFilterCheck->IsSelected();
+ }
+ else
+ {
+ m_bFilterReplayServers = false;
+ }
+
+ m_vecServerFilters.RemoveAll();
+
+ bool bFilterNoEmpty = m_bFilterNoEmptyServers;
+ bool bFilterNoFull = m_bFilterNoFullServers;
+ bool bFilterNoPassword = m_bFilterNoPasswordedServers;
+ int iFilterSecure = m_iSecureFilter;
+
+ if ( m_pQuickList->IsVisible() == true )
+ {
+ bFilterNoEmpty = true;
+ bFilterNoFull = true;
+ bFilterNoPassword = true;
+ iFilterSecure = FILTER_SECURESERVERSONLY;
+ }
+
+ extern IRunGameEngine *g_pRunGameEngine;
+ if ( sb_filter_incompatible_versions.GetBool() && g_pRunGameEngine != NULL )
+ {
+ const char *pszVersion = g_pRunGameEngine->GetProductVersionString();
+ const char k_VersionFromP4[] = "2000"; // magic version string we use when we're running from P4
+ if ( pszVersion && *pszVersion && ( V_strcmp( pszVersion, k_VersionFromP4 ) != 0 ) )
+ {
+ m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "version_match", pszVersion ) );
+ }
+ }
+
+ // update master filter string text
+ if (m_szGameFilter[0] && m_iLimitToAppID.AppID() != 1002 ) // HACKHACK: Alfred - don't use a dir filter for RDKF
+ {
+ m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "gamedir", m_szGameFilter ) );
+ }
+ if (bFilterNoEmpty)
+ {
+ m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "empty", "1" ) );
+ }
+ if (bFilterNoFull)
+ {
+ m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "full", "1" ) );
+ }
+ if (iFilterSecure == FILTER_SECURESERVERSONLY)
+ {
+ m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "secure", "1" ) );
+ }
+ int regCode = GetRegionCodeToFilter();
+ if ( ( regCode >= 0 ) && ( regCode < 255 ) )
+ {
+ char szRegCode[ 32 ];
+ Q_snprintf( szRegCode, sizeof(szRegCode), "%i", regCode );
+ m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "region", szRegCode ) );
+ }
+
+ // copy filter settings into filter file
+ KeyValues *filter = ServerBrowserDialog().GetFilterSaveData(GetName());
+
+ // only save the game filter if we're not overriding it
+ if (!ServerBrowserDialog().GetActiveModName())
+ {
+ filter->SetString("game", m_szGameFilter);
+ filter->SetUint64( "appid", m_iLimitToAppID.ToUint64() );
+ }
+
+ filter->SetString("map", m_szMapFilter);
+ filter->SetInt("MaxPlayerCount", m_iMaxPlayerFilter);
+ filter->SetInt("ping", m_iPingFilter);
+
+ if ( m_pLocationFilter->GetItemCount() > 1 )
+ {
+ // only save this if there are options to choose from
+ filter->SetInt("location", m_pLocationFilter->GetActiveItem());
+ }
+
+ filter->SetInt("NoFull", m_bFilterNoFullServers);
+ filter->SetInt("NoEmpty", m_bFilterNoEmptyServers);
+ filter->SetInt("NoPassword", m_bFilterNoPasswordedServers);
+ filter->SetInt("Secure", m_iSecureFilter);
+ filter->SetInt("QuickList", m_pQuickListCheckButton->IsSelected() );
+ filter->SetInt("tagsinclude", m_pTagsIncludeFilter->GetActiveItem() );
+ if ( m_pWorkshopFilter )
+ {
+ filter->SetInt("workshopfilter", m_pWorkshopFilter->GetActiveItem() );
+ }
+ filter->SetInt("Replay", m_bFilterReplayServers);
+
+ OnSaveFilter(filter);
+
+ RecalculateFilterString();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: allow derived classes access to the saved filter string
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnSaveFilter(KeyValues *filter)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: allow derived classes access to the saved filter string
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnLoadFilter(KeyValues *filter)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: reconstructs the filter description string from the current filter settings
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::RecalculateFilterString()
+{
+ wchar_t unicode[2048], tempUnicode[128], spacerUnicode[8];
+ unicode[0] = 0;
+ int iTempUnicodeSize = sizeof( tempUnicode );
+
+ Q_UTF8ToUnicode( "; ", spacerUnicode, sizeof( spacerUnicode ) );
+
+ if (m_szGameFilter[0])
+ {
+ Q_UTF8ToUnicode( ModList().GetModNameForModDir( m_iLimitToAppID ), tempUnicode, iTempUnicodeSize );
+ wcscat( unicode, tempUnicode );
+ wcscat( unicode, spacerUnicode );
+ }
+
+ if (m_iSecureFilter == FILTER_SECURESERVERSONLY)
+ {
+ wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescSecureOnly" ) );
+ wcscat( unicode, spacerUnicode );
+ }
+ else if (m_iSecureFilter == FILTER_INSECURESERVERSONLY)
+ {
+ wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescInsecureOnly" ) );
+ wcscat( unicode, spacerUnicode );
+ }
+
+ if (m_pLocationFilter->GetActiveItem() > 0)
+ {
+ m_pLocationFilter->GetText(tempUnicode, sizeof(tempUnicode));
+ wcscat( unicode, tempUnicode );
+ wcscat( unicode, spacerUnicode );
+ }
+
+ if (m_iPingFilter)
+ {
+ char tmpBuf[16];
+ _snprintf( tmpBuf, sizeof(tmpBuf), "%d", m_iPingFilter );
+
+ wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescLatency" ) );
+ Q_UTF8ToUnicode( " < ", tempUnicode, iTempUnicodeSize );
+ wcscat( unicode, tempUnicode );
+ Q_UTF8ToUnicode(tmpBuf, tempUnicode, iTempUnicodeSize );
+ wcscat( unicode, tempUnicode );
+ wcscat( unicode, spacerUnicode );
+ }
+
+ if ( m_iMaxPlayerFilter )
+ {
+ char tmpBuf[16];
+ _snprintf( tmpBuf, sizeof(tmpBuf), "%d", m_iMaxPlayerFilter );
+
+ wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescMaxPlayers" ) );
+ Q_UTF8ToUnicode( " <= ", tempUnicode, iTempUnicodeSize );
+ wcscat( unicode, tempUnicode );
+ Q_UTF8ToUnicode(tmpBuf, tempUnicode, iTempUnicodeSize );
+ wcscat( unicode, tempUnicode );
+ wcscat( unicode, spacerUnicode );
+ }
+
+ if (m_bFilterNoFullServers)
+ {
+ wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescNotFull" ) );
+ wcscat( unicode, spacerUnicode );
+ }
+
+ if (m_bFilterNoEmptyServers)
+ {
+ wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescNotEmpty" ) );
+ wcscat( unicode, spacerUnicode );
+ }
+
+ if (m_bFilterNoPasswordedServers)
+ {
+ wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescNoPassword" ) );
+ wcscat( unicode, spacerUnicode );
+ }
+
+ if (m_bFilterReplayServers)
+ {
+ wcscat( unicode, g_pVGuiLocalize->Find( "#ServerBrowser_FilterDescReplays" ) );
+ wcscat( unicode, spacerUnicode );
+ }
+
+ if (m_szMapFilter[0])
+ {
+ Q_UTF8ToUnicode( m_szMapFilter, tempUnicode, iTempUnicodeSize );
+ wcscat( unicode, tempUnicode );
+ }
+
+ m_pFilterString->SetText(unicode);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks to see if the server passes the primary filters
+// if the server fails the filters, it will not be refreshed again
+//-----------------------------------------------------------------------------
+bool CBaseGamesPage::CheckPrimaryFilters( gameserveritem_t &server )
+{
+ if (m_szGameFilter[0] && ( server.m_szGameDir[0] || server.m_nPing ) && Q_stricmp(m_szGameFilter, server.m_szGameDir ) )
+ {
+ return false;
+ }
+
+ // If it's blacklisted, we ignore it too
+ if ( ServerBrowserDialog().IsServerBlacklisted( server ) )
+ {
+ m_iServersBlacklisted++;
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks to see if a server passes the secondary set of filters
+// server will be continued to be pinged if it fails the filter, since
+// the relvent server data is dynamic
+//-----------------------------------------------------------------------------
+bool CBaseGamesPage::CheckSecondaryFilters( gameserveritem_t &server )
+{
+ bool bFilterNoEmpty = m_bFilterNoEmptyServers;
+ bool bFilterNoFull = m_bFilterNoFullServers;
+ int iFilterPing = m_iPingFilter;
+ int iFilterMaxPlayerCount = m_iMaxPlayerFilter;
+ bool bFilterNoPassword = m_bFilterNoPasswordedServers;
+ int iFilterSecure = m_iSecureFilter;
+
+ if ( m_pQuickList->IsVisible() == true )
+ {
+ bFilterNoEmpty = true;
+ bFilterNoFull = true;
+ iFilterPing = QUICKLIST_FILTER_MIN_PING;
+ bFilterNoPassword = true;
+ iFilterSecure = FILTER_SECURESERVERSONLY;
+ iFilterMaxPlayerCount = sb_mod_suggested_maxplayers.GetInt();
+ }
+
+ if ( bFilterNoEmpty && (server.m_nPlayers - server.m_nBotPlayers) < 1 )
+ {
+ return false;
+ }
+
+ if ( bFilterNoFull && server.m_nPlayers >= server.m_nMaxPlayers )
+ {
+ return false;
+ }
+
+ if ( iFilterPing && server.m_nPing > iFilterPing )
+ {
+ return false;
+ }
+
+ if ( iFilterMaxPlayerCount && server.m_nMaxPlayers > iFilterMaxPlayerCount )
+ {
+ return false;
+ }
+
+ if ( bFilterNoPassword && server.m_bPassword )
+ {
+ return false;
+ }
+
+ if ( iFilterSecure == FILTER_SECURESERVERSONLY && !server.m_bSecure )
+ {
+ return false;
+ }
+
+ if ( iFilterSecure == FILTER_INSECURESERVERSONLY && server.m_bSecure )
+ {
+ return false;
+ }
+
+ if ( m_bFilterReplayServers && !IsReplayServer( server ) )
+ {
+ return false;
+ }
+
+ if ( m_pQuickList->IsVisible() == false )
+ {
+ // compare the first few characters of the filter name
+ int count = Q_strlen( m_szMapFilter );
+ if ( count && Q_strnicmp( server.m_szMap, m_szMapFilter, count ) )
+ {
+ return false;
+ }
+ }
+
+ return CheckTagFilter( server ) && CheckWorkshopFilter( server );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+uint32 CBaseGamesPage::GetServerFilters( MatchMakingKeyValuePair_t **pFilters )
+{
+ *pFilters = m_vecServerFilters.Base();
+ return m_vecServerFilters.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: call to let the UI now whether the game list is currently refreshing
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::SetRefreshing(bool state)
+{
+ if (state)
+ {
+ ServerBrowserDialog().UpdateStatusText("#ServerBrowser_RefreshingServerList");
+
+ // clear message in panel
+ m_pGameList->SetEmptyListText("");
+ m_pRefreshAll->SetText("#ServerBrowser_StopRefreshingList");
+ m_pRefreshAll->SetCommand("stoprefresh");
+ m_pRefreshQuick->SetEnabled(false);
+ }
+ else
+ {
+ ServerBrowserDialog().UpdateStatusText("");
+ if (SupportsItem(IGameList::GETNEWLIST))
+ {
+ m_pRefreshAll->SetText("#ServerBrowser_RefreshAll");
+ }
+ else
+ {
+ m_pRefreshAll->SetText("#ServerBrowser_Refresh");
+ }
+ m_pRefreshAll->SetCommand("GetNewList");
+
+ // 'refresh quick' button is only enabled if there are servers in the list
+ if (m_pGameList->GetItemCount() > 0)
+ {
+ m_pRefreshQuick->SetEnabled(true);
+ }
+ else
+ {
+ m_pRefreshQuick->SetEnabled(false);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnCommand(const char *command)
+{
+ if (!Q_stricmp(command, "Connect"))
+ {
+ OnBeginConnect();
+ }
+ else if (!Q_stricmp(command, "stoprefresh"))
+ {
+ // cancel the existing refresh
+ StopRefresh();
+ }
+ else if ( !Q_stricmp(command, "refresh") )
+ {
+ if ( steamapicontext->SteamMatchmakingServers() )
+ steamapicontext->SteamMatchmakingServers()->RefreshQuery( m_hRequest );
+ SetRefreshing( true );
+ m_iServerRefreshCount = 0;
+ ClearQuickList();
+ }
+ else if (!Q_stricmp(command, "GetNewList"))
+ {
+ GetNewServerList();
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when a row gets selected in the list
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnItemSelected()
+{
+ if ( GetSelectedServerID() == -1 )
+ {
+ m_pConnect->SetEnabled(false);
+ }
+ else
+ {
+ m_pConnect->SetEnabled(true);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes server list on F5
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnKeyCodePressed(vgui::KeyCode code)
+{
+ if ( code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A )
+ {
+ m_pConnect->DoClick();
+ }
+ else if ( code == KEY_F5 || code == KEY_XBUTTON_X || code == STEAMCONTROLLER_X )
+ {
+ StartRefresh();
+ }
+ else if ( m_pGameList->GetItemCount() > 0 &&
+ ( code == KEY_XBUTTON_UP || code == KEY_XSTICK1_UP || code == KEY_XSTICK2_UP || code == STEAMCONTROLLER_DPAD_UP ||
+ code == KEY_XBUTTON_DOWN || code == KEY_XSTICK1_DOWN || code == KEY_XSTICK2_DOWN || code == STEAMCONTROLLER_DPAD_DOWN ) )
+ {
+ m_pGameList->RequestFocus();
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed(code);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle enter pressed in the games list page. Return true
+// to intercept the message instead of passing it on through vgui.
+//-----------------------------------------------------------------------------
+bool CBaseGamesPage::OnGameListEnterPressed()
+{
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the # items selected in the game list.
+//-----------------------------------------------------------------------------
+int CBaseGamesPage::GetSelectedItemsCount()
+{
+ return m_pGameList->GetSelectedItemsCount();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a server to the favorites
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnAddToFavorites()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ // loop through all the selected favorites
+ for (int i = 0; i < m_pGameList->GetSelectedItemsCount(); i++)
+ {
+ int serverID = m_pGameList->GetItemUserData(m_pGameList->GetSelectedItem(i));
+
+ gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()->GetServerDetails( m_hRequest, serverID );
+ if ( pServer )
+ {
+ // add to favorites list
+ ServerBrowserDialog().AddServerToFavorites(*pServer);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a server to the blacklist
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnAddToBlacklist()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ // loop through all the selected favorites
+ for (int i = 0; i < m_pGameList->GetSelectedItemsCount(); i++)
+ {
+ int serverID = m_pGameList->GetItemUserData(m_pGameList->GetSelectedItem(i));
+
+ gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()->GetServerDetails( m_hRequest, serverID );
+ if ( pServer )
+ {
+ ServerBrowserDialog().AddServerToBlacklist(*pServer);
+ }
+ }
+ ServerBrowserDialog().BlacklistsChanged();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::ServerFailedToRespond( HServerListRequest hReq, int iServer )
+{
+ ServerResponded( hReq, iServer );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: removes the server from the UI list
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::RemoveServer( serverdisplay_t &server )
+{
+ if ( m_pGameList->IsValidItemID( server.m_iListID ) )
+ {
+ // don't remove the server from list, just hide since this is a lot faster
+ m_pGameList->SetItemVisible( server.m_iListID, false );
+
+ // find the row in the list and kill
+ // m_pGameList->RemoveItem(server.listEntryID);
+ // server.listEntryID = GetInvalidServerListID();
+ }
+
+ UpdateStatus();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes a single server
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnRefreshServer( int serverID )
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ // walk the list of selected servers refreshing them
+ for (int i = 0; i < m_pGameList->GetSelectedItemsCount(); i++)
+ {
+ int serverID = m_pGameList->GetItemUserData(m_pGameList->GetSelectedItem(i));
+
+ // refresh this server
+ steamapicontext->SteamMatchmakingServers()->RefreshServer( m_hRequest, serverID );
+ }
+
+ SetRefreshing(IsRefreshing());
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: starts the servers refreshing
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::StartRefresh()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ ClearServerList();
+ MatchMakingKeyValuePair_t *pFilters;
+ int nFilters = GetServerFilters( &pFilters );
+
+ if ( m_hRequest )
+ {
+ steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
+ m_hRequest = NULL;
+ }
+ switch ( m_eMatchMakingType )
+ {
+ case eFavoritesServer:
+ m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestFavoritesServerList( GetFilterAppID().AppID(), &pFilters, nFilters, this );
+ break;
+ case eHistoryServer:
+ m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestHistoryServerList( GetFilterAppID().AppID(), &pFilters, nFilters, this );
+ break;
+ case eInternetServer:
+ m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestInternetServerList( GetFilterAppID().AppID(), &pFilters, nFilters, this );
+ break;
+ case eSpectatorServer:
+ m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestSpectatorServerList( GetFilterAppID().AppID(), &pFilters, nFilters, this );
+ break;
+ case eFriendsServer:
+ m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestFriendsServerList( GetFilterAppID().AppID(), &pFilters, nFilters, this );
+ break;
+ case eLANServer:
+ m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestLANServerList( GetFilterAppID().AppID(), this );
+ break;
+ default:
+ Assert( !"Unknown server type" );
+ break;
+ }
+
+ SetRefreshing( true );
+
+ m_iServerRefreshCount = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::ClearQuickList( void )
+{
+ m_pQuickList->DeleteAllItems();
+ m_vecMapNamesFound.RemoveAll();
+
+ int iIndex = m_quicklistserverlist.First();
+
+ while ( iIndex != m_quicklistserverlist.InvalidIndex() )
+ {
+ CQuickListMapServerList *vecMapServers = &m_quicklistserverlist[iIndex];
+
+ vecMapServers->RemoveAll();
+
+ iIndex = m_quicklistserverlist.Next( iIndex );
+ }
+
+ m_quicklistserverlist.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove all the servers we currently have
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::ClearServerList()
+{
+ m_mapServers.RemoveAll();
+ m_mapServerIP.RemoveAll();
+ m_pGameList->RemoveAll();
+ m_iServersBlacklisted = 0;
+
+ ClearQuickList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: get a new list of servers from the backend
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::GetNewServerList()
+{
+ StartRefresh();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: stops current refresh/GetNewServerList()
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::StopRefresh()
+{
+ // clear update states
+ m_iServerRefreshCount = 0;
+
+ // Stop the server list refreshing
+ if ( steamapicontext->SteamMatchmakingServers() )
+ steamapicontext->SteamMatchmakingServers()->CancelQuery( m_hRequest );
+
+ // update UI
+ RefreshComplete( m_hRequest, eServerResponded );
+
+ // apply settings
+ ApplyGameFilters();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::RefreshComplete( HServerListRequest hRequest, EMatchMakingServerResponse response )
+{
+ SelectQuickListServers();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the list is currently refreshing servers
+//-----------------------------------------------------------------------------
+bool CBaseGamesPage::IsRefreshing()
+{
+ return steamapicontext->SteamMatchmakingServers() && steamapicontext->SteamMatchmakingServers()->IsRefreshing( m_hRequest );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activates the page, starts refresh
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnPageShow()
+{
+ StartRefresh();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called on page hide, stops any refresh
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnPageHide()
+{
+ StopRefresh();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+vgui::Panel *CBaseGamesPage::GetActiveList( void )
+{
+ if ( m_pQuickList->IsVisible() )
+ return m_pQuickList;
+
+ return m_pGameList;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseGamesPage::GetSelectedServerID( KeyValues **pKV )
+{
+ int serverID = -1;
+ if ( pKV )
+ {
+ *pKV = NULL;
+ }
+
+ if ( m_pQuickList->IsVisible() == true )
+ {
+ if ( IsRefreshing() == true )
+ return -1;
+
+ if ( m_pQuickList->GetSelectedPanel() )
+ {
+ CQuickListPanel *pQuickPanel = dynamic_cast<CQuickListPanel*>( m_pQuickList->GetSelectedPanel() );
+
+ if ( pQuickPanel )
+ {
+ serverID = m_pGameList->GetItemUserData( pQuickPanel->GetListID() );
+ if ( pKV )
+ {
+ *pKV = m_pGameList->GetItem( pQuickPanel->GetListID() );
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!m_pGameList->GetSelectedItemsCount())
+ return -1;
+
+ // get the server
+ serverID = m_pGameList->GetItemUserData( m_pGameList->GetSelectedItem(0) );
+
+ if ( pKV )
+ {
+ *pKV = m_pGameList->GetItem( m_pGameList->GetSelectedItem(0) );
+ }
+ }
+
+ return serverID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dialog which warns the user about the server they're joining
+//-----------------------------------------------------------------------------
+class CDialogServerWarning : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( CDialogServerWarning, vgui::Frame );
+public:
+ CDialogServerWarning(vgui::Panel *parent, IGameList *gameList, int serverID );
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual void OnCommand(const char *command);
+
+ MESSAGE_FUNC_PTR_INT( OnButtonToggled, "ButtonToggled", panel, state );
+
+private:
+ IGameList *m_pGameList;
+ int m_iServerID;
+ vgui::CheckButton *m_pDontShowThisAgainCheckButton;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// Input : *gameList - game list to add specified server to
+//-----------------------------------------------------------------------------
+CDialogServerWarning::CDialogServerWarning(vgui::Panel *parent, IGameList *gameList, int serverID ) : Frame(parent, "DialogServerWarning")
+{
+ m_pGameList = gameList;
+ m_iServerID = serverID;
+
+ m_pDontShowThisAgainCheckButton = new CheckButton(this, "DontShowThisAgainCheckbutton", "");
+
+ SetDeleteSelfOnClose(true);
+ SetSizeable( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDialogServerWarning::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings("Servers/DialogServerWarning.res");
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: button command handler
+//-----------------------------------------------------------------------------
+void CDialogServerWarning::OnCommand(const char *command)
+{
+ if ( Q_stricmp(command, "OK") == 0 )
+ {
+ // mark ourselves to be closed
+ PostMessage(this, new KeyValues("Close"));
+
+ // join the game
+ ServerBrowserDialog().JoinGame( m_pGameList, m_iServerID );
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles filter dropdown being toggled
+//-----------------------------------------------------------------------------
+void CDialogServerWarning::OnButtonToggled(Panel *panel, int state)
+{
+ ConVarRef sb_dontshow_maxplayer_warning( "sb_dontshow_maxplayer_warning", true );
+ if ( sb_dontshow_maxplayer_warning.IsValid() )
+ {
+ sb_dontshow_maxplayer_warning.SetValue( state );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: initiates server connection
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnBeginConnect()
+{
+ KeyValues *pKV = NULL;
+ int serverID = GetSelectedServerID( &pKV );
+
+ if ( serverID == -1 )
+ return;
+
+ // Stop the current refresh
+ StopRefresh();
+
+ ConVarRef sb_dontshow_maxplayer_warning( "sb_dontshow_maxplayer_warning", true );
+ if ( sb_dontshow_maxplayer_warning.IsValid() )
+ {
+ // If the server is above the suggested maxplayers, warn the player
+ int iMaxP = sb_mod_suggested_maxplayers.GetInt();
+ if ( iMaxP && pKV && !sb_dontshow_maxplayer_warning.GetBool() )
+ {
+ int iMaxCount = pKV->GetInt( "MaxPlayerCount", 0 );
+ if ( iMaxCount > iMaxP )
+ {
+ CDialogServerWarning *dlg = vgui::SETUP_PANEL( new CDialogServerWarning( this, this, serverID ) );
+ dlg->MoveToCenterOfScreen();
+ dlg->DoModal();
+
+ wchar_t wszWarning[512];
+ wchar_t wszServerMaxPlayers[12];
+ wchar_t wszDesignedMaxPlayers[12];
+ wchar_t wszGameName[256];
+ _snwprintf( wszServerMaxPlayers, Q_ARRAYSIZE(wszServerMaxPlayers), L"%d", iMaxCount );
+ _snwprintf( wszDesignedMaxPlayers, Q_ARRAYSIZE(wszDesignedMaxPlayers), L"%d", iMaxP );
+ Q_UTF8ToUnicode( ModList().GetModNameForModDir( m_iLimitToAppID ), wszGameName, Q_ARRAYSIZE(wszGameName) );
+ g_pVGuiLocalize->ConstructString( wszWarning, sizeof( wszWarning ), g_pVGuiLocalize->Find( "#ServerBrowser_ServerWarning_MaxPlayers"), 4, wszServerMaxPlayers, wszGameName, wszDesignedMaxPlayers, wszDesignedMaxPlayers );
+ dlg->SetDialogVariable( "warning", wszWarning );
+
+ return;
+ }
+ }
+ }
+
+ // join the game
+ ServerBrowserDialog().JoinGame(this, serverID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Displays the current game info without connecting
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnViewGameInfo()
+{
+ int serverID = GetSelectedServerID();
+
+ if ( serverID == -1 )
+ return;
+
+ // Stop the current refresh
+ StopRefresh();
+
+ // join the game
+ ServerBrowserDialog().OpenGameInfoDialog(this, serverID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return code to use for tracking how people are connecting to servers
+//-----------------------------------------------------------------------------
+const char *CBaseGamesPage::GetConnectCode()
+{
+ // Determine code to use, for the "connect" command.
+ //
+ // E.g.: "connect serverbrowser" (This command primarily exists so i can grep the code....)
+
+ const char *pszConnectCode = "serverbrowser";
+ switch ( m_eMatchMakingType )
+ {
+ default:
+ AssertMsg1( false, "Unknown matchmaking type %d", m_eMatchMakingType );
+ break;
+
+ case eInternetServer:
+ pszConnectCode = "serverbrowser_internet";
+ break;
+ case eLANServer:
+ pszConnectCode = "serverbrowser_lan";
+ break;
+ case eFriendsServer:
+ pszConnectCode = "serverbrowser_friends";
+ break;
+ case eFavoritesServer:
+ pszConnectCode = "serverbrowser_favorites";
+ break;
+ case eHistoryServer:
+ pszConnectCode = "serverbrowser_history";
+ break;
+ case eSpectatorServer:
+ pszConnectCode = "serverbrowser_spectator";
+ break;
+ };
+
+ return pszConnectCode;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Refresh if our favorites list changed
+//-----------------------------------------------------------------------------
+void CBaseGamesPage::OnFavoritesMsg( FavoritesListChanged_t *pFavListChanged )
+{
+ if ( !pFavListChanged->m_nIP ) // a zero for IP means the whole list was reloaded and we need to reload
+ {
+ switch ( m_eMatchMakingType )
+ {
+ case eInternetServer:
+ case eLANServer:
+ case eSpectatorServer:
+ case eFriendsServer:
+ return;
+ case eFavoritesServer:
+ case eHistoryServer:
+ // check containing property sheet to see if the page is visible.
+ // if not, don't bother initiating a server list grab right now -
+ // it will happen when the dialog is activated later.
+ if ( reinterpret_cast< PropertySheet* >( GetParent() )->GetActivePage() == this &&
+ GetParent()->IsVisible() && ServerBrowserDialog().IsVisible() )
+ {
+ GetNewServerList();
+ }
+ return;
+ default:
+ Assert( !"unknown matchmaking type" );
+ }
+ return;
+ }
+
+ switch ( m_eMatchMakingType )
+ {
+ case eInternetServer:
+ case eLANServer:
+ case eSpectatorServer:
+ case eFriendsServer:
+ break;
+ case eFavoritesServer:
+ case eHistoryServer:
+ {
+ int iIPServer = m_mapServerIP.Find( netadr_t( pFavListChanged->m_nIP, pFavListChanged->m_nConnPort ) );
+ if ( iIPServer == m_mapServerIP.InvalidIndex() )
+ {
+ if ( pFavListChanged->m_bAdd )
+ {
+ if ( steamapicontext->SteamMatchmakingServers() )
+ steamapicontext->SteamMatchmakingServers()->PingServer( pFavListChanged->m_nIP, pFavListChanged->m_nQueryPort, this );
+ }
+ // ignore deletes of fav's we didn't have
+ }
+ else
+ {
+ if ( pFavListChanged->m_bAdd )
+ {
+ if ( m_mapServerIP[ iIPServer ] > 0 )
+ ServerResponded( m_hRequest, m_mapServerIP[ iIPServer ] );
+ }
+ else
+ {
+ int iServer = m_mapServers.Find( m_mapServerIP[ iIPServer ] );
+ serverdisplay_t &server = m_mapServers[ iServer ];
+ RemoveServer( server );
+ }
+ }
+ }
+ break;
+ default:
+ Assert( !"unknown matchmaking type" );
+ };
+}
+
+void CCheckBoxWithStatus::OnCursorEntered()
+{
+ ServerBrowserDialog().UpdateStatusText("#ServerBrowser_QuickListExplanation");
+}
+
+void CCheckBoxWithStatus::OnCursorExited()
+{
+ ServerBrowserDialog().UpdateStatusText("");
+}