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.

532 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "DialogGameInfo.h"
  8. #include "Info.h"
  9. #include "IRunGameEngine.h"
  10. #include "IGameList.h"
  11. #include "TrackerProtocol.h"
  12. #include "serverpage.h"
  13. #include "ServerList.h"
  14. #include "DialogServerPassword.h"
  15. #include <VGUI_Controls.h>
  16. #include <VGUI_ISystem.h>
  17. #include <VGUI_ISurface.h>
  18. #include <VGUI_IVGui.h>
  19. #include <VGUI_KeyValues.h>
  20. #include <VGUI_Label.h>
  21. #include <VGUI_TextEntry.h>
  22. #include <VGUI_Button.h>
  23. #include <VGUI_ToggleButton.h>
  24. #include <VGUI_RadioButton.h>
  25. #include <stdio.h>
  26. using namespace vgui;
  27. static const long RETRY_TIME = 10000; // refresh server every 10 seconds
  28. //-----------------------------------------------------------------------------
  29. // Purpose: Constructor
  30. //-----------------------------------------------------------------------------
  31. CDialogGameInfo::CDialogGameInfo(IGameList *gameList, unsigned int serverID, int serverIP, int serverPort) : Frame(NULL, "DialogGameInfo"), m_Servers(this)
  32. {
  33. MakePopup();
  34. SetBounds(0, 0, 512, 384);
  35. m_bConnecting = false;
  36. m_bServerFull = false;
  37. m_bShowAutoRetryToggle = false;
  38. m_bServerNotResponding = false;
  39. m_bShowingExtendedOptions = false;
  40. m_szPassword[0] = 0;
  41. m_iServerID = serverID;
  42. m_pConnectButton = new Button(this, "Connect", "&Join Game");
  43. m_pCloseButton = new Button(this, "Close", "&Close");
  44. m_pRefreshButton = new Button(this, "Refresh", "&Refresh");
  45. m_pInfoLabel = new Label(this, "InfoLabel", "");
  46. m_pAutoRetry = new ToggleButton(this, "AutoRetry", "&Auto-Retry");
  47. m_pAutoRetry->AddActionSignalTarget(this);
  48. m_pAutoRetryAlert = new RadioButton(this, "AutoRetryAlert", "A&lert me when a player slot is available on server.");
  49. m_pAutoRetryJoin = new RadioButton(this, "AutoRetryJoin", "J&oin the server as soon as a player slot is available.");
  50. m_pAutoRetryAlert->SetSelected(true);
  51. m_pConnectButton->SetCommand(new KeyValues("Connect"));
  52. m_pCloseButton->SetCommand(new KeyValues("Close"));
  53. m_pRefreshButton->SetCommand(new KeyValues("Refresh"));
  54. m_iRequestRetry = 0;
  55. SetSizeable(false);
  56. if (gameList)
  57. {
  58. // we already have the game info, fill it in
  59. serveritem_t &server = gameList->GetServer(serverID);
  60. m_iServerID = m_Servers.AddNewServer(server);
  61. }
  62. else
  63. {
  64. // create a new server to watch
  65. serveritem_t server;
  66. memset(&server, 0, sizeof(server));
  67. *((int *)server.ip) = serverIP;
  68. server.port = serverPort;
  69. m_iServerID = m_Servers.AddNewServer(server);
  70. }
  71. // refresh immediately
  72. RequestInfo();
  73. // let us be ticked every frame
  74. ivgui()->AddTickSignal(this->GetVPanel());
  75. LoadControlSettings("Admin\\DialogGameInfo.res");
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Purpose: Destructor
  79. //-----------------------------------------------------------------------------
  80. CDialogGameInfo::~CDialogGameInfo()
  81. {
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Purpose: Activates the dialog
  85. //-----------------------------------------------------------------------------
  86. void CDialogGameInfo::Run(const char *titleName)
  87. {
  88. char buf[512];
  89. if (titleName)
  90. {
  91. sprintf(buf, "Game Info - %s", titleName);
  92. }
  93. else
  94. {
  95. strcpy(buf, "Game Info");
  96. }
  97. SetTitle(buf, true);
  98. // get the info from the user
  99. RequestInfo();
  100. RequestFocus();
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose: Changes which server to watch
  104. //-----------------------------------------------------------------------------
  105. void CDialogGameInfo::ChangeGame(int serverIP, int serverPort)
  106. {
  107. // check to see if it's the same game
  108. serveritem_t &server = m_Servers.GetServer(m_iServerID);
  109. if (*(int *)server.ip == serverIP && server.port == serverPort)
  110. {
  111. return;
  112. }
  113. // change the server
  114. m_Servers.Clear();
  115. // create a new server to watch
  116. serveritem_t newServer;
  117. memset(&newServer, 0, sizeof(newServer));
  118. *((int *)newServer.ip) = serverIP;
  119. newServer.port = serverPort;
  120. m_iServerID = m_Servers.AddNewServer(newServer);
  121. // start refresh immediately
  122. RequestInfo();
  123. }
  124. //-----------------------------------------------------------------------------
  125. // Purpose: Relayouts the data
  126. //-----------------------------------------------------------------------------
  127. void CDialogGameInfo::PerformLayout()
  128. {
  129. BaseClass::PerformLayout();
  130. // get the server we're watching
  131. serveritem_t &server = m_Servers.GetServer(m_iServerID);
  132. SetControlText("ServerText", server.name);
  133. SetControlText("GameText", server.gameDescription);
  134. SetControlText("MapText", server.map);
  135. char buf[128];
  136. if (server.maxPlayers > 0)
  137. {
  138. sprintf(buf, "%d / %d", server.players, server.maxPlayers);
  139. }
  140. else
  141. {
  142. buf[0] = 0;
  143. }
  144. SetControlText("PlayersText", buf);
  145. if (server.ip[0] && server.port)
  146. {
  147. char buf[64];
  148. sprintf(buf, "%d.%d.%d.%d:%d", server.ip[0], server.ip[1], server.ip[2], server.ip[3], server.port);
  149. SetControlText("ServerIPText", buf);
  150. m_pConnectButton->SetEnabled(true);
  151. }
  152. else
  153. {
  154. SetControlText("ServerIPText", "");
  155. m_pConnectButton->SetEnabled(false);
  156. }
  157. sprintf(buf, "%d", server.ping);
  158. SetControlText("PingText", buf);
  159. // set the info text
  160. if (m_pAutoRetry->IsSelected())
  161. {
  162. if (server.players < server.maxPlayers)
  163. {
  164. m_pInfoLabel->SetText("Press 'Join Game' to connect to the server.");
  165. }
  166. else if (m_pAutoRetryJoin->IsSelected())
  167. {
  168. m_pInfoLabel->SetText("You will join the server as soon as a player slot is free.");
  169. }
  170. else
  171. {
  172. m_pInfoLabel->SetText("You will be alerted as soon player slot is free on the server.");
  173. }
  174. }
  175. else if (m_bServerFull)
  176. {
  177. m_pInfoLabel->SetText("Could not connect - server is full.");
  178. }
  179. else if (m_bServerNotResponding)
  180. {
  181. char text[100];
  182. _snprintf(text,100,"Server is not responding.%d.%d.%d.%d:%d", server.ip[0], server.ip[1], server.ip[2], server.ip[3], server.port);
  183. m_pInfoLabel->SetText(text);
  184. }
  185. else
  186. {
  187. // clear the status
  188. m_pInfoLabel->SetText("");
  189. }
  190. // auto-retry layout
  191. m_pAutoRetry->SetVisible(m_bShowAutoRetryToggle);
  192. if (m_pAutoRetry->IsSelected())
  193. {
  194. m_pAutoRetryAlert->SetVisible(true);
  195. m_pAutoRetryJoin->SetVisible(true);
  196. }
  197. else
  198. {
  199. m_pAutoRetryAlert->SetVisible(false);
  200. m_pAutoRetryJoin->SetVisible(false);
  201. }
  202. Repaint();
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose: Sets up the current scheme colors
  206. //-----------------------------------------------------------------------------
  207. void CDialogGameInfo::ApplySchemeSettings(vgui::IScheme *pScheme)
  208. {
  209. BaseClass::ApplySchemeSettings(pScheme);
  210. // force the label to get it's scheme settings
  211. m_pInfoLabel->InvalidateLayout(true);
  212. // override them
  213. m_pInfoLabel->SetFgColor(GetSchemeColor("BrightControlText"));
  214. }
  215. //-----------------------------------------------------------------------------
  216. // Purpose: Forces the game info dialog to try and connect
  217. //-----------------------------------------------------------------------------
  218. void CDialogGameInfo::Connect()
  219. {
  220. OnConnect();
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Purpose: Connects the user to this game
  224. //-----------------------------------------------------------------------------
  225. void CDialogGameInfo::OnConnect()
  226. {
  227. // flag that we are attempting connection
  228. m_bConnecting = true;
  229. // reset state
  230. m_bServerFull = false;
  231. m_bServerNotResponding = false;
  232. InvalidateLayout();
  233. // need to refresh server before attempting to connect, to make sure there is enough room on the server
  234. RequestInfo();
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose: Handles Refresh button press, starts a re-ping of the server
  238. //-----------------------------------------------------------------------------
  239. void CDialogGameInfo::OnRefresh()
  240. {
  241. // re-ask the server for the game info
  242. RequestInfo();
  243. }
  244. //-----------------------------------------------------------------------------
  245. // Purpose: Deletes the dialog when it's closed
  246. //-----------------------------------------------------------------------------
  247. void CDialogGameInfo::OnClose()
  248. {
  249. BaseClass::OnClose();
  250. MarkForDeletion();
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Purpose: Forces the whole dialog to redraw when the auto-retry button is toggled
  254. //-----------------------------------------------------------------------------
  255. void CDialogGameInfo::OnButtonToggled(Panel *panel)
  256. {
  257. if (panel == m_pAutoRetry)
  258. {
  259. ShowAutoRetryOptions(m_pAutoRetry->IsSelected());
  260. }
  261. InvalidateLayout();
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Purpose: Sets whether the extended auto-retry options are visible or not
  265. // Input : state -
  266. //-----------------------------------------------------------------------------
  267. void CDialogGameInfo::ShowAutoRetryOptions(bool state)
  268. {
  269. // we need to extend the dialog
  270. int growSize = 60;
  271. if (!state)
  272. {
  273. growSize = -growSize;
  274. }
  275. // alter the dialog size accordingly
  276. int wide, tall;
  277. GetSize(wide, tall);
  278. tall += growSize;
  279. SetSize(wide, tall);
  280. InvalidateLayout();
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose: Sets the text of a control by name
  284. //-----------------------------------------------------------------------------
  285. void CDialogGameInfo::SetControlText(const char *textEntryName, const char *text)
  286. {
  287. TextEntry *entry = dynamic_cast<TextEntry *>(FindChildByName(textEntryName));
  288. if (entry)
  289. {
  290. entry->SetText(text);
  291. }
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose: Requests the right info from the server
  295. //-----------------------------------------------------------------------------
  296. void CDialogGameInfo::RequestInfo()
  297. {
  298. // reset the time at which we auto-refresh
  299. m_iRequestRetry = system()->GetTimeMillis() + RETRY_TIME;
  300. if (!m_Servers.IsRefreshing())
  301. {
  302. m_Servers.AddServerToRefreshList(m_iServerID);
  303. m_Servers.StartRefresh();
  304. }
  305. }
  306. //-----------------------------------------------------------------------------
  307. // Purpose: Called every frame, handles resending network messages
  308. //-----------------------------------------------------------------------------
  309. void CDialogGameInfo::OnTick()
  310. {
  311. // check to see if we should perform an auto-refresh
  312. if (m_iRequestRetry && m_iRequestRetry < system()->GetTimeMillis())
  313. {
  314. // reask
  315. RequestInfo();
  316. }
  317. m_Servers.RunFrame();
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Purpose: called when the server has successfully responded
  321. //-----------------------------------------------------------------------------
  322. void CDialogGameInfo::ServerResponded(serveritem_t &server)
  323. {
  324. if (m_bConnecting)
  325. {
  326. ConnectToServer();
  327. }
  328. else if (m_pAutoRetry->IsSelected())
  329. {
  330. // auto-retry is enabled, see if we can join
  331. if (server.players < server.maxPlayers)
  332. {
  333. // there is a slot free, we can join
  334. // make the sound
  335. surface()->PlaySound("Servers\\game_ready.wav");
  336. // flash this window
  337. FlashWindow();
  338. // if it's set, connect right away
  339. if (m_pAutoRetryJoin->IsSelected())
  340. {
  341. ConnectToServer();
  342. }
  343. }
  344. }
  345. m_bServerNotResponding = false;
  346. InvalidateLayout();
  347. Repaint();
  348. }
  349. //-----------------------------------------------------------------------------
  350. // Purpose: called when a server response has timed out
  351. //-----------------------------------------------------------------------------
  352. void CDialogGameInfo::ServerFailedToRespond(serveritem_t &server)
  353. {
  354. // the server didn't respond, mark that in the UI
  355. // only mark if we haven't ever received a response
  356. if (!server.hadSuccessfulResponse)
  357. {
  358. m_bServerNotResponding = true;
  359. }
  360. InvalidateLayout();
  361. Repaint();
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Purpose: Connects to the server
  365. //-----------------------------------------------------------------------------
  366. void CDialogGameInfo::ConnectToServer()
  367. {
  368. m_bConnecting = false;
  369. serveritem_t &server = m_Servers.GetServer(m_iServerID);
  370. // check to see if we need a password
  371. if (server.password && !m_szPassword[0])
  372. {
  373. CDialogServerPassword *box = new CDialogServerPassword();
  374. box->AddActionSignalTarget(this);
  375. box->Activate(server.name, server.serverID);
  376. return;
  377. }
  378. // check the player count
  379. if (server.players >= server.maxPlayers)
  380. {
  381. // mark why we cannot connect
  382. m_bServerFull = true;
  383. // give them access to auto-retry options
  384. m_bShowAutoRetryToggle = true;
  385. InvalidateLayout();
  386. return;
  387. }
  388. // tell the engine to connect
  389. char buf[64];
  390. sprintf(buf, "%d.%d.%d.%d:%d", server.ip[0], server.ip[1], server.ip[2], server.ip[3], server.port);
  391. const char *gameDir = server.gameDir;
  392. if (g_pRunGameEngine->IsRunning())
  393. {
  394. char command[256];
  395. // set the server password, if any
  396. if (m_szPassword[0])
  397. {
  398. sprintf(command, "password \"%s\"\n", m_szPassword);
  399. g_pRunGameEngine->AddTextCommand(command);
  400. }
  401. // send engine command to change servers
  402. sprintf(command, "connect %s\n", buf);
  403. g_pRunGameEngine->AddTextCommand(command);
  404. }
  405. else
  406. {
  407. char command[256];
  408. // sprintf(command, " -game %s +connect %s", gameDir, buf);
  409. sprintf(command, " +connect %s", buf);
  410. if (m_szPassword[0])
  411. {
  412. strcat(command, " +password \"");
  413. strcat(command, m_szPassword);
  414. strcat(command, "\"");\
  415. }
  416. g_pRunGameEngine->RunEngine(gameDir, command);
  417. }
  418. // close this dialog
  419. PostMessage(this, new KeyValues("Close"));
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose: called when the current refresh list is complete
  423. //-----------------------------------------------------------------------------
  424. void CDialogGameInfo::RefreshComplete()
  425. {
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Purpose: handles response from the get password dialog
  429. //-----------------------------------------------------------------------------
  430. void CDialogGameInfo::OnJoinServerWithPassword(const char *password)
  431. {
  432. // copy out the password
  433. v_strncpy(m_szPassword, password, sizeof(m_szPassword));
  434. // retry connecting to the server again
  435. OnConnect();
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose: Message map
  439. //-----------------------------------------------------------------------------
  440. MessageMapItem_t CDialogGameInfo::m_MessageMap[] =
  441. {
  442. MAP_MESSAGE( CDialogGameInfo, "Refresh", OnRefresh ),
  443. MAP_MESSAGE( CDialogGameInfo, "Connect", OnConnect ),
  444. MAP_MESSAGE_PTR( CDialogGameInfo, "ButtonToggled", OnButtonToggled, "panel" ),
  445. MAP_MESSAGE_PTR( CDialogGameInfo, "RadioButtonChecked", OnButtonToggled, "panel" ),
  446. MAP_MESSAGE_CONSTCHARPTR( CDialogGameInfo, "JoinServerWithPassword", OnJoinServerWithPassword, "password" ),
  447. };
  448. IMPLEMENT_PANELMAP( CDialogGameInfo, Frame );