/* 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: Christoph Renner (rennerc@ee.ethz.ch) co-programmer: Patrick Boenzli (patrick@orxonox.ethz.ch) */ #define DEBUG_MODULE_NETWORK #include "shared_network_data.h" #include "network_stream.h" #include "netdefs.h" #include "network_log.h" #include "network_game_manager.h" #include "state.h" #include #include "synchronizeable.h" #include "converter.h" ObjectListDefinition(Synchronizeable); /** * default constructor */ Synchronizeable::Synchronizeable() { this->registerObject(this, Synchronizeable::_objectList); this->owner = 0; // this->setIsServer(SharedNetworkData::getInstance()->getHostID() == 0); this->uniqueID = NET_UID_UNASSIGNED; this->networkStream = NULL; this->bSynchronize = false; if( State::isOnline()) { NetworkStream* nd = SharedNetworkData::getInstance()->getDefaultSyncStream(); assert(nd != NULL); nd->connectSynchronizeable(*this); this->setUniqueID(SharedNetworkData::getInstance()->getNewUniqueID()); } /* make sure loadClassId is first synced var because this is read by networkStream */ assert( syncVarList.size() == 0 ); mLeafClassId = this->registerVarId( new SynchronizeableInt( (int*)&this->getLeafClassID(), (int*)&this->getLeafClassID(), "leafClassId", PERMISSION_MASTER_SERVER) ); this->registerVar( new SynchronizeableInt( &this->owner, &this->owner, "owner", PERMISSION_MASTER_SERVER ) ); this->registerVar( new SynchronizeableString( &this->objectName, &this->objectName, "objectName", PERMISSION_MASTER_SERVER ) ); } /** * default destructor deletes all unneded stuff */ Synchronizeable::~Synchronizeable() { if ( this->networkStream ) { this->networkStream->disconnectSynchronizeable(*this); // remove the message manager only by the server if ( (SharedNetworkData::getInstance()->isMasterServer() ) && this->beSynchronized() && this->getUniqueID() > 0 && !this->isA( MessageManager::staticClassID() ) ) NetworkGameManager::getInstance()->removeSynchronizeable( this->getUniqueID() ); } for ( SyncVarList::iterator it = syncVarList.begin(); it != syncVarList.end(); it++ ) { delete *it; } syncVarList.clear(); for ( UserStateHistory::iterator it = recvStates.begin(); it != recvStates.end(); it++ ) { for ( StateHistory::iterator it2 = it->begin(); it2 != it->end(); it2++ ) { if ( (*it2)->data ) { delete [] (*it2)->data; (*it2)->data = NULL; } delete *it2; } } for ( UserStateHistory::iterator it = sentStates.begin(); it != sentStates.end(); it++ ) { for ( StateHistory::iterator it2 = it->begin(); it2 != it->end(); it2++ ) { if ( (*it2)->data ) { delete [] (*it2)->data; (*it2)->data = NULL; } delete *it2; } } } /** * creates a diff image from two states * @param userId: the userid of the user where the image will be sent to * @param data: the binary data array to write to * @param maxLength: maximal length of the data written (length of available space in the array) * @param stateId: the state id that this diff will represent * @param priorityTH: the priority threshold: all syncs below this threshold won't be synchronized * * @todo check for permissions */ int Synchronizeable::getStateDiff( int userId, byte* data, int maxLength, int stateId, int fromStateId, int priorityTH ) { // make sure this user has his history or resize for new clients if ( (int)sentStates.size() <= userId ) sentStates.resize( userId+1 ); //calculate needed memory int neededSize = 0; // calculate the needed space for network packet by summing up for ( SyncVarList::iterator it = syncVarList.begin(); it != syncVarList.end(); it++ ) { //PRINTF(0)("SIZE = %d %s\n", (*it)->getSize(), (*it)->getName().c_str()); neededSize += (*it)->getSize(); } if ( !( neededSize <= maxLength ) ) { PRINTF(0)( "%d > %d\n", neededSize, maxLength ); assert(false); } //remove older states from history than fromStateId StateHistory::iterator it = sentStates[userId].begin(); while ( it != sentStates[userId].end() && (*it)->stateId < fromStateId ) it++; if ( it != sentStates[userId].begin() ) { for ( StateHistory::iterator it2 = sentStates[userId].begin(); it2 != it; it2++ ) { if ( (*it2)->data != NULL ) { delete [] (*it2)->data; (*it2)->data = NULL; } delete *it2; } sentStates[userId].erase( sentStates[userId].begin(), it ); } //find state to create diff from StateHistoryEntry * stateFrom = NULL; it = sentStates[userId].begin(); while ( it != sentStates[userId].end() && (*it)->stateId != fromStateId ) it++; if ( it == sentStates[userId].end() ) { StateHistoryEntry * initialEntry = new StateHistoryEntry(); initialEntry->stateId = fromStateId; initialEntry->dataLength = 0; initialEntry->data = NULL; stateFrom = initialEntry; sentStates[userId].push_back( stateFrom ); } else stateFrom = (*it); StateHistoryEntry * stateTo = new StateHistoryEntry; sentStates[userId].push_back( stateTo ); stateTo->stateId = stateId; stateTo->dataLength = neededSize; stateTo->data = new byte[ neededSize ]; std::list::iterator sizeIter = stateFrom->sizeList.begin(); int i = 0; int n; bool sizeChanged = false; // now do the actual synchronization: kick all variables to write into a common buffer for ( SyncVarList::iterator it = syncVarList.begin(); it != syncVarList.end(); it++ ) { //////////////////////////////// // Data SENDING Permissions //////////////////////////////// bool hasPermission = false; bool b1, b2, b3, b4, b5, b6, b7, b8, b9; b1 = b2 = b3 = b4 = b5 = b6 = b7 = b8 = b9 = false; // Permission OWNER accept if: // I am the owner if( (*it)->checkPermission( PERMISSION_OWNER ) && this->owner == SharedNetworkData::getInstance()->getHostID()) { hasPermission = true; b1 = true; } // reciever != owner && owner is local else if( (*it)->checkPermission( PERMISSION_OWNER ) && userId != this->owner && (SharedNetworkData::getInstance()->isUserLocal(this->owner) || this->owner == SharedNetworkData::getInstance()->getHostID())) { hasPermission = true; b2 = true; } // Permission MASTER_SERVER accept if: // im MASTER_SERVER else if( (*it)->checkPermission( PERMISSION_MASTER_SERVER ) && SharedNetworkData::getInstance()->isMasterServer()) { hasPermission = true; b3 = true; } // im PROXY_SERVER && reciever == CLIENT else if( (*it)->checkPermission( PERMISSION_MASTER_SERVER ) && SharedNetworkData::getInstance()->isProxyServerActive() && SharedNetworkData::getInstance()->isUserClient( userId)) { hasPermission = true; b4 = true; } // Pemission SERVER accept if: // i am server && reciever == CLIENT else if( (*it)->checkPermission( PERMISSION_SERVER ) && !SharedNetworkData::getInstance()->isClient() && SharedNetworkData::getInstance()->isUserClient( userId)) { hasPermission = true; b5 = true; } // i am SERVER && reciever == SERVER && reciever != owner && ( owner is local || i am owner) else if( (*it)->checkPermission( PERMISSION_SERVER ) && !SharedNetworkData::getInstance()->isClient() && userId != this->owner && ( SharedNetworkData::getInstance()->isUserLocal( this->owner) || this->owner == SharedNetworkData::getInstance()->getHostID())) { hasPermission = true; b6 = true; } // Permission ALL accept if: else if( (*it)->checkPermission( PERMISSION_ALL )) { hasPermission = true; b7 = true; } // or else refuse sending data else hasPermission = false; if ( sizeIter == stateFrom->sizeList.end() || *sizeIter != (*it)->getSize() ) sizeChanged = true; if ( ( hasPermission && (*it)->getPriority() >= priorityTH ) || sizeChanged ) { n = (*it)->writeToBuf( stateTo->data+i, stateTo->dataLength - i ); //NETPRINTF(0)("getvar %s %d\n", (*it)->getName().c_str(), n); // PRINTF(0)("sending %s %d\n", (*it)->getName().c_str(), n); // if( this->isA(CL_PLAYABLE)) // { // PRINTF(0)("ms: %i, ps: %i, c: %i, sender: %i, reciever: %i, owner: %i, perm: (ow %i, ms %i, s %i, a %i)\n", // SharedNetworkData::getInstance()->isMasterServer(), SharedNetworkData::getInstance()->isProxyServerActive(), SharedNetworkData::getInstance()->isClient(), // SharedNetworkData::getInstance()->getHostID(), userId, this->owner, // (*it)->checkPermission( PERMISSION_OWNER ), (*it)->checkPermission( PERMISSION_MASTER_SERVER ), // (*it)->checkPermission( PERMISSION_SERVER ), (*it)->checkPermission( PERMISSION_ALL )); // PRINTF(0)("hasPermission: %i, sizeChanged: %i, eval: %i, %i, %i, %i, %i, %i, %i\n", hasPermission, sizeChanged, b1, b2, b3, b4, b5, b6, b7); // PRINTF(0)("sending %s %s %d\n", this->getClassCName(), (*it)->getName().c_str(), n); // } stateTo->sizeList.push_back( n ); // this is only for very hardcore debug sessions // (*it)->debug(); i += n; } else { for ( int j = 0; j<(*sizeIter); j++ ) { assert( i < stateFrom->dataLength ); stateTo->data[i] = stateFrom->data[i]; i++; } //NETPRINTF(0)("getvar %s %d\n", (*it)->getName().c_str(), *sizeIter); stateTo->sizeList.push_back( (*sizeIter) ); } if ( sizeIter != stateFrom->sizeList.end() ) sizeIter++; } if ( i != neededSize ) { PRINTF(0)("strange error: (%s) %d != %d\n", this->getClassCName(), i, neededSize); assert(false); } //write diff to data for ( i = 0; idataLength ) data[i] = stateTo->data[i] - stateFrom->data[i]; else data[i] = stateTo->data[i]; } return neededSize; } /** * sets a new state out of a diff created on another host (recieving data) * @param userId hostId of user who send me that diff * @param data pointer to diff * @param length length of diff * @param stateId id of current state * @param fromStateId id of the base state id * @return number bytes read * * @todo check for permissions */ int Synchronizeable::setStateDiff( int userId, byte* data, int length, int stateId, int fromStateId ) { //make sure this user has his history if ( (int)recvStates.size() <= userId ) recvStates.resize( userId+1 ); //create new state StateHistoryEntry * stateTo = new StateHistoryEntry(); stateTo->stateId = stateId; stateTo->dataLength = length; stateTo->data = new byte[ length ]; //find state to apply diff to StateHistoryEntry * stateFrom = NULL; // search the state from wich the diff is made of StateHistory::iterator it = recvStates[userId].begin(); while ( it != recvStates[userId].end() && (*it)->stateId != fromStateId ) it++; // if this is the first state to receive if ( it == recvStates[userId].end() ) { StateHistoryEntry * initialEntry = new StateHistoryEntry(); initialEntry->stateId = fromStateId; initialEntry->dataLength = 0; initialEntry->data = NULL; stateFrom = initialEntry; recvStates[userId].push_back( stateFrom ); } else stateFrom = (*it); // apply diff for ( int i = 0; idataLength ) stateTo->data[i] = stateFrom->data[i] + data[i]; else stateTo->data[i] = data[i]; } //add state to state history recvStates[userId].push_back( stateTo ); int i = 0; int n = 0; std::list changes; // extract the new state for every client for ( SyncVarList::iterator it = syncVarList.begin(); it != syncVarList.end(); it++ ) { // DATA PERMISSIONS // check if this synchronizeable has the permissions to write the data bool hasPermission = false; bool b1, b2, b3, b4, b5, b6, b7, b8, b9; b1 = b2 = b3 = b4 = b5 = b6 = b7 = b8 = b9 = false; //////////////////////////////// // Data RECIEVING Permissions //////////////////////////////// // i should never ever receive a state update from a synchronizeable, that belongs to me! If it does somethings wrong with the send rules // assert( !((*it)->checkPermission( PERMISSION_OWNER ) && this->owner == SharedNetworkData::getInstance()->getHostID())); // Permission OWNER accept if: // sender == owner if( (*it)->checkPermission( PERMISSION_OWNER ) && this->owner == userId) { hasPermission = true; b1 = true; } // sender == MASTER_SERVER else if( (*it)->checkPermission( PERMISSION_OWNER ) && SharedNetworkData::getInstance()->isUserMasterServer( userId) && this->owner != SharedNetworkData::getInstance()->getHostID()) { hasPermission = true; b2 = true; } // sender == PROXY_SERVER else if( (*it)->checkPermission( PERMISSION_OWNER ) && SharedNetworkData::getInstance()->isUserProxyServerActive( userId) && this->owner != SharedNetworkData::getInstance()->getHostID()) { hasPermission = true; b3 = true; } // Permission MASTER_SERVER accept if: // sender == MASTER_SERVER else if( (*it)->checkPermission( PERMISSION_MASTER_SERVER) && SharedNetworkData::getInstance()->isUserMasterServer( userId)) { hasPermission = true; b4 = true; } // sender == PROXY_SERVER && im not MASTER_SERVER && im not PROXY_SERVER else if( (*it)->checkPermission( PERMISSION_MASTER_SERVER) && SharedNetworkData::getInstance()->isClient() && SharedNetworkData::getInstance()->isUserProxyServerActive( userId)) { hasPermission = true; b5 = true; } // Permission SERVER accept if: // sender == SERVER else if( (*it)->checkPermission( PERMISSION_SERVER ) && !SharedNetworkData::getInstance()->isUserClient( userId) /*&& SharedNetworkData::getInstance()->isClient()*/) { hasPermission = true; b6 = true; } // Pemission ALL accept if: else if( (*it)->checkPermission( PERMISSION_ALL )) { hasPermission = true; b8 = true; } // no rights to over-write local data else hasPermission = false; // if it has the permission to write do it if( hasPermission) { n = (*it)->readFromBuf( stateTo->data + i, stateTo->dataLength - i ); i += n; //NETPRINTF(0)("%s::setvar %s %d\n", getClassCName(), (*it)->getName().c_str(), n); // PRINTF(0)("recieving: %s %d\n", (*it)->getName().c_str(), n); //(*it)->debug(); // if( this->isA(CL_PLAYABLE)) // { // PRINTF(0)("ms: %i, ps: %i, c: %i, sender: %i, reciever: %i, owner: %i, perm: (ow %i, ms %i, s %i, a %i)\n", // SharedNetworkData::getInstance()->isMasterServer(), SharedNetworkData::getInstance()->isProxyServerActive(), SharedNetworkData::getInstance()->isClient(), // userId, SharedNetworkData::getInstance()->getHostID(), this->owner, // (*it)->checkPermission( PERMISSION_OWNER ), (*it)->checkPermission( PERMISSION_MASTER_SERVER ), // (*it)->checkPermission( PERMISSION_SERVER ), (*it)->checkPermission( PERMISSION_ALL )); // PRINTF(0)("hasPermission: %i, eval: %i, %i, %i, %i, %i, %i, %i, %i\n", hasPermission, b1, b2, b3, b4, b5, b6, b7, b8); // PRINTF(0)("rec %s %s %d\n", this->getClassCName(), (*it)->getName().c_str(), n); // } if ( (*it)->getHasChanged() ) { changes.push_back( (*it)->getVarId() ); } } else { // PRINTF(0)("DONT SET VAR BECAUSE OF PERMISSION: %s perm: %d %d %d - %d %d %d\n", (*it)->getName().c_str(), (*it)->checkPermission( PERMISSION_MASTER_SERVER ), (*it)->checkPermission( PERMISSION_OWNER ), (*it)->checkPermission( PERMISSION_ALL ), networkStream->isUserMasterServer( userId ), this->owner, userId ); n = (*it)->getSizeFromBuf( stateTo->data + i, stateTo->dataLength - i ); //NETPRINTF(0)("%s::setvar %s %d\n", getClassCName(), (*it)->getName().c_str(), n); //(*it)->debug(); i += n; } } this->varChangeHandler( changes ); return i; } /** * override this function to be notified on change * of your registred variables. * @param id id's which have changed */ void Synchronizeable::varChangeHandler( std::list & id ) { } /** * registers a varable to be synchronized over network * @param var see src/lib/network/synchronizeable_var/ for available classes */ void Synchronizeable::registerVar( SynchronizeableVar * var ) { syncVarList.push_back( var ); } /** * registers a varable to be synchronized over network * return value is passed to varChangeHandler on change * @param var see src/lib/network/synchronizeable_var/ for available classes * @return handle passed to varChangeHandler on changes */ int Synchronizeable::registerVarId( SynchronizeableVar * var ) { syncVarList.push_back( var ); var->setWatched( true ); var->setVarId( syncVarList.size()-1 ); return syncVarList.size()-1; } /** * removed user's states from memory * @param userId user to clean */ void Synchronizeable::cleanUpUser( int userId ) { if ( (int)recvStates.size() > userId ) { for ( std::list::iterator it = recvStates[userId].begin(); it != recvStates[userId].end(); it++ ) { if ( (*it)->data ) { delete [] (*it)->data; (*it)->data = NULL; } delete *it; } recvStates[userId].clear(); } if ( (int)sentStates.size() > userId ) { for ( std::list::iterator it = sentStates[userId].begin(); it != sentStates[userId].end(); it++ ) { if ( (*it)->data ) { delete [] (*it)->data; (*it)->data = NULL; } delete *it; } sentStates[userId].clear(); } } /** * this function is called after recieving a state. * @param userId * @param stateId * @param fromStateId */ void Synchronizeable::handleRecvState( int userId, int stateId, int fromStateId ) { //make sure this user has his history if ( (int)recvStates.size() <= userId ) recvStates.resize( userId+1 ); //remove old states StateHistory::iterator it = recvStates[userId].begin(); #if 0 while ( it != recvStates[userId].end() && (*it)->stateId < fromStateId ) it++; if ( it != recvStates[userId].begin() ) { for ( StateHistory::iterator it2 = recvStates[userId].begin(); it2 != it; it2++ ) { if ( (*it2)->data != NULL ) { delete [] (*it2)->data; (*it2)->data = NULL; } } recvStates[userId].erase( recvStates[userId].begin(), it ); } #endif for ( it = recvStates[userId].begin(); it != recvStates[userId].end(); ) { if ( (*it)->stateId < fromStateId ) { StateHistory::iterator delIt = it; it ++; if ( (*delIt)->data ) { delete [] (*delIt)->data; (*delIt)->data = NULL; } delete *delIt; recvStates[userId].erase( delIt ); continue; } it++; } StateHistory::iterator fromState = recvStates[userId].end(); StateHistory::iterator toState = recvStates[userId].end(); for ( it = recvStates[userId].begin(); it != recvStates[userId].end(); it++ ) { if ( (*it)->stateId == stateId ) toState = it; if ( (*it)->stateId == fromStateId ) fromState = it; if ( fromState != recvStates[userId].end() && toState != recvStates[userId].end() ) break; } // setStateDiff was not called and i know fromStateId if ( fromState != recvStates[userId].end() && toState == recvStates[userId].end() ) { StateHistoryEntry * entry = new StateHistoryEntry; entry->dataLength = (*fromState)->dataLength; if ( entry->dataLength > 0 ) { entry->data = new byte[entry->dataLength]; assert( (*fromState)->data ); memcpy( entry->data, (*fromState)->data, entry->dataLength ); } else entry->data = NULL; entry->sizeList = (*fromState)->sizeList; entry->stateId = stateId; recvStates[userId].push_back(entry); } } /** * this function is called after sending a state * @param userId * @param stateId * @param fromStateId */ void Synchronizeable::handleSentState( int userId, int stateId, int fromStateId ) { //make sure this user has his history if ( (int)sentStates.size() <= userId ) sentStates.resize( userId+1 ); //remove old states StateHistory::iterator it = sentStates[userId].begin(); for ( it = sentStates[userId].begin(); it != sentStates[userId].end(); ) { if ( (*it)->stateId < fromStateId ) { StateHistory::iterator delIt = it; it ++; if ( (*delIt)->data ) { delete [] (*delIt)->data; (*delIt)->data = NULL; } delete *delIt; sentStates[userId].erase( delIt ); continue; } it++; } StateHistory::iterator fromState = sentStates[userId].end(); StateHistory::iterator toState = sentStates[userId].end(); for ( it = sentStates[userId].begin(); it != sentStates[userId].end(); it++ ) { if ( (*it)->stateId == stateId ) toState = it; if ( (*it)->stateId == fromStateId ) fromState = it; if ( fromState != sentStates[userId].end() && toState != sentStates[userId].end() ) break; } // getStateDiff was not called and i know fromStateId if ( fromState != sentStates[userId].end() && toState == sentStates[userId].end() ) { StateHistoryEntry * entry = new StateHistoryEntry; entry->dataLength = (*fromState)->dataLength; if ( entry->dataLength > 0 ) { entry->data = new byte[entry->dataLength]; assert( (*fromState)->data ); memcpy( entry->data, (*fromState)->data, entry->dataLength ); } else entry->data = NULL; entry->sizeList = (*fromState)->sizeList; entry->stateId = stateId; sentStates[userId].push_back(entry); } }