/* 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: Silvan Nellen co-programmer: Benjamin Wuest */ #define DEBUG_MODULE_NETWORK #include "shared_network_data.h" #include "network_stream.h" #include "netdefs.h" #include "state.h" #include #include "synchronizeable.h" /** * default constructor */ Synchronizeable::Synchronizeable() { this->setClassID(CL_SYNCHRONIZEABLE, "Synchronizeable"); this->owner = -1; this->hostID = SharedNetworkData::getInstance()->getHostID(); this->setIsServer(this->hostID == 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()); } this->registerVar( new SynchronizeableString( &this->objectName, &this->objectName, "objectName" ) ); } /** * default destructor deletes all unneded stuff */ Synchronizeable::~Synchronizeable() { if ( this->networkStream ) this->networkStream->disconnectSynchronizeable(*this); } /** * Sets the server flag to a given value * @param isServer: the boolean value which the server flag is to set to */ void Synchronizeable::setIsServer(bool isServer) { if( isServer ) this->state = this->state | STATE_SERVER; else this->state = this->state & (~STATE_SERVER); } /** * Determines if the server flag is set * @return true, if the server flag is true, false else */ bool Synchronizeable::isServer() { return (this->state & STATE_SERVER) >0; } /** * get the diff to last acked state of userId * * each synchrinizeable defines its own stack of states received and sent over the network. The stack contains * a per user entry for the last sent/received state This function returns a delta compressed state of the * synchronizeable. This state will be transmitted over the network to the other participants * * @param userId user to create diff for * @param data buffer to copy diff in * @param maxLength max bytes to copy into data * @param stateId id of current state * @param fromStateId the reference state for the delta state * @param priorityTH tells getStateDiff to not send element with priority \< priorityTH * @return n bytes copied into data */ int Synchronizeable::getStateDiff( int userId, byte* data, int maxLength, int stateId, int fromStateId, int priorityTH ) { //make sure this user has his history if ( sentStates.size() <= userId ) sentStates.resize( userId+1 ); //calculate needed memory int neededSize = 0; for ( SyncVarList::iterator it = syncVarList.begin(); it != syncVarList.end(); it++ ) neededSize += (*it)->getSize(); assert( neededSize <= maxLength ); //remove older states from history than fromStateId StateHistory::iterator it = sentStates[userId].begin(); while ( it != sentStates[userId].end() && (*it)->stateId < fromStateId ) it++; ///FIXED: altered begin() -> end() if ( it != sentStates[userId].end() ) { for ( StateHistory::iterator it2 = sentStates[userId].begin(); it2 != it; it2++ ) { if ( (*it2)->data != NULL ) { delete (*it2)->data; (*it2)->data = NULL; } } 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; } else stateFrom = (*it); StateHistoryEntry * stateTo = new StateHistoryEntry(); stateTo->stateId = stateId; stateTo->dataLength = neededSize; stateTo->data = (byte*)malloc( neededSize ); std::list::iterator sizeIter = stateFrom->sizeList.begin(); int i = 0; int n; // now do the actual synchronization: kick all variables to write into a common buffer for ( SyncVarList::iterator it = syncVarList.begin(); it != syncVarList.end(); it++ ) { if ( (*it)->getPriority() >= priorityTH || sizeIter == stateFrom->sizeList.end() ) { n = (*it)->writeToBuf( stateTo->data+i, stateTo->dataLength - i ); stateTo->sizeList.push_back( n ); i += n; } else { for ( int j = 0; j<(*sizeIter); j++ ) { assert( i < stateFrom->dataLength ); stateTo->data[i] = stateFrom->data[i]; i++; } stateTo->sizeList.push_back( (*sizeIter) ); } if ( sizeIter != stateFrom->sizeList.end() ) sizeIter++; } sentStates[userId].push_back( stateTo ); //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 * @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 true on success */ bool Synchronizeable::setStateDiff( int userId, byte* data, int length, int stateId, int fromStateId ) { //make sure this user has his history if ( recvStates.size() <= userId ) recvStates.resize( userId+1 ); //create new state StateHistoryEntry * stateTo = new StateHistoryEntry(); stateTo->stateId = stateId; stateTo->dataLength = length; stateTo->data = (byte*)malloc( length ); //remove old states StateHistory::iterator it = recvStates[userId].begin(); 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 ); } //find state to apply diff to StateHistoryEntry * stateFrom = NULL; it = recvStates[userId].begin(); while ( it != recvStates[userId].end() && (*it)->stateId != fromStateId ) it++; if ( it == recvStates[userId].end() ) { StateHistoryEntry * initialEntry = new StateHistoryEntry(); initialEntry->stateId = fromStateId; initialEntry->dataLength = 0; initialEntry->data = NULL; stateFrom = initialEntry; } 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; for ( SyncVarList::iterator it = syncVarList.begin(); it != syncVarList.end(); it++ ) { i += (*it)->readFromBuf( stateTo->data + i, stateTo->dataLength - i ); } assert( i == length -1 ); return length; } /** * 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 ); return syncVarList.size()-1; }