/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx 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 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Wuest co-programmer: ... */ /* this is for debug output. It just says, that all calls to PRINT() belong to the DEBUG_MODULE_NETWORK module For more information refere to https://www.orxonox.net/cgi-bin/trac.cgi/wiki/DebugOutput */ #define DEBUG_MODULE_NETWORK #include "factory.h" #include "network_stream.h" #include "converter.h" #include "p_node.h" #include "state.h" #include "game_world.h" #include "world_entity.h" #include "playable.h" #include "player.h" #include "network_manager.h" /* include your own header */ #include "network_game_manager.h" /* using namespace std is default, this needs to be here */ using namespace std; NetworkGameManager* NetworkGameManager::singletonRef = NULL; /*! * Standard constructor */ NetworkGameManager::NetworkGameManager() : Synchronizeable() { PRINTF(0)("START\n"); /* set the class id for the base object */ this->setClassID(CL_NETWORK_GAME_MANAGER, "NetworkGameManager"); newUniqueID = MAX_CONNECTIONS + 2; hasRequestedWorld = false; this->setSynchronized(true); } /*! * Standard destructor */ NetworkGameManager::~NetworkGameManager() { for ( int i = 0; i0 ) { int nbytes = outBuffer[i].length; outBuffer[i].length = 0; if ( nbytes > maxLength ) { PRINTF(1)("OutBuffer.length (%d) > (%d) networkStreamBuffer.maxLength\n", nbytes, maxLength); return 0; } memcpy(data, outBuffer[i].buffer, nbytes); return nbytes; } } return 0; } void NetworkGameManager::writeDebug() const { } void NetworkGameManager::readDebug() const { } /*! * Checks whether this is connected to a server or a client * and afterwards creates the needed entity * @param classID: The ID of the class of which an entity should be created */ int NetworkGameManager::createEntity( ClassID classID, int owner ) { if ( this->isServer() ) { if ( newUniqueID < 0 ) { PRINTF(1)("Cannot create entity! There are no more uniqueIDs left!\n"); return -1; } return this->executeCreateEntity( classID, newUniqueID++, owner ); } else { this->requestCreateEntity( classID ); return -1; } } /*! * Checks whether this is connected to a server or a client * and afterwards creates the needed entity * @param classID: The ID of the class of which an entity should be created */ BaseObject* NetworkGameManager::createEntity(const TiXmlElement* element) { if ( this->isServer() ) { if ( newUniqueID < 0 ) { PRINTF(1)("Cannot create entity! There are no more uniqueIDs left!\n"); return NULL; } BaseObject * b = Factory::fabricate( element ); if ( !b ) { PRINTF(1)("Could not fabricate Object with className %s\n", element->Value() ); return NULL; } if ( b->isA(CL_SYNCHRONIZEABLE) ) { Synchronizeable * s = dynamic_cast(b); s->setUniqueID( newUniqueID++ ); s->setOwner( 0 ); // all entities created via this function are added automaticaly to the synchronizeable list s->setSynchronized(true); return b; } else { PRINTF(1)("Class %s is not a synchronizeable!\n", b->getClassName() ); delete b; } } else { PRINTF(1)("This node is not a server and cannot create id %x\n", element->Value()); } return NULL; } /*! * Checks whether this is connected to a server or a client * and afterwards removes the specified entity * @param uniqueID: The ID of the entity object which should be removed */ void NetworkGameManager::removeEntity(int uniqueID) { if ( this->isServer() ) { this->executeRemoveEntity( uniqueID ); } else { this->requestRemoveEntity( uniqueID ); } } /*! * Creates the needed entity on the server if possible * @param classID: The ID of the class of which an entity should be created */ void NetworkGameManager::requestCreateEntity(ClassID classID) { for ( int i = 0; inetworkStream->isUserIdActive( i ) ) continue; if ( !writeToClientBuffer( outBuffer[i], (byte)NET_REQUEST_CREATE ) ) return; if ( !writeToClientBuffer( outBuffer[i], (int)classID ) ) return; } } /*! * Removes the specified entity on the server * @param uniqueID: The ID of the entity object which should be removed */ void NetworkGameManager::requestRemoveEntity(int uniqueID) { for ( int i = 0; inetworkStream->isUserIdActive( i ) ) continue; if ( !writeToClientBuffer( outBuffer[i], (byte)NET_REQUEST_REMOVE ) ) return; if ( !writeToClientBuffer( outBuffer[i], uniqueID ) ) return; } } /*! * Creates the needed entity if possible * This function is called if this is a server * @param classID: The ID of the class of which an entity should be created */ int NetworkGameManager::executeCreateEntity(ClassID classID, int uniqueID, int owner) { for ( int i = 0; inetworkStream->isUserIdActive( i ) ) continue; if ( !writeToClientBuffer( outBuffer[i], (byte)NET_CREATE_ENTITY ) ) return -1; if ( !writeToClientBuffer( outBuffer[i], (int)classID ) ) return -1; if ( !writeToClientBuffer( outBuffer[i], uniqueID ) ) return -1; if ( !writeToClientBuffer( outBuffer[i], owner ) ) return -1; } doCreateEntity( classID, uniqueID, owner ); return uniqueID; } /*! * Removes the specified entity * This function is called if this is a server * @param uniqueID: The ID of the entity object which should be removed */ void NetworkGameManager::executeRemoveEntity(int uniqueID) { for ( int i = 0; inetworkStream->isUserIdActive( i ) ) continue; if ( !writeToClientBuffer( outBuffer[i], (byte)NET_REMOVE_ENTITY ) ) return; if ( !writeToClientBuffer( outBuffer[i], uniqueID ) ) return; } doRemoveEntity(uniqueID); } /*! * Checks whether it is possible to create an entity of a given class * @return: true if the entity can be created, false otherwise */ bool NetworkGameManager::canCreateEntity(ClassID classID) { return true; } /*! * Sends the Entities to the new connected client * @param userID: The ID of the user */ void NetworkGameManager::sendEntityList( int userID ) { if ( !isServer() ) return; if ( userID >= outBuffer.size() ) resizeBufferVector( userID ); SynchronizeableList::const_iterator it, e; it = this->networkStream->getSyncBegin(); e = this->networkStream->getSyncEnd(); if ( !writeToClientBuffer( outBuffer[userID], (byte)NET_CREATE_ENTITY_LIST ) ) return; // -2 because you must not send network_game_manager and handshake if ( !writeToClientBuffer( outBuffer[userID], networkStream->getSyncCount() ) ) return; //PRINTF(0)("SendEntityList: n = %d\n", networkStream->getSyncCount()-2 ); int n = 0; while ( it != e ) { if( (*it)->beSynchronized()) { PRINTF(0)("SENDING ENTITY %s id %d\n", (*it)->getClassName(), (*it)->getUniqueID() ); if ( !writeToClientBuffer( outBuffer[userID], (int)((*it)->getLeafClassID()) ) ) return; if ( !writeToClientBuffer( outBuffer[userID], (int)((*it)->getUniqueID()) ) ) return; if ( !writeToClientBuffer( outBuffer[userID], (int)((*it)->getOwner()) ) ) return; } it++; } signalNewPlayer( userID ); } bool NetworkGameManager::signalNewPlayer(int userId) { if ( userId >= outBuffer.size() ) resizeBufferVector( userId ); /* create new playable for Player*/ PRINTF(0)("Request for creation: %i\n", userId); int uniqueId = this->createEntity(CL_SPACE_SHIP, userId); PRINTF(0)("Request for creation: userid: %i, uniqueid: %i\n", userId, uniqueId); this->sendYouAre(uniqueId, userId); } /** * Creates a buffer for user n * @param n The ID of the user */ void NetworkGameManager::resizeBufferVector( int n ) { for ( int i = outBuffer.size(); i<=n; i++) { clientBuffer outBuf; outBuf.length = 0; outBuf.maxLength = 5*1024; outBuf.buffer = new byte[5*1014]; outBuffer.push_back(outBuf); } } /** * Creates the entity on this host * @param classID: ClassID of the entity to create * @param uniqueID: Unique ID to assign to the synchronizeable * @param owner: owner of this synchronizealbe */ BaseObject* NetworkGameManager::doCreateEntity( ClassID classID, int uniqueID, int owner ) { BaseObject * b = Factory::fabricate( classID ); if ( !b ) { PRINTF(1)("Could not fabricate Object with classID %x\n", classID); return NULL; } if ( b->isA(CL_SYNCHRONIZEABLE) ) { Synchronizeable * s = dynamic_cast(b); s->setUniqueID( uniqueID ); s->setOwner( owner ); s->setSynchronized(true); //this->networkStream->connectSynchronizeable( *s ); if ( !isServer() ) s->setIsOutOfSync( true ); PRINTF(0)("Fabricated %s with id %d\n", s->getClassName(), s->getUniqueID()); //HACK: hack to prevent collision if ( b->isA(CL_WORLD_ENTITY) && !b->isA(CL_PLAYABLE) ) { if ( NetworkManager::getInstance()->getHostID()!=0 ) { static Vector pos = Vector(1000.0, 1000.0, 1000.0); PNode *p = dynamic_cast(b); p->setAbsCoor(pos); //p->updateNode(0); pos += Vector(1000.0, 1000.0, 1000.0); } } return b; } else { PRINTF(1)("Class with ID %x is not a synchronizeable!", (int)classID); delete b; } return NULL; } /** * Removes a entity on this host * @param uniqueID: unique ID assigned with the entity to remove */ void NetworkGameManager::doRemoveEntity( int uniqueID ) { SynchronizeableList::const_iterator it,e; it = this->networkStream->getSyncBegin(); e = this->networkStream->getSyncEnd(); while ( it != e ) { if ( (*it)->getUniqueID() == uniqueID ) { delete *it; break; } it++; } } /** * Tell the synchronizeable that a user's synchronizeable is out of sync * @param uniqueID: unique ID assigned with the entity which is out of sync * @param userID: user ID who's synchronizeable is out of sync */ void NetworkGameManager::doRequestSync( int uniqueID, int userID ) { SynchronizeableList::const_iterator it,e; it = this->networkStream->getSyncBegin(); e = this->networkStream->getSyncEnd(); while ( it != e ) { if ( (*it)->getUniqueID() == uniqueID ) { (*it)->requestSync( userID ); break; } it++; } } /** * Copies length bytes to the clientBuffer with error checking * @param clientBuffer: the clientBuffer to write to * @param data: buffer to the data * @param length: length of data * @return false on error true else */ bool NetworkGameManager::writeToClientBuffer( clientBuffer &cb, byte * data, int length ) { if ( length > cb.maxLength-cb.length ) { PRINTF(1)("No space left in clientBuffer\n"); return false; } memcpy( cb.buffer+cb.length, data, length ); return true; } /** * Reads data from clientBuffer with error checking * @param clientBuffer: the clientBuffer to read from * @param data: pointer to the buffer * @param length: * @return */ bool NetworkGameManager::readFromClientBuffer( clientBuffer &cb, byte * data, int length ) { if ( cb.length < length ) { PRINTF(0)("There is not enough data in clientBuffer\n"); return 0; } memcpy( data, cb.buffer+cb.length-length, length ); return true; } /** * Tells this client that he has to control this entity * @param uniqueID: the entity's uniqeID */ void NetworkGameManager::doYouAre( int uniqueID ) { SynchronizeableList::const_iterator it = this->networkStream->getSyncBegin(); Playable *p = NULL; Synchronizeable *s = NULL; for ( ; it !=networkStream->getSyncEnd(); it++ ) { if ( (*it)->getUniqueID()==uniqueID ) { if ( (*it)->isA( CL_SYNCHRONIZEABLE ) ) { s = dynamic_cast(*it); } if ( (*it)->isA( CL_PLAYABLE ) ) { p = dynamic_cast(*it); break; } else { PRINTF(1)("UniqueID is not a Playable\n"); } } } Player* player = State::getPlayer(); assert(p != NULL); assert(s != NULL); assert(player != NULL); s->setIsOutOfSync( true ); PRINTF(0)("uniqueID = %d\n", s->getUniqueID()); player->setControllable(p); } /** * Tells a remote client that he has to control this entity * @param uniqueID: the entity's uniqeID * @param userID: the users ID */ void NetworkGameManager::sendYouAre( int uniqueID, int userID ) { if ( !isServer() ) return; if ( userID != 0 ) { if ( !writeToClientBuffer( outBuffer[userID], (byte)NET_YOU_ARE_ENTITY ) ) return; if ( !writeToClientBuffer( outBuffer[userID], uniqueID ) ) return; } else { doYouAre(uniqueID); } } bool NetworkGameManager::handleRequestCreate( int & i, const byte * data, int length, int sender ) { if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read classID from buffer! Not enough data left!\n"); return false; } int classID; i += Converter::byteArrayToInt( &data[i], &classID ); createEntity( (ClassID)classID ); return true; } bool NetworkGameManager::handleRequestRemove( int & i, const byte * data, int length, int sender ) { if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read uniqueID from buffer! Not enough data left!\n"); return false; } int uniqueID; i += Converter::byteArrayToInt( &data[i], &uniqueID ); removeEntity( uniqueID ); return true; } bool NetworkGameManager::handleCreateEntity( int & i, const byte * data, int length, int sender ) { if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read classID from buffer! Not enough data left!\n"); return false; } int classID; i += Converter::byteArrayToInt( &data[i], &classID ); if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read uniqueID from buffer! Not enough data left!\n"); return false; } int uniqueID; i += Converter::byteArrayToInt( &data[i], &uniqueID ); if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read owner from buffer! Not enough data left!\n"); return false; } int owner; i += Converter::byteArrayToInt( &data[i], &owner ); doCreateEntity( (ClassID)classID, uniqueID, owner ); return true; } bool NetworkGameManager::handleRemoveEntity( int & i, const byte * data, int length, int sender ) { if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read uniqueID from buffer! Not enough data left!\n"); return false; } int uniqueID; i += Converter::byteArrayToInt( &data[i], &uniqueID ); doRemoveEntity( uniqueID ); return true; } bool NetworkGameManager::handleCreateEntityList( int & i, const byte * data, int length, int sender ) { if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read n from buffer! Not enough data left!\n"); return false; } PRINTF(0)("HandleCreateEntityList: data[i..i+3] = %d %d %d %d\n", data[i], data[i+1], data[i+2], data[i+3]); int n; i += Converter::byteArrayToInt( &data[i], &n ); PRINTF(0)("HandleCreateEntityList: n = %d\n", n); int classID, uniqueID, owner; for ( int j = 0; j length-i ) { PRINTF(1)("Cannot read classID from buffer! Not enough data left!\n"); return false; } i += Converter::byteArrayToInt( &data[i], &classID ); if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read uniqueID from buffer! Not enough data left!\n"); return false; } i += Converter::byteArrayToInt( &data[i], &uniqueID ); if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read owner from buffer! Not enough data left!\n"); return false; } i += Converter::byteArrayToInt( &data[i], &owner ); if ( classID != CL_NETWORK_GAME_MANAGER && classID != CL_HANDSHAKE ) { BaseObject* b = doCreateEntity( (ClassID)classID, uniqueID, owner ); /*if ( b != NULL ) { if ( b->isA(CL_WORLD_ENTITY) ) { int n = dynamic_cast(b)->writeState( data, length, sender ); i += n; } }*/ } } return true; } bool NetworkGameManager::handleRemoveEntityList( int & i, const byte * data, int length, int sender ) { if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read n from buffer! Not enough data left!\n"); return false; } int n; i += Converter::byteArrayToInt( &data[i], &n ); int uniqueID; for ( int j = 0; j length-i ) { PRINTF(1)("Cannot read uniqueID from buffer! Not enough data left!\n"); return false; } i += Converter::byteArrayToInt( &data[i], &uniqueID ); doRemoveEntity( uniqueID ); } return true; } bool NetworkGameManager::handleYouAreEntity( int & i, const byte * data, int length, int sender ) { if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read uniqueID from buffer! Not enough data left!\n"); return false; } int uniqueID; i += Converter::byteArrayToInt( &data[i], &uniqueID ); doYouAre( uniqueID ); return true; } bool NetworkGameManager::handleRequestSync( int & i, const byte * data, int length, int sender ) { if ( INTSIZE > length-i ) { PRINTF(1)("Cannot read uniqueID from buffer! Not enough data left!\n"); return false; } int uniqueID; i += Converter::byteArrayToInt( &data[i], &uniqueID ); doRequestSync( uniqueID, sender ); return true; } /** * handles the network signal NET_REQUEST_PNODE_PATH * @param i byte offset in the buffer * @param data data array * @param length length of the data arary * @param sender the sender id * @return true if process terminated sucessfully */ bool NetworkGameManager::handleRequestPNodePath(int& i, const byte* data, int length, int sender) { if( INTSIZE > length-i ) { PRINTF(1)("Cannot read n from buffer! Not enough data left!\n"); return false; } PRINTF(0)("HandleRequestPNodePath: data[i..i+3] = %d %d %d %d\n", data[i], data[i+1], data[i+2], data[i+3]); int uniqueID1, uniqueID2; if( INTSIZE > length-i ) { PRINTF(1)("Cannot read uniqueID from buffer! Not enough data left!\n"); return false; } i += Converter::byteArrayToInt( &data[i], &uniqueID1 ); if( INTSIZE > length-i ) { PRINTF(1)("Cannot read uniqueID from buffer! Not enough data left!\n"); return false; } i += Converter::byteArrayToInt( &data[i], &uniqueID2 ); PRINTF(0)("HandleRequestPNodePath: got a request for path from uid %i to uid %i\n", uniqueID1, uniqueID2); return true; } bool NetworkGameManager::writeToClientBuffer( clientBuffer & cb, byte b ) { if ( cb.maxLength-cb.length < 1 ) { PRINTF(1)("Cannot write to clientBuffer! Not enough space for 1 byte\n"); return false; } cb.buffer[cb.length++] = b; return true; } bool NetworkGameManager::writeToClientBuffer( clientBuffer & cb, int i ) { int n = Converter::intToByteArray( i, cb.buffer+cb.length, cb.maxLength-cb.length ); cb.length += n; if ( n <= 0 ) { PRINTF(1)("Cannot write to clientBuffer! Not enough space for 1 int\n"); return false; } return true; } void NetworkGameManager::sync( int uniqueID, int owner ) { /*if ( owner==this->getHostID() ) return;*/ if ( !isServer() ) executeRequestSync( uniqueID, 0 ); else executeRequestSync( uniqueID, owner ); } void NetworkGameManager::executeRequestSync( int uniqueID, int user ) { if ( user >= outBuffer.size() ) resizeBufferVector( user ); if ( !writeToClientBuffer( outBuffer[user], (byte)NET_REQUEST_SYNC ) ) return; if ( !writeToClientBuffer( outBuffer[user], uniqueID ) ) return; }