/* $Id: serverconnectiontcpudp.cpp,v 1.10.2.1 2006/03/26 08:24:22 chfreund Exp $ */ #include "serverconnectiontcpudp.hpp" #include "global.hpp" #include "message.hpp" #include "tcpconnection.hpp" #include "serverconnectiontcp.hpp" #include "client.hpp" ServerConnectionTCPUDP::ServerConnectionTCPUDP( RemoteServerEntry& entry ) : m_serverEntry( entry ), m_lastReceivedFrame( 0 ), m_world( 0 ) { LOG( 2 ) INFO( "ServerConnectionTCPUDP::ServerConnectionTCPUDP: creating\n" ); m_tcpSocketSet = SDLNet_AllocSocketSet( 1 ); ASSERT( m_tcpSocketSet, "ServerConnectionTCPUDP::ServerConnectionTCPUDP: could not create TCP socket set\n" ); // set the UDP port range with default values m_udpPortStart = 0xb8b8; m_udpPortEnd = 0xb8c7; // find the first free port in the given UDP port range for ( m_udpPort = m_udpPortStart; m_udpPort <= m_udpPortEnd; m_udpPort++ ) { m_udpSocket = SDLNet_UDP_Open( m_udpPort ); if ( m_udpSocket ) break; } ASSERT( m_udpSocket, "ServerConnectionTCPUDP::ServerConnectionTCPUDP: could not create UDP socket\n" ); DBG( 2 ) INFO( "ServerConnectionTCPUDP::ServerConnectionTCPUDP: opened UDP socket at port %d\n", m_udpPort ); m_udpSocketSet = SDLNet_AllocSocketSet( 1 ); ASSERT( m_udpSocketSet, "ServerConnectionTCPUDP::ServerConnectionTCPUDP: could not create UDP socket set\n" ); SDLNet_UDP_AddSocket( m_udpSocketSet, m_udpSocket ); EventMessage eventMessage( MAX_NUMBER_OF_PLAYERS ); m_eventPacket = SDLNet_AllocPacket( eventMessage.getSerializeBufferSize() ); ASSERT( m_eventPacket, "ServerConnectionTCPUDP::ServerConnectionTCPUDP: could not allocate event packet\n" ); EchoMessage echoMessage; m_echoPacket = SDLNet_AllocPacket( echoMessage.getSerializeBufferSize() ); ASSERT( m_echoPacket, "ServerConnectionTCPUDP::ServerConnectionTCPUDP: could not allocate echo packet\n" ); } ServerConnectionTCPUDP::~ServerConnectionTCPUDP() { LOG( 2 ) INFO( "ServerConnectionTCPUDP::~ServerConnectionTCPUDP: cleaning up\n" ); // clean up remaining messages for ( std::deque::iterator it = m_messageQueue.begin(); it != m_messageQueue.end(); it++ ) delete *it; for ( std::deque::iterator it = m_echoMessageQueue.begin(); it != m_echoMessageQueue.end(); it++ ) delete *it; if ( m_tcpSocket ) SDLNet_TCP_Close( m_tcpSocket ); if ( m_tcpSocketSet ) SDLNet_FreeSocketSet( m_tcpSocketSet ); if ( m_udpSocket ) SDLNet_UDP_Close( m_udpSocket ); if ( m_udpSocketSet ) SDLNet_FreeSocketSet( m_udpSocketSet ); if ( m_eventPacket ) SDLNet_FreePacket( m_eventPacket ); if ( m_echoPacket ) SDLNet_FreePacket( m_echoPacket ); } bool ServerConnectionTCPUDP::checkConnection( ServerEntry* entry ) { // TODO: implement this return true; } bool ServerConnectionTCPUDP::openConnection() { LOG( 2 ) INFO( "ServerConnectionTCPUDP::openConnection: opening connection\n" ); m_tcpSocket = SDLNet_TCP_Open( &m_serverEntry.address ); if ( !m_tcpSocket ) { LOG( 2 ) INFO( "ServerConnectionTCPUDP::openConnection: could not open " "connection: %s\n", SDLNet_GetError() ); return false; } SDLNet_TCP_AddSocket( m_tcpSocketSet, m_tcpSocket ); /* RequestMessage requestMessage; requestMessage.request = Message::CONNECT; TCPConnection::sendMessage( m_tcpSocket, &requestMessage );*/ ConnectMessage connMessage; connMessage.udpPort = m_udpPort; TCPConnection::sendMessage( m_tcpSocket, &connMessage ); Message* message = 0; int result = TCPConnection::recvMessage( m_tcpSocket, message ); ASSERT( !result, "ServerConnectionTCPUDP::openConnection: lost connection\n" ); ASSERT( message->type == Message::CONNECT, "ServerConnectionTCPUDP::openConnection: expected CONNECT message\n" ); ConnectMessage* connectMessage = static_cast( message ); Uint8 clientID = connectMessage->clientID; m_udpAddress.host = m_serverEntry.address.host; SDLNet_Write16( connectMessage->udpPort, &m_udpAddress.port ); delete connectMessage; m_lastUdpActivity = SDL_GetTicks(); m_clientID = clientID; return true; } void ServerConnectionTCPUDP::closeConnection() { LOG( 2 ) INFO( "ServerConnectionTCPUDP::closeConnection: closing connection\n" ); // Send quit message to the server QuitMessage quitMessage; TCPConnection::sendMessage( m_tcpSocket, &quitMessage ); if ( m_tcpSocketSet && m_tcpSocket ) { SDLNet_TCP_DelSocket( m_tcpSocketSet, m_tcpSocket ); } if ( m_tcpSocket ) { SDLNet_TCP_Close( m_tcpSocket ); m_tcpSocket = 0; } } bool ServerConnectionTCPUDP::checkUdpConnection() { LOG( 3 ) INFO( "ServerConnectionTCPUDP: checking for working UDP connection\n" ); RequestMessage requestMessage; requestMessage.request = Message::REQUEST; UDPpacket* packet = SDLNet_AllocPacket( requestMessage.getSerializeBufferSize() ); packet->len = requestMessage.getSerializeBufferSize(); Uint8* bufferPointer = packet->data; requestMessage.serialize( bufferPointer ); packet->address = m_udpAddress; LOG( 4 ) INFO( "ServerConnectionTCPUDP: sending UDP packets to server\n" ); for ( int i = 0; i < 1; i++ ) { SDLNet_UDP_Send( m_udpSocket, -1, packet ); } // wait for an answer Uint32 tickReference = SDL_GetTicks(); Uint32 currentTicks = tickReference; bool answerReceived = false; while ( !answerReceived && currentTicks - tickReference < 500 ) { if ( SDLNet_CheckSockets( m_udpSocketSet, 0 ) == 0 ) { SDL_Delay( 100 ); currentTicks = SDL_GetTicks(); continue; } if ( SDLNet_UDP_Recv( m_udpSocket, packet ) == 1 ) { DBG( 4 ) INFO( "ServerConnectionTCPUDP: received a UDP packet\n" ); bufferPointer = packet->data; Message headerMessage; headerMessage.deserialize( bufferPointer ); if ( headerMessage.type == Message::REQUEST ) { RequestMessage* answerMessage = NEW RequestMessage( headerMessage ); answerMessage->deserializeData( bufferPointer ); if ( answerMessage->request == Message::REQUEST ) { // yes, we have an answer LOG( 4 ) INFO( "ServerConnectionTCPUDP: received UDP answer from server\n" ); answerReceived = true; } else DBG( 4 ) INFO( "ClientConnectionTCPUDP: request type %d\n", answerMessage->request ); delete answerMessage; } else { DBG( 4 ) INFO( "ServerConnectionTCPUDP::checkUdpConnection: got a UDP message " "not of type REQUEST (size = %d)\n", packet->len ); } } currentTicks = SDL_GetTicks(); } // send confirmation to the server if ( !answerReceived ) requestMessage.request = Message::UNKNOWN; TCPConnection::sendMessage( m_tcpSocket, &requestMessage ); // check confirmation message from the server Message* message = 0; TCPConnection::recvMessage( m_tcpSocket, message ); ASSERT( message->type == Message::REQUEST, "ServerConnectionTCPUDP::checkUdpConnection: protocol error\n" ); RequestMessage* answerMessage = static_cast( message ); bool packetArrived = false; if ( answerMessage->request == Message::REQUEST ) { LOG( 4 ) INFO( "ServerConnectionTCPUDP: server received our UDP packet\n" ); packetArrived = true; } else { LOG( 4 ) INFO( "ServerConnectionTCPUDP: server did not receive our UDP packet\n" ); } m_lastUdpActivity = SDL_GetTicks(); return answerReceived && packetArrived; } void ServerConnectionTCPUDP::sendMessage( Message* message ) { if ( message->type == Message::EVENT ) { // fill in info about last received frame EventMessage* eventMessage = static_cast( message ); eventMessage->lastReceivedFrame = m_lastReceivedFrame; m_eventPacket->len = message->getSerializeBufferSize(); ASSERT( m_eventPacket->len <= m_eventPacket->maxlen, "ServerConnectionTCPUDP::sendMessage: event message too large\n" ); Uint8* bufferPointer = m_eventPacket->data; message->serialize( bufferPointer ); m_eventPacket->address = m_udpAddress; SDLNet_UDP_Send( m_udpSocket, -1, m_eventPacket ); DBG( 5 ) { INFO( "ServerConnectionTCPUDP::sendMessage: sent UDP message of size %d\n", m_eventPacket->len ); INFO( "\tContent:\n" ); for ( int i = 0; i < m_eventPacket->len; i++ ) { INFO( "\t\t%x\n", m_eventPacket->data[i] ); } } } else TCPConnection::sendMessage( m_tcpSocket, message, m_compress, m_listener ); } EchoMessage* ServerConnectionTCPUDP::getEchoMessage( Uint32 frame ) { while ( recvEchoMessage() ) ; while ( m_echoMessageQueue.size() > 0 ) { EchoMessage* message = m_echoMessageQueue.front(); if ( message->frame == frame ) { m_echoMessageQueue.pop_front(); if ( frame > m_lastReceivedFrame ) m_lastReceivedFrame = frame; return message; } else if ( message->frame < frame ) { DBG( 4 ) INFO( "ServerConnectionTCPUDP::getEchoMessage: discarding old echo message for frame %d (waiting for frame %d)\n", message->frame, frame ); // this is a resent packet and can be discarded m_echoMessageQueue.pop_front(); delete message; continue; } else { // message->frame > frame DBG( 4 ) INFO( "ServerConnectionTCPUDP::getEchoMessage: still waiting for echo frame %d (next in queue is frame %d)\n", frame, message->frame ); // this is a packet that belongs to the future // TODO: what happens if a packet is received out of order? break; } } return 0; } Message* ServerConnectionTCPUDP::popMessage() { while ( recvMessage() ) ; if ( m_messageQueue.size() > 0 ) { Message* message = m_messageQueue.front(); m_messageQueue.pop_front(); return message; } return 0; } Message* ServerConnectionTCPUDP::peekMessage() { while ( recvMessage() ) ; if ( m_messageQueue.size() > 0 ) { Message* message = m_messageQueue.front(); return message; } return 0; } ServerConnectionTCP* ServerConnectionTCPUDP::createServerConnectionTCP() { ServerConnectionTCP* tcpConnection = NEW ServerConnectionTCP( m_serverEntry ); // copy member variables as needed tcpConnection->m_socket = m_tcpSocket; m_tcpSocket = 0; SDLNet_FreeSocketSet( tcpConnection->m_socketSet ); tcpConnection->m_socketSet = m_tcpSocketSet; m_tcpSocketSet = 0; tcpConnection->m_clientID = m_clientID; // TODO: copy message queue, world return tcpConnection; } bool ServerConnectionTCPUDP::recvMessage() { if ( SDLNet_CheckSockets( m_tcpSocketSet, 0 ) == 0 ) return false; Message* message = 0; int result = TCPConnection::recvMessage( m_tcpSocket, message, m_listener ); if ( result == -1 ) { // the connection has been lost LOG( 2 ) INFO( "ServerConnectionTCPUDP::recvMessage: connection has " "been lost: %s\n", SDLNet_GetError() ); // inform client m_valid = false; } else { if ( message ) { if ( message->type == Message::ECHO ) m_echoMessageQueue.push_back( static_cast( message ) ); else m_messageQueue.push_back( message ); return true; } } return false; } bool ServerConnectionTCPUDP::recvEchoMessage() { if ( SDLNet_CheckSockets( m_udpSocketSet, 0 ) == 0 ) { // give up connection after 5 seconds of silence on the UDP socket if ( SDL_GetTicks() - m_lastUdpActivity > 5000 ) { LOG( 2 ) INFO( "ServerConnectionTCPUDP::recvEchoMessage: no " "response on the UDP socket, giving up\n" ); m_valid = false; } return false; } int result = SDLNet_UDP_Recv( m_udpSocket, m_echoPacket ); if ( result == 1 ) { /* ASSERT( m_echoPacket->address == m_udpAddress, "ServerConnectionTCPUDP::recvMessage: event packet address does not match\n" );*/ Uint8* bufferPointer = m_echoPacket->data; Message headerMessage; headerMessage.deserialize( bufferPointer ); if ( headerMessage.type == Message::ECHO ) { EchoMessage* echoMessage = NEW EchoMessage( headerMessage ); echoMessage->deserializeData( bufferPointer ); // update information about last received frame if ( echoMessage->frame == m_lastReceivedFrame + 1 ) m_lastReceivedFrame = echoMessage->frame; // sort the echo message in the right place in the queue if ( m_echoMessageQueue.size() == 0 || m_echoMessageQueue.back()->frame < echoMessage->frame ) { // the frame number of the arrived echo message is larger than the one // of the last message in the queue (this should be the default case) m_echoMessageQueue.push_back( echoMessage ); } else { // we have to sort the message in the right place std::deque::iterator it = m_echoMessageQueue.begin(); while ( it != m_echoMessageQueue.end() ) { if ( (*it)->frame >= echoMessage->frame ) m_echoMessageQueue.insert( it, echoMessage ); it++; } } // check regularly for hanging progress windows if ( m_client && !( echoMessage->frame % 100 ) ) m_client->hideJoinProgress(); } else if ( headerMessage.type == Message::SYNC_PROGRESS ) { if ( m_client ) { SyncProgressMessage syncMessage( headerMessage ); syncMessage.deserializeData( bufferPointer ); m_client->showJoinProgress( syncMessage.messageSize, syncMessage.dataSent ); DBG( 4 ) INFO( "ServerConnectionTCPUDP::recvMessage: sync progress is " "%d of %d\n", syncMessage.dataSent, syncMessage.messageSize ); } } else { DBG( 4 ) INFO( "ServerConnectionTCPUDP::recvMessage: got a UDP message " "not of type ECHO or SYNC_PROGRESS (size = %d)\n", m_echoPacket->len ); } m_lastUdpActivity = SDL_GetTicks(); return true; } else if ( result == -1 ) { LOG( 2 ) INFO( "ServerConnectionTCPUDP::recvEchoMessage: an error has " "occurred: %s\n", SDLNet_GetError() ); } return false; }