summaryrefslogtreecommitdiff
path: root/serverbrowser
diff options
context:
space:
mode:
Diffstat (limited to 'serverbrowser')
-rw-r--r--serverbrowser/BaseGamesPage.cpp2323
-rw-r--r--serverbrowser/BaseGamesPage.h325
-rw-r--r--serverbrowser/BlacklistedServers.cpp500
-rw-r--r--serverbrowser/BlacklistedServers.h68
-rw-r--r--serverbrowser/CustomGames.cpp448
-rw-r--r--serverbrowser/CustomGames.h70
-rw-r--r--serverbrowser/DialogAddServer.cpp392
-rw-r--r--serverbrowser/DialogAddServer.h75
-rw-r--r--serverbrowser/DialogGameInfo.cpp802
-rw-r--r--serverbrowser/DialogGameInfo.h126
-rw-r--r--serverbrowser/DialogServerPassword.cpp89
-rw-r--r--serverbrowser/DialogServerPassword.h46
-rw-r--r--serverbrowser/FavoriteGames.cpp214
-rw-r--r--serverbrowser/FavoriteGames.h55
-rw-r--r--serverbrowser/FriendsGames.cpp88
-rw-r--r--serverbrowser/FriendsGames.h39
-rw-r--r--serverbrowser/HistoryGames.cpp136
-rw-r--r--serverbrowser/HistoryGames.h46
-rw-r--r--serverbrowser/InternetGames.cpp319
-rw-r--r--serverbrowser/InternetGames.h83
-rw-r--r--serverbrowser/LanGames.cpp160
-rw-r--r--serverbrowser/LanGames.h81
-rw-r--r--serverbrowser/ModList.cpp125
-rw-r--r--serverbrowser/ModList.h62
-rw-r--r--serverbrowser/QuickListPanel.cpp317
-rw-r--r--serverbrowser/QuickListPanel.h83
-rw-r--r--serverbrowser/ServerBrowser.cpp489
-rw-r--r--serverbrowser/ServerBrowser.h82
-rw-r--r--serverbrowser/ServerBrowser.vpc106
-rw-r--r--serverbrowser/ServerBrowserDialog.cpp816
-rw-r--r--serverbrowser/ServerBrowserDialog.h159
-rw-r--r--serverbrowser/ServerContextMenu.cpp63
-rw-r--r--serverbrowser/ServerContextMenu.h34
-rw-r--r--serverbrowser/ServerListCompare.cpp289
-rw-r--r--serverbrowser/ServerListCompare.h32
-rw-r--r--serverbrowser/SpectateGames.cpp33
-rw-r--r--serverbrowser/SpectateGames.h38
-rw-r--r--serverbrowser/VACBannedConnRefusedDialog.cpp22
-rw-r--r--serverbrowser/VACBannedConnRefusedDialog.h29
-rw-r--r--serverbrowser/igamelist.h91
-rw-r--r--serverbrowser/pch_serverbrowser.cpp7
-rw-r--r--serverbrowser/pch_serverbrowser.h86
-rw-r--r--serverbrowser/resource.h15
-rw-r--r--serverbrowser/xbox/xbox.def3
44 files changed, 9466 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("");
+}
diff --git a/serverbrowser/BaseGamesPage.h b/serverbrowser/BaseGamesPage.h
new file mode 100644
index 0000000..2d69b1d
--- /dev/null
+++ b/serverbrowser/BaseGamesPage.h
@@ -0,0 +1,325 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef BASEGAMESPAGE_H
+#define BASEGAMESPAGE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tier1/utldict.h"
+
+class CBaseGamesPage;
+
+//-----------------------------------------------------------------------------
+// Purpose: Acts like a regular ListPanel but forwards enter key presses
+// to its outer control.
+//-----------------------------------------------------------------------------
+class CGameListPanel : public vgui::ListPanel
+{
+public:
+ DECLARE_CLASS_SIMPLE( CGameListPanel, vgui::ListPanel );
+
+ CGameListPanel( CBaseGamesPage *pOuter, const char *pName );
+
+ virtual void OnKeyCodePressed(vgui::KeyCode code);
+
+private:
+ CBaseGamesPage *m_pOuter;
+};
+
+class CQuickListMapServerList : public CUtlVector< int >
+{
+public:
+ CQuickListMapServerList() : CUtlVector< int >( 1, 0 )
+ {
+ }
+
+ CQuickListMapServerList( const CQuickListMapServerList& src )
+ {
+ CopyArray( src.Base(), src.Count() );
+ }
+
+ CQuickListMapServerList &operator=( const CQuickListMapServerList &src )
+ {
+ CopyArray( src.Base(), src.Count() );
+ return *this;
+ }
+};
+
+
+class CCheckBoxWithStatus : public vgui::CheckButton
+{
+public:
+ DECLARE_CLASS_SIMPLE( CCheckBoxWithStatus, vgui::CheckButton );
+
+ CCheckBoxWithStatus(Panel *parent, const char *panelName, const char *text) : vgui::CheckButton( parent, panelName, text )
+ {
+ }
+
+ virtual void OnCursorEntered();
+ virtual void OnCursorExited();
+};
+
+struct servermaps_t
+{
+ const char *pOriginalName;
+ const char *pFriendlyName;
+ int iPanelIndex;
+ bool bOnDisk;
+};
+
+struct gametypes_t
+{
+ const char *pPrefix;
+ const char *pGametypeName;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Base property page for all the games lists (internet/favorites/lan/etc.)
+//-----------------------------------------------------------------------------
+class CBaseGamesPage : public vgui::PropertyPage, public IGameList, public ISteamMatchmakingServerListResponse, public ISteamMatchmakingPingResponse
+{
+ DECLARE_CLASS_SIMPLE( CBaseGamesPage, vgui::PropertyPage );
+
+public:
+ enum EPageType
+ {
+ eInternetServer,
+ eLANServer,
+ eFriendsServer,
+ eFavoritesServer,
+ eHistoryServer,
+ eSpectatorServer
+ };
+
+ // Column indices
+ enum
+ {
+ k_nColumn_Password = 0,
+ k_nColumn_Secure = 1,
+ k_nColumn_Replay = 2,
+ k_nColumn_Name = 3,
+ k_nColumn_IPAddr = 4,
+ k_nColumn_GameDesc = 5,
+ k_nColumn_Players = 6,
+ k_nColumn_Bots = 7,
+ k_nColumn_Map = 8,
+ k_nColumn_Ping = 9,
+ };
+
+ CBaseGamesPage( vgui::Panel *parent, const char *name, EPageType eType, const char *pCustomResFilename=NULL);
+ ~CBaseGamesPage();
+
+ virtual void PerformLayout();
+ virtual void ApplySchemeSettings(vgui::IScheme *pScheme);
+
+ // gets information about specified server
+ virtual gameserveritem_t *GetServer(unsigned int serverID);
+ virtual const char *GetConnectCode();
+
+ uint32 GetServerFilters( MatchMakingKeyValuePair_t **pFilters );
+
+ virtual void SetRefreshing(bool state);
+
+ // loads filter settings from disk
+ virtual void LoadFilterSettings();
+
+ // Called by CGameList when the enter key is pressed.
+ // This is overridden in the add server dialog - since there is no Connect button, the message
+ // never gets handled, but we want to add a server when they dbl-click or press enter.
+ virtual bool OnGameListEnterPressed();
+
+ int GetSelectedItemsCount();
+
+ // adds a server to the favorites
+ MESSAGE_FUNC( OnAddToFavorites, "AddToFavorites" );
+ MESSAGE_FUNC( OnAddToBlacklist, "AddToBlacklist" );
+
+ virtual void StartRefresh();
+
+ virtual void UpdateDerivedLayouts( void );
+
+ void PrepareQuickListMap( const char *pMapName, int iListID );
+ void SelectQuickListServers( void );
+ vgui::Panel *GetActiveList( void );
+ virtual bool IsQuickListButtonChecked()
+ {
+ return m_pQuickListCheckButton ? m_pQuickListCheckButton->IsSelected() : false;
+ }
+
+ STEAM_CALLBACK( CBaseGamesPage, OnFavoritesMsg, FavoritesListChanged_t, m_CallbackFavoritesMsg );
+
+ // applies games filters to current list
+ void ApplyGameFilters();
+
+ void OnLoadingStarted()
+ {
+ StopRefresh();
+ }
+
+protected:
+ virtual void OnCommand(const char *command);
+ virtual void OnKeyCodePressed(vgui::KeyCode code);
+ virtual int GetRegionCodeToFilter() { return 255; }
+
+ MESSAGE_FUNC( OnItemSelected, "ItemSelected" );
+
+ // updates server count UI
+ void UpdateStatus();
+
+ // ISteamMatchmakingServerListResponse callbacks
+ virtual void ServerResponded( HServerListRequest hReq, int iServer );
+ virtual void ServerResponded( int iServer, gameserveritem_t *pServerItem );
+ virtual void ServerFailedToRespond( HServerListRequest hReq, int iServer );
+ virtual void RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response ) = 0;
+
+ // ISteamMatchmakingPingResponse callbacks
+ virtual void ServerResponded( gameserveritem_t &server );
+ virtual void ServerFailedToRespond() {}
+
+ // Removes server from list
+ void RemoveServer( serverdisplay_t &server );
+
+ virtual bool BShowServer( serverdisplay_t &server ) { return server.m_bDoNotRefresh; }
+ void ClearServerList();
+
+ // filtering methods
+ // returns true if filters passed; false if failed
+ virtual bool CheckPrimaryFilters( gameserveritem_t &server);
+ virtual bool CheckSecondaryFilters( gameserveritem_t &server );
+ virtual bool CheckTagFilter( gameserveritem_t &server ) { return true; }
+ virtual bool CheckWorkshopFilter( gameserveritem_t &server ) { return true; }
+ virtual int GetInvalidServerListID();
+
+ virtual void OnSaveFilter(KeyValues *filter);
+ virtual void OnLoadFilter(KeyValues *filter);
+ virtual void UpdateFilterSettings();
+
+ // whether filter settings limit which master server to query
+ CGameID &GetFilterAppID() { return m_iLimitToAppID; }
+
+ virtual void GetNewServerList();
+ virtual void StopRefresh();
+ virtual bool IsRefreshing();
+ virtual void OnPageShow();
+ virtual void OnPageHide();
+
+ // called when Connect button is pressed
+ MESSAGE_FUNC( OnBeginConnect, "ConnectToServer" );
+ // called to look at game info
+ MESSAGE_FUNC( OnViewGameInfo, "ViewGameInfo" );
+ // refreshes a single server
+ MESSAGE_FUNC_INT( OnRefreshServer, "RefreshServer", serverID );
+
+ // If true, then we automatically select the first item that comes into the games list.
+ bool m_bAutoSelectFirstItemInGameList;
+
+ CGameListPanel *m_pGameList;
+ vgui::PanelListPanel *m_pQuickList;
+
+ vgui::ComboBox *m_pLocationFilter;
+
+ // command buttons
+ vgui::Button *m_pConnect;
+ vgui::Button *m_pRefreshAll;
+ vgui::Button *m_pRefreshQuick;
+ vgui::Button *m_pAddServer;
+ vgui::Button *m_pAddCurrentServer;
+ vgui::Button *m_pAddToFavoritesButton;
+ vgui::ToggleButton *m_pFilter;
+
+ CUtlMap<uint64, int> m_mapGamesFilterItem;
+ CUtlMap<int, serverdisplay_t> m_mapServers;
+ CUtlMap<netadr_t, int> m_mapServerIP;
+ CUtlVector<MatchMakingKeyValuePair_t> m_vecServerFilters;
+ CUtlDict< CQuickListMapServerList, int > m_quicklistserverlist;
+ int m_iServerRefreshCount;
+ CUtlVector< servermaps_t > m_vecMapNamesFound;
+
+
+ EPageType m_eMatchMakingType;
+ HServerListRequest m_hRequest;
+
+ int GetSelectedServerID( KeyValues **pKV = NULL );
+
+ void ClearQuickList( void );
+
+ bool TagsExclude( void );
+
+ enum eWorkshopMode {
+ // These correspond to the dropdown indices
+ eWorkshop_None = 0,
+ eWorkshop_WorkshopOnly = 1,
+ eWorkshop_SubscribedOnly = 2
+ };
+ eWorkshopMode WorkshopMode();
+
+ void HideReplayFilter( void );
+
+protected:
+ virtual void CreateFilters();
+ virtual void UpdateGameFilter();
+
+ MESSAGE_FUNC_PTR_CHARPTR( OnTextChanged, "TextChanged", panel, text );
+ MESSAGE_FUNC_PTR_INT( OnButtonToggled, "ButtonToggled", panel, state );
+
+ void UpdateFilterAndQuickListVisibility();
+ bool BFiltersVisible() { return m_bFiltersVisible; }
+
+private:
+ void RequestServersResponse( int iServer, EMatchMakingServerResponse response, bool bLastServer ); // callback for matchmaking interface
+
+ void RecalculateFilterString();
+
+ void SetQuickListEnabled( bool bEnabled );
+ void SetFiltersVisible( bool bVisible );
+
+ // If set, it uses the specified resfile name instead of its default one.
+ const char *m_pCustomResFilename;
+
+ // filter controls
+ vgui::ComboBox *m_pGameFilter;
+ vgui::TextEntry *m_pMapFilter;
+ vgui::TextEntry *m_pMaxPlayerFilter;
+ vgui::ComboBox *m_pPingFilter;
+ vgui::ComboBox *m_pSecureFilter;
+ vgui::ComboBox *m_pTagsIncludeFilter;
+ vgui::ComboBox *m_pWorkshopFilter;
+ vgui::CheckButton *m_pNoFullServersFilterCheck;
+ vgui::CheckButton *m_pNoEmptyServersFilterCheck;
+ vgui::CheckButton *m_pNoPasswordFilterCheck;
+ CCheckBoxWithStatus *m_pQuickListCheckButton;
+ vgui::Label *m_pFilterString;
+ char m_szComboAllText[64];
+ vgui::CheckButton *m_pReplayFilterCheck;
+
+ KeyValues *m_pFilters; // base filter data
+ bool m_bFiltersVisible; // true if filter section is currently visible
+ vgui::HFont m_hFont;
+
+ int m_nImageIndexPassword;
+ int m_nImageIndexSecure;
+ int m_nImageIndexSecureVacBanned;
+ int m_nImageIndexReplay;
+
+ // filter data
+ char m_szGameFilter[32];
+ char m_szMapFilter[32];
+ int m_iMaxPlayerFilter;
+ int m_iPingFilter;
+ bool m_bFilterNoFullServers;
+ bool m_bFilterNoEmptyServers;
+ bool m_bFilterNoPasswordedServers;
+ int m_iSecureFilter;
+ int m_iServersBlacklisted;
+ bool m_bFilterReplayServers;
+
+ CGameID m_iLimitToAppID;
+};
+
+#endif // BASEGAMESPAGE_H
diff --git a/serverbrowser/BlacklistedServers.cpp b/serverbrowser/BlacklistedServers.cpp
new file mode 100644
index 0000000..c310f39
--- /dev/null
+++ b/serverbrowser/BlacklistedServers.cpp
@@ -0,0 +1,500 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+ConVar sb_showblacklists( "sb_showblacklists", "0", FCVAR_NONE, "If set to 1, blacklist rules will be printed to the console as they're applied." );
+
+//-----------------------------------------------------------------------------
+// Purpose: Server name comparison function
+//-----------------------------------------------------------------------------
+int __cdecl BlacklistedServerNameCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ blacklisted_server_t *pSvr1 = ServerBrowserDialog().GetBlacklistPage()->GetBlacklistedServer( p1.userData );
+ blacklisted_server_t *pSvr2 = ServerBrowserDialog().GetBlacklistPage()->GetBlacklistedServer( p2.userData );
+
+ if ( !pSvr1 && pSvr2 )
+ return -1;
+ if ( !pSvr2 && pSvr1 )
+ return 1;
+ if ( !pSvr1 && !pSvr2 )
+ return 0;
+
+ return Q_stricmp( pSvr1->m_szServerName, pSvr2->m_szServerName );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: list column sort function
+//-----------------------------------------------------------------------------
+int __cdecl BlacklistedIPAddressCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ blacklisted_server_t *pSvr1 = ServerBrowserDialog().GetBlacklistPage()->GetBlacklistedServer( p1.userData );
+ blacklisted_server_t *pSvr2 = ServerBrowserDialog().GetBlacklistPage()->GetBlacklistedServer( p2.userData );
+
+ if ( !pSvr1 && pSvr2 )
+ return -1;
+ if ( !pSvr2 && pSvr1 )
+ return 1;
+ if ( !pSvr1 && !pSvr2 )
+ return 0;
+
+ if ( pSvr1->m_NetAdr < pSvr2->m_NetAdr )
+ return -1;
+ else if ( pSvr2->m_NetAdr < pSvr1->m_NetAdr )
+ return 1;
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player number comparison function
+//-----------------------------------------------------------------------------
+int __cdecl BlacklistedAtCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ blacklisted_server_t *pSvr1 = ServerBrowserDialog().GetBlacklistPage()->GetBlacklistedServer( p1.userData );
+ blacklisted_server_t *pSvr2 = ServerBrowserDialog().GetBlacklistPage()->GetBlacklistedServer( p2.userData );
+
+ if ( !pSvr1 && pSvr2 )
+ return -1;
+ if ( !pSvr2 && pSvr1 )
+ return 1;
+ if ( !pSvr1 && !pSvr2 )
+ return 0;
+
+ if ( pSvr1->m_ulTimeBlacklistedAt > pSvr2->m_ulTimeBlacklistedAt )
+ return -1;
+ if ( pSvr1->m_ulTimeBlacklistedAt < pSvr2->m_ulTimeBlacklistedAt )
+ return 1;
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CBlacklistedServers::CBlacklistedServers( vgui::Panel *parent ) :
+ vgui::PropertyPage( parent, "BlacklistedGames" )
+{
+ SetSize( 624, 278 );
+
+ m_pAddServer = new Button(this, "AddServerButton", "#ServerBrowser_AddServer");
+ m_pAddCurrentServer = new Button(this, "AddCurrentServerButton", "#ServerBrowser_AddCurrentServer");
+ m_pGameList = vgui::SETUP_PANEL( new vgui::ListPanel(this, "gamelist") );
+ m_pGameList->SetAllowUserModificationOfColumns(true);
+
+ // Add the column headers
+ m_pGameList->AddColumnHeader(0, "Name", "#ServerBrowser_BlacklistedServers", 50, ListPanel::COLUMN_RESIZEWITHWINDOW | ListPanel::COLUMN_UNHIDABLE);
+ m_pGameList->AddColumnHeader(1, "IPAddr", "#ServerBrowser_IPAddress", 64, ListPanel::COLUMN_HIDDEN);
+ m_pGameList->AddColumnHeader(2, "BlacklistedAt", "#ServerBrowser_BlacklistedDate", 100);
+
+ //m_pGameList->SetColumnHeaderTooltip(0, "#ServerBrowser_PasswordColumn_Tooltip");
+
+ // setup fast sort functions
+ m_pGameList->SetSortFunc(0, BlacklistedServerNameCompare);
+ m_pGameList->SetSortFunc(1, BlacklistedIPAddressCompare);
+ m_pGameList->SetSortFunc(2, BlacklistedAtCompare);
+
+ // Sort by name by default
+ m_pGameList->SetSortColumn(0);
+
+ m_blackList.Reset();
+ m_blackListTimestamp = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CBlacklistedServers::~CBlacklistedServers()
+{
+ ClearServerList();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: loads the initial blacklist from disk
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::LoadBlacklistedList()
+{
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoBlacklistedServers");
+
+ ClearServerList();
+ m_blackList.Reset();
+
+ int count = m_blackList.LoadServersFromFile( BLACKLIST_DEFAULT_SAVE_FILE, false );
+
+ m_blackListTimestamp = g_pFullFileSystem->GetFileTime( BLACKLIST_DEFAULT_SAVE_FILE );
+
+ for( int i=0; i<count; ++i )
+ {
+ UpdateBlacklistUI( m_blackList.GetServer(i) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds all the servers inside the specified file to the blacklist
+//-----------------------------------------------------------------------------
+bool CBlacklistedServers::AddServersFromFile( const char *pszFilename, bool bResetTimes )
+{
+ KeyValues *pKV = new KeyValues( "serverblacklist" );
+ if ( !pKV->LoadFromFile( g_pFullFileSystem, pszFilename, "GAME" ) )
+ return false;
+
+ for ( KeyValues *pData = pKV->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() )
+ {
+ const char *pszName = pData->GetString( "name" );
+
+ uint32 ulDate = pData->GetInt( "date" );
+ if ( bResetTimes )
+ {
+ time_t today;
+ time( &today );
+ ulDate = today;
+ }
+
+ const char *pszNetAddr = pData->GetString( "addr" );
+
+ if ( pszNetAddr && pszNetAddr[0] && pszName && pszName[0] )
+ {
+ blacklisted_server_t *blackServer = m_blackList.AddServer( pszName, pszNetAddr, ulDate );
+
+ UpdateBlacklistUI( blackServer );
+ }
+ }
+
+ // write out blacklist to preserve changes
+ SaveBlacklistedList();
+
+ pKV->deleteThis();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: save blacklist to disk
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::SaveBlacklistedList()
+{
+ m_blackList.SaveToFile( BLACKLIST_DEFAULT_SAVE_FILE );
+ m_blackListTimestamp = g_pFullFileSystem->GetFileTime( BLACKLIST_DEFAULT_SAVE_FILE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::AddServer( gameserveritem_t &server )
+{
+ blacklisted_server_t *blackServer = m_blackList.AddServer( server );
+
+ if ( !blackServer )
+ {
+ return;
+ }
+
+ SaveBlacklistedList();
+
+ UpdateBlacklistUI( blackServer );
+
+ if ( GameSupportsReplay() )
+ {
+ // send command to propagate to the client so the client can send it on to the GC
+ char command[ 256 ];
+ Q_snprintf( command, Q_ARRAYSIZE( command ), "rbgc %s\n", blackServer->m_NetAdr.ToString() );
+ g_pRunGameEngine->AddTextCommand( command );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+blacklisted_server_t *CBlacklistedServers::GetBlacklistedServer( int iServerID )
+{
+ return m_blackList.GetServer( iServerID );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBlacklistedServers::IsServerBlacklisted( gameserveritem_t &server )
+{
+ // if something has changed the blacklist file, reload it
+ if ( g_pFullFileSystem->GetFileTime( BLACKLIST_DEFAULT_SAVE_FILE ) != m_blackListTimestamp )
+ {
+ LoadBlacklistedList();
+ }
+
+ return m_blackList.IsServerBlacklisted( server );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::UpdateBlacklistUI( blacklisted_server_t *blackServer )
+{
+ if ( !blackServer )
+ return;
+
+ KeyValues *kv;
+ int iItemId = m_pGameList->GetItemIDFromUserData( blackServer->m_nServerID );
+ if ( m_pGameList->IsValidItemID( iItemId ) )
+ {
+ // we're updating an existing entry
+ kv = m_pGameList->GetItem( iItemId );
+ m_pGameList->SetUserData( iItemId, blackServer->m_nServerID );
+ }
+ else
+ {
+ // new entry
+ kv = new KeyValues("Server");
+ }
+
+ kv->SetString( "name", blackServer->m_szServerName );
+
+ // construct a time string for blacklisted time
+ struct tm *now;
+ now = localtime( (time_t*)&blackServer->m_ulTimeBlacklistedAt );
+ if ( now )
+ {
+ char buf[64];
+ strftime(buf, sizeof(buf), "%a %d %b %I:%M%p", now);
+ Q_strlower(buf + strlen(buf) - 4);
+ kv->SetString("BlacklistedAt", buf);
+ }
+
+ kv->SetString( "IPAddr", blackServer->m_NetAdr.ToString() );
+
+ if ( !m_pGameList->IsValidItemID( iItemId ) )
+ {
+ // new server, add to list
+ iItemId = m_pGameList->AddItem(kv, blackServer->m_nServerID, false, false);
+ kv->deleteThis();
+ }
+ else
+ {
+ // tell the list that we've changed the data
+ m_pGameList->ApplyItemChanges( iItemId );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::ApplySchemeSettings(vgui::IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ const char *pPathID = "PLATFORM";
+ const char *pszFileName = "servers/BlacklistedServersPage.res";
+ if ( g_pFullFileSystem->FileExists( pszFileName, "MOD" ) )
+ {
+ pPathID = "MOD";
+ }
+ LoadControlSettings( pszFileName, pPathID );
+
+ vgui::HFont hFont = pScheme->GetFont( "ListSmall", IsProportional() );
+ if ( !hFont )
+ {
+ hFont = pScheme->GetFont( "DefaultSmall", IsProportional() );
+ }
+ m_pGameList->SetFont( hFont );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::OnPageShow( void )
+{
+ // reload list since client may have changed it
+ LoadBlacklistedList();
+
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoBlacklistedServers");
+ m_pGameList->SortList();
+
+ BaseClass::OnPageShow();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBlacklistedServers::GetSelectedServerID( void )
+{
+ int serverID = -1;
+ if ( m_pGameList->GetSelectedItemsCount() )
+ {
+ serverID = m_pGameList->GetItemUserData( m_pGameList->GetSelectedItem(0) );
+ }
+
+ return serverID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: opens context menu (user right clicked on a server)
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::OnOpenContextMenu(int itemID)
+{
+ CServerContextMenu *menu = ServerBrowserDialog().GetContextMenu( m_pGameList );
+
+ // get the server
+ int serverID = GetSelectedServerID();
+
+ menu->ShowMenu( this,(uint32)-1, false, false, false, false );
+ if ( serverID != -1 )
+ {
+ menu->AddMenuItem("RemoveServer", "#ServerBrowser_RemoveServerFromBlacklist", new KeyValues("RemoveFromBlacklist"), this);
+ }
+
+ menu->AddMenuItem("AddServerByName", "#ServerBrowser_AddServerByIP", new KeyValues("AddServerByName"), this);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a server by IP address
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::OnAddServerByName()
+{
+ // open the add server dialog
+ CDialogAddBlacklistedServer *dlg = new CDialogAddBlacklistedServer( &ServerBrowserDialog(), NULL );
+ dlg->MoveToCenterOfScreen();
+ dlg->DoModal();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes a server from the blacklist
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::OnRemoveFromBlacklist()
+{
+ // iterate the selection
+ for ( int iGame = (m_pGameList->GetSelectedItemsCount() - 1); iGame >= 0; iGame-- )
+ {
+ int itemID = m_pGameList->GetSelectedItem( iGame );
+ int serverID = m_pGameList->GetItemData( itemID )->userData;
+
+ m_pGameList->RemoveItem( itemID );
+ m_blackList.RemoveServer( serverID );
+ }
+
+ InvalidateLayout();
+ Repaint();
+
+ ServerBrowserDialog().BlacklistsChanged();
+
+ SaveBlacklistedList();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::ClearServerList( void )
+{
+ m_pGameList->RemoveAll();
+ m_blackList.Reset();
+ m_blackListTimestamp = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds the currently connected server to the list
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::OnAddCurrentServer()
+{
+ gameserveritem_t *pConnected = ServerBrowserDialog().GetCurrentConnectedServer();
+
+ if ( pConnected )
+ {
+ blacklisted_server_t *blackServer = m_blackList.AddServer( *pConnected );
+
+ if ( !blackServer )
+ {
+ return;
+ }
+
+ UpdateBlacklistUI( blackServer );
+
+ ServerBrowserDialog().BlacklistsChanged();
+
+ SaveBlacklistedList();
+
+ if ( GameSupportsReplay() )
+ {
+ // send command to propagate to the client so the client can send it on to the GC
+ char command[ 256 ];
+ Q_snprintf( command, Q_ARRAYSIZE( command ), "rbgc %s\n", pConnected->m_NetAdr.GetConnectionAddressString() );
+ g_pRunGameEngine->AddTextCommand( command );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::OnImportBlacklist()
+{
+ if ( m_hImportDialog.Get() )
+ {
+ m_hImportDialog.Get()->MarkForDeletion();
+ }
+
+ m_hImportDialog = new FileOpenDialog( this, "#ServerBrowser_ImportBlacklistTitle", true );
+ if ( m_hImportDialog.Get() )
+ {
+ m_hImportDialog->SetStartDirectory( "cfg/" );
+ m_hImportDialog->AddFilter( "*.txt", "#ServerBrowser_BlacklistFiles", true );
+ m_hImportDialog->DoModal( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::OnFileSelected( char const *fullpath )
+{
+ AddServersFromFile( fullpath, true );
+
+ if ( m_hImportDialog.Get() )
+ {
+ m_hImportDialog.Get()->MarkForDeletion();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse posted messages
+//
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::OnCommand(const char *command)
+{
+ if (!Q_stricmp(command, "AddServerByName"))
+ {
+ OnAddServerByName();
+ }
+ else if (!Q_stricmp(command, "AddCurrentServer" ))
+ {
+ OnAddCurrentServer();
+ }
+ else if (!Q_stricmp(command, "ImportBlacklist" ))
+ {
+ OnImportBlacklist();
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: enables adding server
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::OnConnectToGame()
+{
+ m_pAddCurrentServer->SetEnabled( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: disables adding current server
+//-----------------------------------------------------------------------------
+void CBlacklistedServers::OnDisconnectFromGame( void )
+{
+ m_pAddCurrentServer->SetEnabled( false );
+}
diff --git a/serverbrowser/BlacklistedServers.h b/serverbrowser/BlacklistedServers.h
new file mode 100644
index 0000000..ce8b9b5
--- /dev/null
+++ b/serverbrowser/BlacklistedServers.h
@@ -0,0 +1,68 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef BLACKLISTEDSERVERS_H
+#define BLACKLISTEDSERVERS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "ServerBrowser/blacklisted_server_manager.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Blacklisted servers list
+//-----------------------------------------------------------------------------
+class CBlacklistedServers : public vgui::PropertyPage
+{
+ DECLARE_CLASS_SIMPLE( CBlacklistedServers, vgui::PropertyPage );
+
+public:
+ CBlacklistedServers(vgui::Panel *parent);
+ ~CBlacklistedServers();
+
+ // blacklist list, loads/saves from file
+ void LoadBlacklistedList();
+ void SaveBlacklistedList();
+ void AddServer(gameserveritem_t &server);
+
+ virtual void ApplySchemeSettings(vgui::IScheme *pScheme);
+
+ // passed from main server browser window instead of messages
+ void OnConnectToGame();
+ void OnDisconnectFromGame( void );
+
+ blacklisted_server_t *GetBlacklistedServer( int iServerID );
+ bool IsServerBlacklisted(gameserveritem_t &server);
+
+private:
+ // context menu message handlers
+ MESSAGE_FUNC( OnPageShow, "PageShow" );
+ MESSAGE_FUNC_INT( OnOpenContextMenu, "OpenContextMenu", itemID );
+ MESSAGE_FUNC( OnAddServerByName, "AddServerByName" );
+ MESSAGE_FUNC( OnRemoveFromBlacklist, "RemoveFromBlacklist" );
+ MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath );
+
+ void ClearServerList( void );
+ void OnAddCurrentServer( void );
+ void OnImportBlacklist( void );
+ void OnCommand(const char *command);
+ void UpdateBlacklistUI( blacklisted_server_t *blackServer );
+ int GetSelectedServerID( void );
+ bool AddServersFromFile( const char *pszFilename, bool bResetTimes );
+
+private:
+ vgui::Button *m_pAddServer;
+ vgui::Button *m_pAddCurrentServer;
+ vgui::ListPanel *m_pGameList;
+ vgui::DHANDLE< vgui::FileOpenDialog > m_hImportDialog;
+
+ CBlacklistedServerManager m_blackList;
+ long m_blackListTimestamp;
+};
+
+
+#endif // BLACKLISTEDSERVERS_H
diff --git a/serverbrowser/CustomGames.cpp b/serverbrowser/CustomGames.cpp
new file mode 100644
index 0000000..26ebb55
--- /dev/null
+++ b/serverbrowser/CustomGames.cpp
@@ -0,0 +1,448 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+#include <vgui_controls/HTML.h>
+#include <vgui_controls/MessageDialog.h>
+
+using namespace vgui;
+
+#define NUM_COMMON_TAGS 20
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+TagMenuButton::TagMenuButton(Panel *parent, const char *panelName, const char *text) : BaseClass(parent,panelName,text)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TagMenuButton::OnShowMenu( vgui::Menu *menu )
+{
+ PostActionSignal(new KeyValues("TagMenuButtonOpened"));
+ BaseClass::OnShowMenu(menu);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CCustomServerInfoURLQuery : public vgui::QueryBox
+{
+ DECLARE_CLASS_SIMPLE( CCustomServerInfoURLQuery, vgui::QueryBox );
+public:
+ CCustomServerInfoURLQuery(const char *title, const char *queryText,vgui::Panel *parent) : BaseClass( title, queryText, parent )
+ {
+ SetOKButtonText( "#ServerBrowser_CustomServerURLButton" );
+ }
+};
+
+DECLARE_BUILD_FACTORY( TagInfoLabel );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+TagInfoLabel::TagInfoLabel(Panel *parent, const char *panelName) : BaseClass(parent,panelName, (const char *)NULL, NULL)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+TagInfoLabel::TagInfoLabel(Panel *parent, const char *panelName, const char *text, const char *pszURL) : BaseClass(parent,panelName,text,pszURL)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If we were left clicked on, launch the URL
+//-----------------------------------------------------------------------------
+void TagInfoLabel::OnMousePressed(MouseCode code)
+{
+ if (code == MOUSE_LEFT)
+ {
+ if ( GetURL() )
+ {
+ // Pop up the dialog with the url in it
+ CCustomServerInfoURLQuery *qb = new CCustomServerInfoURLQuery( "#ServerBrowser_CustomServerURLWarning", "#ServerBrowser_CustomServerURLOpen", this );
+ if (qb != NULL)
+ {
+ qb->SetOKCommand( new KeyValues("DoOpenCustomServerInfoURL") );
+ qb->AddActionSignalTarget(this);
+ qb->MoveToFront();
+ qb->DoModal();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TagInfoLabel::DoOpenCustomServerInfoURL( void )
+{
+ if ( GetURL() )
+ {
+ system()->ShellExecute("open", GetURL() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CCustomGames::CCustomGames(vgui::Panel *parent) :
+ BaseClass(parent, "CustomGames", eInternetServer )
+{
+ m_pGameList->AddColumnHeader(10, "Tags", "#ServerBrowser_Tags", 200);
+ m_pGameList->SetSortFunc(10, TagsCompare);
+
+ if ( !IsSteamGameServerBrowsingEnabled() )
+ {
+ m_pGameList->SetEmptyListText("#ServerBrowser_OfflineMode");
+ m_pConnect->SetEnabled( false );
+ m_pRefreshAll->SetEnabled( false );
+ m_pRefreshQuick->SetEnabled( false );
+ m_pAddServer->SetEnabled( false );
+ m_pFilter->SetEnabled( false );
+ }
+
+ m_szTagFilter[0] = 0;
+
+ m_pTagFilter = new TextEntry(this, "TagFilter");
+ m_pTagFilter->SetEnabled( false );
+ m_pTagFilter->SetMaximumCharCount( MAX_TAG_CHARACTERS );
+
+ m_pAddTagList = new TagMenuButton( this, "AddTagList", "#ServerBrowser_AddCommonTags" );
+ m_pTagListMenu = new Menu( m_pAddTagList, "TagList" );
+ m_pAddTagList->SetMenu( m_pTagListMenu );
+ m_pAddTagList->SetOpenDirection( Menu::UP );
+ m_pAddTagList->SetEnabled( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CCustomGames::~CCustomGames()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCustomGames::UpdateDerivedLayouts( void )
+{
+ const char *pPathID = "PLATFORM";
+
+ KeyValues *pConditions = NULL;
+ if ( ServerBrowser().IsWorkshopEnabled() )
+ {
+ pConditions = new KeyValues( "conditions" );
+ if ( pConditions )
+ {
+ KeyValues *pNewKey = new KeyValues( "if_workshop_enabled" );
+ if ( pNewKey )
+ {
+ pConditions->AddSubKey( pNewKey );
+ }
+ }
+ }
+
+ if ( m_pFilter->IsSelected() )
+ {
+ if ( g_pFullFileSystem->FileExists( "servers/CustomGamesPage_Filters.res", "MOD" ) )
+ {
+ pPathID = "MOD";
+ }
+
+ LoadControlSettings( "servers/CustomGamesPage_Filters.res", pPathID, NULL, pConditions );
+ }
+ else
+ {
+ if ( g_pFullFileSystem->FileExists( "servers/CustomGamesPage.res", "MOD" ) )
+ {
+ pPathID = "MOD";
+ }
+
+ LoadControlSettings( "servers/CustomGamesPage.res", pPathID, NULL, pConditions );
+ }
+
+ if ( pConditions )
+ {
+ pConditions->deleteThis();
+ }
+
+ if ( !GameSupportsReplay() )
+ {
+ HideReplayFilter();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCustomGames::OnLoadFilter(KeyValues *filter)
+{
+ BaseClass::OnLoadFilter( filter );
+
+ Q_strncpy(m_szTagFilter, filter->GetString("gametype"), sizeof(m_szTagFilter));
+
+ if ( m_pTagFilter )
+ {
+ m_pTagFilter->SetText(m_szTagFilter);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCustomGames::CheckTagFilter( gameserveritem_t &server )
+{
+ bool bRetVal = true;
+
+ // Custom games substring matches tags with the server's tags
+ int count = Q_strlen( m_szTagFilter );
+ if ( count )
+ {
+ CUtlVector<char*> TagList;
+ V_SplitString( m_szTagFilter, ",", TagList );
+ for ( int i = 0; i < TagList.Count(); i++ )
+ {
+ if ( ( Q_strnistr( server.m_szGameTags, TagList[i], MAX_TAG_CHARACTERS ) > 0 ) == TagsExclude() )
+ {
+ bRetVal = false;
+ break;
+ }
+ }
+
+ TagList.PurgeAndDeleteElements();
+ }
+
+ return bRetVal;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks the workshop filtering setting, taking into account workshop filtering might be disabled
+//-----------------------------------------------------------------------------
+bool CCustomGames::CheckWorkshopFilter( gameserveritem_t &server )
+{
+ eWorkshopMode workshopMode = WorkshopMode();
+ const char szWorkshopPrefix[] = "workshop/";
+ if ( workshopMode == eWorkshop_WorkshopOnly )
+ {
+ return V_strncasecmp( server.m_szMap, szWorkshopPrefix, sizeof( szWorkshopPrefix ) - 1 ) == 0;
+ }
+ else if ( workshopMode == eWorkshop_SubscribedOnly )
+ {
+ return ServerBrowser().IsWorkshopSubscribedMap( server.m_szMap );
+ }
+
+ Assert( workshopMode == eWorkshop_None );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets filter settings from controls
+//-----------------------------------------------------------------------------
+void CCustomGames::OnSaveFilter(KeyValues *filter)
+{
+ BaseClass::OnSaveFilter( filter );
+
+ if ( m_pTagFilter )
+ {
+ // tags
+ m_pTagFilter->GetText(m_szTagFilter, sizeof(m_szTagFilter) - 1);
+ }
+
+ if ( m_szTagFilter[0] )
+ {
+ Q_strlower(m_szTagFilter);
+ }
+
+ if ( TagsExclude() )
+ {
+ m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "gametype", "" ) );
+ }
+ else
+ {
+ m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "gametype", m_szTagFilter ) );
+ }
+
+ filter->SetString("gametype", m_szTagFilter);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCustomGames::SetRefreshing(bool state)
+{
+ if ( state )
+ {
+ m_pAddTagList->SetEnabled( false );
+ }
+
+ BaseClass::SetRefreshing( state );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCustomGames::ServerResponded( int iServer, gameserveritem_t *pServerItem )
+{
+ CBaseGamesPage::ServerResponded( iServer, pServerItem );
+
+ // If we've found a server with some tags, enable the add tag button
+ if ( pServerItem->m_szGameTags[0] )
+ {
+ m_pAddTagList->SetEnabled( true );
+ }
+}
+
+struct tagentry_t
+{
+ const char *pszTag;
+ int iCount;
+};
+int __cdecl SortTagsInUse( const tagentry_t *pTag1, const tagentry_t *pTag2 )
+{
+ return (pTag1->iCount < pTag2->iCount);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCustomGames::RecalculateCommonTags( void )
+{
+ // Regenerate our tag list
+ m_pTagListMenu->DeleteAllItems();
+
+ // Loop through our servers, and build a list of all the tags
+ CUtlVector<tagentry_t> aTagsInUse;
+
+ int iCount = m_pGameList->GetItemCount();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ int serverID = m_pGameList->GetItemUserData( i );
+ gameserveritem_t *pServer = GetServer( serverID );
+ if ( pServer && pServer->m_szGameTags && pServer->m_szGameTags[0] )
+ {
+ CUtlVector<char*> TagList;
+ V_SplitString( pServer->m_szGameTags, ",", TagList );
+
+ for ( int iTag = 0; iTag < TagList.Count(); iTag++ )
+ {
+ // First make sure it's not already in our list
+ bool bFound = false;
+ for ( int iCheck = 0; iCheck < aTagsInUse.Count(); iCheck++ )
+ {
+ if ( !Q_strnicmp(TagList[iTag], aTagsInUse[iCheck].pszTag, MAX_TAG_CHARACTERS ) )
+ {
+ aTagsInUse[iCheck].iCount++;
+ bFound = true;
+ }
+ }
+
+ if ( !bFound )
+ {
+ int iIdx = aTagsInUse.AddToTail();
+ aTagsInUse[iIdx].pszTag = TagList[iTag];
+ aTagsInUse[iIdx].iCount = 0;
+ }
+ }
+ }
+ }
+
+ aTagsInUse.Sort( SortTagsInUse );
+
+ int iTagsToAdd = min( aTagsInUse.Count(), NUM_COMMON_TAGS );
+ for ( int i = 0; i < iTagsToAdd; i++ )
+ {
+ const char *pszTag = aTagsInUse[i].pszTag;
+ m_pTagListMenu->AddMenuItem( pszTag, new KeyValues("AddTag", "tag", pszTag), this, new KeyValues( "data", "tag", pszTag ) );
+ }
+
+ m_pTagListMenu->SetFixedWidth( m_pAddTagList->GetWide() );
+ m_pTagListMenu->InvalidateLayout( true, false );
+ m_pTagListMenu->PositionRelativeToPanel( m_pAddTagList, Menu::UP );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCustomGames::OnTagMenuButtonOpened( void )
+{
+ RecalculateCommonTags();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the text from the message
+//-----------------------------------------------------------------------------
+void CCustomGames::OnAddTag(KeyValues *params)
+{
+ KeyValues *pkvText = params->FindKey("tag", false);
+ if (!pkvText)
+ return;
+
+ AddTagToFilterList( pkvText->GetString() );
+}
+
+
+int SortServerTags( char* const *p1, char* const *p2 )
+{
+ return ( Q_strcmp( *p1, *p2 ) > 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCustomGames::AddTagToFilterList( const char *pszTag )
+{
+ char txt[ 128 ];
+ m_pTagFilter->GetText( txt, sizeof( txt ) );
+
+ CUtlVector<char*> TagList;
+ V_SplitString( txt, ",", TagList );
+
+ if ( txt[0] )
+ {
+ for ( int i = 0; i < TagList.Count(); i++ )
+ {
+ // Already in the tag list?
+ if ( !Q_stricmp( TagList[i], pszTag ) )
+ {
+ TagList.PurgeAndDeleteElements();
+ return;
+ }
+ }
+ }
+
+ char *pszNewTag = new char[64];
+ Q_strncpy( pszNewTag, pszTag, 64 );
+ TagList.AddToHead( pszNewTag );
+
+ TagList.Sort( SortServerTags );
+
+ // Append it
+ char tmptags[MAX_TAG_CHARACTERS];
+ tmptags[0] = '\0';
+
+ for ( int i = 0; i < TagList.Count(); i++ )
+ {
+ if ( i > 0 )
+ {
+ Q_strncat( tmptags, ",", MAX_TAG_CHARACTERS );
+ }
+
+ Q_strncat( tmptags, TagList[i], MAX_TAG_CHARACTERS );
+ }
+
+ m_pTagFilter->SetText( tmptags );
+ TagList.PurgeAndDeleteElements();
+
+ // Update & apply filters now that the tag list has changed
+ UpdateFilterSettings();
+ ApplyGameFilters();
+}
diff --git a/serverbrowser/CustomGames.h b/serverbrowser/CustomGames.h
new file mode 100644
index 0000000..279b586
--- /dev/null
+++ b/serverbrowser/CustomGames.h
@@ -0,0 +1,70 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef CUSTOMGAMES_H
+#define CUSTOMGAMES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define MAX_TAG_CHARACTERS 128
+
+class TagInfoLabel : public vgui::URLLabel
+{
+ DECLARE_CLASS_SIMPLE( TagInfoLabel, vgui::URLLabel );
+public:
+ TagInfoLabel(Panel *parent, const char *panelName);
+ TagInfoLabel(Panel *parent, const char *panelName, const char *text, const char *pszURL);
+
+ virtual void OnMousePressed(vgui::MouseCode code);
+
+ MESSAGE_FUNC( DoOpenCustomServerInfoURL, "DoOpenCustomServerInfoURL" );
+};
+
+class TagMenuButton : public vgui::MenuButton
+{
+ DECLARE_CLASS_SIMPLE( TagMenuButton, vgui::MenuButton );
+public:
+ TagMenuButton( Panel *parent, const char *panelName, const char *text);
+
+ virtual void OnShowMenu(vgui::Menu *menu);
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Internet games with tags
+//-----------------------------------------------------------------------------
+
+class CCustomGames : public CInternetGames
+{
+ DECLARE_CLASS_SIMPLE( CCustomGames, CInternetGames );
+public:
+ CCustomGames(vgui::Panel *parent);
+ ~CCustomGames();
+
+ virtual void UpdateDerivedLayouts( void ) OVERRIDE;
+ virtual void OnLoadFilter(KeyValues *filter) OVERRIDE;
+ virtual void OnSaveFilter(KeyValues *filter) OVERRIDE;
+ bool CheckTagFilter( gameserveritem_t &server ) OVERRIDE;
+ bool CheckWorkshopFilter( gameserveritem_t &server ) OVERRIDE;
+ virtual void SetRefreshing(bool state) OVERRIDE;
+ virtual void ServerResponded( int iServer, gameserveritem_t *pServerItem ) OVERRIDE;
+
+ MESSAGE_FUNC_PARAMS( OnAddTag, "AddTag", params );
+ MESSAGE_FUNC( OnTagMenuButtonOpened, "TagMenuButtonOpened" );
+
+ void RecalculateCommonTags( void );
+ void AddTagToFilterList( const char *pszTag );
+
+private:
+ TagInfoLabel *m_pTagInfoURL;
+ TagMenuButton *m_pAddTagList;
+ vgui::Menu *m_pTagListMenu;
+ vgui::TextEntry *m_pTagFilter;
+ char m_szTagFilter[MAX_TAG_CHARACTERS];
+};
+
+
+#endif // CUSTOMGAMES_H
diff --git a/serverbrowser/DialogAddServer.cpp b/serverbrowser/DialogAddServer.cpp
new file mode 100644
index 0000000..cbba58e
--- /dev/null
+++ b/serverbrowser/DialogAddServer.cpp
@@ -0,0 +1,392 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// Input : *gameList - game list to add specified server to
+//-----------------------------------------------------------------------------
+CDialogAddServer::CDialogAddServer(vgui::Panel *parent, IGameList *gameList) : Frame(parent, "DialogAddServer")
+{
+ SetDeleteSelfOnClose(true);
+
+ m_pGameList = gameList;
+
+ SetTitle("#ServerBrowser_AddServersTitle", true);
+ SetSizeable( false );
+
+ m_pTabPanel = new PropertySheet(this, "GameTabs");
+ m_pTabPanel->SetTabWidth(72);
+
+ m_pDiscoveredGames = new ListPanel( this, "Servers" );
+
+ // Add the column headers
+ m_pDiscoveredGames->AddColumnHeader(0, "Password", "#ServerBrowser_Password", 16, ListPanel::COLUMN_FIXEDSIZE | ListPanel::COLUMN_IMAGE);
+ m_pDiscoveredGames->AddColumnHeader(1, "Bots", "#ServerBrowser_Bots", 16, ListPanel::COLUMN_FIXEDSIZE | ListPanel::COLUMN_IMAGE | ListPanel::COLUMN_HIDDEN);
+ m_pDiscoveredGames->AddColumnHeader(2, "Secure", "#ServerBrowser_Secure", 16, ListPanel::COLUMN_FIXEDSIZE | ListPanel::COLUMN_IMAGE);
+
+ bool bGameSupportsReplay = GameSupportsReplay();
+
+ int nReplayWidth = 16;
+ if ( !bGameSupportsReplay )
+ {
+ nReplayWidth = 0;
+ }
+
+ m_pDiscoveredGames->AddColumnHeader(3, "Replay", "#ServerBrowser_Replay", nReplayWidth, ListPanel::COLUMN_FIXEDSIZE | ListPanel::COLUMN_IMAGE);
+ m_pDiscoveredGames->AddColumnHeader(4, "Name", "#ServerBrowser_Servers", 20, ListPanel::COLUMN_RESIZEWITHWINDOW | ListPanel::COLUMN_UNHIDABLE);
+ m_pDiscoveredGames->AddColumnHeader(5, "IPAddr", "#ServerBrowser_IPAddress", 60, ListPanel::COLUMN_HIDDEN);
+ m_pDiscoveredGames->AddColumnHeader(6, "GameDesc", "#ServerBrowser_Game", 150);
+ m_pDiscoveredGames->AddColumnHeader(7, "Players", "#ServerBrowser_Players", 60);
+ m_pDiscoveredGames->AddColumnHeader(8, "Map", "#ServerBrowser_Map", 80);
+ m_pDiscoveredGames->AddColumnHeader(9, "Ping", "#ServerBrowser_Latency", 60);
+
+ m_pDiscoveredGames->SetColumnHeaderTooltip(0, "#ServerBrowser_PasswordColumn_Tooltip");
+ m_pDiscoveredGames->SetColumnHeaderTooltip(1, "#ServerBrowser_BotColumn_Tooltip");
+ m_pDiscoveredGames->SetColumnHeaderTooltip(2, "#ServerBrowser_SecureColumn_Tooltip");
+
+ if ( bGameSupportsReplay )
+ {
+ m_pDiscoveredGames->SetColumnHeaderTooltip(3, "#ServerBrowser_ReplayColumn_Tooltip");
+ }
+
+ // setup fast sort functions
+ m_pDiscoveredGames->SetSortFunc(0, PasswordCompare);
+ m_pDiscoveredGames->SetSortFunc(1, BotsCompare);
+ m_pDiscoveredGames->SetSortFunc(2, SecureCompare);
+
+ if ( bGameSupportsReplay )
+ {
+ m_pDiscoveredGames->SetSortFunc(3, ReplayCompare);
+ }
+
+ m_pDiscoveredGames->SetSortFunc(4, ServerNameCompare);
+ m_pDiscoveredGames->SetSortFunc(5, IPAddressCompare);
+ m_pDiscoveredGames->SetSortFunc(6, GameCompare);
+ m_pDiscoveredGames->SetSortFunc(7, PlayersCompare);
+ m_pDiscoveredGames->SetSortFunc(8, MapCompare);
+ m_pDiscoveredGames->SetSortFunc(9, PingCompare);
+
+ m_pDiscoveredGames->SetSortColumn(9); // sort on ping
+
+ m_pTextEntry = new vgui::TextEntry( this, "ServerNameText" );
+ m_pTextEntry->AddActionSignalTarget( this );
+
+ m_pTestServersButton = new vgui::Button( this, "TestServersButton", "" );
+ m_pAddServerButton = new vgui::Button( this, "OKButton", "" );
+ m_pAddSelectedServerButton = new vgui::Button( this, "SelectedOKButton", "", this, "addselected" );
+
+ m_pTabPanel->AddPage( m_pDiscoveredGames, "#ServerBrowser_Servers" );
+
+ LoadControlSettings("Servers/DialogAddServer.res");
+
+ // Setup the buttons. We leave them disabled until there is text in the textbox.
+ m_pAddServerButton->SetEnabled( false );
+ m_pTestServersButton->SetEnabled( false );
+ m_pAddSelectedServerButton->SetEnabled( false );
+ m_pAddSelectedServerButton->SetVisible( false );
+ m_pTabPanel->SetVisible( false );
+
+ m_pTextEntry->RequestFocus();
+
+ // Initially, we aren't high enough to show the tab panel.
+ int x, y;
+ m_pTabPanel->GetPos( x, y );
+ m_OriginalHeight = m_pTabPanel->GetTall() + y + 50;
+ SetTall( y );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CDialogAddServer::~CDialogAddServer()
+{
+ FOR_EACH_VEC( m_Queries, i )
+ {
+ if ( steamapicontext->SteamMatchmakingServers() )
+ steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_Queries[ i ] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Lets us know when the text entry has changed.
+//-----------------------------------------------------------------------------
+void CDialogAddServer::OnTextChanged()
+{
+ bool bAnyText = (m_pTextEntry->GetTextLength() > 0);
+ m_pAddServerButton->SetEnabled( bAnyText );
+ m_pTestServersButton->SetEnabled( bAnyText );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: button command handler
+//-----------------------------------------------------------------------------
+void CDialogAddServer::OnCommand(const char *command)
+{
+ if ( Q_stricmp(command, "OK") == 0 )
+ {
+ OnOK();
+ }
+ else if ( Q_stricmp( command, "TestServers" ) == 0 )
+ {
+ SetTall( m_OriginalHeight );
+ m_pTabPanel->SetVisible( true );
+ m_pAddSelectedServerButton->SetVisible( true );
+
+ TestServers();
+ }
+ else if ( !Q_stricmp( command, "addselected" ) )
+ {
+ if ( m_pDiscoveredGames->GetSelectedItemsCount() )
+ {
+ // get the server
+ int serverID = m_pDiscoveredGames->GetItemUserData( m_pDiscoveredGames->GetSelectedItem(0) );
+ FinishAddServer( m_Servers[ serverID ] );
+ m_pDiscoveredGames->RemoveItem( m_pDiscoveredGames->GetSelectedItem(0) ); // as we add to favs remove from the list
+ m_pDiscoveredGames->SetEmptyListText( "" );
+ }
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles the OK button being pressed; adds the server to the game list
+//-----------------------------------------------------------------------------
+void CDialogAddServer::OnOK()
+{
+ // try and parse out IP address
+ const char *address = GetControlString("ServerNameText", "");
+ netadr_t netaddr;
+ netaddr.SetFromString( address, true );
+ if ( !netaddr.GetPort() && !AllowInvalidIPs() )
+ {
+ // use the default port since it was not entered
+ netaddr.SetPort( 27015 );
+ }
+
+ if ( AllowInvalidIPs() || netaddr.IsValid() )
+ {
+ gameserveritem_t server;
+ memset(&server, 0, sizeof(server));
+ server.SetName( address );
+
+ // We assume here that the query and connection ports are the same. This is why it's much
+ // better if they click "Servers" and choose a server in there.
+ server.m_NetAdr.Init( netaddr.GetIPHostByteOrder(), netaddr.GetPort(), netaddr.GetPort() );
+
+ server.m_nAppID = 0;
+ FinishAddServer( server );
+ }
+ else
+ {
+ // could not parse the ip address, popup an error
+ MessageBox *dlg = new MessageBox("#ServerBrowser_AddServerErrorTitle", "#ServerBrowser_AddServerError");
+ dlg->DoModal();
+ }
+
+ // mark ourselves to be closed
+ PostMessage(this, new KeyValues("Close"));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Ping a particular IP for server presence
+//-----------------------------------------------------------------------------
+void CDialogAddServer::TestServers()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ m_pDiscoveredGames->SetEmptyListText( "" );
+ m_pDiscoveredGames->RemoveAll();
+
+ // If they specified a port, then send a query to that port.
+ const char *address = GetControlString("ServerNameText", "");
+ netadr_t netaddr;
+ netaddr.SetFromString( address, true );
+
+ m_Servers.RemoveAll();
+ CUtlVector<netadr_t> vecAdress;
+
+ if ( netaddr.GetPort() == 0 )
+ {
+ // No port specified. Go to town on the ports.
+ CUtlVector<uint16> portsToTry;
+ GetMostCommonQueryPorts( portsToTry );
+
+ for ( int i=0; i < portsToTry.Count(); i++ )
+ {
+ netadr_t newAddr = netaddr;
+ newAddr.SetPort( portsToTry[i] );
+ vecAdress.AddToTail( newAddr );
+ }
+ }
+ else
+ {
+ vecAdress.AddToTail( netaddr );
+ }
+
+ // Change the text on the tab panel..
+ m_pTabPanel->RemoveAllPages();
+
+ wchar_t wstr[512];
+ if ( address[0] == 0 )
+ {
+ Q_wcsncpy( wstr, g_pVGuiLocalize->Find( "#ServerBrowser_ServersRespondingLocal"), sizeof( wstr ) );
+ }
+ else
+ {
+ wchar_t waddress[512];
+ Q_UTF8ToUnicode( address, waddress, sizeof( waddress ) );
+ g_pVGuiLocalize->ConstructString( wstr, sizeof( wstr ), g_pVGuiLocalize->Find( "#ServerBrowser_ServersResponding"), 1, waddress );
+ }
+
+ char str[512];
+ Q_UnicodeToUTF8( wstr, str, sizeof( str ) );
+ m_pTabPanel->AddPage( m_pDiscoveredGames, str );
+ m_pTabPanel->InvalidateLayout();
+
+ FOR_EACH_VEC( vecAdress, iAddress )
+ {
+ m_Queries.AddToTail( steamapicontext->SteamMatchmakingServers()->PingServer( vecAdress[ iAddress ].GetIPHostByteOrder(), vecAdress[ iAddress ].GetPort(), this ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: A server answered our ping
+//-----------------------------------------------------------------------------
+void CDialogAddServer::ServerResponded( gameserveritem_t &server )
+{
+ KeyValues *kv = new KeyValues( "Server" );
+
+ kv->SetString( "name", server.GetName() );
+ kv->SetString( "map", server.m_szMap );
+ kv->SetString( "GameDir", server.m_szGameDir );
+ kv->SetString( "GameDesc", server.m_szGameDescription );
+ kv->SetString( "GameTags", server.m_szGameTags );
+ kv->SetInt( "password", server.m_bPassword ? 1 : 0);
+ kv->SetInt( "bots", server.m_nBotPlayers ? 2 : 0);
+ kv->SetInt( "Replay", IsReplayServer( server ) ? 5 : 0 );
+
+ if ( server.m_bSecure )
+ {
+ // show the denied icon if banned from secure servers, the secure icon otherwise
+ kv->SetInt("secure", ServerBrowser().IsVACBannedFromGame( server.m_nAppID ) ? 4 : 3);
+ }
+ else
+ {
+ kv->SetInt("secure", 0);
+ }
+
+ netadr_t reportedIPAddr;
+ reportedIPAddr.SetIP( server.m_NetAdr.GetIP() );
+ reportedIPAddr.SetPort( server.m_NetAdr.GetConnectionPort() );
+ kv->SetString("IPAddr", reportedIPAddr.ToString() );
+
+ char buf[32];
+ Q_snprintf(buf, sizeof(buf), "%d / %d", server.m_nPlayers, server.m_nMaxPlayers);
+ kv->SetString("Players", buf);
+
+ kv->SetInt("Ping", server.m_nPing);
+
+ // new server, add to list
+ int iServer = m_Servers.AddToTail( server );
+ int iListID = m_pDiscoveredGames->AddItem(kv, iServer, false, false);
+ if ( m_pDiscoveredGames->GetItemCount() == 1 )
+ {
+ m_pDiscoveredGames->AddSelectedItem( iListID );
+ }
+ kv->deleteThis();
+
+ m_pDiscoveredGames->InvalidateLayout();
+}
+
+void CDialogAddServer::ServerFailedToRespond()
+{
+ m_pDiscoveredGames->SetEmptyListText( "#ServerBrowser_ServerNotResponding" );
+}
+
+void CDialogAddServer::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ ImageList *imageList = new ImageList(false);
+ imageList->AddImage(scheme()->GetImage("servers/icon_password", false));
+ imageList->AddImage(scheme()->GetImage("servers/icon_bots", false));
+ imageList->AddImage(scheme()->GetImage("servers/icon_robotron", false));
+ imageList->AddImage(scheme()->GetImage("servers/icon_secure_deny", false));
+ 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_pDiscoveredGames->SetImageList(imageList, true);
+ vgui::HFont hFont = pScheme->GetFont( "ListSmall", IsProportional() );
+ if ( !hFont )
+ hFont = pScheme->GetFont( "DefaultSmall", IsProportional() );
+
+ m_pDiscoveredGames->SetFont( hFont );
+ m_pDiscoveredGames->SetColumnHeaderImage(0, passwordColumnImage);
+ m_pDiscoveredGames->SetColumnHeaderImage(1, botColumnImage);
+ m_pDiscoveredGames->SetColumnHeaderImage(2, secureColumnImage);
+ m_pDiscoveredGames->SetColumnHeaderImage(3, replayColumnImage);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: A server on the listed IP responded
+//-----------------------------------------------------------------------------
+void CDialogAddServer::OnItemSelected()
+{
+ int nSelectedItem = m_pDiscoveredGames->GetSelectedItem(0);
+ if( nSelectedItem != -1 )
+ {
+ m_pAddSelectedServerButton->SetEnabled( true );
+ }
+ else
+ {
+ m_pAddSelectedServerButton->SetEnabled( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDialogAddServer::FinishAddServer( gameserveritem_t &pServer )
+{
+ ServerBrowserDialog().AddServerToFavorites( pServer );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDialogAddBlacklistedServer::FinishAddServer( gameserveritem_t &pServer )
+{
+ ServerBrowserDialog().AddServerToBlacklist( pServer );
+ ServerBrowserDialog().BlacklistsChanged();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDialogAddBlacklistedServer::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ m_pAddServerButton->SetText( "#ServerBrowser_AddAddressToBlacklist" );
+ m_pAddSelectedServerButton->SetText( "#ServerBrowser_AddSelectedToBlacklist" );
+}
diff --git a/serverbrowser/DialogAddServer.h b/serverbrowser/DialogAddServer.h
new file mode 100644
index 0000000..7abaee1
--- /dev/null
+++ b/serverbrowser/DialogAddServer.h
@@ -0,0 +1,75 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef DIALOGADDSERVER_H
+#define DIALOGADDSERVER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CAddServerGameList;
+class IGameList;
+
+//-----------------------------------------------------------------------------
+// Purpose: Dialog which lets the user add a server by IP address
+//-----------------------------------------------------------------------------
+class CDialogAddServer : public vgui::Frame, public ISteamMatchmakingPingResponse
+{
+ DECLARE_CLASS_SIMPLE( CDialogAddServer, vgui::Frame );
+ friend class CAddServerGameList;
+
+public:
+ CDialogAddServer(vgui::Panel *parent, IGameList *gameList);
+ ~CDialogAddServer();
+
+ void ServerResponded( gameserveritem_t &server );
+ void ServerFailedToRespond();
+
+ void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ MESSAGE_FUNC( OnItemSelected, "ItemSelected" );
+private:
+ virtual void OnCommand(const char *command);
+
+ void OnOK();
+
+ void TestServers();
+ MESSAGE_FUNC( OnTextChanged, "TextChanged" );
+
+ virtual void FinishAddServer( gameserveritem_t &pServer );
+ virtual bool AllowInvalidIPs( void ) { return false; }
+
+protected:
+ IGameList *m_pGameList;
+
+ vgui::Button *m_pTestServersButton;
+ vgui::Button *m_pAddServerButton;
+ vgui::Button *m_pAddSelectedServerButton;
+
+ vgui::PropertySheet *m_pTabPanel;
+ vgui::TextEntry *m_pTextEntry;
+ vgui::ListPanel *m_pDiscoveredGames;
+ int m_OriginalHeight;
+ CUtlVector<gameserveritem_t> m_Servers;
+ CUtlVector<HServerQuery> m_Queries;
+};
+
+class CDialogAddBlacklistedServer : public CDialogAddServer
+{
+ DECLARE_CLASS_SIMPLE( CDialogAddBlacklistedServer, CDialogAddServer );
+public:
+ CDialogAddBlacklistedServer( vgui::Panel *parent, IGameList *gameList) :
+ CDialogAddServer( parent, gameList )
+ {
+ }
+
+ virtual void FinishAddServer( gameserveritem_t &pServer );
+ void ApplySchemeSettings( vgui::IScheme *pScheme );
+ virtual bool AllowInvalidIPs( void ) { return true; }
+};
+
+#endif // DIALOGADDSERVER_H
diff --git a/serverbrowser/DialogGameInfo.cpp b/serverbrowser/DialogGameInfo.cpp
new file mode 100644
index 0000000..fc0ed36
--- /dev/null
+++ b/serverbrowser/DialogGameInfo.cpp
@@ -0,0 +1,802 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+static const long RETRY_TIME = 10000; // refresh server every 10 seconds
+static const long CHALLENGE_ENTRIES = 1024;
+
+extern "C"
+{
+ DLL_EXPORT bool JoiningSecureServerCall()
+ {
+ return true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Comparison function used in query redblack tree
+//-----------------------------------------------------------------------------
+bool QueryLessFunc( const struct challenge_s &item1, const struct challenge_s &item2 )
+{
+ // compare port then ip
+ if ( item1.addr.GetPort() < item2.addr.GetPort() )
+ return true;
+ else if ( item1.addr.GetPort() > item2.addr.GetPort() )
+ return false;
+
+ int ip1 = item1.addr.GetIPNetworkByteOrder();
+ int ip2 = item2.addr.GetIPNetworkByteOrder();
+
+ return ip1 < ip2;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CDialogGameInfo::CDialogGameInfo( vgui::Panel *parent, int serverIP, int queryPort, unsigned short connectionPort, const char *pszConnectCode ) :
+ Frame(parent, "DialogGameInfo"),
+ m_CallbackPersonaStateChange( this, &CDialogGameInfo::OnPersonaStateChange ),
+ m_sConnectCode( pszConnectCode )
+{
+ SetBounds(0, 0, 512, 512);
+ SetMinimumSize(416, 340);
+ SetDeleteSelfOnClose(true);
+ m_bConnecting = false;
+ m_bServerFull = false;
+ m_bShowAutoRetryToggle = false;
+ m_bServerNotResponding = false;
+ m_bShowingExtendedOptions = false;
+ m_SteamIDFriend = 0;
+ m_hPingQuery = HSERVERQUERY_INVALID;
+ m_hPlayersQuery = HSERVERQUERY_INVALID;
+ m_bPlayerListUpdatePending = false;
+
+ m_szPassword[0] = 0;
+
+ m_pConnectButton = new Button(this, "Connect", "#ServerBrowser_JoinGame");
+ m_pCloseButton = new Button(this, "Close", "#ServerBrowser_Close");
+ m_pRefreshButton = new Button(this, "Refresh", "#ServerBrowser_Refresh");
+ m_pInfoLabel = new Label(this, "InfoLabel", "");
+ m_pAutoRetry = new ToggleButton(this, "AutoRetry", "#ServerBrowser_AutoRetry");
+ m_pAutoRetry->AddActionSignalTarget(this);
+
+ m_pAutoRetryAlert = new RadioButton(this, "AutoRetryAlert", "#ServerBrowser_AlertMeWhenSlotOpens");
+ m_pAutoRetryJoin = new RadioButton(this, "AutoRetryJoin", "#ServerBrowser_JoinWhenSlotOpens");
+ m_pPlayerList = new ListPanel(this, "PlayerList");
+ m_pPlayerList->AddColumnHeader(0, "PlayerName", "#ServerBrowser_PlayerName", 156);
+ m_pPlayerList->AddColumnHeader(1, "Score", "#ServerBrowser_Score", 64);
+ m_pPlayerList->AddColumnHeader(2, "Time", "#ServerBrowser_Time", 64);
+
+ m_pPlayerList->SetSortFunc(2, &PlayerTimeColumnSortFunc);
+
+ // set the defaults for sorting
+ // hack, need to make this more explicit functions in ListPanel
+ PostMessage(m_pPlayerList, new KeyValues("SetSortColumn", "column", 2));
+ PostMessage(m_pPlayerList, new KeyValues("SetSortColumn", "column", 1));
+ PostMessage(m_pPlayerList, new KeyValues("SetSortColumn", "column", 1));
+
+ m_pAutoRetryAlert->SetSelected(true);
+
+ m_pConnectButton->SetCommand(new KeyValues("Connect"));
+ m_pCloseButton->SetCommand(new KeyValues("Close"));
+ m_pRefreshButton->SetCommand(new KeyValues("Refresh"));
+
+ m_iRequestRetry = 0;
+
+ // create a new server to watch
+ memset(&m_Server, 0, sizeof(m_Server) );
+ m_Server.m_NetAdr.Init( serverIP, queryPort, connectionPort );
+
+ // refresh immediately
+ RequestInfo();
+
+ // let us be ticked every frame
+ ivgui()->AddTickSignal(this->GetVPanel());
+
+ LoadControlSettings("Servers/DialogGameInfo.res");
+ RegisterControlSettingsFile( "Servers/DialogGameInfo_SinglePlayer.res" );
+ RegisterControlSettingsFile( "Servers/DialogGameInfo_AutoRetry.res" );
+ MoveToCenterOfScreen();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CDialogGameInfo::~CDialogGameInfo()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ if ( m_hPingQuery != HSERVERQUERY_INVALID )
+ steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPingQuery );
+ if ( m_hPlayersQuery != HSERVERQUERY_INVALID )
+ steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPlayersQuery );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: send a player query to a server
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::SendPlayerQuery( uint32 unIP, uint16 usQueryPort )
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ if ( m_hPlayersQuery != HSERVERQUERY_INVALID )
+ steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPlayersQuery );
+ m_hPlayersQuery = steamapicontext->SteamMatchmakingServers()->PlayerDetails( unIP, usQueryPort, this );
+ m_bPlayerListUpdatePending = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Activates the dialog
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::Run(const char *titleName)
+{
+ if ( titleName )
+ {
+ SetTitle( "#ServerBrowser_GameInfoWithNameTitle", true );
+ }
+ else
+ {
+ SetTitle( "#ServerBrowser_GameInfoWithNameTitle", true );
+ }
+ SetDialogVariable( "game", titleName );
+
+ // get the info from the user
+ RequestInfo();
+ Activate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Changes which server to watch
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ChangeGame( int serverIP, int queryPort, unsigned short connectionPort )
+{
+ memset( &m_Server, 0x0, sizeof(m_Server) );
+
+ m_Server.m_NetAdr.Init( serverIP, queryPort, connectionPort );
+
+ // remember the dialogs position so we can keep it the same
+ int x, y;
+ GetPos( x, y );
+
+ // see if we need to change dialog state
+ if ( !m_Server.m_NetAdr.GetIP() || !m_Server.m_NetAdr.GetQueryPort() )
+ {
+ // not in a server, load the simple settings dialog
+ SetMinimumSize(0, 0);
+ SetSizeable( false );
+ LoadControlSettings( "Servers/DialogGameInfo_SinglePlayer.res" );
+ }
+ else
+ {
+ // moving from a single-player game -> multiplayer, reset dialog
+ SetMinimumSize(416, 340);
+ SetSizeable( true );
+ LoadControlSettings( "Servers/DialogGameInfo.res" );
+ }
+ SetPos( x, y );
+
+ // Start refresh immediately
+ m_iRequestRetry = 0;
+ RequestInfo();
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: updates the dialog if it's watching a friend who changes servers
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnPersonaStateChange( PersonaStateChange_t *pPersonaStateChange )
+{
+#if 0 // TBD delete this func
+ if ( m_SteamIDFriend && m_SteamIDFriend == pPersonaStateChange->m_ulSteamID )
+ {
+ // friend may have changed servers
+ uint64 nGameID;
+ uint32 unGameIP;
+ uint16 usGamePort;
+ uint16 usQueryPort;
+
+ if ( SteamFriends()->GetFriendGamePlayed( m_SteamIDFriend, &nGameID, &unGameIP, &usGamePort, &usQueryPort ) )
+ {
+ if ( pPersonaStateChange->m_nChangeFlags & k_EPersonaChangeGamePlayed )
+ {
+ ChangeGame( unGameIP, usQueryPort, usGamePort );
+ }
+ }
+ else
+ {
+ // bugbug johnc: change to not be in a game anymore
+ }
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Associates a user with this dialog
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::SetFriend( uint64 ulSteamIDFriend )
+{
+ // set the title to include the friends name
+ SetTitle( "#ServerBrowser_GameInfoWithNameTitle", true );
+ SetDialogVariable( "game", steamapicontext->SteamFriends()->GetFriendPersonaName( ulSteamIDFriend ) );
+ SetDialogVariable( "friend", steamapicontext->SteamFriends()->GetFriendPersonaName( ulSteamIDFriend ) );
+
+ // store the friend we're associated with
+ m_SteamIDFriend = ulSteamIDFriend;
+
+ FriendGameInfo_t friendGameInfo;
+ if ( steamapicontext->SteamFriends()->GetFriendGamePlayed( ulSteamIDFriend, &friendGameInfo ) )
+ {
+ uint16 usConnPort = friendGameInfo.m_usGamePort;
+ if ( friendGameInfo.m_usQueryPort < QUERY_PORT_ERROR )
+ usConnPort = friendGameInfo.m_usQueryPort;
+ ChangeGame( friendGameInfo.m_unGameIP, usConnPort, friendGameInfo.m_usGamePort );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data access
+//-----------------------------------------------------------------------------
+uint64 CDialogGameInfo::GetAssociatedFriend()
+{
+ return m_SteamIDFriend;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out the data
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ SetControlString( "ServerText", m_Server.GetName() );
+ SetControlString( "GameText", m_Server.m_szGameDescription );
+ SetControlString( "MapText", m_Server.m_szMap );
+ SetControlString( "GameTags", m_Server.m_szGameTags );
+
+
+ if ( !m_Server.m_bHadSuccessfulResponse )
+ {
+ SetControlString("SecureText", "");
+ }
+ else if ( m_Server.m_bSecure )
+ {
+ SetControlString("SecureText", "#ServerBrowser_Secure");
+ }
+ else
+ {
+ SetControlString("SecureText", "#ServerBrowser_NotSecure");
+ }
+
+ char buf[128];
+ if ( m_Server.m_nMaxPlayers > 0)
+ {
+ Q_snprintf(buf, sizeof(buf), "%d / %d", m_Server.m_nPlayers, m_Server.m_nMaxPlayers);
+ }
+ else
+ {
+ buf[0] = 0;
+ }
+ SetControlString("PlayersText", buf);
+
+ if ( m_Server.m_NetAdr.GetIP() && m_Server.m_NetAdr.GetQueryPort() )
+ {
+ SetControlString("ServerIPText", m_Server.m_NetAdr.GetConnectionAddressString() );
+ m_pConnectButton->SetEnabled(true);
+ if ( m_pAutoRetry->IsSelected() )
+ {
+ m_pAutoRetryAlert->SetVisible(true);
+ m_pAutoRetryJoin->SetVisible(true);
+ }
+ else
+ {
+ m_pAutoRetryAlert->SetVisible(false);
+ m_pAutoRetryJoin->SetVisible(false);
+ }
+ }
+ else
+ {
+ SetControlString("ServerIPText", "");
+ m_pConnectButton->SetEnabled(false);
+ }
+
+ if ( m_Server.m_bHadSuccessfulResponse )
+ {
+ Q_snprintf(buf, sizeof(buf), "%d", m_Server.m_nPing );
+ SetControlString("PingText", buf);
+ }
+ else
+ {
+ SetControlString("PingText", "");
+ }
+
+ // set the info text
+ if ( m_pAutoRetry->IsSelected() )
+ {
+ if ( m_Server.m_nPlayers < m_Server.m_nMaxPlayers )
+ {
+ m_pInfoLabel->SetText("#ServerBrowser_PressJoinToConnect");
+ }
+ else if (m_pAutoRetryJoin->IsSelected())
+ {
+ m_pInfoLabel->SetText("#ServerBrowser_JoinWhenSlotIsFree");
+ }
+ else
+ {
+ m_pInfoLabel->SetText("#ServerBrowser_AlertWhenSlotIsFree");
+ }
+ }
+ else if (m_bServerFull)
+ {
+ m_pInfoLabel->SetText("#ServerBrowser_CouldNotConnectServerFull");
+ }
+ else if (m_bServerNotResponding)
+ {
+ m_pInfoLabel->SetText("#ServerBrowser_ServerNotResponding");
+ }
+ else
+ {
+ // clear the status
+ m_pInfoLabel->SetText("");
+ }
+
+ if ( m_Server.m_bHadSuccessfulResponse && !(m_Server.m_nPlayers + m_Server.m_nBotPlayers) )
+ {
+ m_pPlayerList->SetEmptyListText("#ServerBrowser_ServerHasNoPlayers");
+ }
+ else
+ {
+ m_pPlayerList->SetEmptyListText("#ServerBrowser_ServerNotResponding");
+ }
+
+ // auto-retry layout
+ m_pAutoRetry->SetVisible(m_bShowAutoRetryToggle);
+
+ Repaint();
+}
+
+void CDialogGameInfo::OnKeyCodePressed( vgui::KeyCode code )
+{
+ if ( code == KEY_XBUTTON_B || code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A || code == STEAMCONTROLLER_B )
+ {
+ m_pCloseButton->DoClick();
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed(code);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forces the game info dialog to try and connect
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::Connect()
+{
+ OnConnect();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Connects the user to this game
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnConnect()
+{
+ // flag that we are attempting connection
+ m_bConnecting = true;
+
+ // reset state
+ m_bServerFull = false;
+ m_bServerNotResponding = false;
+
+ InvalidateLayout();
+
+ // need to refresh server before attempting to connect, to make sure there is enough room on the server
+ m_iRequestRetry = 0;
+ RequestInfo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Cancel auto-retry if we connect to the game by other means
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnConnectToGame( int ip, int port )
+{
+ // if we just connected to the server we were looking at, close the dialog
+ // important so that we don't auto-retry a server that we are already on
+ if ( m_Server.m_NetAdr.GetIP() == (uint32)ip && m_Server.m_NetAdr.GetConnectionPort() == (uint16)port )
+ {
+ // close this dialog
+ Close();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles Refresh button press, starts a re-ping of the server
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnRefresh()
+{
+ m_iRequestRetry = 0;
+ // re-ask the server for the game info
+ RequestInfo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forces the whole dialog to redraw when the auto-retry button is toggled
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnButtonToggled(Panel *panel)
+{
+ if (panel == m_pAutoRetry)
+ {
+ ShowAutoRetryOptions(m_pAutoRetry->IsSelected());
+ }
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets whether the extended auto-retry options are visible or not
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ShowAutoRetryOptions(bool state)
+{
+ // we need to extend the dialog
+ int growSize = 60;
+ if (!state)
+ {
+ growSize = -growSize;
+ }
+
+ // alter the dialog size accordingly
+ int x, y, wide, tall;
+ GetBounds( x, y, wide, tall );
+
+ // load a new layout file depending on the state
+ SetMinimumSize(416, 340);
+ if ( state )
+ LoadControlSettings( "Servers/DialogGameInfo_AutoRetry.res" );
+ else
+ LoadControlSettings( "Servers/DialogGameInfo.res" );
+
+ // restore size and position as
+ // load control settings will override them
+ SetBounds( x, y, wide, tall + growSize );
+
+ // restore other properties of the dialog
+ PerformLayout();
+
+ m_pAutoRetryAlert->SetSelected( true );
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Requests the right info from the server
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::RequestInfo()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ if ( m_iRequestRetry == 0 )
+ {
+ // reset the time at which we auto-refresh
+ m_iRequestRetry = system()->GetTimeMillis() + RETRY_TIME;
+ if ( m_hPingQuery != HSERVERQUERY_INVALID )
+ steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPingQuery );
+ m_hPingQuery = steamapicontext->SteamMatchmakingServers()->PingServer( m_Server.m_NetAdr.GetIP(), m_Server.m_NetAdr.GetQueryPort(), this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame, handles resending network messages
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnTick()
+{
+ // check to see if we should perform an auto-refresh
+ if ( m_iRequestRetry && m_iRequestRetry < system()->GetTimeMillis() )
+ {
+ m_iRequestRetry = 0;
+ RequestInfo();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the server has successfully responded
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ServerResponded( gameserveritem_t &server )
+{
+ if( m_Server.m_NetAdr.GetQueryPort() &&
+ m_Server.m_NetAdr.GetQueryPort() != server.m_NetAdr.GetQueryPort() )
+ {
+ return; // this is not the guy we talked about
+ }
+
+ uint16 connectionPort = m_Server.m_NetAdr.GetConnectionPort();
+
+ // 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.
+ server.m_nPlayers = (uint8)(int8)server.m_nPlayers;
+ server.m_nBotPlayers = (uint8)(int8)server.m_nBotPlayers;
+ server.m_nMaxPlayers = (uint8)(int8)server.m_nMaxPlayers;
+
+ m_hPingQuery = HSERVERQUERY_INVALID;
+ m_Server = server;
+
+ // Preserve our connection port, since we may be querying the sourceTV port but getting a response for the real
+ // server. This is a limitation of the steam Matchmaking API where it doesn't properly send us a sourcetv response
+ // but instead the main server's response (unless we're connecting to a proxy, THEN we get the sourcetv response!)
+ m_Server.m_NetAdr.SetConnectionPort( connectionPort );
+
+ if ( m_bConnecting )
+ {
+ ConnectToServer();
+ }
+ else if ( m_pAutoRetry->IsSelected() && server.m_nPlayers < server.m_nMaxPlayers )
+ {
+ // there is a slot free, we can join
+
+ // make the sound
+ surface()->PlaySound("Servers/game_ready.wav");
+
+ // flash this window
+ FlashWindow();
+
+ // if it's set, connect right away
+ if (m_pAutoRetryJoin->IsSelected())
+ {
+ ConnectToServer();
+ }
+ }
+ else
+ {
+ SendPlayerQuery( server.m_NetAdr.GetIP(), server.m_NetAdr.GetQueryPort() );
+ }
+
+ m_bServerNotResponding = false;
+
+ InvalidateLayout();
+ Repaint();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: called when a server response has timed out
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ServerFailedToRespond()
+{
+ // the server didn't respond, mark that in the UI
+ // only mark if we haven't ever received a response
+ if ( !m_Server.m_bHadSuccessfulResponse )
+ {
+ m_bServerNotResponding = true;
+ }
+
+ InvalidateLayout();
+ Repaint();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructs a command to send a running game to connect to a server,
+// based on the server type
+//
+// TODO it would be nice to push this logic into the IRunGameEngine interface; that
+// way we could ask the engine itself to construct arguments in ways that fit.
+// Might be worth the effort as we start to add more engines.
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ApplyConnectCommand( const gameserveritem_t &server )
+{
+ char command[ 256 ];
+ // set the server password, if any
+ if ( m_szPassword[0] )
+ {
+ Q_snprintf( command, Q_ARRAYSIZE( command ), "password \"%s\"\n", m_szPassword );
+ g_pRunGameEngine->AddTextCommand( command );
+ }
+ // send engine command to change servers
+ Q_snprintf( command, Q_ARRAYSIZE( command ), "connect %s %s\n", server.m_NetAdr.GetConnectionAddressString(), m_sConnectCode.String() );
+ g_pRunGameEngine->AddTextCommand( command );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructs game options to use when running a game to connect to a server
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ConstructConnectArgs( char *pchOptions, int cchOptions, const gameserveritem_t &server )
+{
+ Q_snprintf( pchOptions, cchOptions, " +connect %s", server.m_NetAdr.GetConnectionAddressString() );
+ if ( m_szPassword[0] )
+ {
+ Q_strcat( pchOptions, " +password \"", cchOptions );
+ Q_strcat( pchOptions, m_szPassword, cchOptions );
+ Q_strcat( pchOptions, "\"", cchOptions );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Connects to the server
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ConnectToServer()
+{
+ m_bConnecting = false;
+
+ // check VAC status
+ if ( m_Server.m_bSecure && ServerBrowser().IsVACBannedFromGame( m_Server.m_nAppID ) )
+ {
+ // refuse the user
+ CVACBannedConnRefusedDialog *pDlg = new CVACBannedConnRefusedDialog( GetVParent(), "VACBannedConnRefusedDialog" );
+ pDlg->Activate();
+ Close();
+ return;
+ }
+
+
+ // check to see if we need a password
+ if ( m_Server.m_bPassword && !m_szPassword[0] )
+ {
+ CDialogServerPassword *box = new CDialogServerPassword(this);
+ box->AddActionSignalTarget(this);
+ box->Activate( m_Server.GetName(), 0 );
+ return;
+ }
+
+ // check the player count
+ if ( m_Server.m_nPlayers >= m_Server.m_nMaxPlayers )
+ {
+ // mark why we cannot connect
+ m_bServerFull = true;
+ // give them access to auto-retry options
+ m_bShowAutoRetryToggle = true;
+ InvalidateLayout();
+ return;
+ }
+
+ // tell the engine to connect
+ const char *gameDir = m_Server.m_szGameDir;
+ if (g_pRunGameEngine->IsRunning())
+ {
+ ApplyConnectCommand( m_Server );
+ }
+ else
+ {
+ char connectArgs[256];
+ ConstructConnectArgs( connectArgs, Q_ARRAYSIZE( connectArgs ), m_Server );
+
+ if ( ( m_Server.m_bSecure && JoiningSecureServerCall() )|| !m_Server.m_bSecure )
+ {
+ switch ( g_pRunGameEngine->RunEngine( m_Server.m_nAppID, gameDir, connectArgs ) )
+ {
+ case IRunGameEngine::k_ERunResultModNotInstalled:
+ {
+ MessageBox *dlg = new MessageBox( "#ServerBrowser_GameInfoTitle", "#ServerBrowser_ModNotInstalled" );
+ dlg->DoModal();
+ SetVisible(false);
+ return;
+ }
+ break;
+ case IRunGameEngine::k_ERunResultAppNotFound:
+ {
+ MessageBox *dlg = new MessageBox( "#ServerBrowser_GameInfoTitle", "#ServerBrowser_AppNotFound" );
+ dlg->DoModal();
+ SetVisible(false);
+ return;
+ }
+ break;
+ case IRunGameEngine::k_ERunResultNotInitialized:
+ {
+ MessageBox *dlg = new MessageBox( "#ServerBrowser_GameInfoTitle", "#ServerBrowser_NotInitialized" );
+ dlg->DoModal();
+ SetVisible(false);
+ return;
+ }
+ break;
+ case IRunGameEngine::k_ERunResultOkay:
+ default:
+ break;
+ };
+ }
+ }
+
+ // close this dialog
+ PostMessage(this, new KeyValues("Close"));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the current refresh list is complete
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::RefreshComplete( EMatchMakingServerResponse response )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: handles response from the get password dialog
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnJoinServerWithPassword(const char *password)
+{
+ // copy out the password
+ Q_strncpy(m_szPassword, password, sizeof(m_szPassword));
+
+ // retry connecting to the server again
+ OnConnect();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: player list received
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ClearPlayerList()
+{
+ m_pPlayerList->DeleteAllItems();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: on individual player added
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::AddPlayerToList(const char *playerName, int score, float timePlayedSeconds)
+{
+ if ( m_bPlayerListUpdatePending )
+ {
+ m_bPlayerListUpdatePending = false;
+ m_pPlayerList->RemoveAll();
+ }
+
+ KeyValues *player = new KeyValues("player");
+ player->SetString("PlayerName", playerName);
+ player->SetInt("Score", score);
+ player->SetInt("TimeSec", (int)timePlayedSeconds);
+
+ // construct a time string
+ int seconds = (int)timePlayedSeconds;
+ int minutes = seconds / 60;
+ int hours = minutes / 60;
+ seconds %= 60;
+ minutes %= 60;
+ char buf[64];
+ buf[0] = 0;
+ if (hours)
+ {
+ Q_snprintf(buf, sizeof(buf), "%dh %dm %ds", hours, minutes, seconds);
+ }
+ else if (minutes)
+ {
+ Q_snprintf(buf, sizeof(buf), "%dm %ds", minutes, seconds);
+ }
+ else
+ {
+ Q_snprintf(buf, sizeof(buf), "%ds", seconds);
+ }
+ player->SetString("Time", buf);
+
+ m_pPlayerList->AddItem(player, 0, false, true);
+ player->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sorting function for time column
+//-----------------------------------------------------------------------------
+int CDialogGameInfo::PlayerTimeColumnSortFunc(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ int p1time = p1.kv->GetInt("TimeSec");
+ int p2time = p2.kv->GetInt("TimeSec");
+
+ if (p1time > p2time)
+ return -1;
+ if (p1time < p2time)
+ return 1;
+
+ return 0;
+}
+
diff --git a/serverbrowser/DialogGameInfo.h b/serverbrowser/DialogGameInfo.h
new file mode 100644
index 0000000..586c1c4
--- /dev/null
+++ b/serverbrowser/DialogGameInfo.h
@@ -0,0 +1,126 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef DIALOGGAMEINFO_H
+#define DIALOGGAMEINFO_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+struct challenge_s
+{
+ netadr_t addr;
+ int challenge;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Dialog for displaying information about a game server
+//-----------------------------------------------------------------------------
+class CDialogGameInfo : public vgui::Frame, public ISteamMatchmakingPlayersResponse, public ISteamMatchmakingPingResponse
+{
+ DECLARE_CLASS_SIMPLE( CDialogGameInfo, vgui::Frame );
+
+public:
+ CDialogGameInfo(vgui::Panel *parent, int serverIP, int queryPort, unsigned short connectionPort, const char *pszConnectCode );
+ ~CDialogGameInfo();
+
+ void Run(const char *titleName);
+ void ChangeGame(int serverIP, int queryPort, unsigned short connectionPort);
+ void SetFriend( uint64 ulSteamIDFriend );
+ uint64 GetAssociatedFriend();
+
+ // forces the dialog to attempt to connect to the server
+ void Connect();
+
+ // implementation of IServerRefreshResponse interface
+ // called when the server has successfully responded
+ virtual void ServerResponded( gameserveritem_t &server );
+
+ // called when a server response has timed out
+ virtual void ServerFailedToRespond();
+
+ // on individual player added
+ virtual void AddPlayerToList(const char *playerName, int score, float timePlayedSeconds);
+ virtual void PlayersFailedToRespond() {}
+ virtual void PlayersRefreshComplete() { m_hPlayersQuery = HSERVERQUERY_INVALID; }
+
+ // called when the current refresh list is complete
+ virtual void RefreshComplete( EMatchMakingServerResponse response );
+
+ // player list received
+ virtual void ClearPlayerList();
+
+ //virtual void SendChallengeQuery( const netadr_t & to );
+ virtual void SendPlayerQuery( uint32 unIP, uint16 usQueryPort );
+ //virtual void InsertChallengeResponse( const netadr_t & to, int nChallenge );
+
+protected:
+ // message handlers
+ MESSAGE_FUNC( OnConnect, "Connect" );
+ MESSAGE_FUNC( OnRefresh, "Refresh" );
+ MESSAGE_FUNC_PTR( OnButtonToggled, "ButtonToggled", panel );
+ MESSAGE_FUNC_PTR( OnRadioButtonChecked, "RadioButtonChecked", panel )
+ {
+ OnButtonToggled( panel );
+ }
+
+ // response from the get password dialog
+ MESSAGE_FUNC_CHARPTR( OnJoinServerWithPassword, "JoinServerWithPassword", password );
+
+ MESSAGE_FUNC_INT_INT( OnConnectToGame, "ConnectedToGame", ip, port );
+
+ // vgui overrides
+ virtual void OnTick();
+ virtual void PerformLayout();
+
+ virtual void OnKeyCodePressed( vgui::KeyCode code );
+
+private:
+ STEAM_CALLBACK( CDialogGameInfo, OnPersonaStateChange, PersonaStateChange_t, m_CallbackPersonaStateChange );
+
+ long m_iRequestRetry; // time at which to retry the request
+ static int PlayerTimeColumnSortFunc(vgui::ListPanel *pPanel, const vgui::ListPanelItem &p1, const vgui::ListPanelItem &p2);
+
+ // methods
+ void RequestInfo();
+ void ConnectToServer();
+ void ShowAutoRetryOptions(bool state);
+ void ConstructConnectArgs( char *pchOptions, int cchOptions, const gameserveritem_t &server );
+ void ApplyConnectCommand( const gameserveritem_t &server );
+
+ vgui::Button *m_pConnectButton;
+ vgui::Button *m_pCloseButton;
+ vgui::Button *m_pRefreshButton;
+ vgui::Label *m_pInfoLabel;
+ vgui::ToggleButton *m_pAutoRetry;
+ vgui::RadioButton *m_pAutoRetryAlert;
+ vgui::RadioButton *m_pAutoRetryJoin;
+ vgui::ListPanel *m_pPlayerList;
+
+ enum { PING_TIMES_MAX = 4 };
+
+ // true if we should try connect to the server when it refreshes
+ bool m_bConnecting;
+
+ // password, if entered
+ char m_szPassword[64];
+
+ // state
+ bool m_bServerNotResponding;
+ bool m_bServerFull;
+ bool m_bShowAutoRetryToggle;
+ bool m_bShowingExtendedOptions;
+ uint64 m_SteamIDFriend;
+
+ CUtlString m_sConnectCode;
+ gameserveritem_t m_Server;
+ HServerQuery m_hPingQuery;
+ HServerQuery m_hPlayersQuery;
+ bool m_bPlayerListUpdatePending;
+};
+
+#endif // DIALOGGAMEINFO_H
diff --git a/serverbrowser/DialogServerPassword.cpp b/serverbrowser/DialogServerPassword.cpp
new file mode 100644
index 0000000..a5b2318
--- /dev/null
+++ b/serverbrowser/DialogServerPassword.cpp
@@ -0,0 +1,89 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CDialogServerPassword::CDialogServerPassword(vgui::Panel *parent) : Frame(parent, "DialogServerPassword")
+{
+ m_iServerID = -1;
+ SetSize(320, 240);
+ SetDeleteSelfOnClose(true);
+ SetSizeable(false);
+
+ m_pInfoLabel = new Label(this, "InfoLabel", "#ServerBrowser_ServerRequiresPassword");
+ m_pGameLabel = new Label(this, "GameLabel", "<game label>");
+ m_pPasswordEntry = new TextEntry(this, "PasswordEntry");
+ m_pConnectButton = new Button(this, "ConnectButton", "#ServerBrowser_Connect");
+ m_pPasswordEntry->SetTextHidden(true);
+
+ LoadControlSettings("Servers/DialogServerPassword.res");
+
+ SetTitle("#ServerBrowser_ServerRequiresPasswordTitle", true);
+
+ // set our initial position in the middle of the workspace
+ MoveToCenterOfScreen();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CDialogServerPassword::~CDialogServerPassword()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: initializes the dialog and brings it to the foreground
+//-----------------------------------------------------------------------------
+void CDialogServerPassword::Activate(const char *serverName, unsigned int serverID)
+{
+ m_pGameLabel->SetText(serverName);
+ m_iServerID = serverID;
+
+ m_pConnectButton->SetAsDefaultButton(true);
+ m_pPasswordEntry->RequestFocus();
+ BaseClass::Activate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *command -
+//-----------------------------------------------------------------------------
+void CDialogServerPassword::OnCommand(const char *command)
+{
+ bool bClose = false;
+
+ if (!Q_stricmp(command, "Connect"))
+ {
+ KeyValues *msg = new KeyValues("JoinServerWithPassword");
+ char buf[64];
+ m_pPasswordEntry->GetText(buf, sizeof(buf)-1);
+ msg->SetString("password", buf);
+ msg->SetInt("serverID", m_iServerID);
+ PostActionSignal(msg);
+
+ bClose = true;
+ }
+ else if (!Q_stricmp(command, "Close"))
+ {
+ bClose = true;
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+
+ if (bClose)
+ {
+ PostMessage(this, new KeyValues("Close"));
+ }
+}
+
diff --git a/serverbrowser/DialogServerPassword.h b/serverbrowser/DialogServerPassword.h
new file mode 100644
index 0000000..eae8dd3
--- /dev/null
+++ b/serverbrowser/DialogServerPassword.h
@@ -0,0 +1,46 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef DIALOGSERVERPASSWORD_H
+#define DIALOGSERVERPASSWORD_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Prompt for user to enter a password to be able to connect to the server
+//-----------------------------------------------------------------------------
+class CDialogServerPassword : public vgui::Frame
+{
+public:
+ CDialogServerPassword(vgui::Panel *parent);
+ ~CDialogServerPassword();
+
+ // initializes the dialog and brings it to the foreground
+ void Activate(const char *serverName, unsigned int serverID);
+
+ /* message returned:
+ "JoinServerWithPassword"
+ "serverID"
+ "password"
+ */
+
+private:
+ virtual void OnCommand(const char *command);
+
+ vgui::Label *m_pInfoLabel;
+ vgui::Label *m_pGameLabel;
+ vgui::TextEntry *m_pPasswordEntry;
+ vgui::Button *m_pConnectButton;
+
+ typedef vgui::Frame BaseClass;
+
+ int m_iServerID;
+};
+
+
+#endif // DIALOGSERVERPASSWORD_H
diff --git a/serverbrowser/FavoriteGames.cpp b/serverbrowser/FavoriteGames.cpp
new file mode 100644
index 0000000..0149baf
--- /dev/null
+++ b/serverbrowser/FavoriteGames.cpp
@@ -0,0 +1,214 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CFavoriteGames::CFavoriteGames(vgui::Panel *parent) :
+ CBaseGamesPage(parent, "FavoriteGames", eFavoritesServer )
+{
+ m_bRefreshOnListReload = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CFavoriteGames::~CFavoriteGames()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: loads favorites list from disk
+//-----------------------------------------------------------------------------
+void CFavoriteGames::LoadFavoritesList()
+{
+ if ( steamapicontext->SteamMatchmaking() && steamapicontext->SteamMatchmaking()->GetFavoriteGameCount() == 0 )
+ {
+ // set empty message
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoFavoriteServers");
+ }
+ else
+ {
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoInternetGamesResponded");
+
+ }
+
+ if ( m_bRefreshOnListReload )
+ {
+ m_bRefreshOnListReload = false;
+ StartRefresh();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the game list supports the specified ui elements
+//-----------------------------------------------------------------------------
+bool CFavoriteGames::SupportsItem(InterfaceItem_e item)
+{
+ switch (item)
+ {
+ case FILTERS:
+ case ADDSERVER:
+ return true;
+
+ case ADDCURRENTSERVER:
+ return !IsSteam() && BFiltersVisible();
+
+ case GETNEWLIST:
+ default:
+ return false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the current refresh list is complete
+//-----------------------------------------------------------------------------
+void CFavoriteGames::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
+{
+ SetRefreshing(false);
+ if ( steamapicontext->SteamMatchmaking() && steamapicontext->SteamMatchmaking()->GetFavoriteGameCount() == 0 )
+ {
+ // set empty message
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoFavoriteServers");
+ }
+ else
+ {
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoInternetGamesResponded");
+
+ }
+ m_pGameList->SortList();
+
+ BaseClass::RefreshComplete( hReq, response );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: opens context menu (user right clicked on a server)
+//-----------------------------------------------------------------------------
+void CFavoriteGames::OnOpenContextMenu(int itemID)
+{
+ CServerContextMenu *menu = ServerBrowserDialog().GetContextMenu(GetActiveList());
+
+ // get the server
+ int serverID = GetSelectedServerID();
+
+ if ( serverID != -1 )
+ {
+ // Activate context menu
+ menu->ShowMenu(this, serverID, true, true, true, false);
+ menu->AddMenuItem("RemoveServer", "#ServerBrowser_RemoveServerFromFavorites", new KeyValues("RemoveFromFavorites"), this);
+ }
+ else
+ {
+ // no selected rows, so don't display default stuff in menu
+ menu->ShowMenu( this,(uint32)-1, false, false, false, false );
+ }
+
+ menu->AddMenuItem("AddServerByName", "#ServerBrowser_AddServerByIP", new KeyValues("AddServerByName"), this);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: removes a server from the favorites
+//-----------------------------------------------------------------------------
+void CFavoriteGames::OnRemoveFromFavorites()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() || !steamapicontext->SteamMatchmaking() )
+ return;
+
+ // iterate the selection
+ for ( int iGame = 0; iGame < m_pGameList->GetSelectedItemsCount(); iGame++ )
+ {
+ int itemID = m_pGameList->GetSelectedItem( iGame );
+ int serverID = m_pGameList->GetItemData(itemID)->userData;
+
+ gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()->GetServerDetails( m_hRequest, serverID );
+
+ if ( pServer )
+ {
+ steamapicontext->SteamMatchmaking()->RemoveFavoriteGame( pServer->m_nAppID, pServer->m_NetAdr.GetIP(), pServer->m_NetAdr.GetConnectionPort(), pServer->m_NetAdr.GetQueryPort(), k_unFavoriteFlagFavorite );
+ }
+ }
+
+ UpdateStatus();
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a server by IP address
+//-----------------------------------------------------------------------------
+void CFavoriteGames::OnAddServerByName()
+{
+ // open the add server dialog
+ CDialogAddServer *dlg = new CDialogAddServer( &ServerBrowserDialog(), this );
+ dlg->MoveToCenterOfScreen();
+ dlg->DoModal();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds the currently connected server to the list
+//-----------------------------------------------------------------------------
+void CFavoriteGames::OnAddCurrentServer()
+{
+ gameserveritem_t *pConnected = ServerBrowserDialog().GetCurrentConnectedServer();
+
+ if ( pConnected && steamapicontext->SteamMatchmaking() )
+ {
+ steamapicontext->SteamMatchmaking()->AddFavoriteGame( pConnected->m_nAppID, pConnected->m_NetAdr.GetIP(), pConnected->m_NetAdr.GetConnectionPort(), pConnected->m_NetAdr.GetQueryPort(), k_unFavoriteFlagFavorite, time( NULL ) );
+ m_bRefreshOnListReload = true;
+
+ if ( GameSupportsReplay() )
+ {
+ // send command to propagate to the client so the client can send it on to the GC
+ char command[ 256 ];
+ Q_snprintf( command, Q_ARRAYSIZE( command ), "rfgc %s\n", pConnected->m_NetAdr.GetConnectionAddressString() );
+ g_pRunGameEngine->AddTextCommand( command );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse posted messages
+//
+//-----------------------------------------------------------------------------
+void CFavoriteGames::OnCommand(const char *command)
+{
+ if (!Q_stricmp(command, "AddServerByName"))
+ {
+ OnAddServerByName();
+ }
+ else if (!Q_stricmp(command, "AddCurrentServer" ))
+ {
+ OnAddCurrentServer();
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: enables adding server
+//-----------------------------------------------------------------------------
+void CFavoriteGames::OnConnectToGame()
+{
+ m_pAddCurrentServer->SetEnabled( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: disables adding current server
+//-----------------------------------------------------------------------------
+void CFavoriteGames::OnDisconnectFromGame( void )
+{
+ m_pAddCurrentServer->SetEnabled( false );
+}
diff --git a/serverbrowser/FavoriteGames.h b/serverbrowser/FavoriteGames.h
new file mode 100644
index 0000000..8355005
--- /dev/null
+++ b/serverbrowser/FavoriteGames.h
@@ -0,0 +1,55 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef FAVORITEGAMES_H
+#define FAVORITEGAMES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Favorite games list
+//-----------------------------------------------------------------------------
+class CFavoriteGames : public CBaseGamesPage
+{
+ DECLARE_CLASS_SIMPLE( CFavoriteGames, CBaseGamesPage );
+
+public:
+ CFavoriteGames(vgui::Panel *parent);
+ ~CFavoriteGames();
+
+ // favorites list, loads/saves into keyvalues
+ void LoadFavoritesList();
+
+ // IGameList handlers
+ // returns true if the game list supports the specified ui elements
+ virtual bool SupportsItem(InterfaceItem_e item);
+
+ // called when the current refresh list is complete
+ virtual void RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response );
+
+ // passed from main server browser window instead of messages
+ void OnConnectToGame();
+ void OnDisconnectFromGame( void );
+
+ void SetRefreshOnReload() { m_bRefreshOnListReload = true; }
+
+private:
+ // context menu message handlers
+ MESSAGE_FUNC_INT( OnOpenContextMenu, "OpenContextMenu", itemID );
+ MESSAGE_FUNC( OnRemoveFromFavorites, "RemoveFromFavorites" );
+ MESSAGE_FUNC( OnAddServerByName, "AddServerByName" );
+
+ void OnAddCurrentServer( void );
+
+ void OnCommand(const char *command);
+
+ bool m_bRefreshOnListReload;
+};
+
+
+#endif // FAVORITEGAMES_H
diff --git a/serverbrowser/FriendsGames.cpp b/serverbrowser/FriendsGames.cpp
new file mode 100644
index 0000000..daa14be
--- /dev/null
+++ b/serverbrowser/FriendsGames.cpp
@@ -0,0 +1,88 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CFriendsGames::CFriendsGames(vgui::Panel *parent) :
+ CBaseGamesPage(parent, "FriendsGames", eFriendsServer )
+{
+ m_iServerRefreshCount = 0;
+
+ if ( !IsSteamGameServerBrowsingEnabled() )
+ {
+ m_pGameList->SetEmptyListText("#ServerBrowser_OfflineMode");
+ m_pConnect->SetEnabled( false );
+ m_pRefreshAll->SetEnabled( false );
+ m_pRefreshQuick->SetEnabled( false );
+ m_pAddServer->SetEnabled( false );
+ m_pFilter->SetEnabled( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CFriendsGames::~CFriendsGames()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the game list supports the specified ui elements
+//-----------------------------------------------------------------------------
+bool CFriendsGames::SupportsItem(InterfaceItem_e item)
+{
+ switch (item)
+ {
+ case FILTERS:
+ return true;
+
+ case GETNEWLIST:
+ default:
+ return false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the current refresh list is complete
+//-----------------------------------------------------------------------------
+void CFriendsGames::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
+{
+ SetRefreshing(false);
+ m_pGameList->SortList();
+ m_iServerRefreshCount = 0;
+
+ if ( IsSteamGameServerBrowsingEnabled() )
+ {
+ // set empty message
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoFriendsServers");
+ }
+
+ BaseClass::RefreshComplete( hReq, response );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: opens context menu (user right clicked on a server)
+//-----------------------------------------------------------------------------
+void CFriendsGames::OnOpenContextMenu(int itemID)
+{
+ // get the server
+ int serverID = GetSelectedServerID();
+
+ if ( serverID == -1 )
+ return;
+
+ // Activate context menu
+ CServerContextMenu *menu = ServerBrowserDialog().GetContextMenu(GetActiveList());
+ menu->ShowMenu(this, serverID, true, true, true, true);
+}
diff --git a/serverbrowser/FriendsGames.h b/serverbrowser/FriendsGames.h
new file mode 100644
index 0000000..84c383d
--- /dev/null
+++ b/serverbrowser/FriendsGames.h
@@ -0,0 +1,39 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef FRIENDSGAMES_H
+#define FRIENDSGAMES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Favorite games list
+//-----------------------------------------------------------------------------
+class CFriendsGames : public CBaseGamesPage
+{
+ DECLARE_CLASS_SIMPLE( CFriendsGames, CBaseGamesPage );
+
+public:
+ CFriendsGames(vgui::Panel *parent);
+ ~CFriendsGames();
+
+ // IGameList handlers
+ // returns true if the game list supports the specified ui elements
+ virtual bool SupportsItem(InterfaceItem_e item);
+
+ // called when the current refresh list is complete
+ virtual void RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response );
+
+private:
+ // context menu message handlers
+ MESSAGE_FUNC_INT( OnOpenContextMenu, "OpenContextMenu", itemID );
+
+ int m_iServerRefreshCount; // number of servers refreshed
+};
+
+#endif // FRIENDSGAMES_H
diff --git a/serverbrowser/HistoryGames.cpp b/serverbrowser/HistoryGames.cpp
new file mode 100644
index 0000000..565c313
--- /dev/null
+++ b/serverbrowser/HistoryGames.cpp
@@ -0,0 +1,136 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CHistoryGames::CHistoryGames(vgui::Panel *parent) :
+ CBaseGamesPage(parent, "HistoryGames", eHistoryServer )
+{
+ m_bRefreshOnListReload = false;
+ m_pGameList->AddColumnHeader(10, "LastPlayed", "#ServerBrowser_LastPlayed", 100);
+ m_pGameList->SetSortFunc(10, LastPlayedCompare);
+ m_pGameList->SetSortColumn(10);
+
+ if ( !IsSteamGameServerBrowsingEnabled() )
+ {
+ m_pGameList->SetEmptyListText("#ServerBrowser_OfflineMode");
+ m_pConnect->SetEnabled( false );
+ m_pRefreshAll->SetEnabled( false );
+ m_pRefreshQuick->SetEnabled( false );
+ m_pAddServer->SetEnabled( false );
+ m_pFilter->SetEnabled( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CHistoryGames::~CHistoryGames()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: loads favorites list from disk
+//-----------------------------------------------------------------------------
+void CHistoryGames::LoadHistoryList()
+{
+ if ( IsSteamGameServerBrowsingEnabled() )
+ {
+ // set empty message
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoServersPlayed");
+ }
+
+ if ( m_bRefreshOnListReload )
+ {
+ m_bRefreshOnListReload = false;
+ StartRefresh();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the game list supports the specified ui elements
+//-----------------------------------------------------------------------------
+bool CHistoryGames::SupportsItem(InterfaceItem_e item)
+{
+ switch (item)
+ {
+ case FILTERS:
+ return true;
+
+ case ADDSERVER:
+ case GETNEWLIST:
+ default:
+ return false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the current refresh list is complete
+//-----------------------------------------------------------------------------
+void CHistoryGames::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
+{
+ SetRefreshing(false);
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoServersPlayed");
+ m_pGameList->SortList();
+
+ BaseClass::RefreshComplete( hReq, response );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: opens context menu (user right clicked on a server)
+//-----------------------------------------------------------------------------
+void CHistoryGames::OnOpenContextMenu(int itemID)
+{
+ CServerContextMenu *menu = ServerBrowserDialog().GetContextMenu(GetActiveList());
+
+ // get the server
+ int serverID = GetSelectedServerID();
+
+ if( serverID != -1 )
+ {
+ // Activate context menu
+ menu->ShowMenu(this, serverID, true, true, true, true);
+ menu->AddMenuItem("RemoveServer", "#ServerBrowser_RemoveServerFromHistory", new KeyValues("RemoveFromHistory"), this);
+ }
+ else
+ {
+ // no selected rows, so don't display default stuff in menu
+ menu->ShowMenu(this, (uint32)-1, false, false, false, false);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: removes a server from the favorites
+//-----------------------------------------------------------------------------
+void CHistoryGames::OnRemoveFromHistory()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() || !steamapicontext->SteamMatchmaking() )
+ return;
+
+ // iterate the selection
+ for ( int i = m_pGameList->GetSelectedItemsCount() - 1; i >= 0; i-- )
+ {
+ int itemID = m_pGameList->GetSelectedItem( i );
+ int serverID = m_pGameList->GetItemData(itemID)->userData;
+
+ gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()->GetServerDetails( m_hRequest, serverID );
+ if ( pServer )
+ steamapicontext->SteamMatchmaking()->RemoveFavoriteGame( pServer->m_nAppID, pServer->m_NetAdr.GetIP(), pServer->m_NetAdr.GetConnectionPort(), pServer->m_NetAdr.GetQueryPort(), k_unFavoriteFlagHistory );
+ }
+
+ UpdateStatus();
+ InvalidateLayout();
+ Repaint();
+}
+
diff --git a/serverbrowser/HistoryGames.h b/serverbrowser/HistoryGames.h
new file mode 100644
index 0000000..6dbbf44
--- /dev/null
+++ b/serverbrowser/HistoryGames.h
@@ -0,0 +1,46 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef HISTORYGAMES_H
+#define HISTORYGAMES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: History of all the servers joined
+//-----------------------------------------------------------------------------
+class CHistoryGames : public CBaseGamesPage
+{
+ DECLARE_CLASS_SIMPLE( CHistoryGames, CBaseGamesPage );
+
+public:
+ CHistoryGames(vgui::Panel *parent);
+ ~CHistoryGames();
+
+ // favorites list, loads/saves into keyvalues
+ void LoadHistoryList();
+
+
+ // IGameList handlers
+ // returns true if the game list supports the specified ui elements
+ virtual bool SupportsItem(InterfaceItem_e item);
+
+ // called when the current refresh list is complete
+ virtual void RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response );
+
+ void SetRefreshOnReload() { m_bRefreshOnListReload = true; }
+
+private:
+ // context menu message handlers
+ MESSAGE_FUNC_INT( OnOpenContextMenu, "OpenContextMenu", itemID );
+ MESSAGE_FUNC( OnRemoveFromHistory, "RemoveFromHistory" );
+
+ bool m_bRefreshOnListReload;
+};
+
+
+#endif // HISTORYGAMES_H
diff --git a/serverbrowser/InternetGames.cpp b/serverbrowser/InternetGames.cpp
new file mode 100644
index 0000000..208bea4
--- /dev/null
+++ b/serverbrowser/InternetGames.cpp
@@ -0,0 +1,319 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+// How often to re-sort the server list
+const float MINIMUM_SORT_TIME = 1.5f;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// NOTE: m_Servers can not use more than 96 sockets, else it will
+// cause internet explorer to Stop working under win98 SE!
+//-----------------------------------------------------------------------------
+CInternetGames::CInternetGames(vgui::Panel *parent, const char *panelName, EPageType eType ) :
+ CBaseGamesPage(parent, panelName, eType )
+{
+ m_fLastSort = 0.0f;
+ m_bDirty = false;
+ m_bRequireUpdate = true;
+ m_bOfflineMode = !IsSteamGameServerBrowsingEnabled();
+
+ m_bAnyServersRetrievedFromMaster = false;
+ m_bNoServersListedOnMaster = false;
+ m_bAnyServersRespondedToQuery = false;
+
+ m_pLocationFilter->DeleteAllItems();
+ KeyValues *kv = new KeyValues("Regions");
+ if (kv->LoadFromFile( g_pFullFileSystem, "servers/Regions.vdf", NULL))
+ {
+ // iterate the list loading all the servers
+ for (KeyValues *srv = kv->GetFirstSubKey(); srv != NULL; srv = srv->GetNextKey())
+ {
+ struct regions_s region;
+
+ region.name = srv->GetString("text");
+ region.code = srv->GetInt("code");
+ KeyValues *regionKV = new KeyValues("region", "code", region.code);
+ m_pLocationFilter->AddItem( region.name.String(), regionKV );
+ regionKV->deleteThis();
+ m_Regions.AddToTail(region);
+ }
+ }
+ else
+ {
+ Assert(!("Could not load file servers/Regions.vdf; server browser will not function."));
+ }
+ kv->deleteThis();
+
+ LoadFilterSettings();
+
+ ivgui()->AddTickSignal( GetVPanel(), 250 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CInternetGames::~CInternetGames()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInternetGames::PerformLayout()
+{
+ if ( !m_bOfflineMode && m_bRequireUpdate && ServerBrowserDialog().IsVisible() )
+ {
+ PostMessage( this, new KeyValues( "GetNewServerList" ), 0.1f );
+ m_bRequireUpdate = false;
+ }
+
+ if ( m_bOfflineMode )
+ {
+ m_pGameList->SetEmptyListText("#ServerBrowser_OfflineMode");
+ m_pConnect->SetEnabled( false );
+ m_pRefreshAll->SetEnabled( false );
+ m_pRefreshQuick->SetEnabled( false );
+ m_pAddServer->SetEnabled( false );
+ m_pFilter->SetEnabled( false );
+ }
+
+ BaseClass::PerformLayout();
+ m_pLocationFilter->SetEnabled(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activates the page, starts refresh if needed
+//-----------------------------------------------------------------------------
+void CInternetGames::OnPageShow()
+{
+ if ( m_pGameList->GetItemCount() == 0 && ServerBrowserDialog().IsVisible() )
+ BaseClass::OnPageShow();
+ // the "internet games" tab (unlike the other browser tabs)
+ // does not automatically start a query when the user
+ // navigates to this tab unless they have no servers listed.
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame, maintains sockets and runs refreshes
+//-----------------------------------------------------------------------------
+void CInternetGames::OnTick()
+{
+ if ( m_bOfflineMode )
+ {
+ BaseClass::OnTick();
+ return;
+ }
+
+ BaseClass::OnTick();
+
+ CheckRedoSort();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles incoming server refresh data
+// updates the server browser with the refreshed information from the server itself
+//-----------------------------------------------------------------------------
+void CInternetGames::ServerResponded( HServerListRequest hReq, int iServer )
+{
+ m_bDirty = true;
+ BaseClass::ServerResponded( hReq, iServer );
+ m_bAnyServersRespondedToQuery = true;
+ m_bAnyServersRetrievedFromMaster = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInternetGames::ServerFailedToRespond( HServerListRequest hReq, int iServer )
+{
+ m_bDirty = true;
+ gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()->GetServerDetails( hReq, iServer );
+ Assert( pServer );
+
+ if ( pServer->m_bHadSuccessfulResponse )
+ {
+ // if it's had a successful response in the past, leave it on
+ ServerResponded( hReq, iServer );
+ }
+ else
+ {
+ int iServerMap = m_mapServers.Find( iServer );
+ if ( iServerMap != m_mapServers.InvalidIndex() )
+ RemoveServer( m_mapServers[ iServerMap ] );
+ // we've never had a good response from this server, remove it from the list
+ m_iServerRefreshCount++;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when server refresh has been completed
+//-----------------------------------------------------------------------------
+void CInternetGames::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
+{
+ SetRefreshing(false);
+ UpdateFilterSettings();
+
+ if ( response != eServerFailedToRespond )
+ {
+ if ( m_bAnyServersRespondedToQuery )
+ {
+ m_pGameList->SetEmptyListText( GetStringNoUnfilteredServers() );
+ }
+ else if ( response == eNoServersListedOnMasterServer )
+ {
+ m_pGameList->SetEmptyListText( GetStringNoUnfilteredServersOnMaster() );
+ }
+ else
+ {
+ m_pGameList->SetEmptyListText( GetStringNoServersResponded() );
+ }
+ }
+ else
+ {
+ m_pGameList->SetEmptyListText("#ServerBrowser_MasterServerNotResponsive");
+ }
+
+ // perform last sort
+ m_bDirty = false;
+ m_fLastSort = Plat_FloatTime();
+ if (IsVisible())
+ {
+ m_pGameList->SortList();
+ }
+
+ UpdateStatus();
+
+ BaseClass::RefreshComplete( hReq, response );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInternetGames::GetNewServerList()
+{
+ BaseClass::GetNewServerList();
+ UpdateStatus();
+
+ m_bRequireUpdate = false;
+ m_bAnyServersRetrievedFromMaster = false;
+ m_bAnyServersRespondedToQuery = false;
+
+ m_pGameList->DeleteAllItems();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the game list supports the specified ui elements
+//-----------------------------------------------------------------------------
+bool CInternetGames::SupportsItem(IGameList::InterfaceItem_e item)
+{
+ switch (item)
+ {
+ case FILTERS:
+ case GETNEWLIST:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInternetGames::CheckRedoSort( void )
+{
+ float fCurTime;
+
+ // No changes detected
+ if ( !m_bDirty )
+ return;
+
+ fCurTime = Plat_FloatTime();
+ // Not time yet
+ if ( fCurTime - m_fLastSort < MINIMUM_SORT_TIME)
+ return;
+
+ // postpone sort if mouse button is down
+ if ( input()->IsMouseDown(MOUSE_LEFT) || input()->IsMouseDown(MOUSE_RIGHT) )
+ {
+ // don't sort for at least another second
+ m_fLastSort = fCurTime - MINIMUM_SORT_TIME + 1.0f;
+ return;
+ }
+
+ // Reset timer
+ m_bDirty = false;
+ m_fLastSort = fCurTime;
+
+ // Force sort to occur now!
+ m_pGameList->SortList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: opens context menu (user right clicked on a server)
+//-----------------------------------------------------------------------------
+void CInternetGames::OnOpenContextMenu(int itemID)
+{
+ // get the server
+ int serverID = GetSelectedServerID();
+
+ if ( serverID == -1 )
+ return;
+
+ // Activate context menu
+ CServerContextMenu *menu = ServerBrowserDialog().GetContextMenu(GetActiveList());
+ menu->ShowMenu(this, serverID, true, true, true, true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes a single server
+//-----------------------------------------------------------------------------
+void CInternetGames::OnRefreshServer(int serverID)
+{
+ BaseClass::OnRefreshServer( serverID );
+
+ ServerBrowserDialog().UpdateStatusText("#ServerBrowser_GettingNewServerList");
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: get the region code selected in the ui
+// Output: returns the region code the user wants to filter by
+//-----------------------------------------------------------------------------
+int CInternetGames::GetRegionCodeToFilter()
+{
+ KeyValues *kv = m_pLocationFilter->GetActiveItemUserData();
+ if ( kv )
+ return kv->GetInt( "code" );
+ else
+ return 255;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CInternetGames::CheckTagFilter( gameserveritem_t &server )
+{
+ // Servers without tags go in the official games, servers with tags go in custom games
+ bool bOfficialServer = !( server.m_szGameTags && server.m_szGameTags[0] );
+ if ( !bOfficialServer )
+ return false;
+
+ return true;
+}
diff --git a/serverbrowser/InternetGames.h b/serverbrowser/InternetGames.h
new file mode 100644
index 0000000..8779d1f
--- /dev/null
+++ b/serverbrowser/InternetGames.h
@@ -0,0 +1,83 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef INTERNETGAMES_H
+#define INTERNETGAMES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "BaseGamesPage.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Internet games list
+//-----------------------------------------------------------------------------
+class CInternetGames : public CBaseGamesPage
+{
+
+ DECLARE_CLASS_SIMPLE( CInternetGames, CBaseGamesPage );
+
+public:
+ CInternetGames( vgui::Panel *parent, const char *panelName = "InternetGames", EPageType eType = eInternetServer );
+ ~CInternetGames();
+
+ // property page handlers
+ virtual void OnPageShow();
+
+ // returns true if the game list supports the specified ui elements
+ virtual bool SupportsItem(IGameList::InterfaceItem_e item);
+
+ // gets a new server list
+ MESSAGE_FUNC( GetNewServerList, "GetNewServerList" );
+
+ // serverlist refresh responses
+ virtual void ServerResponded( HServerListRequest hReq, int iServer );
+ virtual void ServerFailedToRespond( HServerListRequest hReq, int iServer );
+ virtual void RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response );
+ MESSAGE_FUNC_INT( OnRefreshServer, "RefreshServer", serverID );
+
+ virtual int GetRegionCodeToFilter();
+ virtual bool CheckTagFilter( gameserveritem_t &server );
+
+protected:
+ // vgui overrides
+ virtual void PerformLayout();
+ virtual void OnTick();
+
+ virtual const char *GetStringNoUnfilteredServers() { return "#ServerBrowser_NoInternetGames"; }
+ virtual const char *GetStringNoUnfilteredServersOnMaster() { return "#ServerBrowser_MasterServerHasNoServersListed"; }
+ virtual const char *GetStringNoServersResponded() { return "#ServerBrowser_NoInternetGamesResponded"; }
+
+private:
+ // Called once per frame to see if sorting needs to occur again
+ void CheckRedoSort();
+ // Called once per frame to check re-send request to master server
+ void CheckRetryRequest( ESteamServerType serverType );
+ // opens context menu (user right clicked on a server)
+ MESSAGE_FUNC_INT( OnOpenContextMenu, "OpenContextMenu", itemID );
+
+ struct regions_s
+ {
+ CUtlSymbol name;
+ unsigned char code;
+ };
+
+ CUtlVector<struct regions_s> m_Regions; // list of the different regions you can query for
+
+ float m_fLastSort; // Time of last re-sort
+ bool m_bDirty; // Has the list been modified, thereby needing re-sort
+ bool m_bRequireUpdate; // checks whether we need an update upon opening
+
+ // error cases for if no servers are listed
+ bool m_bAnyServersRetrievedFromMaster;
+ bool m_bAnyServersRespondedToQuery;
+ bool m_bNoServersListedOnMaster;
+
+ bool m_bOfflineMode;
+};
+
+#endif // INTERNETGAMES_H
diff --git a/serverbrowser/LanGames.cpp b/serverbrowser/LanGames.cpp
new file mode 100644
index 0000000..c375845
--- /dev/null
+++ b/serverbrowser/LanGames.cpp
@@ -0,0 +1,160 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+const float BROADCAST_LIST_TIMEOUT = 0.4f;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CLanGames::CLanGames(vgui::Panel *parent, bool bAutoRefresh, const char *pCustomResFilename ) :
+ CBaseGamesPage(parent, "LanGames", eLANServer, pCustomResFilename)
+{
+ m_iServerRefreshCount = 0;
+ m_bRequesting = false;
+ m_bAutoRefresh = bAutoRefresh;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CLanGames::~CLanGames()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Activates the page, starts refresh
+//-----------------------------------------------------------------------------
+void CLanGames::OnPageShow()
+{
+ if ( m_bAutoRefresh )
+ StartRefresh();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame
+//-----------------------------------------------------------------------------
+void CLanGames::OnTick()
+{
+ BaseClass::OnTick();
+ CheckRetryRequest();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the game list supports the specified ui elements
+//-----------------------------------------------------------------------------
+bool CLanGames::SupportsItem(InterfaceItem_e item)
+{
+ switch (item)
+ {
+ case FILTERS:
+ return true;
+
+ case GETNEWLIST:
+ default:
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: starts the servers refreshing
+//-----------------------------------------------------------------------------
+void CLanGames::StartRefresh()
+{
+ BaseClass::StartRefresh();
+ m_fRequestTime = Plat_FloatTime();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Control which button are visible.
+//-----------------------------------------------------------------------------
+void CLanGames::ManualShowButtons( bool bShowConnect, bool bShowRefreshAll, bool bShowFilter )
+{
+ m_pConnect->SetVisible( bShowConnect );
+ m_pRefreshAll->SetVisible( bShowRefreshAll );
+ m_pFilter->SetVisible( bShowFilter );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: stops current refresh/GetNewServerList()
+//-----------------------------------------------------------------------------
+void CLanGames::StopRefresh()
+{
+ BaseClass::StopRefresh();
+ // clear update states
+ m_bRequesting = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check to see if we've finished looking for local servers
+//-----------------------------------------------------------------------------
+void CLanGames::CheckRetryRequest()
+{
+ if (!m_bRequesting)
+ return;
+
+ double curtime = Plat_FloatTime();
+ if (curtime - m_fRequestTime <= BROADCAST_LIST_TIMEOUT)
+ {
+ return;
+ }
+
+ // time has elapsed, finish up
+ m_bRequesting = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when a server response has timed out, remove it
+//-----------------------------------------------------------------------------
+void CLanGames::ServerFailedToRespond( HServerListRequest hReq, int iServer )
+{
+ int iServerMap = m_mapServers.Find( iServer );
+ if ( iServerMap != m_mapServers.InvalidIndex() )
+ RemoveServer( m_mapServers[ iServerMap ] );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the current refresh list is complete
+//-----------------------------------------------------------------------------
+void CLanGames::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
+{
+ SetRefreshing( false );
+ m_pGameList->SortList();
+ m_iServerRefreshCount = 0;
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoLanServers");
+ SetEmptyListText();
+
+ BaseClass::RefreshComplete( hReq, response );
+}
+
+void CLanGames::SetEmptyListText()
+{
+ m_pGameList->SetEmptyListText("#ServerBrowser_NoLanServers");
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: opens context menu (user right clicked on a server)
+//-----------------------------------------------------------------------------
+void CLanGames::OnOpenContextMenu(int row)
+{
+ int serverID = GetSelectedServerID();
+
+ if ( serverID == -1 )
+ return;
+
+ // Activate context menu
+ CServerContextMenu *menu = ServerBrowserDialog().GetContextMenu(GetActiveList());
+ menu->ShowMenu(this, serverID, true, true, true, false);
+}
+
diff --git a/serverbrowser/LanGames.h b/serverbrowser/LanGames.h
new file mode 100644
index 0000000..f1e3afa
--- /dev/null
+++ b/serverbrowser/LanGames.h
@@ -0,0 +1,81 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef LANGAMES_H
+#define LANGAMES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CLanBroadcastMsgHandler;
+
+//-----------------------------------------------------------------------------
+// Purpose: Favorite games list
+//-----------------------------------------------------------------------------
+class CLanGames : public CBaseGamesPage
+{
+ DECLARE_CLASS_SIMPLE( CLanGames, CBaseGamesPage );
+
+public:
+ CLanGames(vgui::Panel *parent, bool bAutoRefresh=true, const char *pCustomResFilename=NULL);
+ ~CLanGames();
+
+ // property page handlers
+ virtual void OnPageShow();
+
+ // IGameList handlers
+ // returns true if the game list supports the specified ui elements
+ virtual bool SupportsItem(InterfaceItem_e item);
+
+ // Control which button are visible.
+ void ManualShowButtons( bool bShowConnect, bool bShowRefreshAll, bool bShowFilter );
+
+ // If you pass NULL for pSpecificAddresses, it will broadcast on certain points.
+ // If you pass a non-null value, then it will send info queries directly to those ports.
+ void InternalGetNewServerList( CUtlVector<netadr_t> *pSpecificAddresses );
+
+ virtual void StartRefresh();
+
+ // stops current refresh/GetNewServerList()
+ virtual void StopRefresh();
+
+
+ // IServerRefreshResponse handlers
+ // called when a server response has timed out
+ virtual void ServerFailedToRespond( HServerListRequest hReq, int iServer );
+
+ // called when the current refresh list is complete
+ virtual void RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response );
+
+ // Tell the game list what to put in there when there are no games found.
+ virtual void SetEmptyListText();
+
+private:
+ // vgui message handlers
+ virtual void OnTick();
+
+ // lan timeout checking
+ virtual void CheckRetryRequest();
+
+ // context menu message handlers
+ MESSAGE_FUNC_INT( OnOpenContextMenu, "OpenContextMenu", itemID );
+
+ // number of servers refreshed
+ int m_iServerRefreshCount;
+
+ // true if we're broadcasting for servers
+ bool m_bRequesting;
+
+ // time at which we last broadcasted
+ double m_fRequestTime;
+
+ bool m_bAutoRefresh;
+};
+
+
+
+#endif // LANGAMES_H
diff --git a/serverbrowser/ModList.cpp b/serverbrowser/ModList.cpp
new file mode 100644
index 0000000..b95b304
--- /dev/null
+++ b/serverbrowser/ModList.cpp
@@ -0,0 +1,125 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Singleton accessor
+//-----------------------------------------------------------------------------
+CModList &ModList()
+{
+ static CModList s_ModList;
+ return s_ModList;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CModList::CModList()
+{
+ ParseSteamMods();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns number of mods
+//-----------------------------------------------------------------------------
+int CModList::ModCount()
+{
+ return m_ModList.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+const char *CModList::GetModName(int index)
+{
+ return m_ModList[index].description;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+const char *CModList::GetModDir(int index)
+{
+ return m_ModList[index].gamedir;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+const CGameID &CModList::GetAppID(int index) const
+{
+ return m_ModList[index].m_GameID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: get the modlist index for this app id
+//-----------------------------------------------------------------------------
+int CModList::GetIndex( const CGameID &iAppID ) const
+{
+ mod_t mod;
+ mod.m_GameID = iAppID;
+ return m_ModList.Find( mod );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the mod name for the associated gamedir
+//-----------------------------------------------------------------------------
+const char *CModList::GetModNameForModDir( const CGameID &gameID )
+{
+ int iApp = GetIndex( gameID );
+ if ( iApp != m_ModList.InvalidIndex() )
+ {
+ return m_ModList[iApp].description;
+ }
+
+ if ( ServerBrowserDialog().GetActiveModName() )
+ {
+ return ServerBrowserDialog().GetActiveGameName();
+ }
+ return "";
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: sort the mod list in alphabetical order
+//-----------------------------------------------------------------------------
+int CModList::ModNameCompare( const mod_t *pLeft, const mod_t *pRight )
+{
+ return ( Q_stricmp( pLeft->description, pRight->description ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: gets list of steam games we can filter for
+//-----------------------------------------------------------------------------
+void CModList::ParseSteamMods()
+{
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: load settings for an app
+//-----------------------------------------------------------------------------
+int CModList::LoadAppConfiguration( uint32 nAppID )
+{
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: add a vgui panel to message when the app list changes
+//-----------------------------------------------------------------------------
+void CModList::AddVGUIListener( vgui::VPANEL panel )
+{
+ m_VGUIListeners.AddToTail( panel );
+} \ No newline at end of file
diff --git a/serverbrowser/ModList.h b/serverbrowser/ModList.h
new file mode 100644
index 0000000..b899ad9
--- /dev/null
+++ b/serverbrowser/ModList.h
@@ -0,0 +1,62 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef MODLIST_H
+#define MODLIST_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles parsing of half-life directory for mod info
+//-----------------------------------------------------------------------------
+class CModList
+{
+public:
+ CModList();
+
+ int GetIndex( const CGameID &iAppID ) const;
+ void AddVGUIListener( vgui::VPANEL panel );
+
+ // returns number of mods
+ int ModCount();
+
+ // returns the full name of the mod, index valid in range [0, ModCount)
+ const char *GetModName( int index );
+
+ // returns mod directory string
+ const char *GetModDir( int index );
+
+ const CGameID &GetAppID( int index ) const;
+
+ // returns the mod name for the associated gamedir
+ const char *GetModNameForModDir( const CGameID &iAppID );
+
+private:
+ struct mod_t
+ {
+ char description[64];
+ char gamedir[64];
+ CGameID m_GameID;
+ int m_InternalAppId;
+ bool operator==( const mod_t& rhs ) const { return rhs.m_GameID == m_GameID; }
+ };
+
+ static int ModNameCompare( const mod_t *pLeft, const mod_t *pRight );
+ void ParseInstalledMods();
+ void ParseSteamMods();
+ int LoadAppConfiguration( uint32 nAppID );
+
+ CUtlVector<mod_t> m_ModList;
+ CUtlVector<vgui::VPANEL> m_VGUIListeners;
+};
+
+// singleton accessor
+extern CModList &ModList();
+
+
+#endif // MODLIST_H
diff --git a/serverbrowser/QuickListPanel.cpp b/serverbrowser/QuickListPanel.cpp
new file mode 100644
index 0000000..0b08ca0
--- /dev/null
+++ b/serverbrowser/QuickListPanel.cpp
@@ -0,0 +1,317 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Invisible panel that forwards up mouse movement
+//-----------------------------------------------------------------------------
+class CMouseMessageForwardingPanel : public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CMouseMessageForwardingPanel, vgui::Panel );
+public:
+ CMouseMessageForwardingPanel( Panel *parent, const char *name );
+
+ virtual void PerformLayout( void );
+ virtual void OnMousePressed( vgui::MouseCode code );
+ virtual void OnMouseDoublePressed( vgui::MouseCode code );
+ virtual void OnMouseWheeled(int delta);
+};
+
+CMouseMessageForwardingPanel::CMouseMessageForwardingPanel( Panel *parent, const char *name ) : BaseClass( parent, name )
+{
+ // don't draw an
+ SetPaintEnabled(false);
+ SetPaintBackgroundEnabled(false);
+ SetPaintBorderEnabled(false);
+}
+
+void CMouseMessageForwardingPanel::PerformLayout()
+{
+ // fill out the whole area
+ int w, t;
+ GetParent()->GetSize(w, t);
+ SetBounds(0, 0, w, t);
+}
+
+void CMouseMessageForwardingPanel::OnMousePressed( vgui::MouseCode code )
+{
+ if ( GetParent() )
+ {
+ GetParent()->OnMousePressed( code );
+ }
+}
+
+void CMouseMessageForwardingPanel::OnMouseDoublePressed( vgui::MouseCode code )
+{
+ if ( GetParent() )
+ {
+ GetParent()->OnMouseDoublePressed( code );
+ }
+}
+
+void CMouseMessageForwardingPanel::OnMouseWheeled(int delta)
+{
+ if ( GetParent() )
+ {
+ GetParent()->OnMouseWheeled( delta );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CQuickListPanel::CQuickListPanel( vgui::Panel* pParent, const char *pElementName ) : BaseClass( pParent, pElementName )
+{
+ SetParent( pParent );
+
+ m_pListPanelParent = pParent;
+
+ CMouseMessageForwardingPanel *panel = new CMouseMessageForwardingPanel(this, NULL);
+ panel->SetZPos(3);
+
+ m_pLatencyImage = new ImagePanel( this, "latencyimage" );
+ m_pPlayerCountLabel = new Label( this, "playercount", "" );
+ m_pOtherServersLabel = new Label( this, "otherservercount", "" );
+ m_pServerNameLabel = new Label( this, "servername", "" );
+ m_pBGroundPanel = new Panel( this, "background" );
+ m_pMapImage = new ImagePanel( this, "mapimage" );
+ m_pGameTypeLabel = new Label( this, "gametype", "" );
+ m_pMapNameLabel = new Label( this, "mapname", "" );
+ m_pLatencyLabel = new Label( this, "latencytext", "" );
+ m_pReplayImage = new ImagePanel( this, "replayimage" );
+
+ const char *pPathID = "PLATFORM";
+
+ if ( g_pFullFileSystem->FileExists( "servers/QuickListPanel.res", "MOD" ) )
+ {
+ pPathID = "MOD";
+ }
+
+ LoadControlSettings( "servers/QuickListPanel.res", pPathID );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuickListPanel::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ if ( pScheme && m_pBGroundPanel )
+ {
+ m_pBGroundPanel->SetBgColor( pScheme->GetColor("QuickListBGDeselected", Color(255, 255, 255, 0 ) ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuickListPanel::SetRefreshing( void )
+{
+ if ( m_pServerNameLabel )
+ {
+ m_pServerNameLabel->SetText( g_pVGuiLocalize->Find("#ServerBrowser_QuickListRefreshing") );
+ }
+
+ if ( m_pPlayerCountLabel )
+ {
+ m_pPlayerCountLabel->SetVisible( false );
+ }
+ if ( m_pOtherServersLabel )
+ {
+ m_pOtherServersLabel->SetVisible( false );
+ }
+
+ if ( m_pLatencyImage )
+ {
+ m_pLatencyImage->SetVisible( false );
+ }
+
+ if ( m_pReplayImage )
+ {
+ m_pReplayImage->SetVisible( false );
+ }
+
+ if ( m_pLatencyLabel )
+ {
+ m_pLatencyLabel->SetVisible( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuickListPanel::SetMapName( const char *pMapName )
+{
+ Q_strncpy( m_szMapName, pMapName, sizeof( m_szMapName ) );
+
+ if ( m_pMapNameLabel )
+ {
+ m_pMapNameLabel->SetText( pMapName );
+ m_pMapNameLabel->SizeToContents();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuickListPanel::SetGameType( const char *pGameType )
+{
+ if ( strlen ( pGameType ) == 0 )
+ {
+ m_pGameTypeLabel->SetVisible( false );
+ return;
+ }
+
+ char gametype[ 512 ];
+ Q_snprintf( gametype, sizeof( gametype ), "(%s)", pGameType );
+
+ m_pGameTypeLabel->SetText( gametype );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuickListPanel::SetServerInfo ( KeyValues *pKV, int iListID, int iTotalServers )
+{
+ if ( pKV == NULL )
+ return;
+
+ m_iListID = iListID;
+
+ m_pServerNameLabel->SetText( pKV->GetString( "name", " " ) );
+
+ int iPing = pKV->GetInt( "ping", 0 );
+
+ if ( iPing <= 100 )
+ {
+ m_pLatencyImage->SetImage( "../vgui/icon_con_high.vmt" );
+ }
+ else if ( iPing <= 150 )
+ {
+ m_pLatencyImage->SetImage( "../vgui/icon_con_medium.vmt" );
+ }
+ else
+ {
+ m_pLatencyImage->SetImage( "../vgui/icon_con_low.vmt" );
+ }
+
+ m_pLatencyImage->SetVisible( false );
+
+ if ( GameSupportsReplay() )
+ {
+ if ( pKV->GetInt( "Replay", 0 ) > 0 )
+ {
+ m_pReplayImage->SetVisible( true );
+ }
+ }
+
+ char ping[ 512 ];
+ Q_snprintf( ping, sizeof( ping ), "%d ms", iPing );
+
+ m_pLatencyLabel->SetText( ping );
+ m_pLatencyLabel->SetVisible( true );
+
+ wchar_t players[ 512 ];
+ wchar_t playercount[16];
+ wchar_t *pwszPlayers = g_pVGuiLocalize->Find("#ServerBrowser_Players");
+
+ g_pVGuiLocalize->ConvertANSIToUnicode( pKV->GetString( "players", " " ), playercount, sizeof( playercount ) );
+
+ _snwprintf( players, ARRAYSIZE( players ), L"%ls %ls", playercount, pwszPlayers );
+
+ m_pPlayerCountLabel->SetText( players );
+ m_pPlayerCountLabel->SetVisible( true );
+
+
+ // Now setup the other server count
+ if ( iTotalServers == 2 )
+ {
+ m_pOtherServersLabel->SetText( g_pVGuiLocalize->Find("#ServerBrowser_QuickListOtherServer") );
+ m_pOtherServersLabel->SetVisible( true );
+ }
+ else if ( iTotalServers > 2 )
+ {
+ wchar_t *pwszServers = g_pVGuiLocalize->Find("#ServerBrowser_QuickListOtherServers");
+ _snwprintf( playercount, Q_ARRAYSIZE(playercount), L"%d", (iTotalServers-1) );
+ g_pVGuiLocalize->ConstructString( players, sizeof( players ), pwszServers, 1, playercount );
+ m_pOtherServersLabel->SetText( players );
+ m_pOtherServersLabel->SetVisible( true );
+ }
+ else
+ {
+ m_pOtherServersLabel->SetVisible( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuickListPanel::SetImage( const char *pMapName )
+{
+ char path[ 512 ];
+ Q_snprintf( path, sizeof( path ), "materials/vgui/maps/menu_thumb_%s.vmt", pMapName );
+
+ char map[ 512 ];
+ Q_snprintf( map, sizeof( map ), "maps/%s.bsp", pMapName );
+
+ if ( g_pFullFileSystem->FileExists( map, "MOD" ) == false )
+ {
+ pMapName = "default_download";
+ }
+ else
+ {
+ if ( g_pFullFileSystem->FileExists( path, "MOD" ) == false )
+ {
+ pMapName = "default";
+ }
+ }
+
+ if ( m_pMapImage )
+ {
+ char imagename[ 512 ];
+ Q_snprintf( imagename, sizeof( imagename ), "..\\vgui\\maps\\menu_thumb_%s", pMapName );
+
+ m_pMapImage->SetImage ( imagename );
+ m_pMapImage->SetMouseInputEnabled( false );
+ }
+}
+
+void CQuickListPanel::OnMousePressed( vgui::MouseCode code )
+{
+ if ( m_pListPanelParent )
+ {
+ vgui::PanelListPanel *pParent = dynamic_cast < vgui::PanelListPanel *> ( m_pListPanelParent );
+
+ if ( pParent )
+ {
+ pParent->SetSelectedPanel( this );
+ m_pListPanelParent->CallParentFunction( new KeyValues("ItemSelected", "itemID", -1 ) );
+ }
+
+ if ( code == MOUSE_RIGHT )
+ {
+ m_pListPanelParent->CallParentFunction( new KeyValues("OpenContextMenu", "itemID", -1 ) );
+ }
+
+ }
+}
+
+void CQuickListPanel::OnMouseDoublePressed( vgui::MouseCode code )
+{
+ if ( code == MOUSE_RIGHT )
+ return;
+
+ // call the panel
+ OnMousePressed( code );
+
+ m_pListPanelParent->CallParentFunction( new KeyValues("ConnectToServer", "code", code) );
+}
diff --git a/serverbrowser/QuickListPanel.h b/serverbrowser/QuickListPanel.h
new file mode 100644
index 0000000..5672b2b
--- /dev/null
+++ b/serverbrowser/QuickListPanel.h
@@ -0,0 +1,83 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef QUICKLISTPANEL
+#define QUICKLISTPANEL
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Spectator games list
+//-----------------------------------------------------------------------------
+class CQuickListPanel : public vgui::EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( CQuickListPanel, vgui::EditablePanel );
+
+public:
+ CQuickListPanel( vgui::Panel *parent, const char *panelName );
+
+ virtual void ApplySchemeSettings(vgui::IScheme *pScheme);
+ void SetMapName( const char *pMapName );
+ void SetImage( const char *pMapName );
+ void SetGameType( const char *pGameType );
+ const char *GetMapName( void ) { return m_szMapName; }
+ void SetRefreshing( void );
+
+ virtual void OnMousePressed( vgui::MouseCode code );
+ virtual void OnMouseDoublePressed( vgui::MouseCode code );
+ void SetServerInfo ( KeyValues *pKV, int iListID, int iTotalServers );
+ int GetListID( void ) { return m_iListID; }
+
+
+ MESSAGE_FUNC_INT( OnPanelSelected, "PanelSelected", state )
+ {
+ if ( state )
+ {
+ vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
+
+ if ( pScheme && m_pBGroundPanel )
+ {
+ m_pBGroundPanel->SetBgColor( pScheme->GetColor("QuickListBGSelected", Color(255, 255, 255, 0 ) ) );
+ }
+ }
+ else
+ {
+ vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
+
+ if ( pScheme && m_pBGroundPanel )
+ {
+ m_pBGroundPanel->SetBgColor( pScheme->GetColor("QuickListBGDeselected", Color(255, 255, 255, 0 ) ) );
+ }
+ }
+
+ PostMessage( GetParent()->GetVParent(), new KeyValues("PanelSelected") );
+ }
+
+private:
+
+ char m_szMapName[128];
+
+ vgui::ImagePanel *m_pLatencyImage;
+ vgui::Label *m_pLatencyLabel;
+ vgui::Label *m_pPlayerCountLabel;
+ vgui::Label *m_pOtherServersLabel;
+ vgui::Label *m_pServerNameLabel;
+ vgui::Panel *m_pBGroundPanel;
+ vgui::ImagePanel *m_pMapImage;
+
+ vgui::Panel *m_pListPanelParent;
+ vgui::Label *m_pGameTypeLabel;
+ vgui::Label *m_pMapNameLabel;
+
+ vgui::ImagePanel *m_pReplayImage;
+
+ int m_iListID;
+};
+
+
+#endif // QUICKLISTPANEL
diff --git a/serverbrowser/ServerBrowser.cpp b/serverbrowser/ServerBrowser.cpp
new file mode 100644
index 0000000..ff66bbb
--- /dev/null
+++ b/serverbrowser/ServerBrowser.cpp
@@ -0,0 +1,489 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+// expose the server browser interfaces
+CServerBrowser g_ServerBrowserSingleton;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerBrowser, IServerBrowser, SERVERBROWSER_INTERFACE_VERSION, g_ServerBrowserSingleton);
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerBrowser, IVGuiModule, "VGuiModuleServerBrowser001", g_ServerBrowserSingleton); // the interface loaded by PlatformMenu.vdf
+
+// singleton accessor
+CServerBrowser &ServerBrowser()
+{
+ return g_ServerBrowserSingleton;
+}
+
+IRunGameEngine *g_pRunGameEngine = NULL;
+
+static CSteamAPIContext g_SteamAPIContext;
+CSteamAPIContext *steamapicontext = &g_SteamAPIContext;
+
+IEngineReplay *g_pEngineReplay = NULL;
+
+ConVar sb_firstopentime( "sb_firstopentime", "0", FCVAR_DEVELOPMENTONLY, "Indicates the time the server browser was first opened." );
+ConVar sb_numtimesopened( "sb_numtimesopened", "0", FCVAR_DEVELOPMENTONLY, "Indicates the number of times the server browser was opened this session." );
+
+// the original author of this code felt strdup was not acceptible.
+inline char *CloneString( const char *str )
+{
+ char *cloneStr = new char [ strlen(str)+1 ];
+ strcpy( cloneStr, str );
+ return cloneStr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CServerBrowser::CServerBrowser()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CServerBrowser::~CServerBrowser()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CServerBrowser::CreateDialog()
+{
+ if (!m_hInternetDlg.Get())
+ {
+ m_hInternetDlg = new CServerBrowserDialog(NULL); // SetParent() call below fills this in
+ m_hInternetDlg->Initialize();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: links to vgui and engine interfaces
+//-----------------------------------------------------------------------------
+bool CServerBrowser::Initialize(CreateInterfaceFn *factorylist, int factoryCount)
+{
+ ConnectTier1Libraries( factorylist, factoryCount );
+ ConVar_Register();
+ ConnectTier2Libraries( factorylist, factoryCount );
+ ConnectTier3Libraries( factorylist, factoryCount );
+ g_pRunGameEngine = NULL;
+
+ for ( int i = 0; i < factoryCount; ++i )
+ {
+ if ( !g_pEngineReplay )
+ {
+ g_pEngineReplay = ( IEngineReplay * )factorylist[ i ]( ENGINE_REPLAY_INTERFACE_VERSION, NULL );
+ }
+ }
+
+ SteamAPI_InitSafe();
+ SteamAPI_SetTryCatchCallbacks( false ); // We don't use exceptions, so tell steam not to use try/catch in callback handlers
+ steamapicontext->Init();
+
+ for (int i = 0; i < factoryCount; i++)
+ {
+ if (!g_pRunGameEngine)
+ {
+ g_pRunGameEngine = (IRunGameEngine *)(factorylist[i])(RUNGAMEENGINE_INTERFACE_VERSION, NULL);
+ }
+ }
+
+ // load the vgui interfaces
+#if defined( STEAM ) || defined( HL1 )
+ if ( !vgui::VGuiControls_Init("ServerBrowser", factorylist, factoryCount) )
+#else
+ if ( !vgui::VGui_InitInterfacesList("ServerBrowser", factorylist, factoryCount) )
+#endif
+ return false;
+
+ // load localization file
+ g_pVGuiLocalize->AddFile( "servers/serverbrowser_%language%.txt" );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: links to other modules interfaces (tracker)
+//-----------------------------------------------------------------------------
+bool CServerBrowser::PostInitialize(CreateInterfaceFn *modules, int factoryCount)
+{
+ // find the interfaces we need
+ for (int i = 0; i < factoryCount; i++)
+ {
+ if (!g_pRunGameEngine)
+ {
+ g_pRunGameEngine = (IRunGameEngine *)(modules[i])(RUNGAMEENGINE_INTERFACE_VERSION, NULL);
+ }
+ }
+
+ CreateDialog();
+ m_hInternetDlg->SetVisible(false);
+
+ return g_pRunGameEngine;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: true if the user can't play a game due to VAC banning
+//-----------------------------------------------------------------------------
+bool CServerBrowser::IsVACBannedFromGame( int nAppID )
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Marks that the tool/game loading us intends to feed us workshop information
+//-----------------------------------------------------------------------------
+void CServerBrowser::SetWorkshopEnabled( bool bManaged )
+{
+ m_bWorkshopEnabled = bManaged;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a mapname to our known user-subscribed workshop maps list
+//-----------------------------------------------------------------------------
+void CServerBrowser::AddWorkshopSubscribedMap( const char *pszMapName )
+{
+ CUtlString strMap( pszMapName );
+ if ( !IsWorkshopSubscribedMap( strMap ) )
+ {
+ m_vecWorkshopSubscribedMaps.AddToTail( strMap );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: remove a mapname to our known user-subscribed workshop maps list
+//-----------------------------------------------------------------------------
+void CServerBrowser::RemoveWorkshopSubscribedMap( const char *pszMapName )
+{
+ m_vecWorkshopSubscribedMaps.FindAndFastRemove( CUtlString( pszMapName ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Well, is it?
+//-----------------------------------------------------------------------------
+bool CServerBrowser::IsWorkshopEnabled()
+{
+ return m_bWorkshopEnabled;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if this map is in our subscribed list
+//-----------------------------------------------------------------------------
+bool CServerBrowser::IsWorkshopSubscribedMap( const char *pszMapName )
+{
+ return m_vecWorkshopSubscribedMaps.HasElement( CUtlString( pszMapName ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CServerBrowser::IsValid()
+{
+ return ( g_pRunGameEngine );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CServerBrowser::Activate()
+{
+ static bool firstTimeOpening = true;
+ if ( firstTimeOpening )
+ {
+ m_hInternetDlg->LoadUserData(); // reload the user data the first time the dialog is made visible, helps with the lag between module load and
+ // steamui getting Deactivate() call
+ firstTimeOpening = false;
+ }
+
+ int numTimesOpened = sb_numtimesopened.GetInt() + 1;
+ sb_numtimesopened.SetValue( numTimesOpened );
+ if ( numTimesOpened == 1 )
+ {
+ time_t aclock;
+ time( &aclock );
+ sb_firstopentime.SetValue( (int) aclock );
+ }
+
+ Open();
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the server browser gets used in the game
+//-----------------------------------------------------------------------------
+void CServerBrowser::Deactivate()
+{
+ if (m_hInternetDlg.Get())
+ {
+ m_hInternetDlg->SaveUserData();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the server browser is no longer being used in the game
+//-----------------------------------------------------------------------------
+void CServerBrowser::Reactivate()
+{
+ if (m_hInternetDlg.Get())
+ {
+ m_hInternetDlg->LoadUserData();
+ if (m_hInternetDlg->IsVisible())
+ {
+ m_hInternetDlg->RefreshCurrentPage();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CServerBrowser::Open()
+{
+ m_hInternetDlg->Open();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns direct handle to main server browser dialog
+//-----------------------------------------------------------------------------
+vgui::VPANEL CServerBrowser::GetPanel()
+{
+ return m_hInternetDlg.Get() ? m_hInternetDlg->GetVPanel() : NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the parent panel of the main module panel
+//-----------------------------------------------------------------------------
+void CServerBrowser::SetParent(vgui::VPANEL parent)
+{
+ if (m_hInternetDlg.Get())
+ {
+ m_hInternetDlg->SetParent(parent);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Closes down the server browser for good
+//-----------------------------------------------------------------------------
+void CServerBrowser::Shutdown()
+{
+ if (m_hInternetDlg.Get())
+ {
+ m_hInternetDlg->Close();
+ m_hInternetDlg->MarkForDeletion();
+ }
+
+#if defined( STEAM )
+ vgui::VGuiControls_Shutdown();
+#endif
+
+ DisconnectTier3Libraries();
+ DisconnectTier2Libraries();
+ ConVar_Unregister();
+ DisconnectTier1Libraries();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: opens a game info dialog to watch the specified server; associated with the friend 'userName'
+//-----------------------------------------------------------------------------
+bool CServerBrowser::OpenGameInfoDialog( uint64 ulSteamIDFriend, const char *pszConnectCode )
+{
+#if !defined( _X360 ) // X360TBD: SteamFriends()
+ if ( m_hInternetDlg.Get() )
+ {
+ // activate an already-existing dialog
+ CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->GetDialogGameInfoForFriend( ulSteamIDFriend );
+ if ( pDialogGameInfo )
+ {
+ pDialogGameInfo->Activate();
+ return true;
+ }
+
+ // none yet, create a new dialog
+ FriendGameInfo_t friendGameInfo;
+ if ( steamapicontext->SteamFriends()->GetFriendGamePlayed( ulSteamIDFriend, &friendGameInfo ) )
+ {
+ uint16 usConnPort = friendGameInfo.m_usGamePort;
+ if ( friendGameInfo.m_usQueryPort < QUERY_PORT_ERROR )
+ usConnPort = friendGameInfo.m_usQueryPort;
+ CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->OpenGameInfoDialog( friendGameInfo.m_unGameIP, friendGameInfo.m_usGamePort, usConnPort, pszConnectCode );
+ pDialogGameInfo->SetFriend( ulSteamIDFriend );
+ return true;
+ }
+ }
+#endif
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: joins a specified game - game info dialog will only be opened if the server is fully or passworded
+//-----------------------------------------------------------------------------
+bool CServerBrowser::JoinGame( uint64 ulSteamIDFriend, const char *pszConnectCode )
+{
+ if ( OpenGameInfoDialog( ulSteamIDFriend, pszConnectCode ) )
+ {
+ CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->GetDialogGameInfoForFriend( ulSteamIDFriend );
+ pDialogGameInfo->Connect();
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: joins a game by IP/Port
+//-----------------------------------------------------------------------------
+bool CServerBrowser::JoinGame( uint32 unGameIP, uint16 usGamePort, const char *pszConnectCode )
+{
+ m_hInternetDlg->JoinGame( unGameIP, usGamePort, pszConnectCode );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: forces the game info dialog closed
+//-----------------------------------------------------------------------------
+void CServerBrowser::CloseGameInfoDialog( uint64 ulSteamIDFriend )
+{
+ CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->GetDialogGameInfoForFriend( ulSteamIDFriend );
+ if ( pDialogGameInfo )
+ {
+ pDialogGameInfo->Close();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: closes all the game info dialogs
+//-----------------------------------------------------------------------------
+void CServerBrowser::CloseAllGameInfoDialogs()
+{
+ if ( m_hInternetDlg.Get() )
+ {
+ m_hInternetDlg->CloseAllGameInfoDialogs();
+ }
+}
+
+CUtlVector< gametypes_t > g_GameTypes;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void LoadGameTypes( void )
+{
+ if ( g_GameTypes.Count() > 0 )
+ return;
+
+ #define GAMETYPES_FILE "servers/ServerBrowserGameTypes.txt"
+
+ KeyValues * kv = new KeyValues( GAMETYPES_FILE );
+
+ if ( !kv->LoadFromFile( g_pFullFileSystem, GAMETYPES_FILE, "MOD" ) )
+ {
+ kv->deleteThis();
+ return;
+ }
+
+ g_GameTypes.RemoveAll();
+
+ for ( KeyValues *pData = kv->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() )
+ {
+ gametypes_t gametype;
+
+ gametype.pPrefix = CloneString( pData->GetString( "prefix", "" ) );
+ gametype.pGametypeName = CloneString( pData->GetString( "name", "" ) );
+ g_GameTypes.AddToTail( gametype );
+ }
+
+
+ kv->deleteThis();
+}
+
+const char *GetGameTypeName( const char *pMapName )
+{
+ LoadGameTypes();
+ for ( int i = 0; i < g_GameTypes.Count(); i++ )
+ {
+ int iLength = strlen( g_GameTypes[i].pPrefix );
+
+ if ( !Q_strncmp( pMapName, g_GameTypes[i].pPrefix, iLength ) )
+ {
+ return g_GameTypes[i].pGametypeName;
+ }
+ }
+
+ return "";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose of comments like these: none
+//-----------------------------------------------------------------------------
+const char *CServerBrowser::GetMapFriendlyNameAndGameType( const char *pszMapName, char *szFriendlyMapName, int cchFriendlyName )
+{
+ // Make sure game types are loaded
+ LoadGameTypes();
+
+ // Scan list
+ const char *pszFriendlyGameTypeName = "";
+ for ( int i = 0; i < g_GameTypes.Count(); i++ )
+ {
+ int iLength = strlen( g_GameTypes[i].pPrefix );
+
+ if ( !Q_strnicmp( pszMapName, g_GameTypes[i].pPrefix, iLength ) )
+ {
+ pszMapName += iLength;
+ pszFriendlyGameTypeName = g_GameTypes[i].pGametypeName;
+ break;
+ }
+ }
+
+ // See how many characters from the name to copy.
+ // Start by assuming we'll copy the whole thing.
+ // (After any prefix we just skipped)
+ int l = V_strlen( pszMapName );
+ const char *pszFinal = Q_stristr( pszMapName, "_final" );
+ if ( pszFinal )
+ {
+ // truncate the _final (or _final1) part of the filename if it's at the end of the name
+ const char *pszNextChar = pszFinal + Q_strlen( "_final" );
+ if ( ( *pszNextChar == '\0' ) ||
+ ( ( *pszNextChar == '1' ) && ( *(pszNextChar+1) == '\0' ) ) )
+ {
+ l = pszFinal - pszMapName;
+ }
+ }
+
+ // Safety check against buffer size
+ if ( l >= cchFriendlyName )
+ {
+ Assert( !"Map name too long for buffer!" );
+ l = cchFriendlyName-1;
+ }
+
+ // Copy friendly portion of name only
+ V_memcpy( szFriendlyMapName, pszMapName, l );
+
+ // It's like the Alamo. We never forget.
+ szFriendlyMapName[l] = '\0';
+
+ // Result should be the friendly game type name
+ return pszFriendlyGameTypeName;
+}
+
diff --git a/serverbrowser/ServerBrowser.h b/serverbrowser/ServerBrowser.h
new file mode 100644
index 0000000..7decbe7
--- /dev/null
+++ b/serverbrowser/ServerBrowser.h
@@ -0,0 +1,82 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef SERVERBROWSER_H
+#define SERVERBROWSER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CServerBrowserDialog;
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles the UI and pinging of a half-life game server list
+//-----------------------------------------------------------------------------
+class CServerBrowser : public IServerBrowser, public IVGuiModule
+{
+public:
+ CServerBrowser();
+ ~CServerBrowser();
+
+ // IVGui module implementation
+ virtual bool Initialize(CreateInterfaceFn *factorylist, int numFactories);
+ virtual bool PostInitialize(CreateInterfaceFn *modules, int factoryCount);
+ virtual vgui::VPANEL GetPanel();
+ virtual bool Activate();
+ virtual bool IsValid();
+ virtual void Shutdown();
+ virtual void Deactivate();
+ virtual void Reactivate();
+ virtual void SetParent(vgui::VPANEL parent);
+
+ // IServerBrowser implementation
+ // joins a specified game - game info dialog will only be opened if the server is fully or passworded
+ virtual bool JoinGame( uint32 unGameIP, uint16 usGamePort, const char *pszConnectCode );
+ virtual bool JoinGame( uint64 ulSteamIDFriend, const char *pszConnectCode );
+
+ // opens a game info dialog to watch the specified server; associated with the friend 'userName'
+ virtual bool OpenGameInfoDialog( uint64 ulSteamIDFriend, const char *pszConnectCode );
+
+ // forces the game info dialog closed
+ virtual void CloseGameInfoDialog( uint64 ulSteamIDFriend );
+
+ // closes all the game info dialogs
+ virtual void CloseAllGameInfoDialogs();
+
+ virtual const char *GetMapFriendlyNameAndGameType( const char *pszMapName, char *szFriendlyMapName, int cchFriendlyName ) OVERRIDE;
+
+ // methods
+ virtual void CreateDialog();
+ virtual void Open();
+
+ // true if the user can't play a game
+ bool IsVACBannedFromGame( int nAppID );
+
+ // Enable filtering of workshop maps, requires the game/tool loading us to feed subscription data. This is a
+ // slightly ugly workaround to TF2 not yet having native workshop UI in quickplay, once that is in place this should
+ // either be stripped back out or expanded to be directly aware of the steam workshop without being managed.
+ virtual void SetWorkshopEnabled( bool bManaged ) OVERRIDE;
+ virtual void AddWorkshopSubscribedMap( const char *pszMapName ) OVERRIDE;
+ virtual void RemoveWorkshopSubscribedMap( const char *pszMapName ) OVERRIDE;
+
+ bool IsWorkshopEnabled();
+ bool IsWorkshopSubscribedMap( const char *pszMapName );
+private:
+ vgui::DHANDLE<CServerBrowserDialog> m_hInternetDlg;
+
+ bool m_bWorkshopEnabled;
+ CUtlVector< CUtlString > m_vecWorkshopSubscribedMaps;
+};
+
+// singleton accessor
+CServerBrowser &ServerBrowser();
+
+class CSteamAPIContext;
+extern CSteamAPIContext *steamapicontext;
+
+
+#endif // SERVERBROWSER_H
diff --git a/serverbrowser/ServerBrowser.vpc b/serverbrowser/ServerBrowser.vpc
new file mode 100644
index 0000000..6bfedde
--- /dev/null
+++ b/serverbrowser/ServerBrowser.vpc
@@ -0,0 +1,106 @@
+//-----------------------------------------------------------------------------
+// SERVERBROWSER.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR ".."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;VERSION_SAFE_STEAM_API_INTERFACES;SERVERBROWSER_EXPORTS;GAME_SRC;_USE_32BIT_TIME_T"
+ }
+
+ $Linker
+ {
+ $AdditionalDependencies "$BASE Advapi32.lib wsock32.lib Ws2_32.lib User32.lib" [$WIN32]
+ $AdditionalDependencies "$BASE Xonline.lib" [$X360]
+ $SystemLibraries "iconv" [$OSXALL]
+ }
+}
+
+$Project "ServerBrowser"
+{
+ $Folder "Source Files"
+ {
+ $File "BaseGamesPage.cpp"
+ $File "BlacklistedServers.cpp"
+ $File "CustomGames.cpp"
+ $File "DialogAddServer.cpp"
+ $File "DialogGameInfo.cpp"
+ $File "DialogServerPassword.cpp"
+ $File "FavoriteGames.cpp"
+ $File "FriendsGames.cpp"
+ $File "HistoryGames.cpp"
+ $File "InternetGames.cpp"
+ $File "LanGames.cpp"
+ $File "ModList.cpp"
+ $File "ServerBrowser.cpp"
+ $File "ServerBrowserDialog.cpp"
+ $File "ServerContextMenu.cpp"
+ $File "ServerListCompare.cpp"
+ $File "SpectateGames.cpp"
+ $File "VACBannedConnRefusedDialog.cpp"
+ $File "VACBannedConnRefusedDialog.h"
+
+ $File "QuickListPanel.cpp"
+
+ $File "$SRCDIR\public\vgui_controls\vgui_controls.cpp"
+ {
+ $Configuration
+ {
+ $Compiler
+ {
+ }
+ }
+ }
+
+ $File "$SRCDIR\common\ServerBrowser\blacklisted_server_manager.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "BaseGamesPage.h"
+ $File "BlacklistedServers.h"
+ $File "CustomGames.h"
+ $File "DialogAddServer.h"
+ $File "DialogGameInfo.h"
+ $File "DialogServerPassword.h"
+ $File "FavoriteGames.h"
+ $File "FriendsGames.h"
+ $File "HistoryGames.h"
+ $File "InternetGames.h"
+ $File "LanGames.h"
+ $File "ModList.h"
+ $File "ServerBrowser.h"
+ $File "ServerBrowserDialog.h"
+ $File "ServerContextMenu.h"
+ $File "ServerListCompare.h"
+ $File "SpectateGames.h"
+ $File "QuickListPanel.h"
+
+ $File "$SRCDIR\common\ServerBrowser\blacklisted_server_manager.h"
+ }
+
+ $Folder "Resource Files"
+ {
+ }
+ $Folder "Exposed interfaces"
+ {
+ $File "$SRCDIR\common\ServerBrowser\IServerBrowser.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $ImpLib steam_api
+ $Lib tier2
+ $Lib tier3
+ $Lib vgui_controls
+ $Lib mathlib
+ }
+}
diff --git a/serverbrowser/ServerBrowserDialog.cpp b/serverbrowser/ServerBrowserDialog.cpp
new file mode 100644
index 0000000..0e2f3f4
--- /dev/null
+++ b/serverbrowser/ServerBrowserDialog.cpp
@@ -0,0 +1,816 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================
+#include "pch_serverbrowser.h"
+
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+#if defined( _WIN32 ) && !defined( _X360 )
+#define WIN32_LEAN_AND_MEAN
+#include <winsock.h>
+#endif
+#ifdef LINUX
+#include <arpa/inet.h>
+#endif
+
+using namespace vgui;
+
+ConVar sb_quick_list_bit_field( "sb_quick_list_bit_field", "-1" );
+
+static CServerBrowserDialog *s_InternetDlg = NULL;
+
+CServerBrowserDialog &ServerBrowserDialog()
+{
+ return *CServerBrowserDialog::GetInstance();
+}
+
+
+// Returns a list of the ports that we hit when looking for
+void GetMostCommonQueryPorts( CUtlVector<uint16> &ports )
+{
+ for ( int i=0; i <= 5; i++ )
+ {
+ ports.AddToTail( 27015 + i );
+ ports.AddToTail( 26900 + i );
+ }
+
+ ports.AddToTail(4242); //RDKF
+ ports.AddToTail(27215); //Lost Planet
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CServerBrowserDialog::CServerBrowserDialog(vgui::Panel *parent) : Frame(parent, "CServerBrowserDialog")
+{
+ s_InternetDlg = this;
+
+ m_szGameName[0] = 0;
+ m_szModDir[0] = 0;
+ m_pSavedData = NULL;
+ m_pFilterData = NULL;
+ m_pFavorites = NULL;
+ m_pBlacklist = NULL;
+ m_pHistory = NULL;
+
+ // Do this before LoadUserData() so it loads the blacklist file properly
+ m_pBlacklist = new CBlacklistedServers(this);
+
+ LoadUserData();
+
+ m_pInternetGames = new CCustomGames(this);
+ m_pFavorites = new CFavoriteGames(this);
+ m_pHistory = new CHistoryGames(this);
+ m_pSpectateGames = new CSpectateGames(this);
+ m_pLanGames = new CLanGames(this);
+ m_pFriendsGames = new CFriendsGames(this);
+
+ SetMinimumSize( 640, 384 );
+ SetSize( 640, 384 );
+
+ m_pGameList = m_pInternetGames;
+
+ m_pContextMenu = new CServerContextMenu(this);;
+
+ // property sheet
+ m_pTabPanel = new PropertySheet(this, "GameTabs");
+ m_pTabPanel->SetTabWidth(72);
+ m_pTabPanel->AddPage(m_pInternetGames, "#ServerBrowser_InternetTab");
+ m_pTabPanel->AddPage(m_pFavorites, "#ServerBrowser_FavoritesTab");
+ m_pTabPanel->AddPage(m_pHistory, "#ServerBrowser_HistoryTab");
+ m_pTabPanel->AddPage(m_pSpectateGames, "#ServerBrowser_SpectateTab");
+ m_pTabPanel->AddPage(m_pLanGames, "#ServerBrowser_LanTab");
+ m_pTabPanel->AddPage(m_pFriendsGames, "#ServerBrowser_FriendsTab");
+ if ( m_pBlacklist )
+ {
+ m_pTabPanel->AddPage(m_pBlacklist, "#ServerBrowser_BlacklistTab");
+ }
+ m_pTabPanel->AddActionSignalTarget(this);
+
+ m_pStatusLabel = new Label(this, "StatusLabel", "");
+
+ LoadControlSettingsAndUserConfig("Servers/DialogServerBrowser.res");
+
+ m_pStatusLabel->SetText("");
+
+ // load current tab
+ const char *gameList = m_pSavedData->GetString("GameList");
+
+ if (!Q_stricmp(gameList, "spectate"))
+ {
+ m_pTabPanel->SetActivePage(m_pSpectateGames);
+ }
+ else if (!Q_stricmp(gameList, "favorites"))
+ {
+ m_pTabPanel->SetActivePage(m_pFavorites);
+ }
+ else if (!Q_stricmp(gameList, "history"))
+ {
+ m_pTabPanel->SetActivePage(m_pHistory);
+ }
+ else if (!Q_stricmp(gameList, "lan"))
+ {
+ m_pTabPanel->SetActivePage(m_pLanGames);
+ }
+ else if (!Q_stricmp(gameList, "friends"))
+ {
+ m_pTabPanel->SetActivePage(m_pFriendsGames);
+ }
+ else if (!Q_stricmp(gameList, "blacklist"))
+ {
+ m_pTabPanel->SetActivePage(m_pBlacklist);
+ }
+ else
+ {
+ m_pTabPanel->SetActivePage(m_pInternetGames);
+ }
+
+ ivgui()->AddTickSignal( GetVPanel() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CServerBrowserDialog::~CServerBrowserDialog()
+{
+ delete m_pContextMenu;
+
+ SaveUserData();
+
+ if (m_pSavedData)
+ {
+ m_pSavedData->deleteThis();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called once to set up
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::Initialize()
+{
+ SetTitle("#ServerBrowser_Servers", true);
+ SetVisible(false);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns a server in the list
+//-----------------------------------------------------------------------------
+gameserveritem_t *CServerBrowserDialog::GetServer( unsigned int serverID )
+{
+ if (m_pGameList)
+ return m_pGameList->GetServer( serverID );
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Activates and gives the tab focus
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::Open()
+{
+ BaseClass::Activate();
+ m_pTabPanel->RequestFocus();
+
+ MoveToCenterOfScreen();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame, updates animations for this module
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::OnTick()
+{
+ BaseClass::OnTick();
+ vgui::GetAnimationController()->UpdateAnimations( system()->GetFrameTime() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads filter settings from disk
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::LoadUserData()
+{
+ // free any old filters
+ if (m_pSavedData)
+ {
+ m_pSavedData->deleteThis();
+ }
+
+ m_pSavedData = new KeyValues("Filters");
+ if (!m_pSavedData->LoadFromFile( g_pFullFileSystem, "ServerBrowser.vdf", "CONFIG"))
+ {
+ // doesn't matter if the file is not found, defaults will work successfully and file will be created on exit
+ }
+
+ KeyValues *filters = m_pSavedData->FindKey( "Filters", false );
+ if ( filters )
+ {
+ m_pFilterData = filters->MakeCopy();
+ m_pSavedData->RemoveSubKey( filters );
+ }
+ else
+ {
+ m_pFilterData = new KeyValues( "Filters" );
+ }
+
+
+ // reload all the page settings if necessary
+ if (m_pHistory)
+ {
+ // history
+ m_pHistory->LoadHistoryList();
+ if ( IsVisible() && m_pHistory->IsVisible() )
+ m_pHistory->StartRefresh();
+ }
+
+ if (m_pFavorites)
+ {
+ // favorites
+ m_pFavorites->LoadFavoritesList();
+
+ // filters
+ ReloadFilterSettings();
+
+ if ( IsVisible() && m_pFavorites->IsVisible() )
+ m_pFavorites->StartRefresh();
+ }
+
+ if ( m_pBlacklist )
+ {
+ m_pBlacklist->LoadBlacklistedList();
+ }
+
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::SaveUserData()
+{
+ m_pSavedData->Clear();
+ m_pSavedData->LoadFromFile( g_pFullFileSystem, "ServerBrowser.vdf", "CONFIG");
+
+ // set the current tab
+ if (m_pGameList == m_pSpectateGames)
+ {
+ m_pSavedData->SetString("GameList", "spectate");
+ }
+ else if (m_pGameList == m_pFavorites)
+ {
+ m_pSavedData->SetString("GameList", "favorites");
+ }
+ else if (m_pGameList == m_pLanGames)
+ {
+ m_pSavedData->SetString("GameList", "lan");
+ }
+ else if (m_pGameList == m_pFriendsGames)
+ {
+ m_pSavedData->SetString("GameList", "friends");
+ }
+ else if (m_pGameList == m_pHistory)
+ {
+ m_pSavedData->SetString("GameList", "history");
+ }
+ else
+ {
+ m_pSavedData->SetString("GameList", "internet");
+ }
+
+ m_pSavedData->RemoveSubKey( m_pSavedData->FindKey( "Filters" ) ); // remove the saved subkey and add our subkey
+ m_pSavedData->AddSubKey( m_pFilterData->MakeCopy() );
+ m_pSavedData->SaveToFile( g_pFullFileSystem, "ServerBrowser.vdf", "CONFIG");
+
+ if ( m_pBlacklist )
+ {
+ m_pBlacklist->SaveBlacklistedList();
+ }
+
+ // save per-page config
+ SaveUserConfig();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes the page currently visible
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::RefreshCurrentPage()
+{
+ if (m_pGameList)
+ {
+ m_pGameList->StartRefresh();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::BlacklistsChanged()
+{
+ m_pInternetGames->ApplyGameFilters();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates status test at bottom of window
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::UpdateStatusText(const char *fmt, ...)
+{
+ if ( !m_pStatusLabel )
+ return;
+
+ if ( fmt && strlen(fmt) > 0 )
+ {
+ char str[ 1024 ];
+ va_list argptr;
+ va_start( argptr, fmt );
+ _vsnprintf( str, sizeof(str), fmt, argptr );
+ va_end( argptr );
+
+ m_pStatusLabel->SetText( str );
+ }
+ else
+ {
+ // clear
+ m_pStatusLabel->SetText( "" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates status test at bottom of window
+// Input : wchar_t* (unicode string) -
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::UpdateStatusText(wchar_t *unicode)
+{
+ if ( !m_pStatusLabel )
+ return;
+
+ if ( unicode && wcslen(unicode) > 0 )
+ {
+ m_pStatusLabel->SetText( unicode );
+ }
+ else
+ {
+ // clear
+ m_pStatusLabel->SetText( "" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::OnGameListChanged()
+{
+ m_pGameList = dynamic_cast<IGameList *>(m_pTabPanel->GetActivePage());
+
+ UpdateStatusText("");
+
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns a pointer to a static instance of this dialog
+//-----------------------------------------------------------------------------
+CServerBrowserDialog *CServerBrowserDialog::GetInstance()
+{
+ return s_InternetDlg;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a server to the list of favorites
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::AddServerToFavorites(gameserveritem_t &server)
+{
+ if ( steamapicontext->SteamMatchmaking() )
+ {
+ steamapicontext->SteamMatchmaking()->AddFavoriteGame(
+ server.m_nAppID,
+ server.m_NetAdr.GetIP(),
+ server.m_NetAdr.GetConnectionPort(),
+ server.m_NetAdr.GetQueryPort(),
+ k_unFavoriteFlagFavorite,
+ time( NULL ) );
+
+ if ( GameSupportsReplay() )
+ {
+ // send command to propagate to the client so the client can send it on to the GC
+ char command[ 256 ];
+ Q_snprintf( command, Q_ARRAYSIZE( command ), "rfgc %s\n", server.m_NetAdr.GetConnectionAddressString() );
+ g_pRunGameEngine->AddTextCommand( command );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a server to our list of blacklisted servers
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::AddServerToBlacklist(gameserveritem_t &server)
+{
+ if ( m_pBlacklist )
+ {
+ m_pBlacklist->AddServer( server );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CServerBrowserDialog::IsServerBlacklisted(gameserveritem_t &server)
+{
+ if ( m_pBlacklist )
+ return m_pBlacklist->IsServerBlacklisted( server );
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CServerContextMenu *CServerBrowserDialog::GetContextMenu(vgui::Panel *pPanel)
+{
+ // create a drop down for this object's states
+ if (m_pContextMenu)
+ delete m_pContextMenu;
+ m_pContextMenu = new CServerContextMenu(this);
+ m_pContextMenu->SetAutoDelete( false );
+
+ if (!pPanel)
+ {
+ m_pContextMenu->SetParent(this);
+ }
+ else
+ {
+ m_pContextMenu->SetParent(pPanel);
+ }
+
+ m_pContextMenu->SetVisible(false);
+ return m_pContextMenu;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: begins the process of joining a server from a game list
+// the game info dialog it opens will also update the game list
+//-----------------------------------------------------------------------------
+CDialogGameInfo *CServerBrowserDialog::JoinGame(IGameList *gameList, unsigned int serverIndex)
+{
+ // open the game info dialog, then mark it to attempt to connect right away
+ CDialogGameInfo *gameDialog = OpenGameInfoDialog(gameList, serverIndex);
+
+ // set the dialog name to be the server name
+ gameDialog->Connect();
+
+ return gameDialog;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: joins a game by a specified IP, not attached to any game list
+//-----------------------------------------------------------------------------
+CDialogGameInfo *CServerBrowserDialog::JoinGame(int serverIP, int serverPort, const char *pszConnectCode)
+{
+ // open the game info dialog, then mark it to attempt to connect right away
+ CDialogGameInfo *gameDialog = OpenGameInfoDialog( serverIP, serverPort, serverPort, pszConnectCode );
+
+ // set the dialog name to be the server name
+ gameDialog->Connect();
+
+ return gameDialog;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: opens a game info dialog from a game list
+//-----------------------------------------------------------------------------
+CDialogGameInfo *CServerBrowserDialog::OpenGameInfoDialog( IGameList *gameList, unsigned int serverIndex )
+{
+ gameserveritem_t *pServer = gameList->GetServer( serverIndex );
+ if ( !pServer )
+ return NULL;
+
+ CDialogGameInfo *gameDialog = new CDialogGameInfo( NULL, pServer->m_NetAdr.GetIP(), pServer->m_NetAdr.GetQueryPort(), pServer->m_NetAdr.GetConnectionPort(), gameList->GetConnectCode() );
+ gameDialog->SetParent(GetVParent());
+ gameDialog->AddActionSignalTarget(this);
+ gameDialog->Run( pServer->GetName() );
+ int i = m_GameInfoDialogs.AddToTail();
+ m_GameInfoDialogs[i] = gameDialog;
+ return gameDialog;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: opens a game info dialog by a specified IP, not attached to any game list
+//-----------------------------------------------------------------------------
+CDialogGameInfo *CServerBrowserDialog::OpenGameInfoDialog( int serverIP, uint16 connPort, uint16 queryPort, const char *pszConnectCode )
+{
+ CDialogGameInfo *gameDialog = new CDialogGameInfo(NULL, serverIP, queryPort, connPort, pszConnectCode);
+ gameDialog->AddActionSignalTarget(this);
+ gameDialog->SetParent(GetVParent());
+ gameDialog->Run("");
+ int i = m_GameInfoDialogs.AddToTail();
+ m_GameInfoDialogs[i] = gameDialog;
+ return gameDialog;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: closes all the game info dialogs
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::CloseAllGameInfoDialogs()
+{
+ for (int i = 0; i < m_GameInfoDialogs.Count(); i++)
+ {
+ vgui::Panel *dlg = m_GameInfoDialogs[i];
+ if (dlg)
+ {
+ vgui::ivgui()->PostMessage(dlg->GetVPanel(), new KeyValues("Close"), NULL);
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: finds a dialog
+//-----------------------------------------------------------------------------
+CDialogGameInfo *CServerBrowserDialog::GetDialogGameInfoForFriend( uint64 ulSteamIDFriend )
+{
+ FOR_EACH_VEC( m_GameInfoDialogs, i )
+ {
+ CDialogGameInfo *pDlg = m_GameInfoDialogs[i];
+ if ( pDlg && pDlg->GetAssociatedFriend() == ulSteamIDFriend )
+ {
+ return pDlg;
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: accessor to the filter save data
+//-----------------------------------------------------------------------------
+KeyValues *CServerBrowserDialog::GetFilterSaveData(const char *filterSet)
+{
+ return m_pFilterData->FindKey(filterSet, true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the name of the mod directory we're restricted to accessing, NULL if none
+//-----------------------------------------------------------------------------
+const char *CServerBrowserDialog::GetActiveModName()
+{
+ return m_szModDir[0] ? m_szModDir : NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the name of the mod directory we're restricted to accessing, NULL if none
+//-----------------------------------------------------------------------------
+const char *CServerBrowserDialog::GetActiveGameName()
+{
+ return m_szGameName[0] ? m_szGameName : NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: return the app id to limit game queries to, set by Source/HL1 engines (NOT by filter settings, that is per page)
+//-----------------------------------------------------------------------------
+CGameID &CServerBrowserDialog::GetActiveAppID()
+{
+ // !TEST! Un comment this to force a particular AppID
+ //m_iLimitAppID = CGameID( 440 );
+ return m_iLimitAppID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: receives a specified game is active, so no other game types can be displayed in server list
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::OnActiveGameName( KeyValues *pKV )
+{
+ Q_strncpy(m_szModDir, pKV->GetString( "name" ), sizeof(m_szModDir));
+ Q_strncpy(m_szGameName, pKV->GetString( "game" ), sizeof(m_szGameName));
+ m_iLimitAppID = CGameID( pKV->GetUint64( "appid", 0 ) );
+ // reload filter settings (since they are no forced to be game specific)
+ ReloadFilterSettings();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: resets all pages filter settings
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::ReloadFilterSettings()
+{
+ m_pInternetGames->LoadFilterSettings();
+ m_pSpectateGames->LoadFilterSettings();
+ m_pFavorites->LoadFilterSettings();
+ m_pLanGames->LoadFilterSettings();
+ m_pFriendsGames->LoadFilterSettings();
+ m_pHistory->LoadFilterSettings();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds server to the history, saves as currently connected server
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::OnConnectToGame( KeyValues *pMessageValues )
+{
+ int ip = pMessageValues->GetInt( "ip" );
+ int connectionPort = pMessageValues->GetInt( "connectionport" );
+ int queryPort = pMessageValues->GetInt( "queryport" );
+
+ if ( !ip || !queryPort )
+ return;
+
+ uint32 unIP = htonl( ip );
+
+ memset( &m_CurrentConnection, 0, sizeof(gameserveritem_t) );
+ m_CurrentConnection.m_NetAdr.SetIP( unIP );
+ m_CurrentConnection.m_NetAdr.SetQueryPort( queryPort );
+ m_CurrentConnection.m_NetAdr.SetConnectionPort( (unsigned short)connectionPort );
+
+ if (m_pHistory && steamapicontext->SteamMatchmaking() )
+ {
+ steamapicontext->SteamMatchmaking()->AddFavoriteGame( 0, unIP, connectionPort, queryPort, k_unFavoriteFlagHistory, time( NULL ) );
+ m_pHistory->SetRefreshOnReload();
+ }
+
+ // tell the game info dialogs, so they can cancel if we have connected
+ // to a server they were auto-retrying
+ for (int i = 0; i < m_GameInfoDialogs.Count(); i++)
+ {
+ vgui::Panel *dlg = m_GameInfoDialogs[i];
+ if (dlg)
+ {
+ KeyValues *kv = new KeyValues("ConnectedToGame", "ip", unIP, "connectionport", connectionPort);
+ kv->SetInt( "queryport", queryPort );
+ vgui::ivgui()->PostMessage(dlg->GetVPanel(), kv, NULL);
+ }
+ }
+
+ // forward to favorites
+ m_pFavorites->OnConnectToGame();
+ if ( m_pBlacklist )
+ {
+ m_pBlacklist->OnConnectToGame();
+ }
+
+ m_bCurrentlyConnected = true;
+
+ // Now we want to track which tabs have the quick list button checked
+ int iQuickListBitField = 0;
+ if ( m_pFriendsGames && m_pFriendsGames->IsQuickListButtonChecked() )
+ {
+ iQuickListBitField |= ( 1 << 0 );
+ }
+ if ( m_pLanGames && m_pLanGames->IsQuickListButtonChecked() )
+ {
+ iQuickListBitField |= ( 1 << 1 );
+ }
+ if ( m_pSpectateGames && m_pSpectateGames->IsQuickListButtonChecked() )
+ {
+ iQuickListBitField |= ( 1 << 2 );
+ }
+ if ( m_pHistory && m_pHistory->IsQuickListButtonChecked() )
+ {
+ iQuickListBitField |= ( 1 << 3 );
+ }
+ if ( m_pFavorites && m_pFavorites->IsQuickListButtonChecked() )
+ {
+ iQuickListBitField |= ( 1 << 4 );
+ }
+ if ( m_pInternetGames && m_pInternetGames->IsQuickListButtonChecked() )
+ {
+ iQuickListBitField |= ( 1 << 5 );
+ }
+
+ // Set the value so that the client.dll can use it for gamestats
+ sb_quick_list_bit_field.SetValue( iQuickListBitField );
+
+ // TF2 wants to close this dialog when the player connects to a game
+ if ( GameSupportsReplay() )
+ {
+ ConVarRef sb_close_browser_on_connect( "sb_close_browser_on_connect" );
+ if ( sb_close_browser_on_connect.IsValid() )
+ {
+ if ( sb_close_browser_on_connect.GetBool() == true )
+ {
+ OnClose();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Clears currently connected server
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::OnDisconnectFromGame( void )
+{
+ m_bCurrentlyConnected = false;
+ memset( &m_CurrentConnection, 0, sizeof(gameserveritem_t) );
+
+ // forward to favorites
+ m_pFavorites->OnDisconnectFromGame();
+ if ( m_pBlacklist )
+ {
+ m_pBlacklist->OnDisconnectFromGame();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when start start loading, so we can cease server browser activity
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::OnLoadingStarted( void )
+{
+ m_pInternetGames->OnLoadingStarted();
+ m_pSpectateGames->OnLoadingStarted();
+ m_pFavorites->OnLoadingStarted();
+ m_pLanGames->OnLoadingStarted();
+ m_pFriendsGames->OnLoadingStarted();
+ m_pHistory->OnLoadingStarted();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Passes build mode activation down into the pages
+//-----------------------------------------------------------------------------
+void CServerBrowserDialog::ActivateBuildMode()
+{
+ // no subpanel, no build mode
+ EditablePanel *panel = dynamic_cast<EditablePanel *>(m_pTabPanel->GetActivePage());
+ if (!panel)
+ return;
+
+ panel->ActivateBuildMode();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the default position and size on the screen to appear the first time
+//-----------------------------------------------------------------------------
+bool CServerBrowserDialog::GetDefaultScreenPosition(int &x, int &y, int &wide, int &tall)
+{
+ int wx, wy, ww, wt;
+ surface()->GetWorkspaceBounds( wx, wy, ww, wt );
+ x = wx + (int)(ww * 0.05);
+ y = wy + (int)(wt * 0.4);
+ wide = (int)(ww * 0.5);
+ tall = (int)(wt * 0.55);
+ return true;
+}
+
+void CServerBrowserDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ // Handle close here, CBasePanel parent doesn't support "DialogClosing" command
+ ButtonCode_t nButtonCode = GetBaseButtonCode( code );
+
+ if ( nButtonCode == KEY_XBUTTON_B || nButtonCode == STEAMCONTROLLER_B )
+ {
+ OnCommand( "Close" );
+ return;
+ }
+ else if ( nButtonCode == KEY_XBUTTON_A || nButtonCode == STEAMCONTROLLER_A )
+ {
+ //OnOK( false );
+ //return;
+ }
+ else if ( nButtonCode == KEY_XBUTTON_UP ||
+ nButtonCode == KEY_XSTICK1_UP ||
+ nButtonCode == KEY_XSTICK2_UP ||
+ nButtonCode == STEAMCONTROLLER_DPAD_UP ||
+ nButtonCode == KEY_UP ||
+ nButtonCode == KEY_XBUTTON_DOWN ||
+ nButtonCode == KEY_XSTICK1_DOWN ||
+ nButtonCode == KEY_XSTICK2_DOWN ||
+ nButtonCode == STEAMCONTROLLER_DPAD_DOWN ||
+ nButtonCode == KEY_DOWN )
+ {
+ CBaseGamesPage *pGamesPage = dynamic_cast< CBaseGamesPage* >( m_pTabPanel->GetActivePage() );
+ if ( pGamesPage )
+ {
+ ListPanel *pListPanel = dynamic_cast< ListPanel * >( pGamesPage->GetActiveList() );
+ if ( pListPanel )
+ {
+ if ( pListPanel->GetSelectedItem( 0 ) == -1 )
+ {
+ pListPanel->SetSingleSelectedItem( pListPanel->GetItemIDFromRow( 0 ) );
+ pListPanel->RequestFocus();
+ return;
+ }
+ else if ( !pListPanel->HasFocus() )
+ {
+ pListPanel->RequestFocus();
+ return;
+ }
+ }
+ }
+ }
+
+ BaseClass::OnKeyCodePressed( code );
+} \ No newline at end of file
diff --git a/serverbrowser/ServerBrowserDialog.h b/serverbrowser/ServerBrowserDialog.h
new file mode 100644
index 0000000..2b25186
--- /dev/null
+++ b/serverbrowser/ServerBrowserDialog.h
@@ -0,0 +1,159 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef SERVERBROWSERDIALOG_H
+#define SERVERBROWSERDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+extern class IRunGameEngine *g_pRunGameEngine;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CServerBrowserDialog : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( CServerBrowserDialog, vgui::Frame );
+
+public:
+ // Construction/destruction
+ CServerBrowserDialog( vgui::Panel *parent );
+ ~CServerBrowserDialog( void );
+
+ void Initialize( void );
+
+ // displays the dialog, moves it into focus, updates if it has to
+ void Open( void );
+
+ // gets server info
+ gameserveritem_t *GetServer(unsigned int serverID);
+ // called every frame
+ virtual void OnTick();
+
+ // updates status text at bottom of window
+ void UpdateStatusText(PRINTF_FORMAT_STRING const char *format, ...);
+
+ // updates status text at bottom of window
+ void UpdateStatusText(wchar_t *unicode);
+
+ // context menu access
+ CServerContextMenu *GetContextMenu(vgui::Panel *pParent);
+
+ // returns a pointer to a static instance of this dialog
+ // valid for use only in sort functions
+ static CServerBrowserDialog *GetInstance();
+
+ // Adds a server to the list of favorites
+ void AddServerToFavorites(gameserveritem_t &server);
+ // Adds a server to our list of blacklisted servers
+ void AddServerToBlacklist(gameserveritem_t &server);
+ bool IsServerBlacklisted(gameserveritem_t &server);
+
+ // begins the process of joining a server from a game list
+ // the game info dialog it opens will also update the game list
+ CDialogGameInfo *JoinGame(IGameList *gameList, unsigned int serverIndex);
+
+ // joins a game by a specified IP, not attached to any game list
+ CDialogGameInfo *JoinGame(int serverIP, int serverPort, const char *pszConnectCode);
+
+ // opens a game info dialog from a game list
+ CDialogGameInfo *OpenGameInfoDialog(IGameList *gameList, unsigned int serverIndex);
+
+ // opens a game info dialog by a specified IP, not attached to any game list
+ CDialogGameInfo *OpenGameInfoDialog( int serverIP, uint16 connPort, uint16 queryPort, const char *pszConnectCode );
+
+ // closes all the game info dialogs
+ void CloseAllGameInfoDialogs();
+ CDialogGameInfo *GetDialogGameInfoForFriend( uint64 ulSteamIDFriend );
+
+ // accessor to the filter save data
+ KeyValues *GetFilterSaveData(const char *filterSet);
+
+ // gets the name of the mod directory we're restricted to accessing, NULL if none
+ const char *GetActiveModName();
+ CGameID &GetActiveAppID();
+ const char *GetActiveGameName();
+
+ // load/saves filter & favorites settings from disk
+ void LoadUserData();
+ void SaveUserData();
+
+ // forces the currently active page to refresh
+ void RefreshCurrentPage();
+
+ virtual gameserveritem_t *GetCurrentConnectedServer()
+ {
+ return &m_CurrentConnection;
+ }
+
+ void BlacklistsChanged();
+ CBlacklistedServers *GetBlacklistPage( void ) { return m_pBlacklist; }
+
+private:
+
+ // current game list change
+ MESSAGE_FUNC( OnGameListChanged, "PageChanged" );
+ void ReloadFilterSettings();
+
+ // receives a specified game is active, so no other game types can be displayed in server list
+ MESSAGE_FUNC_PARAMS( OnActiveGameName, "ActiveGameName", name );
+
+ // notification that we connected / disconnected
+ MESSAGE_FUNC_PARAMS( OnConnectToGame, "ConnectedToGame", kv );
+ MESSAGE_FUNC( OnDisconnectFromGame, "DisconnectedFromGame" );
+ MESSAGE_FUNC( OnLoadingStarted, "LoadingStarted" );
+
+ virtual bool GetDefaultScreenPosition(int &x, int &y, int &wide, int &tall);
+ virtual void ActivateBuildMode();
+
+ void OnKeyCodePressed( vgui::KeyCode code );
+
+private:
+ // list of all open game info dialogs
+ CUtlVector<vgui::DHANDLE<CDialogGameInfo> > m_GameInfoDialogs;
+
+ // pointer to current game list
+ IGameList *m_pGameList;
+
+ // Status text
+ vgui::Label *m_pStatusLabel;
+
+ // property sheet
+ vgui::PropertySheet *m_pTabPanel;
+ CFavoriteGames *m_pFavorites;
+ CBlacklistedServers *m_pBlacklist;
+ CHistoryGames *m_pHistory;
+ CInternetGames *m_pInternetGames;
+ CSpectateGames *m_pSpectateGames;
+ CLanGames *m_pLanGames;
+ CFriendsGames *m_pFriendsGames;
+
+ KeyValues *m_pSavedData;
+ KeyValues *m_pFilterData;
+
+ // context menu
+ CServerContextMenu *m_pContextMenu;
+
+ // active game
+ char m_szGameName[128];
+ char m_szModDir[128];
+ CGameID m_iLimitAppID;
+
+ // currently connected game
+ bool m_bCurrentlyConnected;
+ gameserveritem_t m_CurrentConnection;
+};
+
+// singleton accessor
+extern CServerBrowserDialog &ServerBrowserDialog();
+
+// Used by the LAN tab and the add server dialog when trying to find servers without having
+// been given any ports to look for servers on.
+void GetMostCommonQueryPorts( CUtlVector<uint16> &ports );
+
+#endif // SERVERBROWSERDIALOG_H
diff --git a/serverbrowser/ServerContextMenu.cpp b/serverbrowser/ServerContextMenu.cpp
new file mode 100644
index 0000000..0767d06
--- /dev/null
+++ b/serverbrowser/ServerContextMenu.cpp
@@ -0,0 +1,63 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CServerContextMenu::CServerContextMenu(Panel *parent) : Menu(parent, "ServerContextMenu")
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CServerContextMenu::~CServerContextMenu()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activates the menu
+//-----------------------------------------------------------------------------
+void CServerContextMenu::ShowMenu(
+ Panel *target,
+ unsigned int serverID,
+ bool showConnect,
+ bool showViewGameInfo,
+ bool showRefresh,
+ bool showAddToFavorites )
+{
+ if (showConnect)
+ {
+ AddMenuItem("ConnectToServer", "#ServerBrowser_ConnectToServer", new KeyValues("ConnectToServer", "serverID", serverID), target);
+ }
+
+ if (showViewGameInfo)
+ {
+ AddMenuItem("ViewGameInfo", "#ServerBrowser_ViewServerInfo", new KeyValues("ViewGameInfo", "serverID", serverID), target);
+ }
+
+ if (showRefresh)
+ {
+ AddMenuItem("RefreshServer", "#ServerBrowser_RefreshServer", new KeyValues("RefreshServer", "serverID", serverID), target);
+ }
+
+ if (showAddToFavorites)
+ {
+ AddMenuItem("AddToFavorites", "#ServerBrowser_AddServerToFavorites", new KeyValues("AddToFavorites", "serverID", serverID), target);
+ AddMenuItem("AddToBlacklist", "#ServerBrowser_AddServerToBlacklist", new KeyValues("AddToBlacklist", "serverID", serverID), target);
+ }
+
+ int x, y, gx, gy;
+ input()->GetCursorPos(x, y);
+ ipanel()->GetPos(surface()->GetEmbeddedPanel(), gx, gy);
+ SetPos(x - gx, y - gy);
+ SetVisible(true);
+}
diff --git a/serverbrowser/ServerContextMenu.h b/serverbrowser/ServerContextMenu.h
new file mode 100644
index 0000000..1d8ff23
--- /dev/null
+++ b/serverbrowser/ServerContextMenu.h
@@ -0,0 +1,34 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef SERVERCONTEXTMENU_H
+#define SERVERCONTEXTMENU_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Basic right-click context menu for servers
+//-----------------------------------------------------------------------------
+class CServerContextMenu : public vgui::Menu
+{
+public:
+ CServerContextMenu(vgui::Panel *parent);
+ ~CServerContextMenu();
+
+ // call this to Activate the menu
+ void ShowMenu(
+ vgui::Panel *target,
+ unsigned int serverID,
+ bool showConnect,
+ bool showViewGameInfo,
+ bool showRefresh,
+ bool showAddToFavorites);
+};
+
+
+#endif // SERVERCONTEXTMENU_H
diff --git a/serverbrowser/ServerListCompare.cpp b/serverbrowser/ServerListCompare.cpp
new file mode 100644
index 0000000..f17678b
--- /dev/null
+++ b/serverbrowser/ServerListCompare.cpp
@@ -0,0 +1,289 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+bool IsReplayServer( gameserveritem_t &server );
+
+//-----------------------------------------------------------------------------
+// Purpose: List password column sort function
+//-----------------------------------------------------------------------------
+int __cdecl PasswordCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ if ( s1->m_bPassword < s2->m_bPassword )
+ return 1;
+ else if ( s1->m_bPassword > s2->m_bPassword )
+ return -1;
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: list column sort function
+//-----------------------------------------------------------------------------
+int __cdecl BotsCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ if ( s1->m_nBotPlayers < s2->m_nBotPlayers )
+ return 1;
+ else if ( s1->m_nBotPlayers > s2->m_nBotPlayers )
+ return -1;
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: list column sort function
+//-----------------------------------------------------------------------------
+int __cdecl SecureCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ if ( s1->m_bSecure < s2->m_bSecure )
+ return 1;
+ else if ( s1->m_bSecure > s2->m_bSecure )
+ return -1;
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: list column sort function
+//-----------------------------------------------------------------------------
+int __cdecl IPAddressCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ if ( s1->m_NetAdr < s2->m_NetAdr )
+ return -1;
+ else if ( s2->m_NetAdr < s1->m_NetAdr )
+ return 1;
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ping comparison function
+//-----------------------------------------------------------------------------
+int __cdecl PingCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ int ping1 = s1->m_nPing;
+ int ping2 = s2->m_nPing;
+
+ if ( ping1 < ping2 )
+ return -1;
+ else if ( ping1 > ping2 )
+ return 1;
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Map comparison function
+//-----------------------------------------------------------------------------
+int __cdecl MapCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ return Q_stricmp( s1->m_szMap, s2->m_szMap );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Game dir comparison function
+//-----------------------------------------------------------------------------
+int __cdecl GameCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ // make sure we haven't added the same server to the list twice
+ Assert( p1.userData != p2.userData );
+
+ return Q_stricmp( s1->m_szGameDescription, s2->m_szGameDescription );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Server name comparison function
+//-----------------------------------------------------------------------------
+int __cdecl ServerNameCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ return Q_stricmp( s1->GetName(), s2->GetName() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player number comparison function
+//-----------------------------------------------------------------------------
+int __cdecl PlayersCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ int s1p = max( 0, s1->m_nPlayers - s1->m_nBotPlayers );
+ int s1m = max( 0, s1->m_nMaxPlayers - s1->m_nBotPlayers );
+ int s2p = max( 0, s2->m_nPlayers - s2->m_nBotPlayers );
+ int s2m = max( 0, s2->m_nMaxPlayers - s2->m_nBotPlayers );
+
+ // compare number of players
+ if ( s1p > s2p )
+ return -1;
+ if ( s1p < s2p )
+ return 1;
+
+ // compare max players if number of current players is equal
+ if ( s1m > s2m )
+ return -1;
+ if ( s1m < s2m )
+ return 1;
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player number comparison function
+//-----------------------------------------------------------------------------
+int __cdecl LastPlayedCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer( p1.userData );
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer( p2.userData );
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ // compare number of players
+ if ( s1->m_ulTimeLastPlayed > s2->m_ulTimeLastPlayed )
+ return -1;
+ if ( s1->m_ulTimeLastPlayed < s2->m_ulTimeLastPlayed )
+ return 1;
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Tag comparison function
+//-----------------------------------------------------------------------------
+int __cdecl TagsCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ return Q_stricmp( s1->m_szGameTags, s2->m_szGameTags );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Replay comparison function
+//-----------------------------------------------------------------------------
+int __cdecl ReplayCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ gameserveritem_t *s1 = ServerBrowserDialog().GetServer(p1.userData);
+ gameserveritem_t *s2 = ServerBrowserDialog().GetServer(p2.userData);
+
+ if ( !s1 && s2 )
+ return -1;
+ if ( !s2 && s1 )
+ return 1;
+ if ( !s1 && !s2 )
+ return 0;
+
+ bool s1_is_replay = IsReplayServer( *s1 );
+ bool s2_is_replay = IsReplayServer( *s2 );
+
+ if ( s1_is_replay < s2_is_replay )
+ return 1;
+ else if ( s1_is_replay > s2_is_replay )
+ return -1;
+
+ return 0;
+}
+
diff --git a/serverbrowser/ServerListCompare.h b/serverbrowser/ServerListCompare.h
new file mode 100644
index 0000000..1e7a9ad
--- /dev/null
+++ b/serverbrowser/ServerListCompare.h
@@ -0,0 +1,32 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef SERVERLISTCOMPARE_H
+#define SERVERLISTCOMPARE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+using vgui::ListPanel;
+using vgui::ListPanelItem;
+
+
+// these functions are common to most server lists in sorts
+int __cdecl PasswordCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl BotsCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl PingCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl PlayersCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl MapCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl GameCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl ServerNameCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl SecureCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl IPAddressCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl LastPlayedCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl TagsCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+int __cdecl ReplayCompare(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2);
+
+#endif // SERVERLISTCOMPARE_H
diff --git a/serverbrowser/SpectateGames.cpp b/serverbrowser/SpectateGames.cpp
new file mode 100644
index 0000000..32b2999
--- /dev/null
+++ b/serverbrowser/SpectateGames.cpp
@@ -0,0 +1,33 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+
+CSpectateGames::CSpectateGames( vgui::Panel *parent )
+ : CInternetGames( parent, "SpectateGames", eSpectatorServer )
+{
+}
+
+void CSpectateGames::GetNewServerList()
+{
+ m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "proxy", "1" ) );
+ BaseClass::GetNewServerList();
+}
+
+void CSpectateGames::OnPageShow()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CSpectateGames::CheckTagFilter( gameserveritem_t &server )
+{
+ return true;
+}
+
diff --git a/serverbrowser/SpectateGames.h b/serverbrowser/SpectateGames.h
new file mode 100644
index 0000000..49ab16e
--- /dev/null
+++ b/serverbrowser/SpectateGames.h
@@ -0,0 +1,38 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef SPECTATEGAMES_H
+#define SPECTATEGAMES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "InternetGames.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Spectator games list
+//-----------------------------------------------------------------------------
+class CSpectateGames : public CInternetGames
+{
+public:
+ CSpectateGames(vgui::Panel *parent);
+
+ // property page handlers
+ virtual void OnPageShow();
+
+ virtual bool CheckTagFilter( gameserveritem_t &server );
+
+protected:
+ // filters by spectator games
+ virtual void GetNewServerList();
+
+private:
+ typedef CInternetGames BaseClass;
+};
+
+
+#endif // SPECTATEGAMES_H
diff --git a/serverbrowser/VACBannedConnRefusedDialog.cpp b/serverbrowser/VACBannedConnRefusedDialog.cpp
new file mode 100644
index 0000000..4e53105
--- /dev/null
+++ b/serverbrowser/VACBannedConnRefusedDialog.cpp
@@ -0,0 +1,22 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CVACBannedConnRefusedDialog::CVACBannedConnRefusedDialog( VPANEL hVParent, const char *name ) : BaseClass( NULL, name )
+{
+ SetParent( hVParent );
+ SetSize( 480, 220 );
+ SetSizeable( false );
+
+ LoadControlSettings( "servers/VACBannedConnRefusedDialog.res" );
+ MoveToCenterOfScreen();
+}
diff --git a/serverbrowser/VACBannedConnRefusedDialog.h b/serverbrowser/VACBannedConnRefusedDialog.h
new file mode 100644
index 0000000..16981b1
--- /dev/null
+++ b/serverbrowser/VACBannedConnRefusedDialog.h
@@ -0,0 +1,29 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef VACBANNEDCONNREFUSED_H
+#define VACBANNEDCONNREFUSED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Displays information about new VAC bans
+//-----------------------------------------------------------------------------
+class CVACBannedConnRefusedDialog : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( CVACBannedConnRefusedDialog, vgui::Frame );
+
+public:
+ CVACBannedConnRefusedDialog( vgui::VPANEL hVParent, const char *name );
+
+};
+
+
+
+
+#endif // VACBANNEDCONNREFUSED_H \ No newline at end of file
diff --git a/serverbrowser/igamelist.h b/serverbrowser/igamelist.h
new file mode 100644
index 0000000..3944c9b
--- /dev/null
+++ b/serverbrowser/igamelist.h
@@ -0,0 +1,91 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef IGAMELIST_H
+#define IGAMELIST_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class gameserveritem_t;
+#if defined( STEAM )
+#include "steam2common.h"
+#include "FindSteam2Servers.h"
+#else
+#include "steamcommon.h"
+#include "FindSteamServers.h"
+#endif
+#include "netadr.h"
+
+
+typedef enum
+{
+ SERVERVERSION_SAME_VERSION = 0,
+ SERVERVERSION_SERVER_OLD,
+ SERVERVERSION_SERVER_NEWER
+} SERVERVERSION;
+
+struct serverdisplay_t
+{
+ serverdisplay_t()
+ {
+ m_iListID = -1;
+ m_iServerID = -1;
+ m_bDoNotRefresh = true;
+ }
+ int m_iListID; // the VGUI2 list panel index for displaying this server
+ int m_iServerID; // the matchmaking interface index for this server
+ bool m_bDoNotRefresh;
+ bool operator==( const serverdisplay_t &rhs ) const { return rhs.m_iServerID == m_iServerID; }
+
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Interface to accessing a game list
+//-----------------------------------------------------------------------------
+class IGameList
+{
+public:
+
+ enum InterfaceItem_e
+ {
+ FILTERS,
+ GETNEWLIST,
+ ADDSERVER,
+ ADDCURRENTSERVER,
+ };
+
+ // returns true if the game list supports the specified ui elements
+ virtual bool SupportsItem(InterfaceItem_e item) = 0;
+
+ // starts the servers refreshing
+ virtual void StartRefresh() = 0;
+
+ // gets a new server list
+ virtual void GetNewServerList() = 0;
+
+ // stops current refresh/GetNewServerList()
+ virtual void StopRefresh() = 0;
+
+ // returns true if the list is currently refreshing servers
+ virtual bool IsRefreshing() = 0;
+
+ // gets information about specified server
+ virtual gameserveritem_t *GetServer(unsigned int serverID) = 0;
+
+ // called when Connect button is pressed
+ virtual void OnBeginConnect() = 0;
+
+ // invalid server index
+ virtual int GetInvalidServerListID() = 0;
+
+ // Get code to use for tracking how people are connecting to servers
+ virtual const char *GetConnectCode() = 0;
+};
+
+
+#endif // IGAMELIST_H
diff --git a/serverbrowser/pch_serverbrowser.cpp b/serverbrowser/pch_serverbrowser.cpp
new file mode 100644
index 0000000..42352b2
--- /dev/null
+++ b/serverbrowser/pch_serverbrowser.cpp
@@ -0,0 +1,7 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "pch_serverbrowser.h"
diff --git a/serverbrowser/pch_serverbrowser.h b/serverbrowser/pch_serverbrowser.h
new file mode 100644
index 0000000..575382e
--- /dev/null
+++ b/serverbrowser/pch_serverbrowser.h
@@ -0,0 +1,86 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include <winlite.h>
+#undef CreateDialog
+#ifdef WIN32
+#include <direct.h>
+#include <io.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "vstdlib/pch_vstdlib.h"
+#include "tier0/memdbgoff.h"
+#include "vgui_controls/pch_vgui_controls.h"
+#include "vgui_controls/Frame.h"
+#include "tier0/memdbgon.h"
+
+#include "tier3/tier3.h"
+
+// steam3 API
+//#include "steam/steam_querypackets.h"
+#include "steam/steam_api.h"
+#include "steam/isteamuser.h"
+#include "steam/isteammatchmaking.h"
+#include "steam/isteamfriends.h"
+
+#include "ServerBrowser/IServerBrowser.h"
+#include "IVguiModule.h"
+#include "vgui_controls/Controls.h"
+
+#include "netadr.h"
+#include "filesystem.h"
+#include "proto_oob.h"
+#include "ModList.h"
+#include "IRunGameEngine.h"
+
+#include "OfflineMode.h"
+
+// serverbrowser files
+
+#include "igamelist.h"
+#include "ServerListCompare.h"
+#include "ServerBrowser.h"
+#include "VACBannedConnRefusedDialog.h"
+#include "DialogGameInfo.h"
+#include "ServerContextMenu.h"
+#include "DialogServerPassword.h"
+#include "DialogAddServer.h"
+
+// game list
+#include "BaseGamesPage.h"
+#include "BlacklistedServers.h"
+#include "InternetGames.h"
+#include "FavoriteGames.h"
+#include "SpectateGames.h"
+#include "LanGames.h"
+#include "FriendsGames.h"
+#include "HistoryGames.h"
+#include "SpectateGames.h"
+#include "CustomGames.h"
+#include "ServerBrowserDialog.h"
+#include "QuickListPanel.h"
+#include "vgui_controls/PanelListPanel.h"
+
+#include "replay/ienginereplay.h"
+
+extern bool GameSupportsReplay();
+extern bool IsReplayServer( gameserveritem_t &server );
+
+#pragma warning( disable: 4355 ) // warning C4355: 'this' : used in base member initializer list
+
+#if defined( STEAM )
+#define IsSteam() true
+#else
+#define IsSteam() false
+#endif
+
+using namespace vgui;
diff --git a/serverbrowser/resource.h b/serverbrowser/resource.h
new file mode 100644
index 0000000..d568b57
--- /dev/null
+++ b/serverbrowser/resource.h
@@ -0,0 +1,15 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ServerBrowser.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/serverbrowser/xbox/xbox.def b/serverbrowser/xbox/xbox.def
new file mode 100644
index 0000000..38daf14
--- /dev/null
+++ b/serverbrowser/xbox/xbox.def
@@ -0,0 +1,3 @@
+LIBRARY serverbrowser_360.dll
+EXPORTS
+ CreateInterface @1