GameResultsThread.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: PingThread.cpp //////////////////////////////////////////////////////
  24. // Ping thread
  25. // Author: Matthew D. Campbell, August 2002
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include <winsock.h> // This one has to be here. Prevents collisions with winsock2.h
  28. #include "GameNetwork/GameSpy/GameResultsThread.h"
  29. #include "mutex.h"
  30. #include "thread.h"
  31. #include "Common/StackDump.h"
  32. #include "Common/SubsystemInterface.h"
  33. //-------------------------------------------------------------------------
  34. static const Int NumWorkerThreads = 1;
  35. typedef std::queue<GameResultsRequest> RequestQueue;
  36. typedef std::queue<GameResultsResponse> ResponseQueue;
  37. class GameResultsThreadClass;
  38. class GameResultsQueue : public GameResultsInterface
  39. {
  40. public:
  41. virtual ~GameResultsQueue();
  42. GameResultsQueue();
  43. virtual void init() {}
  44. virtual void reset() {}
  45. virtual void update() {}
  46. virtual void startThreads( void );
  47. virtual void endThreads( void );
  48. virtual Bool areThreadsRunning( void );
  49. virtual void addRequest( const GameResultsRequest& req );
  50. virtual Bool getRequest( GameResultsRequest& resp );
  51. virtual void addResponse( const GameResultsResponse& resp );
  52. virtual Bool getResponse( GameResultsResponse& resp );
  53. virtual Bool areGameResultsBeingSent( void );
  54. private:
  55. MutexClass m_requestMutex;
  56. MutexClass m_responseMutex;
  57. RequestQueue m_requests;
  58. ResponseQueue m_responses;
  59. Int m_requestCount;
  60. Int m_responseCount;
  61. GameResultsThreadClass *m_workerThreads[NumWorkerThreads];
  62. };
  63. GameResultsInterface* GameResultsInterface::createNewGameResultsInterface( void )
  64. {
  65. return NEW GameResultsQueue;
  66. }
  67. GameResultsInterface *TheGameResultsQueue;
  68. //-------------------------------------------------------------------------
  69. class GameResultsThreadClass : public ThreadClass
  70. {
  71. public:
  72. GameResultsThreadClass() : ThreadClass() {}
  73. void Thread_Function();
  74. private:
  75. Int sendGameResults( UnsignedInt IP, UnsignedShort port, const std::string& results );
  76. };
  77. //-------------------------------------------------------------------------
  78. GameResultsQueue::GameResultsQueue() : m_requestCount(0), m_responseCount(0)
  79. {
  80. for (Int i=0; i<NumWorkerThreads; ++i)
  81. {
  82. m_workerThreads[i] = NULL;
  83. }
  84. startThreads();
  85. }
  86. GameResultsQueue::~GameResultsQueue()
  87. {
  88. endThreads();
  89. }
  90. void GameResultsQueue::startThreads( void )
  91. {
  92. endThreads();
  93. for (Int i=0; i<NumWorkerThreads; ++i)
  94. {
  95. m_workerThreads[i] = NEW GameResultsThreadClass;
  96. m_workerThreads[i]->Execute();
  97. }
  98. }
  99. void GameResultsQueue::endThreads( void )
  100. {
  101. for (Int i=0; i<NumWorkerThreads; ++i)
  102. {
  103. if (m_workerThreads[i])
  104. {
  105. delete m_workerThreads[i];
  106. m_workerThreads[i] = NULL;
  107. }
  108. }
  109. }
  110. Bool GameResultsQueue::areThreadsRunning( void )
  111. {
  112. for (Int i=0; i<NumWorkerThreads; ++i)
  113. {
  114. if (m_workerThreads[i])
  115. {
  116. if (m_workerThreads[i]->Is_Running())
  117. return true;
  118. }
  119. }
  120. return false;
  121. }
  122. void GameResultsQueue::addRequest( const GameResultsRequest& req )
  123. {
  124. MutexClass::LockClass m(m_requestMutex);
  125. ++m_requestCount;
  126. m_requests.push(req);
  127. }
  128. Bool GameResultsQueue::getRequest( GameResultsRequest& req )
  129. {
  130. MutexClass::LockClass m(m_requestMutex, 0);
  131. if (m.Failed())
  132. return false;
  133. if (m_requests.empty())
  134. return false;
  135. req = m_requests.front();
  136. m_requests.pop();
  137. return true;
  138. }
  139. void GameResultsQueue::addResponse( const GameResultsResponse& resp )
  140. {
  141. {
  142. MutexClass::LockClass m(m_responseMutex);
  143. ++m_responseCount;
  144. m_responses.push(resp);
  145. }
  146. }
  147. Bool GameResultsQueue::getResponse( GameResultsResponse& resp )
  148. {
  149. MutexClass::LockClass m(m_responseMutex, 0);
  150. if (m.Failed())
  151. return false;
  152. if (m_responses.empty())
  153. return false;
  154. resp = m_responses.front();
  155. m_responses.pop();
  156. return true;
  157. }
  158. Bool GameResultsQueue::areGameResultsBeingSent( void )
  159. {
  160. MutexClass::LockClass m(m_requestMutex, 0);
  161. if (m.Failed())
  162. return true;
  163. return m_requestCount > 0;
  164. }
  165. //-------------------------------------------------------------------------
  166. void GameResultsThreadClass::Thread_Function()
  167. {
  168. try {
  169. _set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
  170. GameResultsRequest req;
  171. WSADATA wsaData;
  172. // Fire up winsock (prob already done, but doesn't matter)
  173. WORD wVersionRequested = MAKEWORD(1, 1);
  174. WSAStartup( wVersionRequested, &wsaData );
  175. while ( running )
  176. {
  177. // deal with requests
  178. if (TheGameResultsQueue && TheGameResultsQueue->getRequest(req))
  179. {
  180. // resolve the hostname
  181. const char *hostnameBuffer = req.hostname.c_str();
  182. UnsignedInt IP = 0xFFFFFFFF;
  183. if (isdigit(hostnameBuffer[0]))
  184. {
  185. IP = inet_addr(hostnameBuffer);
  186. in_addr hostNode;
  187. hostNode.s_addr = IP;
  188. DEBUG_LOG(("sending game results to %s - IP = %s\n", hostnameBuffer, inet_ntoa(hostNode) ));
  189. }
  190. else
  191. {
  192. HOSTENT *hostStruct;
  193. in_addr *hostNode;
  194. hostStruct = gethostbyname(hostnameBuffer);
  195. if (hostStruct == NULL)
  196. {
  197. DEBUG_LOG(("sending game results to %s - host lookup failed\n", hostnameBuffer));
  198. // Even though this failed to resolve IP, still need to send a
  199. // callback.
  200. IP = 0xFFFFFFFF; // flag for IP resolve failed
  201. }
  202. hostNode = (in_addr *) hostStruct->h_addr;
  203. IP = hostNode->s_addr;
  204. DEBUG_LOG(("sending game results to %s IP = %s\n", hostnameBuffer, inet_ntoa(*hostNode) ));
  205. }
  206. int result = sendGameResults( IP, req.port, req.results );
  207. GameResultsResponse resp;
  208. resp.hostname = req.hostname;
  209. resp.port = req.port;
  210. resp.sentOk = (result == req.results.length());
  211. }
  212. // end our timeslice
  213. Switch_Thread();
  214. }
  215. WSACleanup();
  216. } catch ( ... ) {
  217. DEBUG_CRASH(("Exception in results thread!"));
  218. }
  219. }
  220. //-------------------------------------------------------------------------
  221. #ifdef DEBUG_LOGGING
  222. #define CASE(x) case (x): return #x;
  223. static const char *getWSAErrorString( Int error )
  224. {
  225. switch (error)
  226. {
  227. CASE(WSABASEERR)
  228. CASE(WSAEINTR)
  229. CASE(WSAEBADF)
  230. CASE(WSAEACCES)
  231. CASE(WSAEFAULT)
  232. CASE(WSAEINVAL)
  233. CASE(WSAEMFILE)
  234. CASE(WSAEWOULDBLOCK)
  235. CASE(WSAEINPROGRESS)
  236. CASE(WSAEALREADY)
  237. CASE(WSAENOTSOCK)
  238. CASE(WSAEDESTADDRREQ)
  239. CASE(WSAEMSGSIZE)
  240. CASE(WSAEPROTOTYPE)
  241. CASE(WSAENOPROTOOPT)
  242. CASE(WSAEPROTONOSUPPORT)
  243. CASE(WSAESOCKTNOSUPPORT)
  244. CASE(WSAEOPNOTSUPP)
  245. CASE(WSAEPFNOSUPPORT)
  246. CASE(WSAEAFNOSUPPORT)
  247. CASE(WSAEADDRINUSE)
  248. CASE(WSAEADDRNOTAVAIL)
  249. CASE(WSAENETDOWN)
  250. CASE(WSAENETUNREACH)
  251. CASE(WSAENETRESET)
  252. CASE(WSAECONNABORTED)
  253. CASE(WSAECONNRESET)
  254. CASE(WSAENOBUFS)
  255. CASE(WSAEISCONN)
  256. CASE(WSAENOTCONN)
  257. CASE(WSAESHUTDOWN)
  258. CASE(WSAETOOMANYREFS)
  259. CASE(WSAETIMEDOUT)
  260. CASE(WSAECONNREFUSED)
  261. CASE(WSAELOOP)
  262. CASE(WSAENAMETOOLONG)
  263. CASE(WSAEHOSTDOWN)
  264. CASE(WSAEHOSTUNREACH)
  265. CASE(WSAENOTEMPTY)
  266. CASE(WSAEPROCLIM)
  267. CASE(WSAEUSERS)
  268. CASE(WSAEDQUOT)
  269. CASE(WSAESTALE)
  270. CASE(WSAEREMOTE)
  271. CASE(WSAEDISCON)
  272. CASE(WSASYSNOTREADY)
  273. CASE(WSAVERNOTSUPPORTED)
  274. CASE(WSANOTINITIALISED)
  275. CASE(WSAHOST_NOT_FOUND)
  276. CASE(WSATRY_AGAIN)
  277. CASE(WSANO_RECOVERY)
  278. CASE(WSANO_DATA)
  279. default:
  280. return "Not a Winsock error";
  281. }
  282. }
  283. #undef CASE
  284. #endif
  285. //-------------------------------------------------------------------------
  286. Int GameResultsThreadClass::sendGameResults( UnsignedInt IP, UnsignedShort port, const std::string& results )
  287. {
  288. int error = 0;
  289. // create the socket
  290. Int sock = socket( AF_INET, SOCK_STREAM, 0 );
  291. if (sock < 0)
  292. {
  293. DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - socket() returned %d(%s)\n", sock, getWSAErrorString(sock)));
  294. return sock;
  295. }
  296. // fill in address info
  297. struct sockaddr_in sockAddr;
  298. memset( &sockAddr, 0, sizeof( sockAddr ) );
  299. sockAddr.sin_family = AF_INET;
  300. sockAddr.sin_addr.s_addr = IP;
  301. sockAddr.sin_port = htons(port);
  302. // Start the connection process....
  303. if( connect( sock, (struct sockaddr *)&sockAddr, sizeof( sockAddr ) ) == -1 )
  304. {
  305. error = WSAGetLastError();
  306. DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - connect() returned %d(%s)\n", error, getWSAErrorString(error)));
  307. if( ( error == WSAEWOULDBLOCK ) || ( error == WSAEINVAL ) || ( error == WSAEALREADY ) )
  308. {
  309. return( -1 );
  310. }
  311. if( error != WSAEISCONN )
  312. {
  313. closesocket( sock );
  314. return( -1 );
  315. }
  316. }
  317. if (send( sock, results.c_str(), results.length(), 0 ) == SOCKET_ERROR)
  318. {
  319. error = WSAGetLastError();
  320. DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - send() returned %d(%s)\n", error, getWSAErrorString(error)));
  321. closesocket(sock);
  322. return WSAGetLastError();
  323. }
  324. closesocket(sock);
  325. return results.length();
  326. }
  327. //-------------------------------------------------------------------------