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.

446 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "IServerRefreshResponse.h"
  8. #include "ServerList.h"
  9. //#include "ServerMsgHandlerDetails.h"
  10. #include "Socket.h"
  11. #include "proto_oob.h"
  12. // for debugging
  13. #include <VGUI_Controls.h>
  14. #include <VGUI_ISystem.h>
  15. #include <VGUI_IVGui.h>
  16. typedef enum
  17. {
  18. NONE = 0,
  19. INFO_REQUESTED,
  20. INFO_RECEIVED
  21. } QUERYSTATUS;
  22. extern void v_strncpy(char *dest, const char *src, int bufsize);
  23. #define min(a,b) (((a) < (b)) ? (a) : (b))
  24. //-----------------------------------------------------------------------------
  25. // Purpose: Comparison function used in query redblack tree
  26. //-----------------------------------------------------------------------------
  27. bool QueryLessFunc( const query_t &item1, const query_t &item2 )
  28. {
  29. // compare port then ip
  30. if (item1.addr.port < item2.addr.port)
  31. return true;
  32. else if (item1.addr.port > item2.addr.port)
  33. return false;
  34. int ip1 = *(int *)&item1.addr.ip;
  35. int ip2 = *(int *)&item2.addr.ip;
  36. return ip1 < ip2;
  37. }
  38. //-----------------------------------------------------------------------------
  39. // Purpose: Constructor
  40. //-----------------------------------------------------------------------------
  41. CServerList::CServerList(IServerRefreshResponse *target) : m_Queries(0, MAX_QUERY_SOCKETS, QueryLessFunc)
  42. {
  43. m_pResponseTarget = target;
  44. m_iUpdateSerialNumber = 1;
  45. // calculate max sockets based on users' rate
  46. char speedBuf[32];
  47. int internetSpeed;
  48. if (!vgui::system()->GetRegistryString("HKEY_CURRENT_USER\\Software\\Valve\\Tracker\\Rate", speedBuf, sizeof(speedBuf)-1))
  49. {
  50. // default to DSL speed if no reg key found (an unlikely occurance)
  51. strcpy(speedBuf, "7500");
  52. }
  53. internetSpeed = atoi(speedBuf);
  54. int maxSockets = (MAX_QUERY_SOCKETS * internetSpeed) / 10000;
  55. if (internetSpeed < 6000)
  56. {
  57. // reduce the number of active queries again for slow internet speeds
  58. maxSockets /= 2;
  59. }
  60. m_nMaxActive = maxSockets;
  61. m_nRampUpSpeed = 1;
  62. m_bQuerying = false;
  63. m_nMaxRampUp = 1;
  64. m_nInvalidServers = 0;
  65. m_nRefreshedServers = 0;
  66. }
  67. //-----------------------------------------------------------------------------
  68. // Purpose: Destructor
  69. //-----------------------------------------------------------------------------
  70. CServerList::~CServerList()
  71. {
  72. // delete m_pQuery;
  73. }
  74. //-----------------------------------------------------------------------------
  75. // Purpose:
  76. //-----------------------------------------------------------------------------
  77. void CServerList::RunFrame()
  78. {
  79. for(int i=0;i<m_Servers.Count();i++) {
  80. m_Servers[i]->RunFrame();
  81. }
  82. QueryFrame();
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Purpose: gets a server from the list by id, range [0, ServerCount)
  86. //-----------------------------------------------------------------------------
  87. serveritem_t &CServerList::GetServer(unsigned int serverID)
  88. {
  89. if (m_Servers.IsValidIndex(serverID))
  90. {
  91. return m_Servers[serverID]->GetServer();
  92. }
  93. // return a dummy
  94. static serveritem_t dummyServer;
  95. memset(&dummyServer, 0, sizeof(dummyServer));
  96. return dummyServer;
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Purpose: returns the number of servers
  100. //-----------------------------------------------------------------------------
  101. int CServerList::ServerCount()
  102. {
  103. return m_Servers.Count();
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose: Returns the number of servers not yet pinged
  107. //-----------------------------------------------------------------------------
  108. int CServerList::RefreshListRemaining()
  109. {
  110. return m_RefreshList.Count();
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Purpose: returns true if the server list is still in the process of talking to servers
  114. //-----------------------------------------------------------------------------
  115. bool CServerList::IsRefreshing()
  116. {
  117. return m_bQuerying;
  118. }
  119. //-----------------------------------------------------------------------------
  120. // Purpose: adds a new server to the list
  121. //-----------------------------------------------------------------------------
  122. unsigned int CServerList::AddNewServer(serveritem_t &server)
  123. {
  124. unsigned int serverID = m_Servers.AddToTail(new CServerInfo(this ,server));
  125. m_Servers[serverID]->serverID = serverID;
  126. return serverID;
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose: Clears all servers from the list
  130. //-----------------------------------------------------------------------------
  131. void CServerList::Clear()
  132. {
  133. StopRefresh();
  134. m_Servers.RemoveAll();
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Purpose: stops all refreshing
  138. //-----------------------------------------------------------------------------
  139. void CServerList::StopRefresh()
  140. {
  141. // Reset query context
  142. m_Queries.RemoveAll();
  143. // reset server received states
  144. int count = ServerCount();
  145. for (int i = 0; i < count; i++)
  146. {
  147. m_Servers[i]->received = 0;
  148. }
  149. m_RefreshList.RemoveAll();
  150. // up the serial number so previous results don't interfere
  151. m_iUpdateSerialNumber++;
  152. m_nInvalidServers = 0;
  153. m_nRefreshedServers = 0;
  154. m_bQuerying = false;
  155. m_nMaxRampUp = m_nRampUpSpeed;
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose: marks a server to be refreshed
  159. //-----------------------------------------------------------------------------
  160. void CServerList::AddServerToRefreshList(unsigned int serverID)
  161. {
  162. if (!m_Servers.IsValidIndex(serverID))
  163. return;
  164. serveritem_t &server = m_Servers[serverID]->GetServer();
  165. server.received = NONE;
  166. m_RefreshList.AddToTail(serverID);
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Purpose:
  170. //-----------------------------------------------------------------------------
  171. void CServerList::StartRefresh()
  172. {
  173. if (m_RefreshList.Count() > 0)
  174. {
  175. m_bQuerying = true;
  176. }
  177. }
  178. void CServerList::ServerResponded()
  179. {
  180. for(int i=0;i<m_Servers.Count();i++) {
  181. if ( m_Servers[i]->Refreshed() ) {
  182. serveritem_t &server = m_Servers[i]->GetServer();
  183. // copy in data necessary for filters
  184. // add to ping times list
  185. server.pings[0] = server.pings[1];
  186. server.pings[1] = server.pings[2];
  187. server.pings[2] = server.ping;
  188. // calculate ping
  189. int ping = CalculateAveragePing(server);
  190. // make sure the ping changes each time so the user can see the server has updated
  191. if (server.ping == ping && ping>0)
  192. {
  193. ping--;
  194. }
  195. server.ping = ping;
  196. server.received = INFO_RECEIVED;
  197. netadr_t adr;
  198. adr.ip[0] = server.ip[0];
  199. adr.ip[1] = server.ip[1];
  200. adr.ip[2] = server.ip[2];
  201. adr.ip[3] = server.ip[3];
  202. adr.port = (server.port & 0xff) << 8 | (server.port & 0xff00) >> 8;
  203. adr.type = NA_IP;
  204. query_t finder;
  205. finder.addr = adr;
  206. m_Queries.Remove(finder);
  207. // notify the UI of the new server info
  208. m_pResponseTarget->ServerResponded(server);
  209. }
  210. }
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose: called when a server response has timed out
  214. //-----------------------------------------------------------------------------
  215. void CServerList::ServerFailedToRespond()
  216. {
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Purpose: recalculates a servers ping, from the last few ping times
  220. //-----------------------------------------------------------------------------
  221. int CServerList::CalculateAveragePing(serveritem_t &server)
  222. {
  223. if (server.pings[0])
  224. {
  225. // three pings, throw away any the most extreme and average the other two
  226. int middlePing = 0, lowPing = 1, highPing = 2;
  227. if (server.pings[0] < server.pings[1])
  228. {
  229. if (server.pings[0] > server.pings[2])
  230. {
  231. lowPing = 2;
  232. middlePing = 0;
  233. highPing = 1;
  234. }
  235. else if (server.pings[1] < server.pings[2])
  236. {
  237. lowPing = 0;
  238. middlePing = 1;
  239. highPing = 2;
  240. }
  241. else
  242. {
  243. lowPing = 0;
  244. middlePing = 2;
  245. highPing = 1;
  246. }
  247. }
  248. else
  249. {
  250. if (server.pings[1] > server.pings[2])
  251. {
  252. lowPing = 2;
  253. middlePing = 1;
  254. highPing = 0;
  255. }
  256. else if (server.pings[0] < server.pings[2])
  257. {
  258. lowPing = 1;
  259. middlePing = 0;
  260. highPing = 2;
  261. }
  262. else
  263. {
  264. lowPing = 1;
  265. middlePing = 2;
  266. highPing = 0;
  267. }
  268. }
  269. // we have the middle ping, see which it's closest to
  270. if ((server.pings[middlePing] - server.pings[lowPing]) < (server.pings[highPing] - server.pings[middlePing]))
  271. {
  272. return (server.pings[middlePing] + server.pings[lowPing]) / 2;
  273. }
  274. else
  275. {
  276. return (server.pings[middlePing] + server.pings[highPing]) / 2;
  277. }
  278. }
  279. else if (server.pings[1])
  280. {
  281. // two pings received, average them
  282. return (server.pings[1] + server.pings[2]) / 2;
  283. }
  284. else
  285. {
  286. // only one ping received so far, use that
  287. return server.pings[2];
  288. }
  289. }
  290. //-----------------------------------------------------------------------------
  291. // Purpose: Called every frame to check queries
  292. //-----------------------------------------------------------------------------
  293. void CServerList::QueryFrame()
  294. {
  295. if (!m_bQuerying)
  296. return;
  297. float curtime = CSocket::GetClock();
  298. // walk the query list, looking for any server timeouts
  299. unsigned short idx = m_Queries.FirstInorder();
  300. while (m_Queries.IsValidIndex(idx))
  301. {
  302. query_t &query = m_Queries[idx];
  303. if ((curtime - query.sendTime) > 1.2f)
  304. {
  305. // server has timed out
  306. serveritem_t &item = m_Servers[query.serverID]->GetServer();
  307. // mark the server
  308. item.pings[0] = item.pings[1];
  309. item.pings[1] = item.pings[2];
  310. item.pings[2] = 1200;
  311. item.ping = CalculateAveragePing(item);
  312. if (!item.hadSuccessfulResponse)
  313. {
  314. // remove the server if it has never responded before
  315. item.doNotRefresh = true;
  316. m_nInvalidServers++;
  317. }
  318. // respond to the game list notifying of the lack of response
  319. m_pResponseTarget->ServerFailedToRespond(item);
  320. item.received = false;
  321. // get the next server now, since we're about to delete it from query list
  322. unsigned short nextidx = m_Queries.NextInorder(idx);
  323. // delete the query
  324. m_Queries.RemoveAt(idx);
  325. // move to next item
  326. idx = nextidx;
  327. }
  328. else
  329. {
  330. // still waiting for server result
  331. idx = m_Queries.NextInorder(idx);
  332. }
  333. }
  334. // increment the number of sockets to use
  335. m_nMaxRampUp = min(m_nMaxActive, m_nMaxRampUp + m_nRampUpSpeed);
  336. // see if we should send more queries
  337. while (m_RefreshList.Count() > 0 && (int)m_Queries.Count() < m_nMaxRampUp)
  338. {
  339. // get the first item from the list to refresh
  340. int currentServer = m_RefreshList[0];
  341. if (!m_Servers.IsValidIndex(currentServer))
  342. break;
  343. serveritem_t &item = m_Servers[currentServer]->GetServer();
  344. item.time = curtime;
  345. //QueryServer(m_pQuery, currentServer);
  346. m_Servers[currentServer]->Query();
  347. query_t query;
  348. netadr_t adr;
  349. adr.ip[0] = item.ip[0];
  350. adr.ip[1] = item.ip[1];
  351. adr.ip[2] = item.ip[2];
  352. adr.ip[3] = item.ip[3];
  353. adr.port = (item.port & 0xff) << 8 | (item.port & 0xff00) >> 8;
  354. adr.type = NA_IP;
  355. query.addr =adr;
  356. query.sendTime=curtime;
  357. query.serverID=item.serverID;
  358. m_Queries.Insert(query);
  359. // remove the server from the refresh list
  360. m_RefreshList.Remove((int)0);
  361. }
  362. // Done querying?
  363. if (m_Queries.Count() < 1)
  364. {
  365. m_bQuerying = false;
  366. m_pResponseTarget->RefreshComplete();
  367. // up the serial number, so that we ignore any late results
  368. m_iUpdateSerialNumber++;
  369. }
  370. }