GameResultsThread.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /*
  2. ** Command & Conquer Generals(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. // Wrap ladder results in HTTP POST
  167. static WrapHTTP( const std::string& hostname, std::string& results )
  168. {
  169. const char HEADER[] =
  170. "PUT / HTTP/1.1\r\n"
  171. "Connection: Close\r\n"
  172. "Host: %s\r\n"
  173. "Content-Length: %d\r\n"
  174. "\r\n";
  175. char szHdr[256] = {0};
  176. _snprintf( szHdr, 255, HEADER, hostname.c_str(), results.length() );
  177. results = szHdr + results;
  178. } //WrapHTTP
  179. //-------------------------------------------------------------------------
  180. void GameResultsThreadClass::Thread_Function()
  181. {
  182. try {
  183. _set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
  184. GameResultsRequest req;
  185. WSADATA wsaData;
  186. // Fire up winsock (prob already done, but doesn't matter)
  187. WORD wVersionRequested = MAKEWORD(1, 1);
  188. WSAStartup( wVersionRequested, &wsaData );
  189. while ( running )
  190. {
  191. // deal with requests
  192. if (TheGameResultsQueue && TheGameResultsQueue->getRequest(req))
  193. {
  194. // resolve the hostname
  195. const char *hostnameBuffer = req.hostname.c_str();
  196. UnsignedInt IP = 0xFFFFFFFF;
  197. if (isdigit(hostnameBuffer[0]))
  198. {
  199. IP = inet_addr(hostnameBuffer);
  200. in_addr hostNode;
  201. hostNode.s_addr = IP;
  202. DEBUG_LOG(("sending game results to %s - IP = %s\n", hostnameBuffer, inet_ntoa(hostNode) ));
  203. }
  204. else
  205. {
  206. HOSTENT *hostStruct;
  207. in_addr *hostNode;
  208. hostStruct = gethostbyname(hostnameBuffer);
  209. if (hostStruct == NULL)
  210. {
  211. DEBUG_LOG(("sending game results to %s - host lookup failed\n", hostnameBuffer));
  212. // Even though this failed to resolve IP, still need to send a
  213. // callback.
  214. IP = 0xFFFFFFFF; // flag for IP resolve failed
  215. }
  216. hostNode = (in_addr *) hostStruct->h_addr;
  217. IP = hostNode->s_addr;
  218. DEBUG_LOG(("sending game results to %s IP = %s\n", hostnameBuffer, inet_ntoa(*hostNode) ));
  219. }
  220. int result = sendGameResults( IP, req.port, req.results );
  221. GameResultsResponse resp;
  222. resp.hostname = req.hostname;
  223. resp.port = req.port;
  224. resp.sentOk = (result == req.results.length());
  225. }
  226. // end our timeslice
  227. Switch_Thread();
  228. }
  229. WSACleanup();
  230. } catch ( ... ) {
  231. DEBUG_CRASH(("Exception in results thread!"));
  232. }
  233. }
  234. //-------------------------------------------------------------------------
  235. #define CASE(x) case (x): return #x;
  236. static const char *getWSAErrorString( Int error )
  237. {
  238. switch (error)
  239. {
  240. CASE(WSABASEERR)
  241. CASE(WSAEINTR)
  242. CASE(WSAEBADF)
  243. CASE(WSAEACCES)
  244. CASE(WSAEFAULT)
  245. CASE(WSAEINVAL)
  246. CASE(WSAEMFILE)
  247. CASE(WSAEWOULDBLOCK)
  248. CASE(WSAEINPROGRESS)
  249. CASE(WSAEALREADY)
  250. CASE(WSAENOTSOCK)
  251. CASE(WSAEDESTADDRREQ)
  252. CASE(WSAEMSGSIZE)
  253. CASE(WSAEPROTOTYPE)
  254. CASE(WSAENOPROTOOPT)
  255. CASE(WSAEPROTONOSUPPORT)
  256. CASE(WSAESOCKTNOSUPPORT)
  257. CASE(WSAEOPNOTSUPP)
  258. CASE(WSAEPFNOSUPPORT)
  259. CASE(WSAEAFNOSUPPORT)
  260. CASE(WSAEADDRINUSE)
  261. CASE(WSAEADDRNOTAVAIL)
  262. CASE(WSAENETDOWN)
  263. CASE(WSAENETUNREACH)
  264. CASE(WSAENETRESET)
  265. CASE(WSAECONNABORTED)
  266. CASE(WSAECONNRESET)
  267. CASE(WSAENOBUFS)
  268. CASE(WSAEISCONN)
  269. CASE(WSAENOTCONN)
  270. CASE(WSAESHUTDOWN)
  271. CASE(WSAETOOMANYREFS)
  272. CASE(WSAETIMEDOUT)
  273. CASE(WSAECONNREFUSED)
  274. CASE(WSAELOOP)
  275. CASE(WSAENAMETOOLONG)
  276. CASE(WSAEHOSTDOWN)
  277. CASE(WSAEHOSTUNREACH)
  278. CASE(WSAENOTEMPTY)
  279. CASE(WSAEPROCLIM)
  280. CASE(WSAEUSERS)
  281. CASE(WSAEDQUOT)
  282. CASE(WSAESTALE)
  283. CASE(WSAEREMOTE)
  284. CASE(WSAEDISCON)
  285. CASE(WSASYSNOTREADY)
  286. CASE(WSAVERNOTSUPPORTED)
  287. CASE(WSANOTINITIALISED)
  288. CASE(WSAHOST_NOT_FOUND)
  289. CASE(WSATRY_AGAIN)
  290. CASE(WSANO_RECOVERY)
  291. CASE(WSANO_DATA)
  292. default:
  293. return "Not a Winsock error";
  294. }
  295. }
  296. #undef CASE
  297. //-------------------------------------------------------------------------
  298. Int GameResultsThreadClass::sendGameResults( UnsignedInt IP, UnsignedShort port, const std::string& results )
  299. {
  300. int error = 0;
  301. // create the socket
  302. Int sock = socket( AF_INET, SOCK_STREAM, 0 );
  303. if (sock < 0)
  304. {
  305. DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - socket() returned %d(%s)\n", sock, getWSAErrorString(sock)));
  306. return sock;
  307. }
  308. // fill in address info
  309. struct sockaddr_in sockAddr;
  310. memset( &sockAddr, 0, sizeof( sockAddr ) );
  311. sockAddr.sin_family = AF_INET;
  312. sockAddr.sin_addr.s_addr = IP;
  313. sockAddr.sin_port = htons(port);
  314. // Start the connection process....
  315. if( connect( sock, (struct sockaddr *)&sockAddr, sizeof( sockAddr ) ) == -1 )
  316. {
  317. error = WSAGetLastError();
  318. DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - connect() returned %d(%s)\n", error, getWSAErrorString(error)));
  319. if( ( error == WSAEWOULDBLOCK ) || ( error == WSAEINVAL ) || ( error == WSAEALREADY ) )
  320. {
  321. return( -1 );
  322. }
  323. if( error != WSAEISCONN )
  324. {
  325. closesocket( sock );
  326. return( -1 );
  327. }
  328. }
  329. if (send( sock, results.c_str(), results.length(), 0 ) == SOCKET_ERROR)
  330. {
  331. error = WSAGetLastError();
  332. DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - send() returned %d(%s)\n", error, getWSAErrorString(error)));
  333. closesocket(sock);
  334. return WSAGetLastError();
  335. }
  336. closesocket(sock);
  337. return results.length();
  338. }
  339. //-------------------------------------------------------------------------