#include "GenericApiCore.h" #include "GenericConnection.h" #include "GenericMessage.h" #include "CTCommon/CTEnum.h" //---------------------------------------- // // WARNING: These files are NOT standard generic API files // They have been modified for this project. // Do NOT replace them with generic API files // //---------------------------------------- #ifdef EXTERNAL_DISTRO namespace NAMESPACE { #endif using namespace std; using namespace Base; GenericConnection::GenericConnection(const char *host, short port, GenericAPICore *apiCore, unsigned reconnectTimeout, unsigned noDataTimeoutSecs, unsigned noAckTimeoutSecs, unsigned incomingBufSizeInKB, unsigned outgoingBufSizeInKB, unsigned keepAlive, unsigned maxRecvMessageSizeInKB) : m_bConnected(CON_NONE), m_apiCore(apiCore), m_con(NULL), m_host(host), m_port(port), m_lastTrack(123455), //random choice != 1 m_conState(CON_DISCONNECT), m_reconnectTimeout(reconnectTimeout) { TcpManager::TcpParams params; params.incomingBufferSize = incomingBufSizeInKB * 1024; params.outgoingBufferSize = outgoingBufSizeInKB * 1024; params.maxConnections = 1; params.port = 0; params.maxRecvMessageSize = maxRecvMessageSizeInKB*1024; params.keepAliveDelay = keepAlive * 1000; params.noDataTimeout = noDataTimeoutSecs * 1000; //params.oldestUnacknowledgedTimeout = noAckTimeoutSecs * 1000; m_manager = new TcpManager(params); } GenericConnection::~GenericConnection() { if(m_con) { m_con->SetHandler(NULL); m_con->Disconnect();//don't worry about onterminated being called, we've set it's handler to null, so it wont m_con->Release(); } m_manager->Release(); } void GenericConnection::disconnect() { if (m_con) { m_con->Disconnect(); //no need to release, since callback to onTerminated releases it, and callback is allways made m_con->Release(); m_con = NULL; } m_conState = CON_DISCONNECT; m_bConnected = CON_NONE; } void GenericConnection::OnTerminated(TcpConnection *con) { // m_apiCore->OnDisconnect(m_host.c_str(), m_port); m_apiCore->OnDisconnect(this); if(m_con) { m_con->Release(); m_con = NULL; } m_conState = CON_DISCONNECT; m_bConnected = CON_NONE; } void GenericConnection::OnRoutePacket(TcpConnection *con, const unsigned char *data, int dataLen) { short type; unsigned track; ByteStream msg(data, dataLen); ByteStream::ReadIterator iter = msg.begin(); get(iter, type); get(iter, track); GenericResponse *res = NULL; // the following if block is a temporary fix that prevents // a crash with a game team in which they occasionally find // themselves receiving a dupe track in consecutive calls to // OnRoutePacket (which then leads to a callback being called // twice and data being invalid on the second call -> crash!). if (track != 0 && track == m_lastTrack) { printf("!!! ERROR !!! Got a duplicate track ID %u\n", track); return; } m_lastTrack = track; // end temporary fix. if(track == 0) // notification message from the server, not as a response to a request from this API { if (type == CTService::CTGAME_REQUEST_CONNECT) { // this is a special case, for when we have identified our game code to server m_bConnected = CON_IDENTIFIED; m_apiCore->OnConnect(this); } else { m_apiCore->responseCallback(type, iter, this); } } else { map::iterator mapIter = m_apiCore->m_pending.find(track); if(mapIter != m_apiCore->m_pending.end()) { res = (*mapIter).second; iter = msg.begin(); res->unpack(iter); m_apiCore->responseCallback(res); m_apiCore->m_pendingCount--; m_apiCore->m_pending.erase(mapIter); delete res; } } } void GenericConnection::process() { switch(m_conState) { case CON_DISCONNECT: // create connection object, attempting to connect and // checking for connection in next state, CON_NEGOTIATE m_con = m_manager->EstablishConnection(m_host.c_str(), m_port); if(m_con) { m_con->SetHandler(this); m_conState = CON_NEGOTIATE; m_conTimeout = time(NULL) + m_reconnectTimeout; } break; case CON_NEGOTIATE: // check for connection if(m_con->GetStatus() == TcpConnection::StatusConnected) { // we're connected m_conState = CON_CONNECT; m_bConnected = CON_CONNECTED; // instead of calling OnConnect() right now, we are going to submit a connection packet // identifying us // m_apiCore->OnConnect(this); Base::ByteStream msg; put(msg, (short)CTService::CTGAME_REQUEST_CONNECT); put(msg, (unsigned)0); // track put(msg, (unsigned)API_VERSION_CODE); put(msg, m_apiCore->getGameCode()); Send(msg); } else if(time(NULL) > m_conTimeout) { // we did not connect m_con->Disconnect(); //no need to release, since callback to onTerminated releases it, and callback is allways made m_con->Release(); m_con = NULL; m_conState = CON_DISCONNECT; m_bConnected = CON_NONE; } break; case CON_CONNECT: // do nothing break; default: // this should not occur, but we revert to CON_DISCONNECT if it does m_conState = CON_DISCONNECT; m_bConnected = CON_NONE; if (m_con) { m_con->Disconnect(); //no need to release, since callback to onTerminated releases it, and callback is allways made m_con->Release(); m_con = NULL; } } m_manager->GiveTime(); } void GenericConnection::Send(Base::ByteStream &msg) { if(m_con && m_con->GetStatus() == TcpConnection::StatusConnected) { m_con->Send((const char *)msg.getBuffer(), msg.getSize()); } } #ifdef EXTERNAL_DISTRO }; #endif