Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

448 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "pch_serverbrowser.h"
  7. #include <vgui_controls/HTML.h>
  8. #include <vgui_controls/MessageDialog.h>
  9. using namespace vgui;
  10. #define NUM_COMMON_TAGS 20
  11. //-----------------------------------------------------------------------------
  12. // Purpose:
  13. //-----------------------------------------------------------------------------
  14. TagMenuButton::TagMenuButton(Panel *parent, const char *panelName, const char *text) : BaseClass(parent,panelName,text)
  15. {
  16. }
  17. //-----------------------------------------------------------------------------
  18. // Purpose:
  19. //-----------------------------------------------------------------------------
  20. void TagMenuButton::OnShowMenu( vgui::Menu *menu )
  21. {
  22. PostActionSignal(new KeyValues("TagMenuButtonOpened"));
  23. BaseClass::OnShowMenu(menu);
  24. }
  25. //-----------------------------------------------------------------------------
  26. // Purpose:
  27. //-----------------------------------------------------------------------------
  28. class CCustomServerInfoURLQuery : public vgui::QueryBox
  29. {
  30. DECLARE_CLASS_SIMPLE( CCustomServerInfoURLQuery, vgui::QueryBox );
  31. public:
  32. CCustomServerInfoURLQuery(const char *title, const char *queryText,vgui::Panel *parent) : BaseClass( title, queryText, parent )
  33. {
  34. SetOKButtonText( "#ServerBrowser_CustomServerURLButton" );
  35. }
  36. };
  37. DECLARE_BUILD_FACTORY( TagInfoLabel );
  38. //-----------------------------------------------------------------------------
  39. // Purpose:
  40. //-----------------------------------------------------------------------------
  41. TagInfoLabel::TagInfoLabel(Panel *parent, const char *panelName) : BaseClass(parent,panelName, (const char *)NULL, NULL)
  42. {
  43. }
  44. //-----------------------------------------------------------------------------
  45. // Purpose:
  46. //-----------------------------------------------------------------------------
  47. TagInfoLabel::TagInfoLabel(Panel *parent, const char *panelName, const char *text, const char *pszURL) : BaseClass(parent,panelName,text,pszURL)
  48. {
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Purpose: If we were left clicked on, launch the URL
  52. //-----------------------------------------------------------------------------
  53. void TagInfoLabel::OnMousePressed(MouseCode code)
  54. {
  55. if (code == MOUSE_LEFT)
  56. {
  57. if ( GetURL() )
  58. {
  59. // Pop up the dialog with the url in it
  60. CCustomServerInfoURLQuery *qb = new CCustomServerInfoURLQuery( "#ServerBrowser_CustomServerURLWarning", "#ServerBrowser_CustomServerURLOpen", this );
  61. if (qb != NULL)
  62. {
  63. qb->SetOKCommand( new KeyValues("DoOpenCustomServerInfoURL") );
  64. qb->AddActionSignalTarget(this);
  65. qb->MoveToFront();
  66. qb->DoModal();
  67. }
  68. }
  69. }
  70. }
  71. //-----------------------------------------------------------------------------
  72. // Purpose:
  73. //-----------------------------------------------------------------------------
  74. void TagInfoLabel::DoOpenCustomServerInfoURL( void )
  75. {
  76. if ( GetURL() )
  77. {
  78. system()->ShellExecute("open", GetURL() );
  79. }
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Purpose: Constructor
  83. //-----------------------------------------------------------------------------
  84. CCustomGames::CCustomGames(vgui::Panel *parent) :
  85. BaseClass(parent, "CustomGames", eInternetServer )
  86. {
  87. m_pGameList->AddColumnHeader(10, "Tags", "#ServerBrowser_Tags", 200);
  88. m_pGameList->SetSortFunc(10, TagsCompare);
  89. if ( !IsSteamGameServerBrowsingEnabled() )
  90. {
  91. m_pGameList->SetEmptyListText("#ServerBrowser_OfflineMode");
  92. m_pConnect->SetEnabled( false );
  93. m_pRefreshAll->SetEnabled( false );
  94. m_pRefreshQuick->SetEnabled( false );
  95. m_pAddServer->SetEnabled( false );
  96. m_pFilter->SetEnabled( false );
  97. }
  98. m_szTagFilter[0] = 0;
  99. m_pTagFilter = new TextEntry(this, "TagFilter");
  100. m_pTagFilter->SetEnabled( false );
  101. m_pTagFilter->SetMaximumCharCount( MAX_TAG_CHARACTERS );
  102. m_pAddTagList = new TagMenuButton( this, "AddTagList", "#ServerBrowser_AddCommonTags" );
  103. m_pTagListMenu = new Menu( m_pAddTagList, "TagList" );
  104. m_pAddTagList->SetMenu( m_pTagListMenu );
  105. m_pAddTagList->SetOpenDirection( Menu::UP );
  106. m_pAddTagList->SetEnabled( false );
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Purpose: Destructor
  110. //-----------------------------------------------------------------------------
  111. CCustomGames::~CCustomGames()
  112. {
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Purpose:
  116. //-----------------------------------------------------------------------------
  117. void CCustomGames::UpdateDerivedLayouts( void )
  118. {
  119. const char *pPathID = "PLATFORM";
  120. KeyValues *pConditions = NULL;
  121. if ( ServerBrowser().IsWorkshopEnabled() )
  122. {
  123. pConditions = new KeyValues( "conditions" );
  124. if ( pConditions )
  125. {
  126. KeyValues *pNewKey = new KeyValues( "if_workshop_enabled" );
  127. if ( pNewKey )
  128. {
  129. pConditions->AddSubKey( pNewKey );
  130. }
  131. }
  132. }
  133. if ( m_pFilter->IsSelected() )
  134. {
  135. if ( g_pFullFileSystem->FileExists( "servers/CustomGamesPage_Filters.res", "MOD" ) )
  136. {
  137. pPathID = "MOD";
  138. }
  139. LoadControlSettings( "servers/CustomGamesPage_Filters.res", pPathID, NULL, pConditions );
  140. }
  141. else
  142. {
  143. if ( g_pFullFileSystem->FileExists( "servers/CustomGamesPage.res", "MOD" ) )
  144. {
  145. pPathID = "MOD";
  146. }
  147. LoadControlSettings( "servers/CustomGamesPage.res", pPathID, NULL, pConditions );
  148. }
  149. if ( pConditions )
  150. {
  151. pConditions->deleteThis();
  152. }
  153. if ( !GameSupportsReplay() )
  154. {
  155. HideReplayFilter();
  156. }
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Purpose:
  160. //-----------------------------------------------------------------------------
  161. void CCustomGames::OnLoadFilter(KeyValues *filter)
  162. {
  163. BaseClass::OnLoadFilter( filter );
  164. Q_strncpy(m_szTagFilter, filter->GetString("gametype"), sizeof(m_szTagFilter));
  165. if ( m_pTagFilter )
  166. {
  167. m_pTagFilter->SetText(m_szTagFilter);
  168. }
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose:
  172. //-----------------------------------------------------------------------------
  173. bool CCustomGames::CheckTagFilter( gameserveritem_t &server )
  174. {
  175. bool bRetVal = true;
  176. // Custom games substring matches tags with the server's tags
  177. int count = Q_strlen( m_szTagFilter );
  178. if ( count )
  179. {
  180. CUtlVector<char*> TagList;
  181. V_SplitString( m_szTagFilter, ",", TagList );
  182. for ( int i = 0; i < TagList.Count(); i++ )
  183. {
  184. if ( ( Q_strnistr( server.m_szGameTags, TagList[i], MAX_TAG_CHARACTERS ) > 0 ) == TagsExclude() )
  185. {
  186. bRetVal = false;
  187. break;
  188. }
  189. }
  190. TagList.PurgeAndDeleteElements();
  191. }
  192. return bRetVal;
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose: Checks the workshop filtering setting, taking into account workshop filtering might be disabled
  196. //-----------------------------------------------------------------------------
  197. bool CCustomGames::CheckWorkshopFilter( gameserveritem_t &server )
  198. {
  199. eWorkshopMode workshopMode = WorkshopMode();
  200. const char szWorkshopPrefix[] = "workshop/";
  201. if ( workshopMode == eWorkshop_WorkshopOnly )
  202. {
  203. return V_strncasecmp( server.m_szMap, szWorkshopPrefix, sizeof( szWorkshopPrefix ) - 1 ) == 0;
  204. }
  205. else if ( workshopMode == eWorkshop_SubscribedOnly )
  206. {
  207. return ServerBrowser().IsWorkshopSubscribedMap( server.m_szMap );
  208. }
  209. Assert( workshopMode == eWorkshop_None );
  210. return true;
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose: gets filter settings from controls
  214. //-----------------------------------------------------------------------------
  215. void CCustomGames::OnSaveFilter(KeyValues *filter)
  216. {
  217. BaseClass::OnSaveFilter( filter );
  218. if ( m_pTagFilter )
  219. {
  220. // tags
  221. m_pTagFilter->GetText(m_szTagFilter, sizeof(m_szTagFilter) - 1);
  222. }
  223. if ( m_szTagFilter[0] )
  224. {
  225. Q_strlower(m_szTagFilter);
  226. }
  227. if ( TagsExclude() )
  228. {
  229. m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "gametype", "" ) );
  230. }
  231. else
  232. {
  233. m_vecServerFilters.AddToTail( MatchMakingKeyValuePair_t( "gametype", m_szTagFilter ) );
  234. }
  235. filter->SetString("gametype", m_szTagFilter);
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose:
  239. //-----------------------------------------------------------------------------
  240. void CCustomGames::SetRefreshing(bool state)
  241. {
  242. if ( state )
  243. {
  244. m_pAddTagList->SetEnabled( false );
  245. }
  246. BaseClass::SetRefreshing( state );
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose:
  250. //-----------------------------------------------------------------------------
  251. void CCustomGames::ServerResponded( int iServer, gameserveritem_t *pServerItem )
  252. {
  253. CBaseGamesPage::ServerResponded( iServer, pServerItem );
  254. // If we've found a server with some tags, enable the add tag button
  255. if ( pServerItem->m_szGameTags[0] )
  256. {
  257. m_pAddTagList->SetEnabled( true );
  258. }
  259. }
  260. struct tagentry_t
  261. {
  262. const char *pszTag;
  263. int iCount;
  264. };
  265. int __cdecl SortTagsInUse( const tagentry_t *pTag1, const tagentry_t *pTag2 )
  266. {
  267. return (pTag1->iCount < pTag2->iCount);
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Purpose:
  271. //-----------------------------------------------------------------------------
  272. void CCustomGames::RecalculateCommonTags( void )
  273. {
  274. // Regenerate our tag list
  275. m_pTagListMenu->DeleteAllItems();
  276. // Loop through our servers, and build a list of all the tags
  277. CUtlVector<tagentry_t> aTagsInUse;
  278. int iCount = m_pGameList->GetItemCount();
  279. for ( int i = 0; i < iCount; i++ )
  280. {
  281. int serverID = m_pGameList->GetItemUserData( i );
  282. gameserveritem_t *pServer = GetServer( serverID );
  283. if ( pServer && pServer->m_szGameTags && pServer->m_szGameTags[0] )
  284. {
  285. CUtlVector<char*> TagList;
  286. V_SplitString( pServer->m_szGameTags, ",", TagList );
  287. for ( int iTag = 0; iTag < TagList.Count(); iTag++ )
  288. {
  289. // First make sure it's not already in our list
  290. bool bFound = false;
  291. for ( int iCheck = 0; iCheck < aTagsInUse.Count(); iCheck++ )
  292. {
  293. if ( !Q_strnicmp(TagList[iTag], aTagsInUse[iCheck].pszTag, MAX_TAG_CHARACTERS ) )
  294. {
  295. aTagsInUse[iCheck].iCount++;
  296. bFound = true;
  297. }
  298. }
  299. if ( !bFound )
  300. {
  301. int iIdx = aTagsInUse.AddToTail();
  302. aTagsInUse[iIdx].pszTag = TagList[iTag];
  303. aTagsInUse[iIdx].iCount = 0;
  304. }
  305. }
  306. }
  307. }
  308. aTagsInUse.Sort( SortTagsInUse );
  309. int iTagsToAdd = min( aTagsInUse.Count(), NUM_COMMON_TAGS );
  310. for ( int i = 0; i < iTagsToAdd; i++ )
  311. {
  312. const char *pszTag = aTagsInUse[i].pszTag;
  313. m_pTagListMenu->AddMenuItem( pszTag, new KeyValues("AddTag", "tag", pszTag), this, new KeyValues( "data", "tag", pszTag ) );
  314. }
  315. m_pTagListMenu->SetFixedWidth( m_pAddTagList->GetWide() );
  316. m_pTagListMenu->InvalidateLayout( true, false );
  317. m_pTagListMenu->PositionRelativeToPanel( m_pAddTagList, Menu::UP );
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Purpose:
  321. //-----------------------------------------------------------------------------
  322. void CCustomGames::OnTagMenuButtonOpened( void )
  323. {
  324. RecalculateCommonTags();
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Purpose: Sets the text from the message
  328. //-----------------------------------------------------------------------------
  329. void CCustomGames::OnAddTag(KeyValues *params)
  330. {
  331. KeyValues *pkvText = params->FindKey("tag", false);
  332. if (!pkvText)
  333. return;
  334. AddTagToFilterList( pkvText->GetString() );
  335. }
  336. int SortServerTags( char* const *p1, char* const *p2 )
  337. {
  338. return ( Q_strcmp( *p1, *p2 ) > 0 );
  339. }
  340. //-----------------------------------------------------------------------------
  341. // Purpose:
  342. //-----------------------------------------------------------------------------
  343. void CCustomGames::AddTagToFilterList( const char *pszTag )
  344. {
  345. char txt[ 128 ];
  346. m_pTagFilter->GetText( txt, sizeof( txt ) );
  347. CUtlVector<char*> TagList;
  348. V_SplitString( txt, ",", TagList );
  349. if ( txt[0] )
  350. {
  351. for ( int i = 0; i < TagList.Count(); i++ )
  352. {
  353. // Already in the tag list?
  354. if ( !Q_stricmp( TagList[i], pszTag ) )
  355. {
  356. TagList.PurgeAndDeleteElements();
  357. return;
  358. }
  359. }
  360. }
  361. char *pszNewTag = new char[64];
  362. Q_strncpy( pszNewTag, pszTag, 64 );
  363. TagList.AddToHead( pszNewTag );
  364. TagList.Sort( SortServerTags );
  365. // Append it
  366. char tmptags[MAX_TAG_CHARACTERS];
  367. tmptags[0] = '\0';
  368. for ( int i = 0; i < TagList.Count(); i++ )
  369. {
  370. if ( i > 0 )
  371. {
  372. Q_strncat( tmptags, ",", MAX_TAG_CHARACTERS );
  373. }
  374. Q_strncat( tmptags, TagList[i], MAX_TAG_CHARACTERS );
  375. }
  376. m_pTagFilter->SetText( tmptags );
  377. TagList.PurgeAndDeleteElements();
  378. // Update & apply filters now that the tag list has changed
  379. UpdateFilterSettings();
  380. ApplyGameFilters();
  381. }