Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/cpp11_v2/src/libraries/network/synchronisable/Synchronisable.cc @ 10918

Last change on this file since 10918 was 10916, checked in by landauf, 10 years ago

use actual types instead of 'auto'. only exception is for complicated template types, e.g. when iterating over a map

  • Property svn:eol-style set to native
File size: 16.5 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Dumeni Manatschal, (C) 2007
24 *      Oliver Scheuss, (C) 2007
25 *   Co-authors:
26 *      ...
27 *
28 */
29
30
31#include "Synchronisable.h"
32
33#include <cstdlib>
34#include "core/CoreIncludes.h"
35#include "core/GameMode.h"
36#include "core/BaseObject.h"
37#include "network/Host.h"
38
39namespace orxonox
40{
41
42  std::map<uint32_t, Synchronisable *> Synchronisable::objectMap_;
43  std::queue<uint32_t> Synchronisable::deletedObjects_;
44
45  uint8_t Synchronisable::state_=0x1; // detemines wheter we are server (default) or client
46
47  RegisterAbstractClass(Synchronisable).inheritsFrom<OrxonoxInterface>();
48
49  /**
50  * Constructor:
51  * Initializes all Variables and sets the right objectID_
52  */
53  Synchronisable::Synchronisable(Context* context)
54  {
55      RegisterObject(Synchronisable);
56    static uint32_t idCounter=0;
57    objectMode_=0x1; // by default do not send data to server
58    if ( GameMode::isMaster()/* || ( Host::running() && Host::isServer() )*/ )
59    {
60      this->setObjectID( idCounter++ );
61    }
62    else
63    {
64      objectID_=OBJECTID_UNKNOWN;
65    }
66    classID_ = static_cast<uint32_t>(-1);
67
68    // set dataSize to 0
69    this->dataSize_ = 0;
70    // set standard priority
71    this->setPriority( Priority::Normal );
72
73    // get context id
74    this->contextID_ = this->findContextID(context);
75  }
76
77  /**
78   * Destructor:
79   * Delete all callback objects and remove objectID_ from the objectMap_
80   */
81  Synchronisable::~Synchronisable()
82  {
83    // delete callback function objects
84    if(!IdentifierManager::getInstance().isCreatingHierarchy()){
85      // remove object from the static objectMap
86      if (this->objectMode_ != 0x0 && (Host::running() && Host::isServer()))
87        deletedObjects_.push(objectID_);
88    }
89    // delete all Synchronisable Variables from syncList_ ( which are also in stringList_ )
90    for(SynchronisableVariableBase* variable : syncList_)
91      delete variable;
92    syncList_.clear();
93    stringList_.clear();
94    std::map<uint32_t, Synchronisable*>::iterator it2;
95    it2 = objectMap_.find(objectID_);
96    if (it2 != objectMap_.end())
97      objectMap_.erase(it2);
98
99  }
100
101  /**
102   * @brief Returns the id of the context.
103   * If the context is not Synchronisable, it moves on to its parent, recursively.
104   */
105  uint32_t Synchronisable::findContextID(Context* context)
106  {
107      if (context == nullptr)
108          return OBJECTID_UNKNOWN;
109
110      Synchronisable* synchronisableContext = orxonox_cast<Synchronisable*>(context);
111      if (synchronisableContext != nullptr)
112          return synchronisableContext->getObjectID();
113      else
114          return this->findContextID(context->getParentContext());
115  }
116
117  /**
118   * This function sets the internal mode for synchronisation
119   * @param b true if this object is located on a client or on a server
120   */
121  void Synchronisable::setClient(bool b)
122  {
123    if(b) // client
124      state_=0x2;
125    else  // server
126      state_=0x1;
127  }
128
129  /**
130   * This function fabricated a new synchrnisable (and children of it), sets calls updateData and create
131   * After calling this function the mem pointer will be increased by the size of the needed data
132   * @param mem pointer to where the appropriate data is located
133   * @param mode defines the mode, how the data should be loaded
134   * @return pointer to the newly created synchronisable
135   */
136  Synchronisable *Synchronisable::fabricate(uint8_t*& mem, uint8_t mode)
137  {
138    SynchronisableHeader header(mem);
139    if( header.isDiffed() )
140    {
141      mem += header.getDataSize() + header.getSize();
142      return nullptr;
143    }
144//     assert( !header.isDiffed() );
145
146    orxout(verbose, context::network) << "fabricating object with id: " << header.getObjectID() << endl;
147
148    Identifier* id = ClassByID(header.getClassID());
149    if (!id)
150    {
151        for(int i = 0; i<160; i++)
152            orxout(user_error, context::network) << "classid: " << i << " identifier: " << ClassByID(i) << endl;
153        orxout(user_error, context::network) << "Assertion failed: Could not find Identifier for ClassID " << header.getClassID() << endl;
154        orxout(user_error, context::network) << "Possible reason for this error: Client received a synchronizable object whose class has no factory." << endl;
155        abort();
156    }
157    assert(id);
158    Context* context = nullptr;
159    if (header.getContextID() != OBJECTID_UNKNOWN)
160    {
161      Synchronisable* synchronisable_context = Synchronisable::getSynchronisable(header.getContextID());
162      if (!synchronisable_context)
163      {
164        mem += header.getDataSize()+SynchronisableHeader::getSize(); //.TODO: this suckz.... remove size from header
165        assert(0); // TODO: uncomment this if we have a clean objecthierarchy (with destruction of children of objects) ^^
166        return nullptr;
167      }
168      else
169        context = orxonox_cast<Context*>(synchronisable_context);
170    }
171    else
172      context = Context::getRootContext();
173
174    assert(getSynchronisable(header.getObjectID())==nullptr);   //make sure no object with this id exists
175    BaseObject *bo = orxonox_cast<BaseObject*>(id->fabricate(context));
176    assert(bo);
177    Synchronisable *no = orxonox_cast<Synchronisable*>(bo);
178    assert(no);
179    assert( Synchronisable::objectMap_.find(header.getObjectID()) == Synchronisable::objectMap_.end() );
180    no->setObjectID(header.getObjectID());
181    //no->contextID=header.getContextID(); //TODO: remove this
182    no->setClassID(header.getClassID());
183    assert(no->contextID_ == header.getContextID());
184    //assert(no->classID_ == header.getClassID());
185    orxout(verbose, context::network) << "fabricate objectID_: " << no->objectID_ << " classID_: " << no->classID_ << endl;
186          // update data and create object/entity...
187    bool b = no->updateData(mem, mode, true);
188    assert(b);
189    if (b)
190    {
191//        b = no->create();
192        assert(b);
193    }
194    return no;
195  }
196
197
198  /**
199   * Finds and deletes the Synchronisable with the appropriate objectID_
200   * @param objectID_ objectID_ of the Synchronisable
201   * @return true/false
202   */
203  bool Synchronisable::deleteObject(uint32_t objectID_)
204  {
205    if(!getSynchronisable(objectID_))
206      return false;
207    assert(getSynchronisable(objectID_)->objectID_==objectID_);
208    Synchronisable *s = getSynchronisable(objectID_);
209    if(s)
210      s->destroy(); // or delete?
211    else
212      return false;
213    return true;
214  }
215
216  /**
217   * This function looks up the objectID_ in the objectMap_ and returns a pointer to the right Synchronisable
218   * @param objectID_ objectID_ of the Synchronisable
219   * @return pointer to the Synchronisable with the objectID_
220   */
221  Synchronisable* Synchronisable::getSynchronisable(uint32_t objectID_)
222  {
223    std::map<uint32_t, Synchronisable*>::iterator it1;
224    it1 = objectMap_.find(objectID_);
225    if (it1 != objectMap_.end())
226      return it1->second;
227    // if the objects not in the map it should'nt exist at all anymore
228    return nullptr;
229  }
230
231
232  /**
233   * This function takes all SynchronisableVariables out of the Synchronisable and saves them together with the size, objectID_ and classID_ to the given memory
234   * takes a pointer to already allocated memory (must have at least getSize bytes length)
235   * structure of the bitstream:
236   * |totalsize,objectID_,classID_,var1,var2,string1_length,string1,var3,...|
237   * length of varx: size saved int syncvarlist
238   * @param mem pointer to allocated memory with enough size
239   * @param sizes vector containing sizes of all objects in gamestate (to be appended)
240   * @param id gamestateid of the gamestate to be saved (important for priorities)
241   * @param mode defines the direction in which the data will be send/received
242   *             0x1: server->client
243   *             0x2: client->server
244   *             0x3: bidirectional
245   * @return true: if !doSync or if everything was successfully saved
246   */
247  uint32_t Synchronisable::getData(uint8_t*& mem, std::vector<uint32_t>& sizes, int32_t id, uint8_t mode)
248  {
249    unsigned int test = 0;
250    if(mode==0x0)
251      mode=state_;
252    //if this tick is we dont synchronise, then abort now
253    if(!doSync(/*id,*/ mode))
254      return 0;
255    uint32_t tempsize = 0;
256#ifndef NDEBUG
257    uint8_t* oldmem = mem;
258    if (this->classID_==0)
259      orxout(internal_info, context::network) << "classid 0 " << this->getIdentifier()->getName() << endl;
260#endif
261
262    if (this->classID_ == static_cast<uint32_t>(-1))
263        this->classID_ = this->getIdentifier()->getNetworkID();
264
265    assert(ClassByID(this->classID_));
266    assert(this->classID_==this->getIdentifier()->getNetworkID());
267    assert(this->objectID_!=OBJECTID_UNKNOWN);
268    std::vector<SynchronisableVariableBase*>::iterator i;
269
270    // start copy header
271    SynchronisableHeader header(mem);
272    mem += SynchronisableHeader::getSize();
273    // end copy header
274
275    orxout(verbose_more, context::network) << "getting data from objectID_: " << objectID_ << ", classID_: " << classID_ << endl;
276//     orxout(verbose, context::network) << "objectid: " << this->objectID_ << ":";
277    // copy to location
278    for(i=syncList_.begin(); i!=syncList_.end(); ++i)
279    {
280      uint32_t varsize = (*i)->getData( mem, mode );
281//       orxout(verbose, context::network) << " " << varsize;
282      tempsize += varsize;
283      sizes.push_back(varsize);
284      ++test;
285      //tempsize += (*i)->getSize( mode );
286    }
287    assert(tempsize!=0);  // if this happens an empty object (with no variables) would be transmitted
288//     orxout(verbose, context::network) << endl;
289
290    header.setObjectID( this->objectID_ );
291    header.setContextID( this->contextID_ );
292    header.setClassID( this->classID_ );
293    header.setDataSize( tempsize );
294    assert( tempsize == mem-oldmem-SynchronisableHeader::getSize() );
295    assert( test == this->getNrOfVariables() );
296    header.setDiffed(false);
297    tempsize += SynchronisableHeader::getSize();
298
299#ifndef NDEBUG
300    uint32_t size;
301    size=getSize(id, mode);
302    assert(tempsize==size);
303#endif
304    return tempsize;
305  }
306
307
308  /**
309   * This function takes a bytestream and loads the data into the registered variables
310   * @param mem pointer to the bytestream
311   * @param mode same as in getData
312   * @param forceCallback this makes updateData call each callback
313   * @return true/false
314   */
315  bool Synchronisable::updateData(uint8_t*& mem, uint8_t mode, bool forceCallback)
316  {
317    if(mode==0x0)
318      mode=state_;
319   
320    if(syncList_.empty())
321    {
322      orxout(internal_warning, context::network) << "Synchronisable::updateData syncList_ is empty" << endl;
323      assert(0);
324      return false;
325    }
326
327    uint8_t* data=mem;
328    // start extract header
329    SynchronisableHeaderLight syncHeaderLight(mem);
330    assert(syncHeaderLight.getObjectID()==this->getObjectID());
331   
332    if( !this->doReceive(mode) )
333    {
334      uint32_t headerSize;
335      if( syncHeaderLight.isDiffed() )
336        headerSize = SynchronisableHeaderLight::getSize();
337      else
338        headerSize = SynchronisableHeader::getSize();
339      mem += syncHeaderLight.getDataSize() + headerSize;
340      return true;
341    }
342
343    //orxout(verbose_more, context::network) << "Synchronisable: objectID_ " << syncHeader.getObjectID() << ", classID_ " << syncHeader.getClassID() << " size: " << syncHeader.getDataSize() << " synchronising data" << endl;
344    if( !syncHeaderLight.isDiffed() )
345    {
346      SynchronisableHeader syncHeader2(mem);
347      assert( this->getClassID() == syncHeader2.getClassID() );
348      assert( this->getContextID() == syncHeader2.getContextID() );
349      mem += SynchronisableHeader::getSize();
350      std::vector<SynchronisableVariableBase *>::iterator i;
351      for(i=syncList_.begin(); i!=syncList_.end(); ++i)
352      {
353        assert( mem <= data+syncHeader2.getDataSize()+SynchronisableHeader::getSize() ); // always make sure we don't exceed the datasize in our stream
354        (*i)->putData( mem, mode, forceCallback );
355      }
356      assert(mem == data+syncHeaderLight.getDataSize()+SynchronisableHeader::getSize() );
357    }
358    else
359    {
360      mem += SynchronisableHeaderLight::getSize();
361//       orxout(debug_output, context::network) << "objectID: " << this->objectID_ << endl;
362      while( mem < data+syncHeaderLight.getDataSize()+SynchronisableHeaderLight::getSize() )
363      {
364        VariableID varID = *(VariableID*)mem;
365//         orxout(debug_output, context::network) << "varID: " << varID << endl;
366        assert( varID < syncList_.size() );
367        mem += sizeof(VariableID);
368        syncList_[varID]->putData( mem, mode, forceCallback );
369      }
370      assert(mem == data+syncHeaderLight.getDataSize()+SynchronisableHeaderLight::getSize() );
371    }
372    return true;
373  }
374
375  /**
376  * This function returns the total amount of bytes needed by getData to save the whole content of the variables
377  * @param id id of the gamestate
378  * @param mode same as getData
379  * @return amount of bytes
380  */
381  uint32_t Synchronisable::getSize(int32_t id, uint8_t mode)
382  {
383    uint32_t tsize=SynchronisableHeader::getSize();
384    if (mode==0x0)
385      mode=state_;
386    if (!doSync(/*id, */mode))
387      return 0;
388    assert( mode==state_ );
389    tsize += this->dataSize_;
390    std::vector<SynchronisableVariableBase*>::iterator i;
391    for(i=stringList_.begin(); i!=stringList_.end(); ++i)
392    {
393      tsize += (*i)->getSize( mode );
394    }
395    return tsize;
396  }
397
398  /**
399   * This function determines, wheter the object should be saved to the bytestream (according to its syncmode/direction)
400   * @param mode Synchronisation mode (toclient, toserver or bidirectional)
401   * @return true/false
402   */
403  bool Synchronisable::doSync(/*int32_t id,*/ uint8_t mode)
404  {
405//     if(mode==0x0)
406//       mode=state_;
407    assert(mode!=0x0);
408    return ( (this->objectMode_ & mode)!=0 && (!syncList_.empty() ) );
409  }
410 
411  /**
412   * This function determines, wheter the object should accept data from the bytestream (according to its syncmode/direction)
413   * @param mode Synchronisation mode (toclient, toserver or bidirectional)
414   * @return true/false
415   */
416  bool Synchronisable::doReceive( uint8_t mode)
417  {
418    //return mode != this->objectMode_ || this->objectMode_ == ObjectDirection::Bidirectional;
419    return true;
420  }
421
422  /**
423   * This function sets the synchronisation mode of the object
424   * If set to 0x0 variables will not be synchronised at all
425   * If set to 0x1 variables will only be synchronised to the client
426   * If set to 0x2 variables will only be synchronised to the server
427   * If set to 0x3 variables will be synchronised bidirectionally (only if set so in registerVar)
428   * @param mode same as in registerVar
429   */
430  void Synchronisable::setSyncMode(uint8_t mode)
431  {
432    assert(mode==0x0 || mode==0x1 || mode==0x2 || mode==0x3);
433    this->objectMode_=mode;
434  }
435
436  template <> void Synchronisable::registerVariable( std::string& variable, uint8_t mode, NetworkCallbackBase *cb, bool bidirectional)
437  {
438    SynchronisableVariableBase* sv;
439    if (bidirectional)
440      sv = new SynchronisableVariableBidirectional<std::string>(variable, mode, cb);
441    else
442      sv = new SynchronisableVariable<std::string>(variable, mode, cb);
443    syncList_.push_back(sv);
444    stringList_.push_back(sv);
445  }
446
447template <> void Synchronisable::unregisterVariable( std::string& variable )
448  {
449    bool unregistered_nonexistent_variable = true;
450    std::vector<SynchronisableVariableBase*>::iterator it = syncList_.begin();
451    while(it!=syncList_.end())
452    {
453      if( ((*it)->getReference()) == &variable )
454      {
455        delete (*it);
456        syncList_.erase(it);
457        unregistered_nonexistent_variable = false;
458        break;
459      }
460      else
461        ++it;
462    }
463    assert(unregistered_nonexistent_variable == false);
464   
465    it = stringList_.begin();
466    while(it!=stringList_.end())
467    {
468      if( ((*it)->getReference()) == &variable )
469      {
470        delete (*it);
471        stringList_.erase(it);
472        return;
473      }
474      else
475        ++it;
476    }
477    unregistered_nonexistent_variable = true;
478    assert(unregistered_nonexistent_variable == false); //if we reach this point something went wrong:
479    // the variable has not been registered before
480  }
481
482
483}
Note: See TracBrowser for help on using the repository browser.