Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/objecthierarchy/src/network/Synchronisable.cc @ 1940

Last change on this file since 1940 was 1940, checked in by landauf, 16 years ago

did some first (and very unfinished) steps to deal with different players on server and client

  • Property svn:eol-style set to native
File size: 15.0 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// C++ Implementation: synchronisable
32//
33// Description:
34//
35//
36// Author:  Dumeni, Oliver Scheuss, (C) 2007
37//
38// Copyright: See COPYING file that comes with this distribution
39//
40
41#include "Synchronisable.h"
42
43#include <cstring>
44#include <iostream>
45#include <assert.h>
46
47#include "core/CoreIncludes.h"
48#include "core/BaseObject.h"
49// #include "core/Identifier.h"
50
51namespace network
52{
53
54
55  std::map<unsigned int, Synchronisable *> Synchronisable::objectMap_;
56  std::queue<unsigned int> Synchronisable::deletedObjects_;
57
58  int Synchronisable::state_=0x1; // detemines wheter we are server (default) or client
59
60  /**
61  * Constructor:
62  * Initializes all Variables and sets the right objectID
63  */
64  Synchronisable::Synchronisable(){
65    RegisterRootObject(Synchronisable);
66    static uint32_t idCounter=0;
67    objectFrequency_=1;
68    objectMode_=0x1; // by default do not send data to server
69    objectID=idCounter++;
70    classID = (unsigned int)-1;
71    syncList = new std::list<synchronisableVariable *>;
72  }
73
74  /**
75   * Destructor:
76   * Delete all callback objects and remove objectID from the objectMap_
77   */
78  Synchronisable::~Synchronisable(){
79    // delete callback function objects
80    if(!orxonox::Identifier::isCreatingHierarchy()){
81      for(std::list<synchronisableVariable *>::iterator it = syncList->begin(); it!=syncList->end(); it++)
82        delete (*it)->callback;
83      deletedObjects_.push(objectID);
84//       COUT(3) << "destruct synchronisable +++" << objectID << " | " << classID << std::endl;
85//       COUT(3) << " bump ---" << objectID << " | " << &objectMap_ << std::endl;
86//       assert(objectMap_[objectID]->objectID==objectID);
87//       objectMap_.erase(objectID);
88    }
89  }
90
91  /**
92   * This function gets called after all neccessary data has been passed to the object
93   * Overload this function and recall the create function of the parent class
94   * @return true/false
95   */
96  bool Synchronisable::create(){
97    this->classID = this->getIdentifier()->getNetworkID();
98//     COUT(4) << "creating synchronisable: setting classid from " << this->getIdentifier()->getName() << " to: " << classID << std::endl;
99
100//     COUT(3) << "construct synchronisable +++" << objectID << " | " << classID << std::endl;
101//     objectMap_[objectID]=this;
102//     assert(objectMap_[objectID]==this);
103//     assert(objectMap_[objectID]->objectID==objectID);
104    return true;
105  }
106
107
108  /**
109   * This function sets the internal mode for synchronisation
110   * @param b true if this object is located on a client or on a server
111   */
112  void Synchronisable::setClient(bool b){
113    if(b) // client
114      state_=0x2;
115    else  // server
116      state_=0x1;
117  }
118
119  /**
120   * This function fabricated a new synchrnisable (and children of it), sets calls updateData and create
121   * After calling this function the mem pointer will be increased by the size of the needed data
122   * @param mem pointer to where the appropriate data is located
123   * @param mode defines the mode, how the data should be loaded
124   * @return pointer to the newly created synchronisable
125   */
126  Synchronisable *Synchronisable::fabricate(uint8_t*& mem, int mode)
127  {
128    synchronisableHeader *header = (synchronisableHeader *)mem;
129
130    COUT(3) << "fabricating object with id: " << header->objectID << std::endl;
131
132    orxonox::Identifier* id = ClassByID(header->classID);
133    assert(id);
134    orxonox::BaseObject *bo = id->fabricate();
135    Synchronisable *no = dynamic_cast<Synchronisable *>(bo);
136    assert(no);
137    no->objectID=header->objectID;
138    no->classID=header->classID;
139    COUT(3) << "fabricate objectID: " << no->objectID << " classID: " << no->classID << std::endl;
140          // update data and create object/entity...
141    bool b = no->updateData(mem, mode);
142    assert(b);
143    b = no->create();
144    assert(b);
145    return no;
146  }
147
148
149  /**
150   * Finds and deletes the Synchronisable with the appropriate objectID
151   * @param objectID objectID of the Synchronisable
152   * @return true/false
153   */
154  bool Synchronisable::deleteObject(unsigned int objectID){
155//     assert(getSynchronisable(objectID));
156    if(!getSynchronisable(objectID))
157      return false;
158    assert(getSynchronisable(objectID)->objectID==objectID);
159//     delete objectMap_[objectID];
160    Synchronisable *s = getSynchronisable(objectID);
161    if(s)
162      delete s;
163    else
164      return false;
165    return true;
166  }
167
168  /**
169   * This function looks up the objectID in the objectMap_ and returns a pointer to the right Synchronisable
170   * @param objectID objectID of the Synchronisable
171   * @return pointer to the Synchronisable with the objectID
172   */
173  Synchronisable* Synchronisable::getSynchronisable(unsigned int objectID){
174    orxonox::ObjectList<Synchronisable>::iterator it;
175    for(it = orxonox::ObjectList<Synchronisable>::begin(); it; ++it){
176      if( it->getObjectID()==objectID )
177           return *it;
178    }
179    return NULL;
180
181//     std::map<unsigned int, Synchronisable *>::iterator i = objectMap_.find(objectID);
182//     if(i==objectMap_.end())
183//       return NULL;
184//     assert(i->second->objectID==objectID);
185//     return (*i).second;
186  }
187
188
189  /**
190  * This function is used to register a variable to be synchronized
191  * also counts the total datasize needed to save the variables
192  * @param var pointer to the variable
193  * @param size size of the datatype the variable consists of
194  * @param t the type of the variable (network::DATA or network::STRING
195  * @param mode same as in getData
196  * @param cb callback object that should get called, if the value of the variable changes
197  */
198  void Synchronisable::registerVar(void *var, int size, variableType t, int mode, NetworkCallbackBase *cb){
199    // create temporary synch.Var struct
200    synchronisableVariable *temp = new synchronisableVariable;
201    temp->size = size;
202    temp->var = var;
203    temp->mode = mode;
204    temp->type = t;
205    temp->callback = cb;
206    COUT(5) << "Syncronisable::registering var with size: " << temp->size << " and type: " << temp->type << std::endl;
207    //std::cout << "push temp to syncList (at the bottom) " << datasize << std::endl;
208    COUT(5) << "Syncronisable::objectID: " << objectID << " this: " << this << " name: " << this->getIdentifier()->getName() << " networkID: " << this->getIdentifier()->getNetworkID() << std::endl;
209    syncList->push_back(temp);
210#ifndef NDEBUG
211    std::list<synchronisableVariable *>::iterator it = syncList->begin();
212    while(it!=syncList->end()){
213      assert(*it!=var);
214      it++;
215    }
216#endif
217  }
218
219  /**
220   * This function takes all SynchronisableVariables out of the Synchronisable and saves them together with the size, objectID and classID to the given memory
221   * takes a pointer to already allocated memory (must have at least getSize bytes length)
222   * structure of the bitstream:
223   * |totalsize,objectID,classID,var1,var2,string1_length,string1,var3,...|
224   * length of varx: size saved int syncvarlist
225   * @param mem pointer to allocated memory with enough size
226   * @param id gamestateid of the gamestate to be saved (important for priorities)
227   * @param mode defines the direction in which the data will be send/received
228   *             0x1: server->client
229   *             0x2: client->server (not recommended)
230   *             0x3: bidirectional
231   * @return true: if !isMyTick or if everything was successfully saved
232   */
233  bool Synchronisable::getData(uint8_t*& mem, unsigned int id, int mode){
234    //if this tick is we dont synchronise, then abort now
235    if(!isMyTick(id))
236      return true;
237    //std::cout << "inside getData" << std::endl;
238    unsigned int tempsize = 0;
239    if(mode==0x0)
240      mode=state_;
241    if(classID==0)
242      COUT(3) << "classid 0 " << this->getIdentifier()->getName() << std::endl;
243
244    if (this->classID == (unsigned int)-1)
245        this->classID = this->getIdentifier()->getNetworkID();
246
247    assert(this->classID==this->getIdentifier()->getNetworkID());
248//     this->classID=this->getIdentifier()->getNetworkID(); // TODO: correct this
249    std::list<synchronisableVariable *>::iterator i;
250    unsigned int size;
251    size=getSize(id, mode);
252
253    // start copy header
254    synchronisableHeader *header = (synchronisableHeader *)mem;
255    header->size = size;
256    header->objectID = this->objectID;
257    header->classID = this->classID;
258    header->dataAvailable = true;
259    tempsize+=sizeof(synchronisableHeader);
260    mem+=sizeof(synchronisableHeader);
261    // end copy header
262
263
264    COUT(5) << "Synchronisable getting data from objectID: " << objectID << " classID: " << classID << " length: " << size << std::endl;
265    // copy to location
266    for(i=syncList->begin(); i!=syncList->end(); ++i){
267      if( ((*i)->mode & mode) == 0 ){
268        COUT(5) << "not getting data: " << std::endl;
269        continue;  // this variable should only be received
270      }
271      switch((*i)->type){
272        case DATA:
273          memcpy( (void *)(mem), (void*)((*i)->var), (*i)->size);
274          mem+=(*i)->size;
275          tempsize+=(*i)->size;
276          break;
277        case STRING:
278          memcpy( (void *)(mem), (void *)&((*i)->size), sizeof(int) );
279          mem+=sizeof(int);
280          const char *data = ( ( *(std::string *) (*i)->var).c_str());
281          memcpy( mem, (void*)data, (*i)->size);
282          COUT(5) << "synchronisable: char: " << (const char *)(mem) << " data: " << data << " string: " << *(std::string *)((*i)->var) << std::endl;
283          mem+=(*i)->size;
284          tempsize+=(*i)->size + 4;
285          break;
286      }
287    }
288    assert(tempsize==size);
289    return true;
290  }
291
292
293  /**
294   * This function takes a bytestream and loads the data into the registered variables
295   * @param mem pointer to the bytestream
296   * @param mode same as in getData
297   * @return true/false
298   */
299  bool Synchronisable::updateData(uint8_t*& mem, int mode){
300    if(mode==0x0)
301      mode=state_;
302    std::list<synchronisableVariable *>::iterator i;
303    if(syncList->empty()){
304      COUT(4) << "Synchronisable::updateData syncList is empty" << std::endl;
305      return false;
306    }
307
308    uint8_t *data=mem;
309    // start extract header
310    synchronisableHeader *syncHeader = (synchronisableHeader *)mem;
311    assert(syncHeader->objectID==this->objectID);
312    if(syncHeader->dataAvailable==false){
313      mem+=syncHeader->size;
314      return true;
315    }
316
317    mem+=sizeof(synchronisableHeader);
318    // stop extract header
319    assert(this->objectID==syncHeader->objectID);
320//    assert(this->classID==syncHeader->classID); //TODO: fix this!!! maybe a problem with the identifier ?
321
322    COUT(5) << "Synchronisable: objectID " << syncHeader->objectID << ", classID " << syncHeader->classID << " size: " << syncHeader->size << " synchronising data" << std::endl;
323    for(i=syncList->begin(); i!=syncList->end() && mem <= data+syncHeader->size; i++){
324      if( ((*i)->mode ^ mode) == 0 ){
325        COUT(5) << "synchronisable: not updating variable " << std::endl;
326        continue;  // this variable should only be set
327      }
328      COUT(5) << "Synchronisable: element size: " << (*i)->size << " type: " << (*i)->type << std::endl;
329      bool callback=false;
330      switch((*i)->type){
331        case DATA:
332          if((*i)->callback) // check whether this variable changed (but only if callback was set)
333            if(strncmp((char *)(*i)->var, (char *)mem, (*i)->size)!=0)
334              callback=true;
335          memcpy((void*)(*i)->var, mem, (*i)->size);
336          mem+=(*i)->size;
337          break;
338        case STRING:
339          (*i)->size = *(uint32_t *)mem;
340          COUT(5) << "string size: " << (*i)->size << std::endl;
341          mem+=sizeof(int);
342          if((*i)->callback) // check whether this string changed
343            if( *(std::string *)((*i)->var) != std::string((char *)mem) )
344              callback=true;
345          *((std::string *)((*i)->var)) = std::string((const char*)mem);
346          COUT(5) << "synchronisable: char: " << (const char*)mem << " string: " << std::string((const char*)mem) << std::endl;
347          mem += (*i)->size;
348          break;
349      }
350      // call the callback function, if defined
351      if(callback && (*i)->callback)
352        (*i)->callback->call();
353    }
354    return true;
355  }
356
357  /**
358  * This function returns the total amount of bytes needed by getData to save the whole content of the variables
359  * @param id id of the gamestate
360  * @param mode same as getData
361  * @return amount of bytes
362  */
363  uint32_t Synchronisable::getSize(unsigned int id, int mode){
364    if(!isMyTick(id))
365      return 0;
366    int tsize=sizeof(synchronisableHeader);
367    if(mode==0x0)
368      mode=state_;
369    std::list<synchronisableVariable *>::iterator i;
370    for(i=syncList->begin(); i!=syncList->end(); i++){
371      if( ((*i)->mode & mode) == 0 )
372        continue;  // this variable should only be received, so dont add its size to the send-size
373      switch((*i)->type){
374      case DATA:
375        tsize+=(*i)->size;
376        break;
377      case STRING:
378        tsize+=sizeof(int);
379        (*i)->size=((std::string *)(*i)->var)->length()+1;
380        COUT(5) << "String size: " << (*i)->size << std::endl;
381        tsize+=(*i)->size;
382        break;
383      }
384    }
385    return tsize;
386  }
387
388  /**
389   * This function determines, wheter the object should be saved to the bytestream (according to its syncmode/direction)
390   * @param id gamestate id
391   * @return true/false
392   */
393  bool Synchronisable::isMyTick(unsigned int id){
394    return ( (objectMode_&state_)!=0 );
395  }
396
397  bool Synchronisable::doSelection(unsigned int id){
398    return ( id==0 || id%objectFrequency_==objectID%objectFrequency_ ) && ((objectMode_&state_)!=0);
399  }
400
401  /**
402   * This function looks at the header located in the bytestream and checks wheter objectID and classID match with the Synchronisables ones
403   * @param mem pointer to the bytestream
404   */
405  bool Synchronisable::isMyData(uint8_t* mem)
406  {
407    synchronisableHeader *header = (synchronisableHeader *)mem;
408    assert(header->objectID==this->objectID);
409    return header->dataAvailable;
410  }
411
412  /**
413   * This function sets the synchronisation mode of the object
414   * If set to 0x1 variables will only be synchronised to the client
415   * If set to 0x2 variables will only be synchronised to the server
416   * If set to 0x3 variables will be synchronised bidirectionally (only if set so in registerVar)
417   * @param mode same as in registerVar
418   */
419  void Synchronisable::setObjectMode(int mode){
420    assert(mode==0x1 || mode==0x2 || mode==0x3);
421    objectMode_=mode;
422  }
423
424
425}
Note: See TracBrowser for help on using the repository browser.