/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: PingThread.cpp ////////////////////////////////////////////////////// // Ping thread // Author: Matthew D. Campbell, August 2002 #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include // This one has to be here. Prevents collisions with winsock2.h #include "GameNetwork/GameSpy/GameResultsThread.h" #include "mutex.h" #include "thread.h" #include "Common/StackDump.h" #include "Common/SubsystemInterface.h" //------------------------------------------------------------------------- static const Int NumWorkerThreads = 1; typedef std::queue RequestQueue; typedef std::queue ResponseQueue; class GameResultsThreadClass; class GameResultsQueue : public GameResultsInterface { public: virtual ~GameResultsQueue(); GameResultsQueue(); virtual void init() {} virtual void reset() {} virtual void update() {} virtual void startThreads( void ); virtual void endThreads( void ); virtual Bool areThreadsRunning( void ); virtual void addRequest( const GameResultsRequest& req ); virtual Bool getRequest( GameResultsRequest& resp ); virtual void addResponse( const GameResultsResponse& resp ); virtual Bool getResponse( GameResultsResponse& resp ); virtual Bool areGameResultsBeingSent( void ); private: MutexClass m_requestMutex; MutexClass m_responseMutex; RequestQueue m_requests; ResponseQueue m_responses; Int m_requestCount; Int m_responseCount; GameResultsThreadClass *m_workerThreads[NumWorkerThreads]; }; GameResultsInterface* GameResultsInterface::createNewGameResultsInterface( void ) { return NEW GameResultsQueue; } GameResultsInterface *TheGameResultsQueue; //------------------------------------------------------------------------- class GameResultsThreadClass : public ThreadClass { public: GameResultsThreadClass() : ThreadClass() {} void Thread_Function(); private: Int sendGameResults( UnsignedInt IP, UnsignedShort port, const std::string& results ); }; //------------------------------------------------------------------------- GameResultsQueue::GameResultsQueue() : m_requestCount(0), m_responseCount(0) { for (Int i=0; iExecute(); } } void GameResultsQueue::endThreads( void ) { for (Int i=0; iIs_Running()) return true; } } return false; } void GameResultsQueue::addRequest( const GameResultsRequest& req ) { MutexClass::LockClass m(m_requestMutex); ++m_requestCount; m_requests.push(req); } Bool GameResultsQueue::getRequest( GameResultsRequest& req ) { MutexClass::LockClass m(m_requestMutex, 0); if (m.Failed()) return false; if (m_requests.empty()) return false; req = m_requests.front(); m_requests.pop(); return true; } void GameResultsQueue::addResponse( const GameResultsResponse& resp ) { { MutexClass::LockClass m(m_responseMutex); ++m_responseCount; m_responses.push(resp); } } Bool GameResultsQueue::getResponse( GameResultsResponse& resp ) { MutexClass::LockClass m(m_responseMutex, 0); if (m.Failed()) return false; if (m_responses.empty()) return false; resp = m_responses.front(); m_responses.pop(); return true; } Bool GameResultsQueue::areGameResultsBeingSent( void ) { MutexClass::LockClass m(m_requestMutex, 0); if (m.Failed()) return true; return m_requestCount > 0; } //------------------------------------------------------------------------- void GameResultsThreadClass::Thread_Function() { try { _set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace. GameResultsRequest req; WSADATA wsaData; // Fire up winsock (prob already done, but doesn't matter) WORD wVersionRequested = MAKEWORD(1, 1); WSAStartup( wVersionRequested, &wsaData ); while ( running ) { // deal with requests if (TheGameResultsQueue && TheGameResultsQueue->getRequest(req)) { // resolve the hostname const char *hostnameBuffer = req.hostname.c_str(); UnsignedInt IP = 0xFFFFFFFF; if (isdigit(hostnameBuffer[0])) { IP = inet_addr(hostnameBuffer); in_addr hostNode; hostNode.s_addr = IP; DEBUG_LOG(("sending game results to %s - IP = %s\n", hostnameBuffer, inet_ntoa(hostNode) )); } else { HOSTENT *hostStruct; in_addr *hostNode; hostStruct = gethostbyname(hostnameBuffer); if (hostStruct == NULL) { DEBUG_LOG(("sending game results to %s - host lookup failed\n", hostnameBuffer)); // Even though this failed to resolve IP, still need to send a // callback. IP = 0xFFFFFFFF; // flag for IP resolve failed } hostNode = (in_addr *) hostStruct->h_addr; IP = hostNode->s_addr; DEBUG_LOG(("sending game results to %s IP = %s\n", hostnameBuffer, inet_ntoa(*hostNode) )); } int result = sendGameResults( IP, req.port, req.results ); GameResultsResponse resp; resp.hostname = req.hostname; resp.port = req.port; resp.sentOk = (result == req.results.length()); } // end our timeslice Switch_Thread(); } WSACleanup(); } catch ( ... ) { DEBUG_CRASH(("Exception in results thread!")); } } //------------------------------------------------------------------------- #ifdef DEBUG_LOGGING #define CASE(x) case (x): return #x; static const char *getWSAErrorString( Int error ) { switch (error) { CASE(WSABASEERR) CASE(WSAEINTR) CASE(WSAEBADF) CASE(WSAEACCES) CASE(WSAEFAULT) CASE(WSAEINVAL) CASE(WSAEMFILE) CASE(WSAEWOULDBLOCK) CASE(WSAEINPROGRESS) CASE(WSAEALREADY) CASE(WSAENOTSOCK) CASE(WSAEDESTADDRREQ) CASE(WSAEMSGSIZE) CASE(WSAEPROTOTYPE) CASE(WSAENOPROTOOPT) CASE(WSAEPROTONOSUPPORT) CASE(WSAESOCKTNOSUPPORT) CASE(WSAEOPNOTSUPP) CASE(WSAEPFNOSUPPORT) CASE(WSAEAFNOSUPPORT) CASE(WSAEADDRINUSE) CASE(WSAEADDRNOTAVAIL) CASE(WSAENETDOWN) CASE(WSAENETUNREACH) CASE(WSAENETRESET) CASE(WSAECONNABORTED) CASE(WSAECONNRESET) CASE(WSAENOBUFS) CASE(WSAEISCONN) CASE(WSAENOTCONN) CASE(WSAESHUTDOWN) CASE(WSAETOOMANYREFS) CASE(WSAETIMEDOUT) CASE(WSAECONNREFUSED) CASE(WSAELOOP) CASE(WSAENAMETOOLONG) CASE(WSAEHOSTDOWN) CASE(WSAEHOSTUNREACH) CASE(WSAENOTEMPTY) CASE(WSAEPROCLIM) CASE(WSAEUSERS) CASE(WSAEDQUOT) CASE(WSAESTALE) CASE(WSAEREMOTE) CASE(WSAEDISCON) CASE(WSASYSNOTREADY) CASE(WSAVERNOTSUPPORTED) CASE(WSANOTINITIALISED) CASE(WSAHOST_NOT_FOUND) CASE(WSATRY_AGAIN) CASE(WSANO_RECOVERY) CASE(WSANO_DATA) default: return "Not a Winsock error"; } } #undef CASE #endif //------------------------------------------------------------------------- Int GameResultsThreadClass::sendGameResults( UnsignedInt IP, UnsignedShort port, const std::string& results ) { int error = 0; // create the socket Int sock = socket( AF_INET, SOCK_STREAM, 0 ); if (sock < 0) { DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - socket() returned %d(%s)\n", sock, getWSAErrorString(sock))); return sock; } // fill in address info struct sockaddr_in sockAddr; memset( &sockAddr, 0, sizeof( sockAddr ) ); sockAddr.sin_family = AF_INET; sockAddr.sin_addr.s_addr = IP; sockAddr.sin_port = htons(port); // Start the connection process.... if( connect( sock, (struct sockaddr *)&sockAddr, sizeof( sockAddr ) ) == -1 ) { error = WSAGetLastError(); DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - connect() returned %d(%s)\n", error, getWSAErrorString(error))); if( ( error == WSAEWOULDBLOCK ) || ( error == WSAEINVAL ) || ( error == WSAEALREADY ) ) { return( -1 ); } if( error != WSAEISCONN ) { closesocket( sock ); return( -1 ); } } if (send( sock, results.c_str(), results.length(), 0 ) == SOCKET_ERROR) { error = WSAGetLastError(); DEBUG_LOG(("GameResultsThreadClass::sendGameResults() - send() returned %d(%s)\n", error, getWSAErrorString(error))); closesocket(sock); return WSAGetLastError(); } closesocket(sock); return results.length(); } //-------------------------------------------------------------------------