/* $Id: clientconnectiontcpudp.cpp,v 1.7.4.2 2006/03/09 14:59:42 chfreund Exp $ */ #include "clientconnectiontcpudp.hpp" #include "global.hpp" #include "wopsettings.hpp" #include "tcpconnection.hpp" #include "clientconnectiontcp.hpp" ClientConnectionTCPUDP::ClientConnectionTCPUDP( TCPsocket tcpSocket ) : m_tcpSocket( tcpSocket ), m_eventMessage( 0 ) { LOG( 2 ) INFO( "ClientConnectionTCPUDP::ClientConnectionTCPUDP: creating\n" ); m_tcpSocketSet = SDLNet_AllocSocketSet( 1 ); ASSERT( m_tcpSocketSet, "ClientConnectionTCPUDP::ClientConnectionTCPUDP: could not create TCP socket set\n" ); SDLNet_TCP_AddSocket( m_tcpSocketSet, m_tcpSocket ); m_udpPortStart = 0xb8b9; m_udpPortEnd = 0xb9b8; for ( m_localUdpPort = m_udpPortStart; m_localUdpPort <= m_udpPortEnd; m_localUdpPort++ ) { m_udpSocket = SDLNet_UDP_Open( m_localUdpPort ); if ( m_udpSocket ) break; } ASSERT( m_udpSocket, "ClientConnectionTCPUDP::ClientConnectionTCPUDP: could not create UDP socket\n" ); DBG( 2 ) INFO( "ClientConnectionTCPUDP::ClientConnectionTCPUDP: opened UDP socket at port %d\n", m_localUdpPort ); m_udpSocketSet = SDLNet_AllocSocketSet( 1 ); ASSERT( m_udpSocketSet, "ClientConnectionTCPUDP::ClientConnectionTCPUDP: 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, "ClientConnectionTCPUDP::ClientConnectionTCPUDP: could not allocate event packet\n" ); EchoMessage echoMessage; m_echoPacket = SDLNet_AllocPacket( echoMessage.getSerializeBufferSize() ); ASSERT( m_echoPacket, "ClientConnectionTCPUDP::ClientConnectionTCPUDP: could not allocate echo packet\n" ); SyncProgressMessage syncMessage; m_syncPacket = SDLNet_AllocPacket( syncMessage.getSerializeBufferSize() ); ASSERT( m_echoPacket, "ClientConnectionTCPUDP::ClientConnectionTCPUDP: could not allocate sync packet\n" ); } ClientConnectionTCPUDP::~ClientConnectionTCPUDP() { LOG( 2 ) INFO( "ClientConnectionTCPUDP::~ClientConnectionTCPUDP: cleaning up\n" ); // delete all remaining messages if ( m_eventMessage ) delete m_eventMessage; for ( std::deque::iterator it = m_messageQueue.begin(); it != m_messageQueue.end(); it++ ) delete *it; for ( std::deque::iterator it = m_echoMessageBuffer.begin(); it != m_echoMessageBuffer.end(); it++ ) delete *it; if ( m_tcpSocket ) SDLNet_TCP_Close( m_tcpSocket ); SDLNet_FreeSocketSet( m_tcpSocketSet ); if ( m_udpSocket ) SDLNet_UDP_Close( m_udpSocket ); SDLNet_FreeSocketSet( m_udpSocketSet ); if ( m_eventPacket ) SDLNet_FreePacket( m_eventPacket ); if ( m_echoPacket ) SDLNet_FreePacket( m_echoPacket ); if ( m_syncPacket ) SDLNet_FreePacket( m_syncPacket ); } void ClientConnectionTCPUDP::openConnection( Uint8 clientID ) { LOG( 2 ) INFO( "ClientConnectionTCPUDP::openConnection: establishing connection\n" ); Message* message = 0; int result = TCPConnection::recvMessage( m_tcpSocket, message ); CHECK( !result, "ClientConnectionTCPUDP::openConnection: connection lost\n" ); CHECK( message && message->type == Message::CONNECT, "ClientConnectionTCPUDP::openConnection: expected CONNECT message\n" ); ConnectMessage* connectMessage = static_cast( message ); m_id = clientID; m_udpAddress = *SDLNet_TCP_GetPeerAddress( m_tcpSocket ); SDLNet_Write16( connectMessage->udpPort, &m_udpAddress.port ); ConnectMessage connectMessage2; connectMessage2.udpPort = m_localUdpPort; connectMessage2.clientID = clientID; TCPConnection::sendMessage( m_tcpSocket, &connectMessage2 ); m_lastReceivedFrame = 0; m_lag = 0; m_clientLag = 0; delete message; } void ClientConnectionTCPUDP::closeConnection() { LOG( 2 ) INFO( "ClientConnectionTCPUDP::closeConnection: closing connection\n" ); if ( m_tcpSocket ) { // Send quit message to the server QuitMessage quitMessage; TCPConnection::sendMessage( m_tcpSocket, &quitMessage ); // Remove socket from socket set SDLNet_TCP_DelSocket( m_tcpSocketSet, m_tcpSocket ); // Close socket SDLNet_TCP_Close( m_tcpSocket ); m_tcpSocket = 0; } } bool ClientConnectionTCPUDP::checkUdpConnection() { LOG( 3 ) INFO( "ClientConnectionTCPUDP: checking for working UDP connection\n" ); RequestMessage requestMessage( Message::REQUEST ); 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( "ClientConnectionTCPUDP: sending UDP packets to client\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( "ClientConnectionTCPUDP: 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( "ClientConnectionTCPUDP: received UDP answer from client\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; bool packetArrived = false; TCPConnection::recvMessage( m_tcpSocket, message ); if ( message ) { if ( message->type != Message::REQUEST ) { LOG( 4 ) INFO( "ServerConnectionTCPUDP::checkUdpConnection: protocol error\n" ); } else { RequestMessage* answerMessage = static_cast( message ); if ( answerMessage->request == Message::REQUEST ) { LOG( 4 ) INFO( "ClientConnectionTCPUDP: client received our UDP packet\n" ); packetArrived = true; } else { LOG( 4 ) INFO( "ClientConnectionTCPUDP: client did not receive our UDP packet\n" ); } } } // clean up delete message; SDLNet_FreePacket( packet ); return answerReceived && packetArrived; } EventMessage* ClientConnectionTCPUDP::getEventMessage() { while ( recvEventMessage() ) ; // TODO: multithreaded access would require synchronisation EventMessage* message = m_eventMessage; m_eventMessage = 0; // increase lag if we have no event message to present if ( !message ) { // disconnect client if lag is too large if ( ++m_lag > m_MAX_SERVER_LAG ) { closeConnection(); } } else { m_lag = 0; // check client's last received echo if ( message->lastReceivedFrame == m_lastReceivedFrame ) { m_clientLag++; DBG( 5 ) INFO( "ClientConnectionTCPUDP::getEventMessage: client still on " "echo %d, client lag is now %d\n", m_lastReceivedFrame, m_clientLag ); // if client's lag is too large, resend messages if ( m_clientLag > m_MAX_CLIENT_LAG ) { EchoMessage* echoMessage = m_echoMessageBuffer.front(); // serialize and send m_echoPacket->len = echoMessage->getSerializeBufferSize(); if ( m_echoPacket->len > m_echoPacket->maxlen ) { LOG( 4 ) INFO( "ClientConnectionTCPUDP::getEventMessage: echo message too large\n" ); // TODO: what to do else here? } Uint8* bufferPointer = m_echoPacket->data; echoMessage->serialize( bufferPointer ); m_echoPacket->address = m_udpAddress; SDLNet_UDP_Send( m_udpSocket, -1, m_echoPacket ); } } else { m_lastReceivedFrame = message->lastReceivedFrame; m_clientLag = 0; // throw out all definitely received echos from the buffer if ( m_echoMessageBuffer.size() > 0 ) { EchoMessage* echoMessage = m_echoMessageBuffer.front(); while( echoMessage->frame <= m_lastReceivedFrame ) { m_echoMessageBuffer.pop_front(); delete echoMessage; if ( m_echoMessageBuffer.size() > 0 ) echoMessage = m_echoMessageBuffer.front(); else break; } } } } return message; } Message* ClientConnectionTCPUDP::getMessage() { while ( recvMessage() ) ; if ( m_messageQueue.size() > 0 ) { Message* message = m_messageQueue.front(); m_messageQueue.pop_front(); return message; } return 0; } void ClientConnectionTCPUDP::sendMessage( Message* message ) { if ( m_tcpSocket ) TCPConnection::sendMessage( m_tcpSocket, message, m_compress, m_listener ); } void ClientConnectionTCPUDP::sendEchoMessage( Message* message ) { if ( message->type == Message::ECHO ) { EchoMessage* echoMessage = static_cast( message ); // store a copy of the message in buffer in case resending is necessary EchoMessage* messageCopy = NEW EchoMessage( *echoMessage ); m_echoMessageBuffer.push_back( messageCopy ); // serialize and send m_echoPacket->len = echoMessage->getSerializeBufferSize(); if ( m_echoPacket->len > m_echoPacket->maxlen ) { LOG( 4 ) INFO( "ClientConnectionTCPUDP::sendEchoMessage: echo message too large\n" ); // TODO: what else to do here? } Uint8* bufferPointer = m_echoPacket->data; echoMessage->serialize( bufferPointer ); m_echoPacket->address = m_udpAddress; SDLNet_UDP_Send( m_udpSocket, -1, m_echoPacket ); DBG( 5 ) { INFO( "ClientConnectionTCPUDP::sendEchoMessage: sent UDP message of size %d\n", m_echoPacket->len ); INFO( "\tContent:\n" ); for ( int i = 0; i < m_echoPacket->len; i++ ) { INFO( "\t\t%x\n", m_echoPacket->data[i] ); } } } else if ( message->type == Message::SYNC_PROGRESS ) { m_syncPacket->len = message->getSerializeBufferSize(); if ( m_syncPacket->len > m_syncPacket->maxlen ) { LOG( 4 ) INFO( "ClientConnectionTCPUDP::sendEchoMessage: sync message too large\n" ); } Uint8* bufferPointer = m_syncPacket->data; message->serialize( bufferPointer ); m_syncPacket->address = m_udpAddress; SDLNet_UDP_Send( m_udpSocket, -1, m_syncPacket ); } else { ASSERT( false, "ClientConnectionTCPUDP::sendEchoMessage: can only send ECHO and " "SYNC_PROGRESS messages\n" ); } } ClientConnectionTCP* ClientConnectionTCPUDP::createClientConnectionTCP() { ClientConnectionTCP* connection = NEW ClientConnectionTCP( m_tcpSocket ); m_tcpSocket = 0; // TODO: copy messages ? return connection; } bool ClientConnectionTCPUDP::recvMessage() { bool messageReceived = false; if ( SDLNet_CheckSockets( m_tcpSocketSet, 0 ) > 0 ) { Message* message = 0; int result = TCPConnection::recvMessage( m_tcpSocket, message, m_listener ); if ( result == -1 ) { // the connection has been lost // Remove socket from socket set SDLNet_TCP_DelSocket( m_tcpSocketSet, m_tcpSocket ); // Close socket SDLNet_TCP_Close( m_tcpSocket ); m_tcpSocket = 0; } else { if ( message ) { if ( message->type == Message::EVENT ) { LOG( 4 ) INFO( "ClientConnectionTCPUDP::recvMessage: received EVENT message over TCP\n" ); // TODO: multithreaded access would require synchronisation! if ( m_eventMessage ) delete m_eventMessage; m_eventMessage = static_cast( message ); } else m_messageQueue.push_back( message ); messageReceived = true; } } } return messageReceived; } bool ClientConnectionTCPUDP::recvEventMessage() { bool messageReceived = false; if ( SDLNet_CheckSockets( m_udpSocketSet, 0 ) > 0 ) { if ( SDLNet_UDP_Recv( m_udpSocket, m_eventPacket ) == 1 ) { /* ASSERT( m_eventPacket->address == m_udpAddress, "ClientConnectionTCPUDP::recvMessage: event packet address does not match\n" );*/ Uint8* bufferPointer = m_eventPacket->data; Message headerMessage; headerMessage.deserialize( bufferPointer ); if ( headerMessage.type != Message::EVENT ) { LOG( 4 ) INFO( "ClientConnectionTCPUDP::recvMessage: got a UDP " "message not of type EVENT (size = %d)\n", m_eventPacket->len ); } else { EventMessage* eventMessage = NEW EventMessage( headerMessage ); eventMessage->deserializeData( bufferPointer ); if ( m_eventMessage ) delete m_eventMessage; m_eventMessage = eventMessage; messageReceived = true; } } } return messageReceived; }