Counter Strike : Global Offensive Source Code
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.

782 lines
24 KiB

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