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.

802 lines
24 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "pch_serverbrowser.h"
  8. using namespace vgui;
  9. static const long RETRY_TIME = 10000; // refresh server every 10 seconds
  10. static const long CHALLENGE_ENTRIES = 1024;
  11. extern "C"
  12. {
  13. DLL_EXPORT bool JoiningSecureServerCall()
  14. {
  15. return true;
  16. }
  17. }
  18. //-----------------------------------------------------------------------------
  19. // Purpose: Comparison function used in query redblack tree
  20. //-----------------------------------------------------------------------------
  21. bool QueryLessFunc( const struct challenge_s &item1, const struct challenge_s &item2 )
  22. {
  23. // compare port then ip
  24. if ( item1.addr.GetPort() < item2.addr.GetPort() )
  25. return true;
  26. else if ( item1.addr.GetPort() > item2.addr.GetPort() )
  27. return false;
  28. int ip1 = item1.addr.GetIPNetworkByteOrder();
  29. int ip2 = item2.addr.GetIPNetworkByteOrder();
  30. return ip1 < ip2;
  31. }
  32. //-----------------------------------------------------------------------------
  33. // Purpose: Constructor
  34. //-----------------------------------------------------------------------------
  35. CDialogGameInfo::CDialogGameInfo( vgui::Panel *parent, int serverIP, int queryPort, unsigned short connectionPort, const char *pszConnectCode ) :
  36. Frame(parent, "DialogGameInfo"),
  37. m_CallbackPersonaStateChange( this, &CDialogGameInfo::OnPersonaStateChange ),
  38. m_sConnectCode( pszConnectCode )
  39. {
  40. SetBounds(0, 0, 512, 512);
  41. SetMinimumSize(416, 340);
  42. SetDeleteSelfOnClose(true);
  43. m_bConnecting = false;
  44. m_bServerFull = false;
  45. m_bShowAutoRetryToggle = false;
  46. m_bServerNotResponding = false;
  47. m_bShowingExtendedOptions = false;
  48. m_SteamIDFriend = 0;
  49. m_hPingQuery = HSERVERQUERY_INVALID;
  50. m_hPlayersQuery = HSERVERQUERY_INVALID;
  51. m_bPlayerListUpdatePending = false;
  52. m_szPassword[0] = 0;
  53. m_pConnectButton = new Button(this, "Connect", "#ServerBrowser_JoinGame");
  54. m_pCloseButton = new Button(this, "Close", "#ServerBrowser_Close");
  55. m_pRefreshButton = new Button(this, "Refresh", "#ServerBrowser_Refresh");
  56. m_pInfoLabel = new Label(this, "InfoLabel", "");
  57. m_pAutoRetry = new ToggleButton(this, "AutoRetry", "#ServerBrowser_AutoRetry");
  58. m_pAutoRetry->AddActionSignalTarget(this);
  59. m_pAutoRetryAlert = new RadioButton(this, "AutoRetryAlert", "#ServerBrowser_AlertMeWhenSlotOpens");
  60. m_pAutoRetryJoin = new RadioButton(this, "AutoRetryJoin", "#ServerBrowser_JoinWhenSlotOpens");
  61. m_pPlayerList = new ListPanel(this, "PlayerList");
  62. m_pPlayerList->AddColumnHeader(0, "PlayerName", "#ServerBrowser_PlayerName", 156);
  63. m_pPlayerList->AddColumnHeader(1, "Score", "#ServerBrowser_Score", 64);
  64. m_pPlayerList->AddColumnHeader(2, "Time", "#ServerBrowser_Time", 64);
  65. m_pPlayerList->SetSortFunc(2, &PlayerTimeColumnSortFunc);
  66. // set the defaults for sorting
  67. // hack, need to make this more explicit functions in ListPanel
  68. PostMessage(m_pPlayerList, new KeyValues("SetSortColumn", "column", 2));
  69. PostMessage(m_pPlayerList, new KeyValues("SetSortColumn", "column", 1));
  70. PostMessage(m_pPlayerList, new KeyValues("SetSortColumn", "column", 1));
  71. m_pAutoRetryAlert->SetSelected(true);
  72. m_pConnectButton->SetCommand(new KeyValues("Connect"));
  73. m_pCloseButton->SetCommand(new KeyValues("Close"));
  74. m_pRefreshButton->SetCommand(new KeyValues("Refresh"));
  75. m_iRequestRetry = 0;
  76. // create a new server to watch
  77. memset(&m_Server, 0, sizeof(m_Server) );
  78. m_Server.m_NetAdr.Init( serverIP, queryPort, connectionPort );
  79. // refresh immediately
  80. RequestInfo();
  81. // let us be ticked every frame
  82. ivgui()->AddTickSignal(this->GetVPanel());
  83. LoadControlSettings("Servers/DialogGameInfo.res");
  84. RegisterControlSettingsFile( "Servers/DialogGameInfo_SinglePlayer.res" );
  85. RegisterControlSettingsFile( "Servers/DialogGameInfo_AutoRetry.res" );
  86. MoveToCenterOfScreen();
  87. }
  88. //-----------------------------------------------------------------------------
  89. // Purpose: Destructor
  90. //-----------------------------------------------------------------------------
  91. CDialogGameInfo::~CDialogGameInfo()
  92. {
  93. if ( !steamapicontext->SteamMatchmakingServers() )
  94. return;
  95. if ( m_hPingQuery != HSERVERQUERY_INVALID )
  96. steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPingQuery );
  97. if ( m_hPlayersQuery != HSERVERQUERY_INVALID )
  98. steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPlayersQuery );
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Purpose: send a player query to a server
  102. //-----------------------------------------------------------------------------
  103. void CDialogGameInfo::SendPlayerQuery( uint32 unIP, uint16 usQueryPort )
  104. {
  105. if ( !steamapicontext->SteamMatchmakingServers() )
  106. return;
  107. if ( m_hPlayersQuery != HSERVERQUERY_INVALID )
  108. steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPlayersQuery );
  109. m_hPlayersQuery = steamapicontext->SteamMatchmakingServers()->PlayerDetails( unIP, usQueryPort, this );
  110. m_bPlayerListUpdatePending = true;
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Purpose: Activates the dialog
  114. //-----------------------------------------------------------------------------
  115. void CDialogGameInfo::Run(const char *titleName)
  116. {
  117. if ( titleName )
  118. {
  119. SetTitle( "#ServerBrowser_GameInfoWithNameTitle", true );
  120. }
  121. else
  122. {
  123. SetTitle( "#ServerBrowser_GameInfoWithNameTitle", true );
  124. }
  125. SetDialogVariable( "game", titleName );
  126. // get the info from the user
  127. RequestInfo();
  128. Activate();
  129. }
  130. //-----------------------------------------------------------------------------
  131. // Purpose: Changes which server to watch
  132. //-----------------------------------------------------------------------------
  133. void CDialogGameInfo::ChangeGame( int serverIP, int queryPort, unsigned short connectionPort )
  134. {
  135. memset( &m_Server, 0x0, sizeof(m_Server) );
  136. m_Server.m_NetAdr.Init( serverIP, queryPort, connectionPort );
  137. // remember the dialogs position so we can keep it the same
  138. int x, y;
  139. GetPos( x, y );
  140. // see if we need to change dialog state
  141. if ( !m_Server.m_NetAdr.GetIP() || !m_Server.m_NetAdr.GetQueryPort() )
  142. {
  143. // not in a server, load the simple settings dialog
  144. SetMinimumSize(0, 0);
  145. SetSizeable( false );
  146. LoadControlSettings( "Servers/DialogGameInfo_SinglePlayer.res" );
  147. }
  148. else
  149. {
  150. // moving from a single-player game -> multiplayer, reset dialog
  151. SetMinimumSize(416, 340);
  152. SetSizeable( true );
  153. LoadControlSettings( "Servers/DialogGameInfo.res" );
  154. }
  155. SetPos( x, y );
  156. // Start refresh immediately
  157. m_iRequestRetry = 0;
  158. RequestInfo();
  159. InvalidateLayout();
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Purpose: updates the dialog if it's watching a friend who changes servers
  163. //-----------------------------------------------------------------------------
  164. void CDialogGameInfo::OnPersonaStateChange( PersonaStateChange_t *pPersonaStateChange )
  165. {
  166. #if 0 // TBD delete this func
  167. if ( m_SteamIDFriend && m_SteamIDFriend == pPersonaStateChange->m_ulSteamID )
  168. {
  169. // friend may have changed servers
  170. uint64 nGameID;
  171. uint32 unGameIP;
  172. uint16 usGamePort;
  173. uint16 usQueryPort;
  174. if ( SteamFriends()->GetFriendGamePlayed( m_SteamIDFriend, &nGameID, &unGameIP, &usGamePort, &usQueryPort ) )
  175. {
  176. if ( pPersonaStateChange->m_nChangeFlags & k_EPersonaChangeGamePlayed )
  177. {
  178. ChangeGame( unGameIP, usQueryPort, usGamePort );
  179. }
  180. }
  181. else
  182. {
  183. // bugbug johnc: change to not be in a game anymore
  184. }
  185. }
  186. #endif
  187. }
  188. //-----------------------------------------------------------------------------
  189. // Purpose: Associates a user with this dialog
  190. //-----------------------------------------------------------------------------
  191. void CDialogGameInfo::SetFriend( uint64 ulSteamIDFriend )
  192. {
  193. // set the title to include the friends name
  194. SetTitle( "#ServerBrowser_GameInfoWithNameTitle", true );
  195. SetDialogVariable( "game", steamapicontext->SteamFriends()->GetFriendPersonaName( ulSteamIDFriend ) );
  196. SetDialogVariable( "friend", steamapicontext->SteamFriends()->GetFriendPersonaName( ulSteamIDFriend ) );
  197. // store the friend we're associated with
  198. m_SteamIDFriend = ulSteamIDFriend;
  199. FriendGameInfo_t friendGameInfo;
  200. if ( steamapicontext->SteamFriends()->GetFriendGamePlayed( ulSteamIDFriend, &friendGameInfo ) )
  201. {
  202. uint16 usConnPort = friendGameInfo.m_usGamePort;
  203. if ( friendGameInfo.m_usQueryPort < QUERY_PORT_ERROR )
  204. usConnPort = friendGameInfo.m_usQueryPort;
  205. ChangeGame( friendGameInfo.m_unGameIP, usConnPort, friendGameInfo.m_usGamePort );
  206. }
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose: data access
  210. //-----------------------------------------------------------------------------
  211. uint64 CDialogGameInfo::GetAssociatedFriend()
  212. {
  213. return m_SteamIDFriend;
  214. }
  215. //-----------------------------------------------------------------------------
  216. // Purpose: lays out the data
  217. //-----------------------------------------------------------------------------
  218. void CDialogGameInfo::PerformLayout()
  219. {
  220. BaseClass::PerformLayout();
  221. SetControlString( "ServerText", m_Server.GetName() );
  222. SetControlString( "GameText", m_Server.m_szGameDescription );
  223. SetControlString( "MapText", m_Server.m_szMap );
  224. SetControlString( "GameTags", m_Server.m_szGameTags );
  225. if ( !m_Server.m_bHadSuccessfulResponse )
  226. {
  227. SetControlString("SecureText", "");
  228. }
  229. else if ( m_Server.m_bSecure )
  230. {
  231. SetControlString("SecureText", "#ServerBrowser_Secure");
  232. }
  233. else
  234. {
  235. SetControlString("SecureText", "#ServerBrowser_NotSecure");
  236. }
  237. char buf[128];
  238. if ( m_Server.m_nMaxPlayers > 0)
  239. {
  240. Q_snprintf(buf, sizeof(buf), "%d / %d", m_Server.m_nPlayers, m_Server.m_nMaxPlayers);
  241. }
  242. else
  243. {
  244. buf[0] = 0;
  245. }
  246. SetControlString("PlayersText", buf);
  247. if ( m_Server.m_NetAdr.GetIP() && m_Server.m_NetAdr.GetQueryPort() )
  248. {
  249. SetControlString("ServerIPText", m_Server.m_NetAdr.GetConnectionAddressString() );
  250. m_pConnectButton->SetEnabled(true);
  251. if ( m_pAutoRetry->IsSelected() )
  252. {
  253. m_pAutoRetryAlert->SetVisible(true);
  254. m_pAutoRetryJoin->SetVisible(true);
  255. }
  256. else
  257. {
  258. m_pAutoRetryAlert->SetVisible(false);
  259. m_pAutoRetryJoin->SetVisible(false);
  260. }
  261. }
  262. else
  263. {
  264. SetControlString("ServerIPText", "");
  265. m_pConnectButton->SetEnabled(false);
  266. }
  267. if ( m_Server.m_bHadSuccessfulResponse )
  268. {
  269. Q_snprintf(buf, sizeof(buf), "%d", m_Server.m_nPing );
  270. SetControlString("PingText", buf);
  271. }
  272. else
  273. {
  274. SetControlString("PingText", "");
  275. }
  276. // set the info text
  277. if ( m_pAutoRetry->IsSelected() )
  278. {
  279. if ( m_Server.m_nPlayers < m_Server.m_nMaxPlayers )
  280. {
  281. m_pInfoLabel->SetText("#ServerBrowser_PressJoinToConnect");
  282. }
  283. else if (m_pAutoRetryJoin->IsSelected())
  284. {
  285. m_pInfoLabel->SetText("#ServerBrowser_JoinWhenSlotIsFree");
  286. }
  287. else
  288. {
  289. m_pInfoLabel->SetText("#ServerBrowser_AlertWhenSlotIsFree");
  290. }
  291. }
  292. else if (m_bServerFull)
  293. {
  294. m_pInfoLabel->SetText("#ServerBrowser_CouldNotConnectServerFull");
  295. }
  296. else if (m_bServerNotResponding)
  297. {
  298. m_pInfoLabel->SetText("#ServerBrowser_ServerNotResponding");
  299. }
  300. else
  301. {
  302. // clear the status
  303. m_pInfoLabel->SetText("");
  304. }
  305. if ( m_Server.m_bHadSuccessfulResponse && !(m_Server.m_nPlayers + m_Server.m_nBotPlayers) )
  306. {
  307. m_pPlayerList->SetEmptyListText("#ServerBrowser_ServerHasNoPlayers");
  308. }
  309. else
  310. {
  311. m_pPlayerList->SetEmptyListText("#ServerBrowser_ServerNotResponding");
  312. }
  313. // auto-retry layout
  314. m_pAutoRetry->SetVisible(m_bShowAutoRetryToggle);
  315. Repaint();
  316. }
  317. void CDialogGameInfo::OnKeyCodePressed( vgui::KeyCode code )
  318. {
  319. if ( code == KEY_XBUTTON_B || code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A || code == STEAMCONTROLLER_B )
  320. {
  321. m_pCloseButton->DoClick();
  322. }
  323. else
  324. {
  325. BaseClass::OnKeyCodePressed(code);
  326. }
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose: Forces the game info dialog to try and connect
  330. //-----------------------------------------------------------------------------
  331. void CDialogGameInfo::Connect()
  332. {
  333. OnConnect();
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose: Connects the user to this game
  337. //-----------------------------------------------------------------------------
  338. void CDialogGameInfo::OnConnect()
  339. {
  340. // flag that we are attempting connection
  341. m_bConnecting = true;
  342. // reset state
  343. m_bServerFull = false;
  344. m_bServerNotResponding = false;
  345. InvalidateLayout();
  346. // need to refresh server before attempting to connect, to make sure there is enough room on the server
  347. m_iRequestRetry = 0;
  348. RequestInfo();
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Purpose: Cancel auto-retry if we connect to the game by other means
  352. //-----------------------------------------------------------------------------
  353. void CDialogGameInfo::OnConnectToGame( int ip, int port )
  354. {
  355. // if we just connected to the server we were looking at, close the dialog
  356. // important so that we don't auto-retry a server that we are already on
  357. if ( m_Server.m_NetAdr.GetIP() == (uint32)ip && m_Server.m_NetAdr.GetConnectionPort() == (uint16)port )
  358. {
  359. // close this dialog
  360. Close();
  361. }
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Purpose: Handles Refresh button press, starts a re-ping of the server
  365. //-----------------------------------------------------------------------------
  366. void CDialogGameInfo::OnRefresh()
  367. {
  368. m_iRequestRetry = 0;
  369. // re-ask the server for the game info
  370. RequestInfo();
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose: Forces the whole dialog to redraw when the auto-retry button is toggled
  374. //-----------------------------------------------------------------------------
  375. void CDialogGameInfo::OnButtonToggled(Panel *panel)
  376. {
  377. if (panel == m_pAutoRetry)
  378. {
  379. ShowAutoRetryOptions(m_pAutoRetry->IsSelected());
  380. }
  381. InvalidateLayout();
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose: Sets whether the extended auto-retry options are visible or not
  385. //-----------------------------------------------------------------------------
  386. void CDialogGameInfo::ShowAutoRetryOptions(bool state)
  387. {
  388. // we need to extend the dialog
  389. int growSize = 60;
  390. if (!state)
  391. {
  392. growSize = -growSize;
  393. }
  394. // alter the dialog size accordingly
  395. int x, y, wide, tall;
  396. GetBounds( x, y, wide, tall );
  397. // load a new layout file depending on the state
  398. SetMinimumSize(416, 340);
  399. if ( state )
  400. LoadControlSettings( "Servers/DialogGameInfo_AutoRetry.res" );
  401. else
  402. LoadControlSettings( "Servers/DialogGameInfo.res" );
  403. // restore size and position as
  404. // load control settings will override them
  405. SetBounds( x, y, wide, tall + growSize );
  406. // restore other properties of the dialog
  407. PerformLayout();
  408. m_pAutoRetryAlert->SetSelected( true );
  409. InvalidateLayout();
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Purpose: Requests the right info from the server
  413. //-----------------------------------------------------------------------------
  414. void CDialogGameInfo::RequestInfo()
  415. {
  416. if ( !steamapicontext->SteamMatchmakingServers() )
  417. return;
  418. if ( m_iRequestRetry == 0 )
  419. {
  420. // reset the time at which we auto-refresh
  421. m_iRequestRetry = system()->GetTimeMillis() + RETRY_TIME;
  422. if ( m_hPingQuery != HSERVERQUERY_INVALID )
  423. steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPingQuery );
  424. m_hPingQuery = steamapicontext->SteamMatchmakingServers()->PingServer( m_Server.m_NetAdr.GetIP(), m_Server.m_NetAdr.GetQueryPort(), this );
  425. }
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Purpose: Called every frame, handles resending network messages
  429. //-----------------------------------------------------------------------------
  430. void CDialogGameInfo::OnTick()
  431. {
  432. // check to see if we should perform an auto-refresh
  433. if ( m_iRequestRetry && m_iRequestRetry < system()->GetTimeMillis() )
  434. {
  435. m_iRequestRetry = 0;
  436. RequestInfo();
  437. }
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose: called when the server has successfully responded
  441. //-----------------------------------------------------------------------------
  442. void CDialogGameInfo::ServerResponded( gameserveritem_t &server )
  443. {
  444. if( m_Server.m_NetAdr.GetQueryPort() &&
  445. m_Server.m_NetAdr.GetQueryPort() != server.m_NetAdr.GetQueryPort() )
  446. {
  447. return; // this is not the guy we talked about
  448. }
  449. uint16 connectionPort = m_Server.m_NetAdr.GetConnectionPort();
  450. // FIXME(johns): This is a workaround for a steam bug, where it inproperly reads signed bytes out of the
  451. // message. Once the upstream fix makes it into our SteamSDK, this block can be removed.
  452. server.m_nPlayers = (uint8)(int8)server.m_nPlayers;
  453. server.m_nBotPlayers = (uint8)(int8)server.m_nBotPlayers;
  454. server.m_nMaxPlayers = (uint8)(int8)server.m_nMaxPlayers;
  455. m_hPingQuery = HSERVERQUERY_INVALID;
  456. m_Server = server;
  457. // Preserve our connection port, since we may be querying the sourceTV port but getting a response for the real
  458. // server. This is a limitation of the steam Matchmaking API where it doesn't properly send us a sourcetv response
  459. // but instead the main server's response (unless we're connecting to a proxy, THEN we get the sourcetv response!)
  460. m_Server.m_NetAdr.SetConnectionPort( connectionPort );
  461. if ( m_bConnecting )
  462. {
  463. ConnectToServer();
  464. }
  465. else if ( m_pAutoRetry->IsSelected() && server.m_nPlayers < server.m_nMaxPlayers )
  466. {
  467. // there is a slot free, we can join
  468. // make the sound
  469. surface()->PlaySound("Servers/game_ready.wav");
  470. // flash this window
  471. FlashWindow();
  472. // if it's set, connect right away
  473. if (m_pAutoRetryJoin->IsSelected())
  474. {
  475. ConnectToServer();
  476. }
  477. }
  478. else
  479. {
  480. SendPlayerQuery( server.m_NetAdr.GetIP(), server.m_NetAdr.GetQueryPort() );
  481. }
  482. m_bServerNotResponding = false;
  483. InvalidateLayout();
  484. Repaint();
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Purpose: called when a server response has timed out
  488. //-----------------------------------------------------------------------------
  489. void CDialogGameInfo::ServerFailedToRespond()
  490. {
  491. // the server didn't respond, mark that in the UI
  492. // only mark if we haven't ever received a response
  493. if ( !m_Server.m_bHadSuccessfulResponse )
  494. {
  495. m_bServerNotResponding = true;
  496. }
  497. InvalidateLayout();
  498. Repaint();
  499. }
  500. //-----------------------------------------------------------------------------
  501. // Purpose: Constructs a command to send a running game to connect to a server,
  502. // based on the server type
  503. //
  504. // TODO it would be nice to push this logic into the IRunGameEngine interface; that
  505. // way we could ask the engine itself to construct arguments in ways that fit.
  506. // Might be worth the effort as we start to add more engines.
  507. //-----------------------------------------------------------------------------
  508. void CDialogGameInfo::ApplyConnectCommand( const gameserveritem_t &server )
  509. {
  510. char command[ 256 ];
  511. // set the server password, if any
  512. if ( m_szPassword[0] )
  513. {
  514. Q_snprintf( command, Q_ARRAYSIZE( command ), "password \"%s\"\n", m_szPassword );
  515. g_pRunGameEngine->AddTextCommand( command );
  516. }
  517. // send engine command to change servers
  518. Q_snprintf( command, Q_ARRAYSIZE( command ), "connect %s %s\n", server.m_NetAdr.GetConnectionAddressString(), m_sConnectCode.String() );
  519. g_pRunGameEngine->AddTextCommand( command );
  520. }
  521. //-----------------------------------------------------------------------------
  522. // Purpose: Constructs game options to use when running a game to connect to a server
  523. //-----------------------------------------------------------------------------
  524. void CDialogGameInfo::ConstructConnectArgs( char *pchOptions, int cchOptions, const gameserveritem_t &server )
  525. {
  526. Q_snprintf( pchOptions, cchOptions, " +connect %s", server.m_NetAdr.GetConnectionAddressString() );
  527. if ( m_szPassword[0] )
  528. {
  529. Q_strcat( pchOptions, " +password \"", cchOptions );
  530. Q_strcat( pchOptions, m_szPassword, cchOptions );
  531. Q_strcat( pchOptions, "\"", cchOptions );
  532. }
  533. }
  534. //-----------------------------------------------------------------------------
  535. // Purpose: Connects to the server
  536. //-----------------------------------------------------------------------------
  537. void CDialogGameInfo::ConnectToServer()
  538. {
  539. m_bConnecting = false;
  540. // check VAC status
  541. if ( m_Server.m_bSecure && ServerBrowser().IsVACBannedFromGame( m_Server.m_nAppID ) )
  542. {
  543. // refuse the user
  544. CVACBannedConnRefusedDialog *pDlg = new CVACBannedConnRefusedDialog( GetVParent(), "VACBannedConnRefusedDialog" );
  545. pDlg->Activate();
  546. Close();
  547. return;
  548. }
  549. // check to see if we need a password
  550. if ( m_Server.m_bPassword && !m_szPassword[0] )
  551. {
  552. CDialogServerPassword *box = new CDialogServerPassword(this);
  553. box->AddActionSignalTarget(this);
  554. box->Activate( m_Server.GetName(), 0 );
  555. return;
  556. }
  557. // check the player count
  558. if ( m_Server.m_nPlayers >= m_Server.m_nMaxPlayers )
  559. {
  560. // mark why we cannot connect
  561. m_bServerFull = true;
  562. // give them access to auto-retry options
  563. m_bShowAutoRetryToggle = true;
  564. InvalidateLayout();
  565. return;
  566. }
  567. // tell the engine to connect
  568. const char *gameDir = m_Server.m_szGameDir;
  569. if (g_pRunGameEngine->IsRunning())
  570. {
  571. ApplyConnectCommand( m_Server );
  572. }
  573. else
  574. {
  575. char connectArgs[256];
  576. ConstructConnectArgs( connectArgs, Q_ARRAYSIZE( connectArgs ), m_Server );
  577. if ( ( m_Server.m_bSecure && JoiningSecureServerCall() )|| !m_Server.m_bSecure )
  578. {
  579. switch ( g_pRunGameEngine->RunEngine( m_Server.m_nAppID, gameDir, connectArgs ) )
  580. {
  581. case IRunGameEngine::k_ERunResultModNotInstalled:
  582. {
  583. MessageBox *dlg = new MessageBox( "#ServerBrowser_GameInfoTitle", "#ServerBrowser_ModNotInstalled" );
  584. dlg->DoModal();
  585. SetVisible(false);
  586. return;
  587. }
  588. break;
  589. case IRunGameEngine::k_ERunResultAppNotFound:
  590. {
  591. MessageBox *dlg = new MessageBox( "#ServerBrowser_GameInfoTitle", "#ServerBrowser_AppNotFound" );
  592. dlg->DoModal();
  593. SetVisible(false);
  594. return;
  595. }
  596. break;
  597. case IRunGameEngine::k_ERunResultNotInitialized:
  598. {
  599. MessageBox *dlg = new MessageBox( "#ServerBrowser_GameInfoTitle", "#ServerBrowser_NotInitialized" );
  600. dlg->DoModal();
  601. SetVisible(false);
  602. return;
  603. }
  604. break;
  605. case IRunGameEngine::k_ERunResultOkay:
  606. default:
  607. break;
  608. };
  609. }
  610. }
  611. // close this dialog
  612. PostMessage(this, new KeyValues("Close"));
  613. }
  614. //-----------------------------------------------------------------------------
  615. // Purpose: called when the current refresh list is complete
  616. //-----------------------------------------------------------------------------
  617. void CDialogGameInfo::RefreshComplete( EMatchMakingServerResponse response )
  618. {
  619. }
  620. //-----------------------------------------------------------------------------
  621. // Purpose: handles response from the get password dialog
  622. //-----------------------------------------------------------------------------
  623. void CDialogGameInfo::OnJoinServerWithPassword(const char *password)
  624. {
  625. // copy out the password
  626. Q_strncpy(m_szPassword, password, sizeof(m_szPassword));
  627. // retry connecting to the server again
  628. OnConnect();
  629. }
  630. //-----------------------------------------------------------------------------
  631. // Purpose: player list received
  632. //-----------------------------------------------------------------------------
  633. void CDialogGameInfo::ClearPlayerList()
  634. {
  635. m_pPlayerList->DeleteAllItems();
  636. Repaint();
  637. }
  638. //-----------------------------------------------------------------------------
  639. // Purpose: on individual player added
  640. //-----------------------------------------------------------------------------
  641. void CDialogGameInfo::AddPlayerToList(const char *playerName, int score, float timePlayedSeconds)
  642. {
  643. if ( m_bPlayerListUpdatePending )
  644. {
  645. m_bPlayerListUpdatePending = false;
  646. m_pPlayerList->RemoveAll();
  647. }
  648. KeyValues *player = new KeyValues("player");
  649. player->SetString("PlayerName", playerName);
  650. player->SetInt("Score", score);
  651. player->SetInt("TimeSec", (int)timePlayedSeconds);
  652. // construct a time string
  653. int seconds = (int)timePlayedSeconds;
  654. int minutes = seconds / 60;
  655. int hours = minutes / 60;
  656. seconds %= 60;
  657. minutes %= 60;
  658. char buf[64];
  659. buf[0] = 0;
  660. if (hours)
  661. {
  662. Q_snprintf(buf, sizeof(buf), "%dh %dm %ds", hours, minutes, seconds);
  663. }
  664. else if (minutes)
  665. {
  666. Q_snprintf(buf, sizeof(buf), "%dm %ds", minutes, seconds);
  667. }
  668. else
  669. {
  670. Q_snprintf(buf, sizeof(buf), "%ds", seconds);
  671. }
  672. player->SetString("Time", buf);
  673. m_pPlayerList->AddItem(player, 0, false, true);
  674. player->deleteThis();
  675. }
  676. //-----------------------------------------------------------------------------
  677. // Purpose: Sorting function for time column
  678. //-----------------------------------------------------------------------------
  679. int CDialogGameInfo::PlayerTimeColumnSortFunc(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
  680. {
  681. int p1time = p1.kv->GetInt("TimeSec");
  682. int p2time = p2.kv->GetInt("TimeSec");
  683. if (p1time > p2time)
  684. return -1;
  685. if (p1time < p2time)
  686. return 1;
  687. return 0;
  688. }