Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network2/src/libraries/network/packet/Gamestate.cc @ 6449

Last change on this file since 6449 was 6449, checked in by scheusso, 14 years ago

changed diff behaviour in order to reduce datasize before and after compress
this reduces time needed for gamestate diff and compress about 50%

  • Property svn:eol-style set to native
File size: 19.6 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 *      Oliver Scheuss
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "Gamestate.h"
30
31#include <zlib.h>
32
33#include "util/Debug.h"
34#include "core/GameMode.h"
35#include "core/ObjectList.h"
36#include "network/synchronisable/Synchronisable.h"
37#include "network/GamestateHandler.h"
38
39namespace orxonox {
40
41namespace packet {
42
43#define GAMESTATE_START(data) (data + GamestateHeader::getSize())
44
45#define PACKET_FLAG_GAMESTATE  PacketFlag::Reliable
46
47inline bool memzero( uint8_t* data, uint32_t datalength)
48{
49  uint64_t* d = (uint64_t*)data;
50
51  for( unsigned int i=0; i<datalength/8; i++ )
52  {
53    if( *(d+i) != 0 )
54      return false;
55  }
56  // now process the rest (when datalength isn't a multiple of 4)
57  for( unsigned int j = 8*(datalength/8); j<datalength; j++ )
58  {
59    if( *(data+j) != 0 )
60      return false;
61  }
62  return true;
63}
64
65
66Gamestate::Gamestate():
67  header_(0)
68{
69  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
70}
71
72
73Gamestate::Gamestate(uint8_t *data, unsigned int clientID):
74  Packet(data, clientID)
75{
76  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
77  header_ = new GamestateHeader(data_);
78}
79
80
81Gamestate::Gamestate(uint8_t *data)
82{
83  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
84  data_ = data;
85  header_ = new GamestateHeader(data_);
86}
87
88
89Gamestate::Gamestate(const Gamestate& g) :
90    Packet( *(Packet*)&g ), nrOfVariables_(0)
91{
92  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
93  header_ = new GamestateHeader(data_);
94  sizes_ = g.sizes_;
95}
96
97
98Gamestate::~Gamestate()
99{
100  if( header_ )
101    delete header_;
102}
103
104
105bool Gamestate::collectData(int id, uint8_t mode)
106{
107  assert(this->header_==0); // make sure the header didn't exist before
108  uint32_t tempsize=0, currentsize=0;
109  assert(data_==0);
110  uint32_t size = calcGamestateSize(id, mode);
111
112  COUT(4) << "G.ST.Man: producing gamestate with id: " << id << std::endl;
113  if(size==0)
114    return false;
115  data_ = new uint8_t[size + GamestateHeader::getSize()];
116  if(!data_)
117  {
118    COUT(2) << "GameStateManager: could not allocate memory" << std::endl;
119    return false;
120  }
121
122  // create the header object
123  assert( header_ == 0 );
124  header_ = new GamestateHeader(data_);
125
126  //start collect data synchronisable by synchronisable
127  uint8_t *mem = data_; // in this stream store all data of the variables and the headers of the synchronisable
128  mem += GamestateHeader::getSize();
129  ObjectList<Synchronisable>::iterator it;
130  for(it = ObjectList<Synchronisable>::begin(); it; ++it)
131  {
132
133//     tempsize=it->getSize(id, mode);
134
135    tempsize = it->getData(mem, this->sizes_, id, mode);
136    if ( tempsize != 0 )
137      dataVector_.push_back( obj(it->getObjectID(), it->getCreatorID(), tempsize, mem-data_) );
138
139#ifndef NDEBUG
140    if(currentsize+tempsize > size)
141    {
142      assert(0); // if we don't use multithreading this part shouldn't be neccessary
143      // start allocate additional memory
144      COUT(3) << "G.St.Man: need additional memory" << std::endl;
145      ObjectList<Synchronisable>::iterator temp = it;
146      uint32_t addsize=tempsize;
147      while(++temp)
148        addsize+=temp->getSize(id, mode);
149      data_ = (uint8_t *)realloc(data_, GamestateHeader::getSize() + currentsize + addsize);
150      if(!data_)
151        return false;
152      size = currentsize+addsize;
153    }// stop allocate additional memory
154#endif
155//     if(!it->getData(mem, id, mode))
156//       return false; // mem pointer gets automatically increased because of call by reference
157    // increase size counter by size of current synchronisable
158    currentsize+=tempsize;
159  }
160
161
162  //start write gamestate header
163  header_->setDataSize( currentsize );
164  header_->setID( id );
165  header_->setBaseID( GAMESTATEID_INITIAL );
166  header_->setDiffed( false );
167  header_->setComplete( true );
168  header_->setCompressed( false );
169  //stop write gamestate header
170
171  COUT(5) << "G.ST.Man: Gamestate size: " << currentsize << std::endl;
172  COUT(5) << "G.ST.Man: 'estimated' (and corrected) Gamestate size: " << size << std::endl;
173  return true;
174}
175
176
177bool Gamestate::spreadData(uint8_t mode)
178{
179  COUT(4) << "processing gamestate with id " << header_->getID() << endl;
180  assert(data_);
181  assert(!header_->isCompressed());
182  uint8_t *mem=data_+GamestateHeader::getSize();
183  bool diffed = header_->isDiffed();
184  Synchronisable *s;
185
186  // update the data of the objects we received
187  while(mem < data_+GamestateHeader::getSize()+header_->getDataSize())
188  {
189    SynchronisableHeader objectheader(mem);
190
191    s = Synchronisable::getSynchronisable( objectheader.getObjectID() );
192    if(!s)
193    {
194      if (!GameMode::isMaster())
195      {
196        Synchronisable::fabricate(mem, diffed, mode);
197      }
198      else
199      {
200        mem += objectheader.getDataSize()+SynchronisableHeader::getSize();
201      }
202    }
203    else
204    {
205      bool b = s->updateData(mem, mode);
206      assert(b);
207    }
208  }
209   // In debug mode, check first, whether there are no duplicate objectIDs
210#ifndef NDEBUG
211  if(this->getID()%1000==1)
212  {
213    std::list<uint32_t> v1;
214    ObjectList<Synchronisable>::iterator it;
215    for (it = ObjectList<Synchronisable>::begin(); it != ObjectList<Synchronisable>::end(); ++it)
216    {
217      if (it->getObjectID() == OBJECTID_UNKNOWN)
218      {
219        if (it->objectMode_ != 0x0)
220        {
221          COUT(0) << "Found object with OBJECTID_UNKNOWN on the client with objectMode != 0x0!" << std::endl;
222          COUT(0) << "Possible reason for this error: Client created a synchronized object without the Server's approval." << std::endl;
223          COUT(0) << "Objects class: " << it->getIdentifier()->getName() << std::endl;
224          assert(false);
225        }
226      }
227      else
228      {
229        std::list<uint32_t>::iterator it2;
230        for (it2 = v1.begin(); it2 != v1.end(); ++it2)
231        {
232          if (it->getObjectID() == *it2)
233          {
234            COUT(0) << "Found duplicate objectIDs on the client!" << std::endl
235                    << "Are you sure you don't create a Sychnronisable objcect with 'new' \
236                        that doesn't have objectMode = 0x0?" << std::endl;
237            assert(false);
238          }
239        }
240        v1.push_back(it->getObjectID());
241      }
242    }
243  }
244#endif
245  return true;
246}
247
248
249uint32_t Gamestate::getSize() const
250{
251  assert(data_);
252  if(header_->isCompressed())
253    return header_->getCompSize()+GamestateHeader::getSize();
254  else
255  {
256    return header_->getDataSize()+GamestateHeader::getSize();
257  }
258}
259
260
261bool Gamestate::operator==(packet::Gamestate gs)
262{
263  uint8_t *d1 = data_+GamestateHeader::getSize();
264  uint8_t *d2 = gs.data_+GamestateHeader::getSize();
265  GamestateHeader* h1 = new GamestateHeader(data_);
266  GamestateHeader* h2 = new GamestateHeader(gs.data_);
267  assert(h1->getDataSize() == h2->getDataSize());
268  assert(!isCompressed());
269  assert(!gs.isCompressed());
270  return memcmp(d1, d2, h1->getDataSize())==0;
271}
272
273
274bool Gamestate::process()
275{
276  return GamestateHandler::addGamestate(this, getClientID());
277}
278
279
280bool Gamestate::compressData()
281{
282  assert(data_);
283  assert(!header_->isCompressed());
284  uLongf buffer = (uLongf)(((header_->getDataSize() + 12)*1.01)+1);
285  if(buffer==0)
286    return false;
287
288  uint8_t *ndata = new uint8_t[buffer+GamestateHeader::getSize()];
289  uint8_t *dest = ndata + GamestateHeader::getSize();
290  uint8_t *source = data_ + GamestateHeader::getSize();
291  int retval;
292  retval = compress( dest, &buffer, source, (uLong)(header_->getDataSize()) );
293  switch ( retval )
294  {
295    case Z_OK: COUT(5) << "G.St.Man: compress: successfully compressed" << std::endl; break;
296    case Z_MEM_ERROR: COUT(1) << "G.St.Man: compress: not enough memory available in gamestate.compress" << std::endl; return false;
297    case Z_BUF_ERROR: COUT(2) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << std::endl; return false;
298    case Z_DATA_ERROR: COUT(2) << "G.St.Man: compress: data corrupted in gamestate.compress" << std::endl; return false;
299  }
300
301  //copy and modify header
302  GamestateHeader *temp = header_;
303  header_ = new GamestateHeader(ndata, temp);
304  delete temp;
305  //delete old data
306  delete[] data_;
307  //save new data
308  data_ = ndata;
309  header_->setCompSize( buffer );
310  header_->setCompressed( true );
311  COUT(0) << "gamestate compress datasize: " << header_->getDataSize() << " compsize: " << header_->getCompSize() << std::endl;
312  return true;
313}
314
315
316bool Gamestate::decompressData()
317{
318  assert(data_);
319  assert(header_->isCompressed());
320  COUT(4) << "GameStateClient: uncompressing gamestate. id: " << header_->getID() << ", baseid: " << header_->getBaseID() << ", datasize: " << header_->getDataSize() << ", compsize: " << header_->getCompSize() << std::endl;
321  uint32_t datasize = header_->getDataSize();
322  uint32_t compsize = header_->getCompSize();
323  uint32_t bufsize;
324  bufsize = datasize;
325  assert(bufsize!=0);
326  uint8_t *ndata = new uint8_t[bufsize + GamestateHeader::getSize()];
327  uint8_t *dest = ndata + GamestateHeader::getSize();
328  uint8_t *source = data_ + GamestateHeader::getSize();
329  int retval;
330  uLongf length=bufsize;
331  retval = uncompress( dest, &length, source, (uLong)compsize );
332  switch ( retval )
333  {
334    case Z_OK: COUT(5) << "successfully decompressed" << std::endl; break;
335    case Z_MEM_ERROR: COUT(1) << "not enough memory available" << std::endl; return false;
336    case Z_BUF_ERROR: COUT(2) << "not enough memory available in the buffer" << std::endl; return false;
337    case Z_DATA_ERROR: COUT(2) << "data corrupted (zlib)" << std::endl; return false;
338  }
339
340  //copy over the header
341  GamestateHeader *temp = header_;
342  header_ = new GamestateHeader( data_, header_ );
343  delete temp;
344
345  if (this->bDataENetAllocated_)
346  {
347    // Memory was allocated by ENet. --> We let it be since enet_packet_destroy will
348    // deallocated it anyway. So data and packet stay together.
349    this->bDataENetAllocated_ = false;
350  }
351  else
352  {
353    // We allocated the memory in the first place (unlikely). So we destroy the old data
354    // and overwrite it with the new decompressed data.
355    delete[] this->data_;
356  }
357
358  //set new pointers
359  data_ = ndata;
360  header_->setCompressed( false );
361  assert(header_->getDataSize()==datasize);
362  assert(header_->getCompSize()==compsize);
363  return true;
364}
365
366
367Gamestate *Gamestate::diff(Gamestate *base)
368{
369  assert(this && base); assert(data_ && base->data_);
370  assert(!header_->isCompressed() && !base->header_->isCompressed());
371  assert(!header_->isDiffed());
372
373
374  // *** first do a raw diff of the two gamestates
375
376  uint8_t *baseData = GAMESTATE_START(base->data_);
377  uint8_t *origData = GAMESTATE_START(this->data_);
378  uint32_t origLength = header_->getDataSize();
379  uint32_t baseLength = base->header_->getDataSize();
380
381  assert( origLength && baseLength );
382
383  COUT(0) << "newSize: " << origLength + GamestateHeader::getSize() + sizeof(uint32_t)*this->nrOfVariables_ << endl;
384  uint8_t *nData = new uint8_t[origLength + GamestateHeader::getSize() + sizeof(uint32_t)*this->nrOfVariables_]; // this is the maximum size needed in the worst case
385  uint8_t *dest = GAMESTATE_START(nData);
386
387  uint32_t baseOffset = 0; //offset in the diffed stream
388  uint32_t origOffset = 0; //offset in the new stream with removed 0's
389  std::vector<uint32_t>::iterator sizes = this->sizes_.begin();
390
391  while( origOffset < origLength )
392  {
393    //iterate through all objects
394
395    SynchronisableHeader h(origData+origOffset);
396
397    // Find (if possible) the current object in the datastream of the old gamestate
398    // Start at the current offset position
399    if(baseOffset >= baseLength)
400      baseOffset = 0;
401    uint8_t* temp = baseData + baseOffset;
402    uint32_t objectID = h.getObjectID();
403    assert(temp < baseData+baseLength);
404    assert(dest < nData + origLength + GamestateHeader::getSize() + sizeof(uint32_t)*this->nrOfVariables_);
405    assert(sizes != this->sizes_.end());
406    while ( temp < baseData+baseLength )
407    {
408      SynchronisableHeader htemp(temp);
409      if ( htemp.getObjectID() == objectID )
410      {
411        assert( h.getClassID() == htemp.getClassID() );
412        goto DODIFF;
413      }
414      temp += htemp.getDataSize()+SynchronisableHeader::getSize();
415    }
416    // If not found start looking at the beginning
417    temp = baseData;
418    while ( temp < baseData+baseOffset )
419    {
420      SynchronisableHeader htemp(temp);
421      if ( htemp.getObjectID() == objectID )
422      {
423        assert( h.getClassID() == htemp.getClassID() );
424        goto DODIFF;
425      }
426      temp += htemp.getDataSize()+SynchronisableHeader::getSize();
427    }
428    // Object is new, thus never transmitted -> just copy over
429    goto DOCOPY;
430
431
432DODIFF:
433    {
434//       if(baseOffset==0)
435//       {
436//         assert(origOffset==0);
437//       }
438      uint32_t objectOffset = SynchronisableHeader::getSize(); // offset inside the object in the origData and baseData
439      // Check whether the whole object stayed the same
440      if( memcmp( origData+origOffset+objectOffset, temp+objectOffset, h.getDataSize()) == 0 )
441      {
442        origOffset += objectOffset+ h.getDataSize(); // skip the whole object
443        baseOffset = temp + h.getDataSize()+SynchronisableHeader::getSize() - baseData;
444        sizes += Synchronisable::getSynchronisable(h.getObjectID())->getNrOfVariables();
445      }
446      else
447      {
448//         COUT(4) << "diff " << h.getObjectID() << ":";
449        // Now start to diff the Object
450        SynchronisableHeader h2(dest);
451        h2 = h; // copy over the objectheader
452        uint32_t variableID = 0;
453        uint32_t newObjectOffset = SynchronisableHeader::getSize();
454        // iterate through all variables
455        while( objectOffset < h.getDataSize()+SynchronisableHeader::getSize() )
456        {
457          // check whether variable changed and write id and copy over variable to the new stream
458          // otherwise skip variable
459          assert(sizes != this->sizes_.end());
460          uint32_t varSize = *sizes;
461          assert( varSize == Synchronisable::getSynchronisable(h.getObjectID())->getVarSize(variableID) );
462          if ( varSize != 0 )
463          {
464            if ( memcmp(origData+origOffset+objectOffset, temp+objectOffset, varSize) != 0 )
465            {
466//               COUT(4) << " c" << varSize;
467              *(uint32_t*)(dest+newObjectOffset) = variableID; // copy over the variableID
468              newObjectOffset += sizeof(uint32_t);
469              memcpy( dest+newObjectOffset, origData+origOffset+objectOffset, varSize );
470              newObjectOffset += varSize;
471              objectOffset += varSize;
472            }
473            else
474            {
475//               COUT(4) << " s" << varSize;
476              objectOffset += varSize;
477            }
478          }
479
480          ++variableID;
481          ++sizes;
482        }
483        if( Synchronisable::getSynchronisable(h.getObjectID())->getNrOfVariables() != variableID )
484          sizes += Synchronisable::getSynchronisable(h.getObjectID())->getNrOfVariables() - variableID;
485//         COUT(4) << endl;
486        h2.setDiffed(true);
487        h2.setDataSize(newObjectOffset-SynchronisableHeader::getSize());
488        assert(objectOffset == h.getDataSize()+SynchronisableHeader::getSize());
489        origOffset += objectOffset;
490        baseOffset += temp + h.getDataSize()+SynchronisableHeader::getSize() - baseData;
491        dest += newObjectOffset;
492      }
493
494      continue;
495    }
496
497DOCOPY:
498    {
499      // Just copy over the whole Object
500      memcpy( dest, origData+origOffset, h.getDataSize()+SynchronisableHeader::getSize() );
501      dest += h.getDataSize()+SynchronisableHeader::getSize();
502      origOffset += h.getDataSize()+SynchronisableHeader::getSize();
503      assert( Synchronisable::getSynchronisable(h.getObjectID()) );
504//       COUT(4) << "copy " << h.getObjectID() << endl;
505//       COUT(4) << "copy " << h.getObjectID() << ":";
506      //sizes += Synchronisable::getSynchronisable(h.getObjectID())->getNrOfVariables();
507      for( unsigned int i = 0; i < Synchronisable::getSynchronisable(h.getObjectID())->getNrOfVariables(); ++i )
508      {
509//         COUT(4) << " " << *sizes;
510        ++sizes;
511      }
512//       COUT(4) << endl;
513      assert(sizes != this->sizes_.end() || origOffset>=origLength);
514      continue;
515    }
516  }
517
518
519  Gamestate *g = new Gamestate(nData, getClientID());
520  assert(g->header_);
521  *(g->header_) = *header_;
522  g->header_->setDiffed( true );
523  g->header_->setBaseID( base->getID() );
524  g->header_->setDataSize(dest - nData - GamestateHeader::getSize());
525  g->flags_=flags_;
526  g->packetDirection_ = packetDirection_;
527  assert(g->isDiffed());
528  assert(!g->isCompressed());
529  return g;
530}
531
532
533Gamestate* Gamestate::doSelection(unsigned int clientID, unsigned int targetSize){
534  assert(data_);
535  std::list<obj>::iterator it;
536
537  // allocate memory for new data
538  uint8_t *gdata = new uint8_t[header_->getDataSize()+GamestateHeader::getSize()];
539  // create a gamestate out of it
540  Gamestate *gs = new Gamestate(gdata);
541  uint8_t *newdata = gdata + GamestateHeader::getSize();
542  uint8_t *origdata = GAMESTATE_START(data_);
543
544  //copy the GamestateHeader
545  assert(gs->header_);
546  *(gs->header_) = *header_;
547
548  uint32_t objectOffset;
549  unsigned int objectsize, destsize=0;
550  // TODO: Why is this variable not used?
551  //Synchronisable *object;
552
553  //call TrafficControl
554  TrafficControl::getInstance()->processObjectList( clientID, header_->getID(), dataVector_ );
555
556  //copy in the zeros
557//   std::list<obj>::iterator itt;
558//   COUT(0) << "myvector contains:";
559//   for ( itt=dataVector_.begin() ; itt!=dataVector_.end(); itt++ )
560//     COUT(0) << " " << (*itt).objID;
561//   COUT(0) << endl;
562  for(it=dataVector_.begin(); it!=dataVector_.end();){
563    SynchronisableHeader oldobjectheader(origdata);
564    SynchronisableHeader newobjectheader(newdata);
565    if ( (*it).objSize == 0 )
566    {
567      ++it;
568      continue;
569    }
570    objectsize = oldobjectheader.getDataSize()+SynchronisableHeader::getSize();
571    objectOffset=SynchronisableHeader::getSize(); //skip the size and the availableData variables in the objectheader
572    if ( (*it).objID == oldobjectheader.getObjectID() ){
573      memcpy(newdata, origdata, objectsize);
574      ++it;
575    }else{
576      newobjectheader = oldobjectheader;
577      memset(newdata+objectOffset, 0, objectsize-objectOffset);
578    }
579    newdata += objectsize;
580    origdata += objectsize;
581    destsize += objectsize;
582  }
583#ifndef NDEBUG
584  uint32_t origsize = destsize;
585  while ( origsize < header_->getDataSize() )
586  {
587    SynchronisableHeader oldobjectheader(origdata);
588    objectsize = oldobjectheader.getDataSize()+SynchronisableHeader::getSize();
589    origdata += objectsize;
590    origsize += objectsize;
591  }
592  assert(origsize==header_->getDataSize());
593  assert(destsize!=0);
594#endif
595  gs->header_->setDataSize( destsize );
596  return gs;
597}
598
599
600uint32_t Gamestate::calcGamestateSize(int32_t id, uint8_t mode)
601{
602  uint32_t size = 0;
603  uint32_t nrOfVariables = 0;
604    // get the start of the Synchronisable list
605  ObjectList<Synchronisable>::iterator it;
606    // get total size of gamestate
607  for(it = ObjectList<Synchronisable>::begin(); it; ++it){
608    size+=it->getSize(id, mode); // size of the actual data of the synchronisable
609    nrOfVariables += it->getNrOfVariables();
610  }
611//   COUT(0) << "allocating " << nrOfVariables << " ints" << endl;
612  this->sizes_.reserve(nrOfVariables);
613  return size;
614}
615
616
617} //namespace packet
618} //namespace orxonox
Note: See TracBrowser for help on using the repository browser.