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.

401 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include <stdio.h>
  8. #include "PlayerPanel.h"
  9. #include "PlayerContextMenu.h"
  10. #include "PlayerListCompare.h"
  11. #include "DialogAddBan.h"
  12. #include <vgui/ISystem.h>
  13. #include <vgui/ISurface.h>
  14. #include <vgui/ILocalize.h>
  15. #include <vgui/IVGui.h>
  16. #include <vgui/KeyCode.h>
  17. #include <KeyValues.h>
  18. #include <vgui_controls/Button.h>
  19. #include <vgui_controls/ListPanel.h>
  20. #include <vgui_controls/QueryBox.h>
  21. using namespace vgui;
  22. //-----------------------------------------------------------------------------
  23. // Purpose: Constructor
  24. //-----------------------------------------------------------------------------
  25. CPlayerPanel::CPlayerPanel(vgui::Panel *parent, const char *name) : vgui::PropertyPage(parent, name)
  26. {
  27. m_pPlayerListPanel = new vgui::ListPanel(this, "Players list");
  28. m_pPlayerListPanel->AddColumnHeader(0, "name", "#Player_Panel_Name", 200, ListPanel::COLUMN_RESIZEWITHWINDOW );
  29. m_pPlayerListPanel->AddColumnHeader(1, "authid", "#Player_Panel_ID", 100);
  30. m_pPlayerListPanel->AddColumnHeader(2, "ping", "#Player_Panel_Ping", 50);
  31. m_pPlayerListPanel->AddColumnHeader(3, "loss", "#Player_Panel_Loss", 50);
  32. m_pPlayerListPanel->AddColumnHeader(4, "frags", "#Player_Panel_Frags", 50);
  33. m_pPlayerListPanel->AddColumnHeader(5, "time", "#Player_Panel_Time", 75);
  34. /*
  35. // TODO: update me!!
  36. m_pPlayerListPanel->SetSortFunc(0, PlayerNameCompare);
  37. m_pPlayerListPanel->SetSortFunc(1, PlayerAuthCompare);
  38. m_pPlayerListPanel->SetSortFunc(2, PlayerPingCompare);
  39. m_pPlayerListPanel->SetSortFunc(3, PlayerLossCompare);
  40. m_pPlayerListPanel->SetSortFunc(4, PlayerFragsCompare);
  41. */
  42. m_pPlayerListPanel->SetSortFunc(5, PlayerTimeCompare);
  43. m_pPlayerListPanel->SetEmptyListText("#Player_Panel_No_Players");
  44. // Sort by ping time by default
  45. m_pPlayerListPanel->SetSortColumn(0);
  46. m_pKickButton = new Button(this, "Kick", "#Player_Panel_Kick");
  47. m_pBanButton = new Button(this, "Ban", "#Player_Panel_Ban");
  48. m_pPlayerContextMenu = new CPlayerContextMenu(this);
  49. m_pPlayerContextMenu->SetVisible(false);
  50. LoadControlSettings("Admin/PlayerPanel.res", "PLATFORM");
  51. m_pKickButton->SetCommand(new KeyValues("KickPlayer"));
  52. m_pBanButton->SetCommand(new KeyValues("BanPlayer"));
  53. OnItemSelected(); // disable the buttons
  54. m_flUpdateTime = 0.0f;
  55. RemoteServer().AddServerMessageHandler(this, "UpdatePlayers");
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose: Destructor
  59. //-----------------------------------------------------------------------------
  60. CPlayerPanel::~CPlayerPanel()
  61. {
  62. RemoteServer().RemoveServerDataResponseTarget(this);
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose: Rebuilds player info
  66. //-----------------------------------------------------------------------------
  67. void CPlayerPanel::OnResetData()
  68. {
  69. RemoteServer().RequestValue(this, "playerlist");
  70. // update once every minute
  71. m_flUpdateTime = (float)system()->GetFrameTime() + (60 * 1.0f);
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose: Checks to see if we should update player info
  75. //-----------------------------------------------------------------------------
  76. void CPlayerPanel::OnThink()
  77. {
  78. if (m_flUpdateTime < vgui::system()->GetFrameTime())
  79. {
  80. OnResetData();
  81. }
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Purpose: Refreshes page if user hits F5
  85. //-----------------------------------------------------------------------------
  86. void CPlayerPanel::OnKeyCodeTyped(vgui::KeyCode code)
  87. {
  88. if (code == KEY_F5)
  89. {
  90. OnResetData();
  91. }
  92. else
  93. {
  94. BaseClass::OnKeyCodeTyped(code);
  95. }
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose: Returns data on the player who is currently selected in the list
  99. //-----------------------------------------------------------------------------
  100. KeyValues *CPlayerPanel::GetSelected()
  101. {
  102. return m_pPlayerListPanel->GetItem(m_pPlayerListPanel->GetSelectedItem(0));
  103. }
  104. static const char *FormatSeconds( int seconds )
  105. {
  106. static char string[64];
  107. int hours = 0;
  108. int minutes = seconds / 60;
  109. if ( minutes > 0 )
  110. {
  111. seconds -= (minutes * 60);
  112. hours = minutes / 60;
  113. if ( hours > 0 )
  114. {
  115. minutes -= (hours * 60);
  116. }
  117. }
  118. if ( hours > 0 )
  119. {
  120. Q_snprintf( string, sizeof(string), "%2i:%02i:%02i", hours, minutes, seconds );
  121. }
  122. else
  123. {
  124. Q_snprintf( string, sizeof(string), "%02i:%02i", minutes, seconds );
  125. }
  126. return string;
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose: called when the server has returned a requested value
  130. //-----------------------------------------------------------------------------
  131. void CPlayerPanel::OnServerDataResponse(const char *value, const char *response)
  132. {
  133. if (!stricmp(value, "UpdatePlayers"))
  134. {
  135. // server has indicated a change, force an update
  136. m_flUpdateTime = 0.0f;
  137. }
  138. else if (!stricmp(value, "playerlist"))
  139. {
  140. // new list of players
  141. m_pPlayerListPanel->DeleteAllItems();
  142. // parse response
  143. const char *parse = response;
  144. while (parse && *parse)
  145. {
  146. // first should be the size of the player name
  147. char name[64];
  148. name[0] = '\0';
  149. char authID[64];
  150. authID[0] = '\0';
  151. char netAdr[32];
  152. netAdr[0] = '\0';
  153. int ping = 0, packetLoss = 0, frags = 0, connectTime = 0;
  154. ivgui()->DPrintf2("orig: %s\n", parse);
  155. // parse out the name, which is quoted
  156. Assert(*parse == '\"');
  157. if (*parse != '\"')
  158. break;
  159. ++parse; // move past start quote
  160. int pos = 0;
  161. while (*parse && *parse != '\"')
  162. {
  163. name[pos++] = *parse;
  164. parse++;
  165. }
  166. name[pos] = 0;
  167. parse++; // move past end quote
  168. if (6 != sscanf(parse, " %s %s %d %d %d %d\n", authID, netAdr, &ping, &packetLoss, &frags, &connectTime))
  169. break;
  170. const char *timeStr = FormatSeconds(connectTime);
  171. ivgui()->DPrintf2("pars: \"%s\" %s %s %d %d %d %s\n", name, authID, netAdr, ping, packetLoss, frags, timeStr);
  172. // add to list
  173. KeyValues *player = new KeyValues("Player");
  174. player->SetString("name", name);
  175. player->SetString("authID", authID);
  176. player->SetString("netAdr", netAdr);
  177. player->SetInt("ping", ping);
  178. player->SetInt("loss", packetLoss);
  179. player->SetInt("frags", frags);
  180. player->SetString("time", timeStr);
  181. m_pPlayerListPanel->AddItem(player, 0, false, false);
  182. // move to next line
  183. parse = strchr(parse, '\n');
  184. if (parse)
  185. {
  186. parse++;
  187. }
  188. }
  189. }
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose: Buttom command handlers
  193. //-----------------------------------------------------------------------------
  194. void CPlayerPanel::OnCommand(const char *command)
  195. {
  196. BaseClass::OnCommand(command);
  197. }
  198. //-----------------------------------------------------------------------------
  199. // Purpose: Handles the UI for kicking a player from the server
  200. //-----------------------------------------------------------------------------
  201. void CPlayerPanel::OnKickButtonPressed()
  202. {
  203. if (m_pPlayerListPanel->GetSelectedItemsCount() < 1)
  204. return;
  205. // open a message box to ask the user if they want to follow through on this
  206. QueryBox *box;
  207. if (m_pPlayerListPanel->GetSelectedItemsCount() > 1)
  208. {
  209. box = new QueryBox("#Kick_Multiple_Players_Title", "#Kick_Multiple_Players_Question");
  210. }
  211. else
  212. {
  213. // show the player name in the message box if only one player is being booted
  214. KeyValues *kv = m_pPlayerListPanel->GetItem(m_pPlayerListPanel->GetSelectedItem(0));
  215. Assert(kv != NULL);
  216. if (!kv)
  217. return;
  218. wchar_t playerName[64];
  219. g_pVGuiLocalize->ConvertANSIToUnicode( kv->GetString("name"), playerName, sizeof(playerName) );
  220. wchar_t msg[512];
  221. g_pVGuiLocalize->ConstructString( msg, sizeof(msg), g_pVGuiLocalize->Find("Kick_Single_Player_Question"), 1, playerName );
  222. box = new QueryBox(g_pVGuiLocalize->Find("#Kick_Single_Player_Title"), msg);
  223. }
  224. box->AddActionSignalTarget(this);
  225. box->SetOKCommand(new KeyValues("KickSelectedPlayers"));
  226. box->ShowWindow();
  227. }
  228. //-----------------------------------------------------------------------------
  229. // Purpose: Prompts a user to be banned
  230. //-----------------------------------------------------------------------------
  231. void CPlayerPanel::OnBanButtonPressed()
  232. {
  233. if (m_pPlayerListPanel->GetSelectedItemsCount() != 1)
  234. return;
  235. // open a message box to ask the user if they want to follow through on this
  236. KeyValues *kv = m_pPlayerListPanel->GetItem(m_pPlayerListPanel->GetSelectedItem(0));
  237. Assert(kv != NULL);
  238. if (!kv)
  239. return;
  240. const char *player = kv->GetString("name");
  241. const char *authid = kv->GetString("authid");
  242. const char *netAdr = kv->GetString("netAdr");
  243. char buf[64];
  244. if ( !strcmp( authid, "UNKNOWN" ) )
  245. {
  246. int s1, s2, s3, s4;
  247. if (4 == sscanf(netAdr, "%d.%d.%d.%d", &s1, &s2, &s3, &s4))
  248. {
  249. Q_snprintf( buf, sizeof(buf), "%d.%d.%d.%d", s1, s2, s3, s4 );
  250. authid = buf;
  251. }
  252. }
  253. CDialogAddBan *box = new CDialogAddBan(this);
  254. box->AddActionSignalTarget(this);
  255. box->Activate("addban", player, authid);
  256. }
  257. //-----------------------------------------------------------------------------
  258. // Purpose: Kicks all the players currently selected
  259. //-----------------------------------------------------------------------------
  260. void CPlayerPanel::KickSelectedPlayers()
  261. {
  262. for (int i = 0; i < m_pPlayerListPanel->GetSelectedItemsCount(); i++)
  263. {
  264. // get the player info
  265. int row = m_pPlayerListPanel->GetSelectedItem(i);
  266. KeyValues *pl = m_pPlayerListPanel->GetItem(row);
  267. if (!pl)
  268. continue;
  269. // kick 'em
  270. char cmd[512];
  271. _snprintf(cmd, sizeof(cmd), "kick \"%s\"", pl->GetString("name"));
  272. RemoteServer().SendCommand(cmd);
  273. }
  274. m_pPlayerListPanel->ClearSelectedItems();
  275. m_pKickButton->SetEnabled(false);
  276. m_pBanButton->SetEnabled(false);
  277. OnResetData();
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose: Adds a new ban
  281. //-----------------------------------------------------------------------------
  282. void CPlayerPanel::AddBanByID(const char *id, const char *newtime)
  283. {
  284. Assert(id && *id);
  285. if (!id || !*id)
  286. return;
  287. // if the newtime string is not valid, then set it to 0 (permanent ban)
  288. if (!newtime || atof(newtime) < 0.001)
  289. {
  290. newtime = "0";
  291. }
  292. const char *banCmd = "banid";
  293. const char *saveCmd = "writeip";
  294. int s1, s2, s3, s4;
  295. if (4 == sscanf(id, "%d.%d.%d.%d", &s1, &s2, &s3, &s4))
  296. {
  297. banCmd = "addip";
  298. saveCmd = "writeid";
  299. }
  300. // send down the ban command
  301. char cmd[512];
  302. _snprintf(cmd, sizeof(cmd) -1, "%s %s %s\n", banCmd, newtime, id);
  303. RemoteServer().SendCommand(cmd);
  304. // force the file to update
  305. RemoteServer().SendCommand(saveCmd);
  306. // refresh
  307. OnResetData();
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Purpose: opens context menu (user right clicked on a server)
  311. //-----------------------------------------------------------------------------
  312. void CPlayerPanel::OnOpenContextMenu(int itemID)
  313. {
  314. /* CODE DISABLED UNTIL VERIFIED AS WORKING
  315. // show the player menus only if the cursor is over it
  316. if (IsCursorOver() && m_pPlayerListPanel->GetNumSelectedRows())
  317. {
  318. // get the server
  319. unsigned int playerID = m_pPlayerListPanel->GetDataItem(m_pPlayerListPanel->GetSelectedRow(0))->userData;
  320. // activate context menu
  321. m_pPlayerContextMenu->ShowMenu(this, playerID);
  322. }
  323. */
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Purpose: called when an item on the list panel is selected.
  327. //-----------------------------------------------------------------------------
  328. void CPlayerPanel::OnItemSelected()
  329. {
  330. bool state = true;
  331. if ( m_pPlayerListPanel->GetSelectedItemsCount() == 0 )
  332. {
  333. state = false;
  334. }
  335. m_pKickButton->SetEnabled(state);
  336. m_pBanButton->SetEnabled(state);
  337. if ( m_pPlayerListPanel->GetSelectedItemsCount() > 1 )
  338. {
  339. m_pBanButton->SetEnabled(false);
  340. }
  341. }