Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core7/src/libraries/network/synchronisable/Synchronisable.cc @ 10362

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

use static identifier initializer to store the inheritance definition of abstract classes. this prevents that identifiers are used (via Class(Name)) before they are properly initialized.

  • Property svn:eol-style set to native
File size: 16.8 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(std::vector<SynchronisableVariableBase*>::iterator it = syncList_.begin(); it!=syncList_.end(); it++)
91      delete (*it);
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 == NULL)
108          return OBJECTID_UNKNOWN;
109
110      Synchronisable* synchronisableContext = orxonox_cast<Synchronisable*>(context);
111      if (synchronisableContext != NULL)
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 0;
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: id" << 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 = 0;
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 0;
167      }
168      else
169        context = orxonox_cast<Context*>(synchronisable_context);
170    }
171    else
172      context = Context::getRootContext();
173
174    assert(getSynchronisable(header.getObjectID())==0);   //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    if( context )
185    {
186      BaseObject* boContext = orxonox_cast<BaseObject*>(context);
187      if (boContext)
188          bo->setLevel(boContext->getLevel()); // Note: this ensures that the level is known on the client for child objects of the scene (and the scene itself)
189    }
190    //assert(no->classID_ == header.getClassID());
191    orxout(verbose, context::network) << "fabricate objectID_: " << no->objectID_ << " classID_: " << no->classID_ << endl;
192          // update data and create object/entity...
193    bool b = no->updateData(mem, mode, true);
194    assert(b);
195    if (b)
196    {
197//        b = no->create();
198        assert(b);
199    }
200    return no;
201  }
202
203
204  /**
205   * Finds and deletes the Synchronisable with the appropriate objectID_
206   * @param objectID_ objectID_ of the Synchronisable
207   * @return true/false
208   */
209  bool Synchronisable::deleteObject(uint32_t objectID_)
210  {
211    if(!getSynchronisable(objectID_))
212      return false;
213    assert(getSynchronisable(objectID_)->objectID_==objectID_);
214    Synchronisable *s = getSynchronisable(objectID_);
215    if(s)
216      s->destroy(); // or delete?
217    else
218      return false;
219    return true;
220  }
221
222  /**
223   * This function looks up the objectID_ in the objectMap_ and returns a pointer to the right Synchronisable
224   * @param objectID_ objectID_ of the Synchronisable
225   * @return pointer to the Synchronisable with the objectID_
226   */
227  Synchronisable* Synchronisable::getSynchronisable(uint32_t objectID_)
228  {
229    std::map<uint32_t, Synchronisable*>::iterator it1;
230    it1 = objectMap_.find(objectID_);
231    if (it1 != objectMap_.end())
232      return it1->second;
233    // if the objects not in the map it should'nt exist at all anymore
234    return NULL;
235  }
236
237
238  /**
239   * This function takes all SynchronisableVariables out of the Synchronisable and saves them together with the size, objectID_ and classID_ to the given memory
240   * takes a pointer to already allocated memory (must have at least getSize bytes length)
241   * structure of the bitstream:
242   * |totalsize,objectID_,classID_,var1,var2,string1_length,string1,var3,...|
243   * length of varx: size saved int syncvarlist
244   * @param mem pointer to allocated memory with enough size
245   * @param sizes vector containing sizes of all objects in gamestate (to be appended)
246   * @param id gamestateid of the gamestate to be saved (important for priorities)
247   * @param mode defines the direction in which the data will be send/received
248   *             0x1: server->client
249   *             0x2: client->server
250   *             0x3: bidirectional
251   * @return true: if !doSync or if everything was successfully saved
252   */
253  uint32_t Synchronisable::getData(uint8_t*& mem, std::vector<uint32_t>& sizes, int32_t id, uint8_t mode)
254  {
255    unsigned int test = 0;
256    if(mode==0x0)
257      mode=state_;
258    //if this tick is we dont synchronise, then abort now
259    if(!doSync(/*id,*/ mode))
260      return 0;
261    uint32_t tempsize = 0;
262#ifndef NDEBUG
263    uint8_t* oldmem = mem;
264    if (this->classID_==0)
265      orxout(internal_info, context::network) << "classid 0 " << this->getIdentifier()->getName() << endl;
266#endif
267
268    if (this->classID_ == static_cast<uint32_t>(-1))
269        this->classID_ = this->getIdentifier()->getNetworkID();
270
271    assert(ClassByID(this->classID_));
272    assert(this->classID_==this->getIdentifier()->getNetworkID());
273    assert(this->objectID_!=OBJECTID_UNKNOWN);
274    std::vector<SynchronisableVariableBase*>::iterator i;
275
276    // start copy header
277    SynchronisableHeader header(mem);
278    mem += SynchronisableHeader::getSize();
279    // end copy header
280
281    orxout(verbose_more, context::network) << "getting data from objectID_: " << objectID_ << ", classID_: " << classID_ << endl;
282//     orxout(verbose, context::network) << "objectid: " << this->objectID_ << ":";
283    // copy to location
284    for(i=syncList_.begin(); i!=syncList_.end(); ++i)
285    {
286      uint32_t varsize = (*i)->getData( mem, mode );
287//       orxout(verbose, context::network) << " " << varsize;
288      tempsize += varsize;
289      sizes.push_back(varsize);
290      ++test;
291      //tempsize += (*i)->getSize( mode );
292    }
293    assert(tempsize!=0);  // if this happens an empty object (with no variables) would be transmitted
294//     orxout(verbose, context::network) << endl;
295
296    header.setObjectID( this->objectID_ );
297    header.setContextID( this->contextID_ );
298    header.setClassID( this->classID_ );
299    header.setDataSize( tempsize );
300    assert( tempsize == mem-oldmem-SynchronisableHeader::getSize() );
301    assert( test == this->getNrOfVariables() );
302    header.setDiffed(false);
303    tempsize += SynchronisableHeader::getSize();
304
305#ifndef NDEBUG
306    uint32_t size;
307    size=getSize(id, mode);
308    assert(tempsize==size);
309#endif
310    return tempsize;
311  }
312
313
314  /**
315   * This function takes a bytestream and loads the data into the registered variables
316   * @param mem pointer to the bytestream
317   * @param mode same as in getData
318   * @param forceCallback this makes updateData call each callback
319   * @return true/false
320   */
321  bool Synchronisable::updateData(uint8_t*& mem, uint8_t mode, bool forceCallback)
322  {
323    if(mode==0x0)
324      mode=state_;
325   
326    if(syncList_.empty())
327    {
328      orxout(internal_warning, context::network) << "Synchronisable::updateData syncList_ is empty" << endl;
329      assert(0);
330      return false;
331    }
332
333    uint8_t* data=mem;
334    // start extract header
335    SynchronisableHeaderLight syncHeaderLight(mem);
336    assert(syncHeaderLight.getObjectID()==this->getObjectID());
337   
338    if( !this->doReceive(mode) )
339    {
340      uint32_t headerSize;
341      if( syncHeaderLight.isDiffed() )
342        headerSize = SynchronisableHeaderLight::getSize();
343      else
344        headerSize = SynchronisableHeader::getSize();
345      mem += syncHeaderLight.getDataSize() + headerSize;
346      return true;
347    }
348
349    //orxout(verbose_more, context::network) << "Synchronisable: objectID_ " << syncHeader.getObjectID() << ", classID_ " << syncHeader.getClassID() << " size: " << syncHeader.getDataSize() << " synchronising data" << endl;
350    if( !syncHeaderLight.isDiffed() )
351    {
352      SynchronisableHeader syncHeader2(mem);
353      assert( this->getClassID() == syncHeader2.getClassID() );
354      assert( this->getContextID() == syncHeader2.getContextID() );
355      mem += SynchronisableHeader::getSize();
356      std::vector<SynchronisableVariableBase *>::iterator i;
357      for(i=syncList_.begin(); i!=syncList_.end(); ++i)
358      {
359        assert( mem <= data+syncHeader2.getDataSize()+SynchronisableHeader::getSize() ); // always make sure we don't exceed the datasize in our stream
360        (*i)->putData( mem, mode, forceCallback );
361      }
362      assert(mem == data+syncHeaderLight.getDataSize()+SynchronisableHeader::getSize() );
363    }
364    else
365    {
366      mem += SynchronisableHeaderLight::getSize();
367//       orxout(debug_output, context::network) << "objectID: " << this->objectID_ << endl;
368      while( mem < data+syncHeaderLight.getDataSize()+SynchronisableHeaderLight::getSize() )
369      {
370        VariableID varID = *(VariableID*)mem;
371//         orxout(debug_output, context::network) << "varID: " << varID << endl;
372        assert( varID < syncList_.size() );
373        mem += sizeof(VariableID);
374        syncList_[varID]->putData( mem, mode, forceCallback );
375      }
376      assert(mem == data+syncHeaderLight.getDataSize()+SynchronisableHeaderLight::getSize() );
377    }
378    return true;
379  }
380
381  /**
382  * This function returns the total amount of bytes needed by getData to save the whole content of the variables
383  * @param id id of the gamestate
384  * @param mode same as getData
385  * @return amount of bytes
386  */
387  uint32_t Synchronisable::getSize(int32_t id, uint8_t mode)
388  {
389    uint32_t tsize=SynchronisableHeader::getSize();
390    if (mode==0x0)
391      mode=state_;
392    if (!doSync(/*id, */mode))
393      return 0;
394    assert( mode==state_ );
395    tsize += this->dataSize_;
396    std::vector<SynchronisableVariableBase*>::iterator i;
397    for(i=stringList_.begin(); i!=stringList_.end(); ++i)
398    {
399      tsize += (*i)->getSize( mode );
400    }
401    return tsize;
402  }
403
404  /**
405   * This function determines, wheter the object should be saved to the bytestream (according to its syncmode/direction)
406   * @param mode Synchronisation mode (toclient, toserver or bidirectional)
407   * @return true/false
408   */
409  bool Synchronisable::doSync(/*int32_t id,*/ uint8_t mode)
410  {
411//     if(mode==0x0)
412//       mode=state_;
413    assert(mode!=0x0);
414    return ( (this->objectMode_ & mode)!=0 && (!syncList_.empty() ) );
415  }
416 
417  /**
418   * This function determines, wheter the object should accept data from the bytestream (according to its syncmode/direction)
419   * @param mode Synchronisation mode (toclient, toserver or bidirectional)
420   * @return true/false
421   */
422  bool Synchronisable::doReceive( uint8_t mode)
423  {
424    //return mode != this->objectMode_ || this->objectMode_ == ObjectDirection::Bidirectional;
425    return true;
426  }
427
428  /**
429   * This function sets the synchronisation mode of the object
430   * If set to 0x0 variables will not be synchronised at all
431   * If set to 0x1 variables will only be synchronised to the client
432   * If set to 0x2 variables will only be synchronised to the server
433   * If set to 0x3 variables will be synchronised bidirectionally (only if set so in registerVar)
434   * @param mode same as in registerVar
435   */
436  void Synchronisable::setSyncMode(uint8_t mode)
437  {
438    assert(mode==0x0 || mode==0x1 || mode==0x2 || mode==0x3);
439    this->objectMode_=mode;
440  }
441
442  template <> void Synchronisable::registerVariable( std::string& variable, uint8_t mode, NetworkCallbackBase *cb, bool bidirectional)
443  {
444    SynchronisableVariableBase* sv;
445    if (bidirectional)
446      sv = new SynchronisableVariableBidirectional<std::string>(variable, mode, cb);
447    else
448      sv = new SynchronisableVariable<std::string>(variable, mode, cb);
449    syncList_.push_back(sv);
450    stringList_.push_back(sv);
451  }
452
453template <> void Synchronisable::unregisterVariable( std::string& variable )
454  {
455    bool unregistered_nonexistent_variable = true;
456    std::vector<SynchronisableVariableBase*>::iterator it = syncList_.begin();
457    while(it!=syncList_.end())
458    {
459      if( ((*it)->getReference()) == &variable )
460      {
461        delete (*it);
462        syncList_.erase(it);
463        unregistered_nonexistent_variable = false;
464        break;
465      }
466      else
467        ++it;
468    }
469    assert(unregistered_nonexistent_variable == false);
470   
471    it = stringList_.begin();
472    while(it!=stringList_.end())
473    {
474      if( ((*it)->getReference()) == &variable )
475      {
476        delete (*it);
477        stringList_.erase(it);
478        return;
479      }
480      else
481        ++it;
482    }
483    unregistered_nonexistent_variable = true;
484    assert(unregistered_nonexistent_variable == false); //if we reach this point something went wrong:
485    // the variable has not been registered before
486  }
487
488
489}
Note: See TracBrowser for help on using the repository browser.