| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- /*
- ** 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 <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (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 <winsock.h> // 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<GameResultsRequest> RequestQueue;
- typedef std::queue<GameResultsResponse> 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; i<NumWorkerThreads; ++i)
- {
- m_workerThreads[i] = NULL;
- }
- startThreads();
- }
- GameResultsQueue::~GameResultsQueue()
- {
- endThreads();
- }
- void GameResultsQueue::startThreads( void )
- {
- endThreads();
- for (Int i=0; i<NumWorkerThreads; ++i)
- {
- m_workerThreads[i] = NEW GameResultsThreadClass;
- m_workerThreads[i]->Execute();
- }
- }
- void GameResultsQueue::endThreads( void )
- {
- for (Int i=0; i<NumWorkerThreads; ++i)
- {
- if (m_workerThreads[i])
- {
- delete m_workerThreads[i];
- m_workerThreads[i] = NULL;
- }
- }
- }
- Bool GameResultsQueue::areThreadsRunning( void )
- {
- for (Int i=0; i<NumWorkerThreads; ++i)
- {
- if (m_workerThreads[i])
- {
- if (m_workerThreads[i]->Is_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();
- }
- //-------------------------------------------------------------------------
|