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.

489 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "pch_serverbrowser.h"
  8. // expose the server browser interfaces
  9. CServerBrowser g_ServerBrowserSingleton;
  10. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerBrowser, IServerBrowser, SERVERBROWSER_INTERFACE_VERSION, g_ServerBrowserSingleton);
  11. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerBrowser, IVGuiModule, "VGuiModuleServerBrowser001", g_ServerBrowserSingleton); // the interface loaded by PlatformMenu.vdf
  12. // singleton accessor
  13. CServerBrowser &ServerBrowser()
  14. {
  15. return g_ServerBrowserSingleton;
  16. }
  17. IRunGameEngine *g_pRunGameEngine = NULL;
  18. static CSteamAPIContext g_SteamAPIContext;
  19. CSteamAPIContext *steamapicontext = &g_SteamAPIContext;
  20. IEngineReplay *g_pEngineReplay = NULL;
  21. ConVar sb_firstopentime( "sb_firstopentime", "0", FCVAR_DEVELOPMENTONLY, "Indicates the time the server browser was first opened." );
  22. ConVar sb_numtimesopened( "sb_numtimesopened", "0", FCVAR_DEVELOPMENTONLY, "Indicates the number of times the server browser was opened this session." );
  23. // the original author of this code felt strdup was not acceptible.
  24. inline char *CloneString( const char *str )
  25. {
  26. char *cloneStr = new char [ strlen(str)+1 ];
  27. strcpy( cloneStr, str );
  28. return cloneStr;
  29. }
  30. //-----------------------------------------------------------------------------
  31. // Purpose: Constructor
  32. //-----------------------------------------------------------------------------
  33. CServerBrowser::CServerBrowser()
  34. {
  35. }
  36. //-----------------------------------------------------------------------------
  37. // Purpose: Destructor
  38. //-----------------------------------------------------------------------------
  39. CServerBrowser::~CServerBrowser()
  40. {
  41. }
  42. //-----------------------------------------------------------------------------
  43. // Purpose:
  44. //-----------------------------------------------------------------------------
  45. void CServerBrowser::CreateDialog()
  46. {
  47. if (!m_hInternetDlg.Get())
  48. {
  49. m_hInternetDlg = new CServerBrowserDialog(NULL); // SetParent() call below fills this in
  50. m_hInternetDlg->Initialize();
  51. }
  52. }
  53. //-----------------------------------------------------------------------------
  54. // Purpose: links to vgui and engine interfaces
  55. //-----------------------------------------------------------------------------
  56. bool CServerBrowser::Initialize(CreateInterfaceFn *factorylist, int factoryCount)
  57. {
  58. ConnectTier1Libraries( factorylist, factoryCount );
  59. ConVar_Register();
  60. ConnectTier2Libraries( factorylist, factoryCount );
  61. ConnectTier3Libraries( factorylist, factoryCount );
  62. g_pRunGameEngine = NULL;
  63. for ( int i = 0; i < factoryCount; ++i )
  64. {
  65. if ( !g_pEngineReplay )
  66. {
  67. g_pEngineReplay = ( IEngineReplay * )factorylist[ i ]( ENGINE_REPLAY_INTERFACE_VERSION, NULL );
  68. }
  69. }
  70. SteamAPI_InitSafe();
  71. SteamAPI_SetTryCatchCallbacks( false ); // We don't use exceptions, so tell steam not to use try/catch in callback handlers
  72. steamapicontext->Init();
  73. for (int i = 0; i < factoryCount; i++)
  74. {
  75. if (!g_pRunGameEngine)
  76. {
  77. g_pRunGameEngine = (IRunGameEngine *)(factorylist[i])(RUNGAMEENGINE_INTERFACE_VERSION, NULL);
  78. }
  79. }
  80. // load the vgui interfaces
  81. #if defined( STEAM ) || defined( HL1 )
  82. if ( !vgui::VGuiControls_Init("ServerBrowser", factorylist, factoryCount) )
  83. #else
  84. if ( !vgui::VGui_InitInterfacesList("ServerBrowser", factorylist, factoryCount) )
  85. #endif
  86. return false;
  87. // load localization file
  88. g_pVGuiLocalize->AddFile( "servers/serverbrowser_%language%.txt" );
  89. return true;
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose: links to other modules interfaces (tracker)
  93. //-----------------------------------------------------------------------------
  94. bool CServerBrowser::PostInitialize(CreateInterfaceFn *modules, int factoryCount)
  95. {
  96. // find the interfaces we need
  97. for (int i = 0; i < factoryCount; i++)
  98. {
  99. if (!g_pRunGameEngine)
  100. {
  101. g_pRunGameEngine = (IRunGameEngine *)(modules[i])(RUNGAMEENGINE_INTERFACE_VERSION, NULL);
  102. }
  103. }
  104. CreateDialog();
  105. m_hInternetDlg->SetVisible(false);
  106. return g_pRunGameEngine;
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Purpose: true if the user can't play a game due to VAC banning
  110. //-----------------------------------------------------------------------------
  111. bool CServerBrowser::IsVACBannedFromGame( int nAppID )
  112. {
  113. return false;
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Purpose: Marks that the tool/game loading us intends to feed us workshop information
  117. //-----------------------------------------------------------------------------
  118. void CServerBrowser::SetWorkshopEnabled( bool bManaged )
  119. {
  120. m_bWorkshopEnabled = bManaged;
  121. }
  122. //-----------------------------------------------------------------------------
  123. // Purpose: Add a mapname to our known user-subscribed workshop maps list
  124. //-----------------------------------------------------------------------------
  125. void CServerBrowser::AddWorkshopSubscribedMap( const char *pszMapName )
  126. {
  127. CUtlString strMap( pszMapName );
  128. if ( !IsWorkshopSubscribedMap( strMap ) )
  129. {
  130. m_vecWorkshopSubscribedMaps.AddToTail( strMap );
  131. }
  132. }
  133. //-----------------------------------------------------------------------------
  134. // Purpose: remove a mapname to our known user-subscribed workshop maps list
  135. //-----------------------------------------------------------------------------
  136. void CServerBrowser::RemoveWorkshopSubscribedMap( const char *pszMapName )
  137. {
  138. m_vecWorkshopSubscribedMaps.FindAndFastRemove( CUtlString( pszMapName ) );
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose: Well, is it?
  142. //-----------------------------------------------------------------------------
  143. bool CServerBrowser::IsWorkshopEnabled()
  144. {
  145. return m_bWorkshopEnabled;
  146. }
  147. //-----------------------------------------------------------------------------
  148. // Purpose: Check if this map is in our subscribed list
  149. //-----------------------------------------------------------------------------
  150. bool CServerBrowser::IsWorkshopSubscribedMap( const char *pszMapName )
  151. {
  152. return m_vecWorkshopSubscribedMaps.HasElement( CUtlString( pszMapName ) );
  153. }
  154. //-----------------------------------------------------------------------------
  155. // Purpose:
  156. //-----------------------------------------------------------------------------
  157. bool CServerBrowser::IsValid()
  158. {
  159. return ( g_pRunGameEngine );
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Purpose:
  163. //-----------------------------------------------------------------------------
  164. bool CServerBrowser::Activate()
  165. {
  166. static bool firstTimeOpening = true;
  167. if ( firstTimeOpening )
  168. {
  169. m_hInternetDlg->LoadUserData(); // reload the user data the first time the dialog is made visible, helps with the lag between module load and
  170. // steamui getting Deactivate() call
  171. firstTimeOpening = false;
  172. }
  173. int numTimesOpened = sb_numtimesopened.GetInt() + 1;
  174. sb_numtimesopened.SetValue( numTimesOpened );
  175. if ( numTimesOpened == 1 )
  176. {
  177. time_t aclock;
  178. time( &aclock );
  179. sb_firstopentime.SetValue( (int) aclock );
  180. }
  181. Open();
  182. return true;
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose: called when the server browser gets used in the game
  186. //-----------------------------------------------------------------------------
  187. void CServerBrowser::Deactivate()
  188. {
  189. if (m_hInternetDlg.Get())
  190. {
  191. m_hInternetDlg->SaveUserData();
  192. }
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose: called when the server browser is no longer being used in the game
  196. //-----------------------------------------------------------------------------
  197. void CServerBrowser::Reactivate()
  198. {
  199. if (m_hInternetDlg.Get())
  200. {
  201. m_hInternetDlg->LoadUserData();
  202. if (m_hInternetDlg->IsVisible())
  203. {
  204. m_hInternetDlg->RefreshCurrentPage();
  205. }
  206. }
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose:
  210. //-----------------------------------------------------------------------------
  211. void CServerBrowser::Open()
  212. {
  213. m_hInternetDlg->Open();
  214. }
  215. //-----------------------------------------------------------------------------
  216. // Purpose: returns direct handle to main server browser dialog
  217. //-----------------------------------------------------------------------------
  218. vgui::VPANEL CServerBrowser::GetPanel()
  219. {
  220. return m_hInternetDlg.Get() ? m_hInternetDlg->GetVPanel() : NULL;
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Purpose: sets the parent panel of the main module panel
  224. //-----------------------------------------------------------------------------
  225. void CServerBrowser::SetParent(vgui::VPANEL parent)
  226. {
  227. if (m_hInternetDlg.Get())
  228. {
  229. m_hInternetDlg->SetParent(parent);
  230. }
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Purpose: Closes down the server browser for good
  234. //-----------------------------------------------------------------------------
  235. void CServerBrowser::Shutdown()
  236. {
  237. if (m_hInternetDlg.Get())
  238. {
  239. m_hInternetDlg->Close();
  240. m_hInternetDlg->MarkForDeletion();
  241. }
  242. #if defined( STEAM )
  243. vgui::VGuiControls_Shutdown();
  244. #endif
  245. DisconnectTier3Libraries();
  246. DisconnectTier2Libraries();
  247. ConVar_Unregister();
  248. DisconnectTier1Libraries();
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose: opens a game info dialog to watch the specified server; associated with the friend 'userName'
  252. //-----------------------------------------------------------------------------
  253. bool CServerBrowser::OpenGameInfoDialog( uint64 ulSteamIDFriend, const char *pszConnectCode )
  254. {
  255. #if !defined( _X360 ) // X360TBD: SteamFriends()
  256. if ( m_hInternetDlg.Get() )
  257. {
  258. // activate an already-existing dialog
  259. CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->GetDialogGameInfoForFriend( ulSteamIDFriend );
  260. if ( pDialogGameInfo )
  261. {
  262. pDialogGameInfo->Activate();
  263. return true;
  264. }
  265. // none yet, create a new dialog
  266. FriendGameInfo_t friendGameInfo;
  267. if ( steamapicontext->SteamFriends()->GetFriendGamePlayed( ulSteamIDFriend, &friendGameInfo ) )
  268. {
  269. uint16 usConnPort = friendGameInfo.m_usGamePort;
  270. if ( friendGameInfo.m_usQueryPort < QUERY_PORT_ERROR )
  271. usConnPort = friendGameInfo.m_usQueryPort;
  272. CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->OpenGameInfoDialog( friendGameInfo.m_unGameIP, friendGameInfo.m_usGamePort, usConnPort, pszConnectCode );
  273. pDialogGameInfo->SetFriend( ulSteamIDFriend );
  274. return true;
  275. }
  276. }
  277. #endif
  278. return false;
  279. }
  280. //-----------------------------------------------------------------------------
  281. // Purpose: joins a specified game - game info dialog will only be opened if the server is fully or passworded
  282. //-----------------------------------------------------------------------------
  283. bool CServerBrowser::JoinGame( uint64 ulSteamIDFriend, const char *pszConnectCode )
  284. {
  285. if ( OpenGameInfoDialog( ulSteamIDFriend, pszConnectCode ) )
  286. {
  287. CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->GetDialogGameInfoForFriend( ulSteamIDFriend );
  288. pDialogGameInfo->Connect();
  289. }
  290. return false;
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Purpose: joins a game by IP/Port
  294. //-----------------------------------------------------------------------------
  295. bool CServerBrowser::JoinGame( uint32 unGameIP, uint16 usGamePort, const char *pszConnectCode )
  296. {
  297. m_hInternetDlg->JoinGame( unGameIP, usGamePort, pszConnectCode );
  298. return true;
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose: forces the game info dialog closed
  302. //-----------------------------------------------------------------------------
  303. void CServerBrowser::CloseGameInfoDialog( uint64 ulSteamIDFriend )
  304. {
  305. CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->GetDialogGameInfoForFriend( ulSteamIDFriend );
  306. if ( pDialogGameInfo )
  307. {
  308. pDialogGameInfo->Close();
  309. }
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Purpose: closes all the game info dialogs
  313. //-----------------------------------------------------------------------------
  314. void CServerBrowser::CloseAllGameInfoDialogs()
  315. {
  316. if ( m_hInternetDlg.Get() )
  317. {
  318. m_hInternetDlg->CloseAllGameInfoDialogs();
  319. }
  320. }
  321. CUtlVector< gametypes_t > g_GameTypes;
  322. //-----------------------------------------------------------------------------
  323. // Purpose:
  324. //-----------------------------------------------------------------------------
  325. void LoadGameTypes( void )
  326. {
  327. if ( g_GameTypes.Count() > 0 )
  328. return;
  329. #define GAMETYPES_FILE "servers/ServerBrowserGameTypes.txt"
  330. KeyValues * kv = new KeyValues( GAMETYPES_FILE );
  331. if ( !kv->LoadFromFile( g_pFullFileSystem, GAMETYPES_FILE, "MOD" ) )
  332. {
  333. kv->deleteThis();
  334. return;
  335. }
  336. g_GameTypes.RemoveAll();
  337. for ( KeyValues *pData = kv->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() )
  338. {
  339. gametypes_t gametype;
  340. gametype.pPrefix = CloneString( pData->GetString( "prefix", "" ) );
  341. gametype.pGametypeName = CloneString( pData->GetString( "name", "" ) );
  342. g_GameTypes.AddToTail( gametype );
  343. }
  344. kv->deleteThis();
  345. }
  346. const char *GetGameTypeName( const char *pMapName )
  347. {
  348. LoadGameTypes();
  349. for ( int i = 0; i < g_GameTypes.Count(); i++ )
  350. {
  351. int iLength = strlen( g_GameTypes[i].pPrefix );
  352. if ( !Q_strncmp( pMapName, g_GameTypes[i].pPrefix, iLength ) )
  353. {
  354. return g_GameTypes[i].pGametypeName;
  355. }
  356. }
  357. return "";
  358. }
  359. //-----------------------------------------------------------------------------
  360. // Purpose of comments like these: none
  361. //-----------------------------------------------------------------------------
  362. const char *CServerBrowser::GetMapFriendlyNameAndGameType( const char *pszMapName, char *szFriendlyMapName, int cchFriendlyName )
  363. {
  364. // Make sure game types are loaded
  365. LoadGameTypes();
  366. // Scan list
  367. const char *pszFriendlyGameTypeName = "";
  368. for ( int i = 0; i < g_GameTypes.Count(); i++ )
  369. {
  370. int iLength = strlen( g_GameTypes[i].pPrefix );
  371. if ( !Q_strnicmp( pszMapName, g_GameTypes[i].pPrefix, iLength ) )
  372. {
  373. pszMapName += iLength;
  374. pszFriendlyGameTypeName = g_GameTypes[i].pGametypeName;
  375. break;
  376. }
  377. }
  378. // See how many characters from the name to copy.
  379. // Start by assuming we'll copy the whole thing.
  380. // (After any prefix we just skipped)
  381. int l = V_strlen( pszMapName );
  382. const char *pszFinal = Q_stristr( pszMapName, "_final" );
  383. if ( pszFinal )
  384. {
  385. // truncate the _final (or _final1) part of the filename if it's at the end of the name
  386. const char *pszNextChar = pszFinal + Q_strlen( "_final" );
  387. if ( ( *pszNextChar == '\0' ) ||
  388. ( ( *pszNextChar == '1' ) && ( *(pszNextChar+1) == '\0' ) ) )
  389. {
  390. l = pszFinal - pszMapName;
  391. }
  392. }
  393. // Safety check against buffer size
  394. if ( l >= cchFriendlyName )
  395. {
  396. Assert( !"Map name too long for buffer!" );
  397. l = cchFriendlyName-1;
  398. }
  399. // Copy friendly portion of name only
  400. V_memcpy( szFriendlyMapName, pszMapName, l );
  401. // It's like the Alamo. We never forget.
  402. szFriendlyMapName[l] = '\0';
  403. // Result should be the friendly game type name
  404. return pszFriendlyGameTypeName;
  405. }