//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #include "pch_serverbrowser.h" #include #include 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 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 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 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 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(); }