PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/MTA10/core/CServerList.cpp

https://github.com/araguzers/multitheftauto
C++ | 452 lines | 305 code | 82 blank | 65 comment | 81 complexity | dff56b6d1e6e848b8c20a5f9fe470cc0 MD5 | raw file
  1. /*****************************************************************************
  2. *
  3. * PROJECT: Multi Theft Auto v1.0
  4. * LICENSE: See LICENSE in the top level directory
  5. * FILE: core/CServerList.cpp
  6. * PURPOSE: Master server list querying
  7. * DEVELOPERS: Cecill Etheredge <ijsf@gmx.net>
  8. * Stanislav Bobrov <lil_toady@hotmail.com>
  9. *
  10. * Multi Theft Auto is available from http://www.multitheftauto.com/
  11. *
  12. *****************************************************************************/
  13. #include "StdInc.h"
  14. using namespace std;
  15. extern CCore* g_pCore;
  16. CServerList::CServerList ( void )
  17. {
  18. m_bUpdated = false;
  19. m_iPass = 0;
  20. m_strStatus = "Idle";
  21. m_nScanned = 0;
  22. m_nSkipped = 0;
  23. m_iRevision = 1;
  24. }
  25. CServerList::~CServerList ( void )
  26. {
  27. Clear ();
  28. }
  29. void CServerList::Clear ( void )
  30. {
  31. // Clear all entries
  32. for ( CServerListIterator i = m_Servers.begin (); i != m_Servers.end (); i++ )
  33. delete *i;
  34. m_Servers.clear ();
  35. m_nScanned = 0;
  36. m_nSkipped = 0;
  37. m_iRevision++;
  38. }
  39. void CServerList::Pulse ( void )
  40. {
  41. unsigned int n = m_Servers.size ();
  42. unsigned int uiQueriesSent = 0;
  43. unsigned int uiRepliesParsed = 0;
  44. unsigned int uiNoReplies = 0;
  45. // Scan all servers in our list, and keep the value of scanned servers
  46. for ( CServerListIterator i = m_Servers.begin (); i != m_Servers.end (); i++ ) {
  47. CServerListItem * pServer = *i;
  48. std::string strResult = pServer->Pulse ();
  49. if ( strResult == "SentQuery" )
  50. uiQueriesSent++;
  51. else
  52. if ( strResult == "ParsedQuery" )
  53. uiRepliesParsed++;
  54. else
  55. if ( strResult == "NoReply" )
  56. uiNoReplies++;
  57. if ( uiQueriesSent >= SERVER_LIST_QUERIES_PER_PULSE ) break;
  58. }
  59. // If we queried any new servers, we should toggle the GUI update flag
  60. m_bUpdated = m_bUpdated || ( uiRepliesParsed > 0 ) || ( uiNoReplies > 0 );
  61. // Check whether we are done scanning
  62. std::stringstream ss;
  63. // Store the new number of scanned servers
  64. m_nScanned += uiRepliesParsed;
  65. m_nSkipped += uiNoReplies;
  66. if ( m_nScanned + m_nSkipped == n ) {
  67. ss << "Found " << m_nScanned << " / " << m_nScanned + m_nSkipped << " servers";
  68. // We are no longer refreshing
  69. m_iPass = 0;
  70. } else {
  71. ss << "Scanned " << m_nScanned << " / " << m_nScanned + m_nSkipped << " servers";
  72. }
  73. // Update our status message
  74. m_strStatus = ss.str () + m_strStatus2;
  75. }
  76. bool CServerList::Exists ( CServerListItem Server )
  77. {
  78. // Look for a duplicate entry
  79. for ( CServerListIterator i = m_Servers.begin (); i != m_Servers.end (); i++ )
  80. if ( **i == Server ) return true;
  81. return false;
  82. }
  83. void CServerList::Add ( CServerListItem Server, bool addAtFront )
  84. {
  85. if ( addAtFront )
  86. m_Servers.push_front ( new CServerListItem ( Server ) );
  87. else
  88. m_Servers.push_back ( new CServerListItem ( Server ) );
  89. }
  90. void CServerList::Remove ( CServerListItem Server )
  91. {
  92. // Look for a duplicate entry
  93. for ( CServerListIterator i = m_Servers.begin (); i != m_Servers.end (); i++ )
  94. {
  95. if ( **i == Server )
  96. {
  97. m_Servers.remove ( *i );
  98. return;
  99. }
  100. }
  101. }
  102. void CServerList::Refresh ( void )
  103. { // Assumes we already have a (saved) list of servers, so we just need to refresh
  104. // Reinitialize each server list item
  105. for ( std::list<CServerListItem*>::iterator iter = m_Servers.begin (); iter != m_Servers.end (); iter++ )
  106. {
  107. CServerListItem* pOldItem = *iter;
  108. *iter = new CServerListItem( *pOldItem );
  109. delete pOldItem;
  110. }
  111. m_iPass = 1;
  112. m_nScanned = 0;
  113. m_nSkipped = 0;
  114. m_iRevision++;
  115. }
  116. void CServerListInternet::Refresh ( void )
  117. { // Gets the server list from the master server and refreshes
  118. m_ulStartTime = CClientTime::GetTime ();
  119. m_HTTP.Get ( SERVER_LIST_MASTER_URL );
  120. m_iPass = 1;
  121. m_bUpdated = true;
  122. // Clear the previous server list
  123. Clear ();
  124. }
  125. void CServerListInternet::Pulse ( void )
  126. { // We also need to take care of the master server list here
  127. unsigned long ulTime = CClientTime::GetTime () - m_ulStartTime;
  128. if ( m_iPass == 1 ) {
  129. // We are polling for the master server list (first pass)
  130. stringstream ss;
  131. ss << "Requesting master server list (" << ulTime << "ms elapsed)";
  132. m_strStatus = ss.str ();
  133. m_strStatus2 = "";
  134. m_bUpdated = true;
  135. // Attempt to get the HTTP data
  136. CHTTPBuffer buffer;
  137. if ( m_HTTP.GetData ( buffer ) ) {
  138. // We got the data, parse it and switch pass
  139. if ( ParseList ( buffer.GetData (), buffer.GetSize () ) ) {
  140. m_iPass++;
  141. } else {
  142. // Abort
  143. m_strStatus = "Master server list could not be parsed.";
  144. m_iPass = 0;
  145. }
  146. } else {
  147. // Take care of timeouts
  148. if ( ulTime > SERVER_LIST_MASTER_TIMEOUT ) {
  149. // Abort
  150. m_strStatus = "Master server list could not be retrieved.";
  151. m_iPass = 0;
  152. }
  153. }
  154. if ( m_iPass == 0 )
  155. {
  156. // If query failed, load from backup
  157. CCore::GetSingleton ().GetLocalGUI ()->GetMainMenu ()->GetServerBrowser ()->LoadInternetList();
  158. m_strStatus2 = string ( " (Backup server list)" );
  159. m_iPass = 2;
  160. }
  161. else
  162. if ( m_iPass == 2 )
  163. {
  164. // If query succeeded, save to backup
  165. CCore::GetSingleton ().GetLocalGUI ()->GetMainMenu ()->GetServerBrowser ()->SaveInternetList();
  166. }
  167. } else if ( m_iPass == 2 ) {
  168. // We are scanning our known servers (second pass)
  169. CServerList::Pulse ();
  170. }
  171. }
  172. bool CServerListInternet::ParseList ( const char *szBuffer, unsigned int nLength )
  173. {
  174. unsigned int i = 0, j = 0;
  175. // Read out the server count
  176. if ( nLength < 2 ) return false;
  177. unsigned int uiCount = ntohs ( *((unsigned short*)&szBuffer[0]) );
  178. i = 2;
  179. // Add all servers until we hit the count or nLength
  180. while ( i < ( nLength - 6 ) && uiCount-- ) {
  181. // Read the IPv4-address
  182. in_addr _Address;
  183. _Address.S_un.S_un_b.s_b1 = szBuffer[i];
  184. _Address.S_un.S_un_b.s_b2 = szBuffer[i+1];
  185. _Address.S_un.S_un_b.s_b3 = szBuffer[i+2];
  186. _Address.S_un.S_un_b.s_b4 = szBuffer[i+3];
  187. // Read the query port
  188. unsigned short _usQueryPort = ntohs ( *((unsigned short*)(&szBuffer[i+4])) );
  189. CServerListItem& item = *new CServerListItem ( _Address, _usQueryPort );
  190. // Add the server
  191. m_Servers.push_back ( &item );
  192. i += 6;
  193. }
  194. return true;
  195. }
  196. void CServerListLAN::Pulse ( void )
  197. {
  198. char szBuffer[32];
  199. // Broadcast the discover packet on a regular interval
  200. if ( (CClientTime::GetTime () - m_ulStartTime) > SERVER_LIST_BROADCAST_REFRESH )
  201. Discover ();
  202. // Poll our socket to discover any new servers
  203. timeval tm;
  204. tm.tv_sec = 0;
  205. tm.tv_usec = 0;
  206. fd_set readfds;
  207. readfds.fd_array[0] = m_Socket;
  208. readfds.fd_count = 1;
  209. int len = sizeof ( m_Remote );
  210. if ( select ( 1, &readfds, NULL, NULL, &tm ) > 0 )
  211. if ( recvfrom ( m_Socket, szBuffer, sizeof (szBuffer), 0, (sockaddr *) &m_Remote, &len ) > 10 )
  212. if ( strncmp ( szBuffer, SERVER_LIST_SERVER_BROADCAST_STR, strlen ( SERVER_LIST_SERVER_BROADCAST_STR ) ) == 0 ) {
  213. unsigned short usPort = ( unsigned short ) atoi ( &szBuffer[strlen ( SERVER_LIST_SERVER_BROADCAST_STR ) + 1] );
  214. CServerListItem Server ( m_Remote.sin_addr, usPort );
  215. // Add the server if doesn't already exist
  216. if ( !Exists ( Server ) ) Add ( Server );
  217. }
  218. // Scan our already known servers
  219. CServerList::Pulse ();
  220. }
  221. void CServerListLAN::Refresh ( void )
  222. { // Gets the server list from LAN-broadcasting servers
  223. m_iPass = 1;
  224. m_bUpdated = true;
  225. // Create the LAN-broadcast socket
  226. closesocket ( m_Socket );
  227. m_Socket = socket ( AF_INET, SOCK_DGRAM, 0 );
  228. const int Flags = 1;
  229. setsockopt ( m_Socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&Flags, sizeof ( Flags ) );
  230. if ( setsockopt ( m_Socket, SOL_SOCKET, SO_BROADCAST, (const char *)&Flags, sizeof ( Flags ) ) != 0 ) {
  231. m_strStatus = "Cannot bind LAN-broadcast socket";
  232. return;
  233. }
  234. // Prepare the structures
  235. memset ( &m_Remote, 0, sizeof (m_Remote) );
  236. m_Remote.sin_family = AF_INET;
  237. m_Remote.sin_port = htons ( SERVER_LIST_BROADCAST_PORT );
  238. m_Remote.sin_addr.s_addr = INADDR_BROADCAST;
  239. // Clear the previous server list
  240. Clear ();
  241. // Discover other servers by sending out the broadcast packet
  242. Discover ();
  243. }
  244. void CServerListLAN::Discover ( void )
  245. {
  246. m_strStatus = "Attempting to discover LAN servers";
  247. // Send out the broadcast packet
  248. std::string strQuery = std::string ( SERVER_LIST_CLIENT_BROADCAST_STR ) + " " + std::string ( MTA_DM_ASE_VERSION );
  249. sendto ( m_Socket, strQuery.c_str (), strQuery.length () + 1, 0, (sockaddr *)&m_Remote, sizeof (m_Remote) );
  250. // Keep the time
  251. m_ulStartTime = CClientTime::GetTime ();
  252. }
  253. std::string CServerListItem::Pulse ( void )
  254. { // Queries the server on it's query port (ASE protocol)
  255. // and returns whether it is done scanning
  256. if ( bScanned || bSkipped ) return "Done";
  257. char szBuffer[SERVER_LIST_QUERY_BUFFER] = {0};
  258. if ( m_ulQueryStart == 0 ) {
  259. Query ();
  260. return "SentQuery";
  261. } else {
  262. // Poll the socket
  263. sockaddr_in clntAddr;
  264. int addrLen = sizeof ( clntAddr );
  265. int len = recvfrom ( m_Socket, szBuffer, SERVER_LIST_QUERY_BUFFER, MSG_PARTIAL, (sockaddr *) &clntAddr, &addrLen );
  266. int error = WSAGetLastError();
  267. if ( len >= 0 ) {
  268. // Parse data
  269. ParseQuery ( szBuffer, len );
  270. return "ParsedQuery";
  271. }
  272. if ( CClientTime::GetTime () - m_ulQueryStart > SERVER_LIST_ITEM_TIMEOUT )
  273. {
  274. bSkipped = true;
  275. return "NoReply";
  276. }
  277. return "WaitingReply";
  278. }
  279. }
  280. void CServerListItem::Query ( void )
  281. { // Performs a query according to ASE protocol
  282. sockaddr_in addr;
  283. memset ( &addr, 0, sizeof(addr) );
  284. addr.sin_family = AF_INET;
  285. addr.sin_addr = Address;
  286. addr.sin_port = htons ( usQueryPort );
  287. int ret = sendto ( m_Socket, "r", 1, 0, (sockaddr *) &addr, sizeof(addr) );
  288. if ( ret == 1 )
  289. m_ulQueryStart = CClientTime::GetTime ();
  290. }
  291. bool ReadString ( std::string &strRead, const char * szBuffer, unsigned int &i, unsigned int nLength )
  292. {
  293. if ( i <= nLength )
  294. {
  295. unsigned char len = szBuffer[i];
  296. if ( i + len <= nLength )
  297. {
  298. const char *ptr = &szBuffer[i + 1];
  299. i += len;
  300. strRead = std::string ( ptr, len - 1 );
  301. return true;
  302. }
  303. i++;
  304. }
  305. return false;
  306. }
  307. bool CServerListItem::ParseQuery ( const char * szBuffer, unsigned int nLength )
  308. {
  309. // Check length
  310. if ( nLength < 15 )
  311. return false;
  312. // Check header
  313. if ( strncmp ( szBuffer, "EYE2", 4 ) != 0 )
  314. return false;
  315. // Get IP as string
  316. const char* szIP = inet_ntoa ( Address );
  317. // Calculate the ping/latency
  318. nPing = ( CClientTime::GetTime () - m_ulQueryStart );
  319. // Parse relevant data
  320. std::string strTemp;
  321. unsigned int i = 4;
  322. // IP
  323. strHost = szIP;
  324. // Game
  325. if ( !ReadString ( strGame, szBuffer, i, nLength ) )
  326. return false;
  327. // Port
  328. if ( !ReadString ( strTemp, szBuffer, i, nLength ) )
  329. return false;
  330. usGamePort = atoi ( strTemp.c_str () );
  331. // Server name
  332. if ( !ReadString ( strName, szBuffer, i, nLength ) )
  333. return false;
  334. // Game type
  335. if ( !ReadString ( strType, szBuffer, i, nLength ) )
  336. return false;
  337. // Map name
  338. if ( !ReadString ( strMap, szBuffer, i, nLength ) )
  339. return false;
  340. // Version
  341. if ( !ReadString ( strVersion, szBuffer, i, nLength ) )
  342. return false;
  343. if ( strVersion != MTA_DM_ASE_VERSION )
  344. return false;
  345. // Got space for password, serial verification, player count, players max?
  346. if ( i + 4 > nLength )
  347. {
  348. return false;
  349. }
  350. bPassworded = ( szBuffer[i++] == 1 );
  351. bSerials = ( szBuffer[i++] == 1 );
  352. nPlayers = (unsigned char)szBuffer[i++];
  353. nMaxPlayers = (unsigned char)szBuffer[i++];
  354. // Get player nicks
  355. vecPlayers.clear ();
  356. while ( i < nLength )
  357. {
  358. std::string strPlayer;
  359. if ( ReadString ( strPlayer, szBuffer, i, nLength ) )
  360. {
  361. // Remove color code, unless that results in an empty string
  362. std::string strResult = RemoveColorCode ( strPlayer.c_str () );
  363. if ( strResult.length () == 0 )
  364. strResult = strPlayer;
  365. vecPlayers.push_back ( strResult );
  366. }
  367. }
  368. bScanned = true;
  369. return true;
  370. }