/* $Id: servercommunicator.cpp,v 1.8.4.1 2006/03/09 14:59:42 chfreund Exp $ */ // #include "tcpconnection.hpp" #include "servercommunicator.hpp" // #include "clientcommunicator.hpp" #include "clientconnection.hpp" #include "clientconnectionlistener.hpp" #include "clientconnectiondirect.hpp" #include "gamereplay.hpp" #include "wopsettings.hpp" ServerCommunicator::ServerCommunicator( World* world, GameReplay* replay ) : m_directConnection( 0 ), m_world( world ), m_frameSequenceCounter( 0 ), m_replay( replay ) { // initialize vector with unused client ids // m_freeClientIDs.reserve(256); for ( Uint8 i = 0; i < 255; i++ ) { m_freeClientIDs.push_back( i ); } // add TCP listener ClientConnectionListenerTCP* listener = new ClientConnectionListenerTCP( this, 0xb8b8 ); m_listeners.push_back( listener ); } ServerCommunicator::~ServerCommunicator() { // delete all listeners for ( unsigned int i = 0; i < m_listeners.size(); i++ ) { ClientConnectionListener* listener = m_listeners[i]; delete listener; } // delete all connections for ( std::vector::iterator it = m_idleConnections.begin(); it != m_idleConnections.end(); it++ ) delete *it; for ( std::vector::iterator it = m_activeConnections.begin(); it != m_activeConnections.end(); it++ ) delete *it; /* if ( m_directConnection ) delete m_directConnection;*/ } void ServerCommunicator::listenForClients() { for ( std::vector::iterator it = m_listeners.begin(); it != m_listeners.end(); it++ ) { while ( ClientConnection* connection = (*it)->getNewConnection( m_freeClientIDs.front() ) ) { LOG( 2 ) INFO( "ServerCommunicator::listenForClients: adding new connection\n" ); addConnection( connection ); // connection->openConnection( clientID ); } } } void ServerCommunicator::collectEvents() { DBG( 5 ) INFO( "ServerCommunicator::collectEvents: collecting events\n" ); std::vector playerVector = m_world->getPlayerList(); // Collect game events from all active connected clients for ( unsigned int c = 0; c < m_activeConnections.size(); c++ ) { ClientConnection* connection = m_activeConnections[c]; EventMessage* eventMessage = connection->getEventMessage(); if ( eventMessage ) { // Copy the events for all players in this client in the global // echo message for ( Uint8 p = 0; p < connection->getNumberConnectedPlayers(); p++ ) { // get global index for player Uint8 index = m_world->getPlayerIndex( connection->getGlobalPlayerNumber( p ) ); if ( index >= m_world->getNumberPlayers() ) { DBG( 2 ) INFO( "ServerCommunicator::collectEvents: player with ID %d not found in player vector -> ignoring event\n", connection->getGlobalPlayerNumber( p ) ); continue; } // store event at the right position if ( p < eventMessage->numberEvents ) { m_echoMessage.event[index] = eventMessage->event[p]; // check for replay bookmark if ( m_replay ) { static int bookmarkDeadTime = 0; if ( bookmarkDeadTime > 0 ) bookmarkDeadTime--; else if ( eventMessage->event[p].testAnyActions( Event::INSERT_REPLAY_BOOKMARK ) ) { LOG( 1 ) INFO( "ServerCommunicator::collectEvents: inserting replay bookmark\n" ); m_replay->addBookmark(); bookmarkDeadTime = 25 * 5; // 5 seconds bookmark dead time } } } else { m_echoMessage.event[index] = Event::EMPTY; } } delete eventMessage; } else { DBG( 2 ) INFO( "ServerCommunicator::collectEvents: no event message " " from connection %d, lag is now %d\n", c, connection->getLag() ); for ( Uint8 p = 0; p < connection->getNumberConnectedPlayers(); p++ ) { Uint8 index = m_world->getPlayerIndex( connection->getGlobalPlayerNumber( p ) ); if ( index >= m_world->getNumberPlayers() ) { DBG( 2 ) INFO( "ServerCommunicator::collectEvents: player with ID %d not found in player vector -> setting event nonetheless to N_A\n", connection->getGlobalPlayerNumber( p ) ); continue; } m_echoMessage.event[index] = Event::N_A; } } } } void ServerCommunicator::broadcastEcho() { m_echoMessage.frame = m_world->getFrame(); m_echoMessage.randomCounter = m_world->getRandom().getCounter(); m_echoMessage.numberMessages = m_frameSequenceCounter; DBG( 5 ) INFO( "ServerCommunicator::broadcastEcho: bc echo for frame %d (random = %d, messages = %d)\n", m_echoMessage.frame, m_echoMessage.randomCounter, m_echoMessage.numberMessages ); for ( unsigned int c = 0; c < m_activeConnections.size(); c++ ) { ClientConnection* connection = m_activeConnections[c]; connection->sendEchoMessage( &m_echoMessage ); } } void ServerCommunicator::handleMessages() { Message* message; m_frameSequenceCounter = 0; for ( unsigned int c = 0; c < m_activeConnections.size(); c++ ) { ClientConnection* connection = m_activeConnections[c]; while ( ( message = connection->getMessage() ) ) { // Handle the message properly switch ( message->type ) { case Message::ADD_PLAYER: { DBG( 2 ) INFO( "ServerCommunicator::handleMessages: handling ADD_PLAYER message\n" ); AddPlayerMessage* addPlayerMessage = static_cast( message ); Player* player = addPlayerMessage->player; Uint8 playerID = m_world->getNextPlayerID(); addPlayerMessage->globalPlayerNumber = playerID; player->setPlayerID( playerID ); connection->setGlobalPlayerNumber( connection->getNumberConnectedPlayers(), playerID ); // TODO: Can we assume that the new player is added to the end of the player vector? Uint8 index = m_world->getNumberPlayers(); DBG( 2 ) INFO( "Setting action for player %d to %x\n", index, Event::EMPTY ); m_echoMessage.event[index].setAction( Event::EMPTY ); connection->addPlayer(); // store event in replay if ( m_replay ) m_replay->addPlayerEnter( *player ); m_world->addPlayer( player ); addPlayerMessage->frame = m_world->getFrame(); addPlayerMessage->frameSequenceIndex = m_frameSequenceCounter++; broadcastMessage( addPlayerMessage ); break; } case Message::CHAT: { DBG( 2 ) INFO( "ServerCommunicator::handleMessages: handling CHAT message\n" ); ChatMessage* chatMessage = static_cast( message ); m_frameSequenceCounter++; // store message in replay if ( m_replay ) m_replay->addChatMessage( chatMessage->message ); broadcastMessage( chatMessage ); break; } case Message::QUIT: { DBG( 2 ) INFO( "ServerCommunicator::handleMessages: handling QUIT message\n" ); // disabled to avoid compiler warning, Tom //QuitMessage* quitMessage = // static_cast( message ); connection->closeConnection(); break; } default: ASSERT( false, "ServerCommunicator::handleMessages: don't know how to handle message of type %d\n", message->type ); break; } delete message; } } // Handle messages from idle connections for ( unsigned int c = 0; c < m_idleConnections.size(); c++ ) { ClientConnection* connection = m_idleConnections[c]; while ( ( message = connection->getMessage() ) ) { DBG( 2 ) INFO( "ServerCommunicator::handleMessages: got message from idle connection %d\n", c ); if ( message->type != Message::REQUEST ) { LOG( 2 ) INFO( "ServerCommunicator::handleMessages: can only handle REQUEST messages from idle clients\n" ); delete message; continue; } RequestMessage* requestMessage = static_cast( message ); if ( requestMessage->request == Message::WORLD ) { WorldMessage* worldMessage = NEW WorldMessage(); worldMessage->world = m_world; DBG( 2 ) INFO( "ServerCommunicator::handleMessages: send world to the client\n" ); connection->setCompression( true ); connection->setProgressListener( this ); connection->sendMessage( worldMessage ); connection->setCompression( false ); connection->setProgressListener( 0 ); delete worldMessage; // change client state from 'idle' to 'active' m_idleConnections.erase( m_idleConnections.begin() + c ); c--; m_activeConnections.push_back( connection ); } else if ( requestMessage->request == Message::INFO ) { WopSettings* settings = WopSettings::getInstance(); InfoMessage infoMessage; infoMessage.theme = settings->getTheme(); infoMessage.gamemode = settings->getGameMode(); infoMessage.numberPlayers = m_world->getNumberPlayers(); for ( int p = 0; p < m_world->getNumberPlayers(); p++ ) { infoMessage.player.push_back( m_world->getPlayer( p ) ); } connection->sendMessage( &infoMessage ); } else { LOG( 2 ) INFO( "ServerCommunicator::handleMessages: cannot handle request of type %d\n", requestMessage->request ); } delete requestMessage; } } // Remove disconnected clients for ( unsigned int c = 0; c < m_activeConnections.size(); c++ ) { ClientConnection* connection = m_activeConnections[c]; if ( !connection->isConnected() ) { DBG( 2 ) INFO( "ServerCommunicator::handleMessages: removing active client %d\n", c ); cleanupConnection( connection ); // recover client ID Uint8 clientID = connection->getId(); m_freeClientIDs.push_back( clientID ); delete connection; m_activeConnections.erase( m_activeConnections.begin() + c ); c--; } } for ( unsigned int c = 0; c < m_idleConnections.size(); c++ ) { ClientConnection* connection = m_idleConnections[c]; if ( !connection->isConnected() ) { DBG( 2 ) INFO( "ServerCommunicator::handleMessages: removing idle client %d\n", c ); // cleanupConnection( connection ); // recover client ID Uint8 clientID = connection->getId(); m_freeClientIDs.push_back( clientID ); delete connection; m_idleConnections.erase( m_idleConnections.begin() + c ); c--; } } } Uint8 ServerCommunicator::addConnection( ClientConnection* connection ) { INFO( "ServerCommunicator::addConnection: client is connecting\n" ); Uint8 newClientID = m_freeClientIDs.front(); m_freeClientIDs.pop_front(); m_idleConnections.push_back( connection ); return newClientID; } ClientConnectionDirect* ServerCommunicator::getDirectConnection ( ServerConnectionDirect* serverConnection ) { // INFO( "ServerCommunicator::getDirectConnection: local client is connecting\n" ); ClientConnectionDirect* clientConnection = new ClientConnectionDirect( this, serverConnection ); // m_connections.push_back( clientConnection ); return clientConnection; } void ServerCommunicator::broadcastMessage( Message* message ) { for ( std::vector::iterator it = m_activeConnections.begin(); it != m_activeConnections.end(); it++ ) { ClientConnection* connection = *it; connection->sendMessage( message ); } } void ServerCommunicator::cleanupConnection( ClientConnection* connection ) { // Remove all players from this client for ( Uint8 p = 0; p < connection->getNumberConnectedPlayers(); p++ ) { Uint8 globalPlayerID = connection->getGlobalPlayerNumber( p ); // fetch player Player* player = m_world->getPlayerByID( globalPlayerID ); // store event in replay if ( m_replay ) m_replay->addPlayerQuit( globalPlayerID ); // remove player from world m_world->removePlayer( globalPlayerID ); // delete player object delete player; // inform all clients RemovePlayerMessage message; message.playerNumber = globalPlayerID; message.frame = m_world->getFrame(); message.frameSequenceIndex = m_frameSequenceCounter++; broadcastMessage( &message ); } } void ServerCommunicator::sendProgress( Sint32 size, Sint32 sent ) { // inform all active clients about sync progress SyncProgressMessage message; message.messageSize = size; message.dataSent = sent; for ( unsigned int c = 0; c < m_activeConnections.size(); c++ ) { ClientConnection* connection = m_activeConnections[c]; connection->sendEchoMessage( &message ); } } void ServerCommunicator::recvProgress( Sint32 size, Sint32 received ) { }