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