Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/network/packet/Gamestate.cc @ 12050

Last change on this file since 12050 was 12027, checked in by merholzl, 6 years ago

Merged Masterserver, refresh button had to be removed

  • Property svn:eol-style set to native
File size: 19.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 *      Oliver Scheuss
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "Gamestate.h"
30
31#include <zlib.h>
32
33#include "util/Output.h"
34#include "util/OrxAssert.h"
35#include "core/CoreIncludes.h"
36#include "core/GameMode.h"
37#include "core/object/ObjectList.h"
38#include "network/synchronisable/Synchronisable.h"
39#include "network/GamestateHandler.h"
40#include "network/Host.h"
41
42namespace orxonox {
43
44namespace packet {
45
46#define GAMESTATE_START(data) (data + GamestateHeader::getSize())
47
48#define PACKET_FLAG_GAMESTATE  0
49
50inline bool memzero( uint8_t* data, uint32_t datalength)
51{
52  uint64_t* d = (uint64_t*)data;
53
54  for( unsigned int i=0; i<datalength/8; i++ )
55  {
56    if( *(d+i) != 0 )
57      return false;
58  }
59  // now process the rest (when datalength isn't a multiple of 4)
60  for( unsigned int j = 8*(datalength/8); j<datalength; j++ )
61  {
62    if( *(data+j) != 0 )
63      return false;
64  }
65  return true;
66}
67
68
69Gamestate::Gamestate():
70  header_()
71{
72  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
73}
74
75
76Gamestate::Gamestate(uint8_t *data, unsigned int clientID):
77  Packet(data, clientID), header_(data)
78{
79  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
80}
81
82
83Gamestate::Gamestate(uint8_t *data):
84  header_(data)
85{
86  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
87  data_ = data;
88}
89
90
91Gamestate::Gamestate(const Gamestate& g) :
92  Packet( *(Packet*)&g ), header_(this->data_), nrOfVariables_(0)
93{
94  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
95  sizes_ = g.sizes_;
96}
97
98
99Gamestate::~Gamestate()
100{
101}
102
103//AV: This takes all synchronisables and packs it in a GameState, to be sent over the network
104bool Gamestate::collectData(int id, uint8_t mode)
105{
106  uint32_t tempsize=0, currentsize=0;
107  assert(data_==nullptr);
108  uint32_t size = calcGamestateSize(id, mode);
109
110  orxout(verbose_more, context::packets) << "G.ST.Man: producing gamestate with id: " << id << endl;
111  if(size==0)
112    return false;
113  data_ = new uint8_t[size + GamestateHeader::getSize()];
114  if(!data_)
115  {
116    orxout(internal_warning, context::packets) << "GameStateManager: could not allocate memory" << endl;
117    return false;
118  }
119
120  // tell the gamestate header where to store the data
121  header_.setData(this->data_);
122
123  //start collect data synchronisable by synchronisable
124  uint8_t *mem = data_; // in this stream store all data of the variables and the headers of the synchronisable
125  mem += GamestateHeader::getSize();
126  ObjectList<Synchronisable>::iterator it;
127  for(it = ObjectList<Synchronisable>().begin(); it; ++it)
128  {
129
130    tempsize = it->getData(mem, this->sizes_, id, mode);
131    if ( tempsize != 0 )
132      dataVector_.emplace_back(it->getObjectID(), it->getContextID(), tempsize, mem-data_);
133
134#ifndef NDEBUG
135    if(currentsize+tempsize > size)
136    {
137      assert(0); // if we don't use multithreading this part shouldn't be neccessary
138      // start allocate additional memory
139      orxout(internal_info, context::packets) << "Gamestate: need additional memory" << endl;
140      ObjectList<Synchronisable>::iterator temp = it;
141      uint32_t addsize=tempsize;
142      while(++temp)
143        addsize+=temp->getSize(id, mode);
144      data_ = (uint8_t *)realloc(data_, GamestateHeader::getSize() + currentsize + addsize);
145      if(!data_)
146        return false;
147      size = currentsize+addsize;
148    }// stop allocate additional memory
149#endif
150//     if(!it->getData(mem, id, mode))
151//       return false; // mem pointer gets automatically increased because of call by reference
152    // increase size counter by size of current synchronisable
153    currentsize+=tempsize;
154  }
155
156
157  //start write gamestate header
158  header_.setDataSize( currentsize );
159  header_.setCompSize( 0 );
160  header_.setID( id );
161  header_.setBaseID( GAMESTATEID_INITIAL );
162  header_.setDiffed( false );
163  header_.setComplete( true );
164  header_.setCompressed( false );
165  //stop write gamestate header
166
167  orxout(verbose_more, context::packets) << "Gamestate: Gamestate size: " << currentsize << endl;
168  orxout(verbose_more, context::packets) << "Gamestate: 'estimated' (and corrected) Gamestate size: " << size << endl;
169  return true;
170}
171
172//AV: This takes the Gamestate received from the network and "unpacks" it back to a list of Objects/Synchronisables, thus updating the data
173bool Gamestate::spreadData(uint8_t mode)
174{
175  orxout(verbose_more, context::packets) << "processing gamestate with id " << header_.getID() << endl;
176  assert(data_);
177  assert(!header_.isCompressed());
178  uint8_t *mem=data_+GamestateHeader::getSize();
179  Synchronisable *s;
180 
181  // update the data of the objects we received
182  while(mem < data_+GamestateHeader::getSize()+header_.getDataSize())
183  {
184    SynchronisableHeader objectheader(mem);
185
186    s = Synchronisable::getSynchronisable( objectheader.getObjectID() );
187    if(!s)
188    {
189      if (!GameMode::isMaster())
190      {
191        Synchronisable::fabricate(mem, mode);
192      }
193      else
194      {
195        mem += objectheader.getDataSize() + ( objectheader.isDiffed() ? SynchronisableHeaderLight::getSize() : SynchronisableHeader::getSize() );
196      }
197    }
198    else
199    {
200      OrxVerify(s->updateData(mem, mode), "ERROR: could not update Synchronisable with Gamestate data");
201    }
202  }
203  assert((uintptr_t)(mem-data_) == GamestateHeader::getSize()+header_.getDataSize());
204  return true;
205}
206
207
208uint32_t Gamestate::getSize() const
209{
210  assert(data_);
211  if(header_.isCompressed())
212    return header_.getCompSize()+GamestateHeader::getSize();
213  else
214  {
215    return header_.getDataSize()+GamestateHeader::getSize();
216  }
217}
218
219
220bool Gamestate::operator==(packet::Gamestate gs)
221{
222  uint8_t *d1 = data_+GamestateHeader::getSize();
223  uint8_t *d2 = gs.data_+GamestateHeader::getSize();
224  GamestateHeader h1(data_);
225  GamestateHeader h2(gs.data_);
226  assert(h1.getDataSize() == h2.getDataSize());
227  assert(!isCompressed());
228  assert(!gs.isCompressed());
229  return memcmp(d1, d2, h1.getDataSize())==0;
230}
231
232
233bool Gamestate::process(orxonox::Host* host)
234{
235  return host->addGamestate(this, getPeerID());
236}
237
238//AV: This function takes the Gamestate and compresses it for transmission over the network
239bool Gamestate::compressData()
240{
241  assert(data_);
242  assert(!header_.isCompressed());
243  uLongf buffer = (uLongf)(((header_.getDataSize() + 12)*1.01)+1);
244  if(buffer==0)
245    return false;
246
247  uint8_t *ndata = new uint8_t[buffer+GamestateHeader::getSize()];
248  uint8_t *dest = ndata + GamestateHeader::getSize();
249  uint8_t *source = data_ + GamestateHeader::getSize();
250  int retval;
251  retval = compress( dest, &buffer, source, (uLong)(header_.getDataSize()) );
252  switch ( retval )
253  {
254    case Z_OK: orxout(verbose_more, context::packets) << "G.St.Man: compress: successfully compressed" << endl; break;
255    case Z_MEM_ERROR: orxout(internal_error, context::packets) << "G.St.Man: compress: not enough memory available in gamestate.compress" << endl; return false;
256    case Z_BUF_ERROR: orxout(internal_warning, context::packets) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << endl; return false;
257    case Z_DATA_ERROR: orxout(internal_warning, context::packets) << "G.St.Man: compress: data corrupted in gamestate.compress" << endl; return false;
258  }
259
260  //copy and modify header
261  GamestateHeader *temp = new GamestateHeader(data_);
262  header_.setData(ndata);
263  header_ = *temp;
264  delete temp;
265  //delete old data
266  delete[] data_;
267  //save new data
268  data_ = ndata;
269  header_.setCompSize( buffer );
270  header_.setCompressed( true );
271  orxout(verbose, context::packets) << "gamestate compress datasize: " << header_.getDataSize() << " compsize: " << header_.getCompSize() << endl;
272  return true;
273}
274
275//AV: This function takes the compressed Gamestate received from the network and decompresses it for further unpacking
276bool Gamestate::decompressData()
277{
278  assert(data_);
279  assert(header_.isCompressed());
280  orxout(verbose, context::packets) << "GameStateClient: uncompressing gamestate. id: " << header_.getID() << ", baseid: " << header_.getBaseID() << ", datasize: " << header_.getDataSize() << ", compsize: " << header_.getCompSize() << endl;
281  uint32_t datasize = header_.getDataSize();
282  uint32_t compsize = header_.getCompSize();
283  uint32_t bufsize;
284  bufsize = datasize;
285  assert(bufsize!=0);
286  uint8_t *ndata = new uint8_t[bufsize + GamestateHeader::getSize()];
287  uint8_t *dest = ndata + GamestateHeader::getSize();
288  uint8_t *source = data_ + GamestateHeader::getSize();
289  int retval;
290  uLongf length=bufsize;
291  retval = uncompress( dest, &length, source, (uLong)compsize );
292  switch ( retval )
293  {
294    case Z_OK: orxout(verbose_more, context::packets) << "successfully decompressed" << endl; break;
295    case Z_MEM_ERROR: orxout(internal_error, context::packets) << "not enough memory available" << endl; return false;
296    case Z_BUF_ERROR: orxout(internal_warning, context::packets) << "not enough memory available in the buffer" << endl; return false;
297    case Z_DATA_ERROR: orxout(internal_warning, context::packets) << "data corrupted (zlib)" << endl; return false;
298  }
299
300  //copy over the header
301  GamestateHeader* temp = new GamestateHeader( data_ );
302  header_.setData(ndata);
303  header_ = *temp;
304  delete temp;
305
306  if (this->bDataENetAllocated_)
307  {
308    // Memory was allocated by ENet. --> We let it be since enet_packet_destroy will
309    // deallocated it anyway. So data and packet stay together.
310    this->bDataENetAllocated_ = false;
311  }
312  else
313  {
314    // We allocated the memory in the first place (unlikely). So we destroy the old data
315    // and overwrite it with the new decompressed data.
316    delete[] this->data_;
317  }
318
319  //set new pointers
320  data_ = ndata;
321  header_.setCompressed( false );
322  assert(header_.getDataSize()==datasize);
323  assert(header_.getCompSize()==compsize);
324  return true;
325}
326
327
328inline void /*Gamestate::*/diffObject( uint8_t*& newDataPtr, uint8_t*& origDataPtr, uint8_t*& baseDataPtr, SynchronisableHeader& objectHeader, std::vector<uint32_t>::iterator& sizes )
329{
330  assert( objectHeader.getDataSize() == SynchronisableHeader(baseDataPtr).getDataSize() );
331 
332  uint32_t objectOffset = SynchronisableHeader::getSize(); // offset inside the object in the origData and baseData
333  // Check whether the whole object stayed the same
334  if( memcmp( origDataPtr+objectOffset, baseDataPtr+objectOffset, objectHeader.getDataSize()) == 0 )
335  {
336    origDataPtr += objectOffset + objectHeader.getDataSize(); // skip the whole object
337    baseDataPtr += objectOffset + objectHeader.getDataSize();
338    sizes += Synchronisable::getSynchronisable(objectHeader.getObjectID())->getNrOfVariables();
339  }
340  else
341  {
342    // Now start to diff the Object
343    SynchronisableHeaderLight newObjectHeader(newDataPtr);
344    newObjectHeader = objectHeader; // copy over the objectheader
345    VariableID variableID = 0;
346    uint32_t diffedObjectOffset = SynchronisableHeaderLight::getSize();
347    // iterate through all variables
348    while( objectOffset < objectHeader.getDataSize()+SynchronisableHeader::getSize() )
349    {
350      // check whether variable changed and write id and copy over variable to the new stream
351      // otherwise skip variable
352      uint32_t varSize = *sizes;
353      assert( varSize == Synchronisable::getSynchronisable(objectHeader.getObjectID())->getVarSize(variableID) );
354      if ( varSize != 0 )
355      {
356        if ( memcmp(origDataPtr+objectOffset, baseDataPtr+objectOffset, varSize) != 0 )
357        {
358          *(VariableID*)(newDataPtr+diffedObjectOffset) = variableID; // copy over the variableID
359          diffedObjectOffset += sizeof(VariableID);
360          memcpy( newDataPtr+diffedObjectOffset, origDataPtr+objectOffset, varSize );
361          diffedObjectOffset += varSize;
362          objectOffset += varSize;
363        }
364        else
365        {
366          objectOffset += varSize;
367        }
368      }
369
370      ++variableID;
371      ++sizes;
372    }
373   
374    // if there are variables from this object with 0 size left in sizes
375    if( Synchronisable::getSynchronisable(objectHeader.getObjectID())->getNrOfVariables() != variableID )
376      sizes += Synchronisable::getSynchronisable(objectHeader.getObjectID())->getNrOfVariables() - variableID;
377   
378    newObjectHeader.setDiffed(true);
379    newObjectHeader.setDataSize(diffedObjectOffset-SynchronisableHeaderLight::getSize());
380    assert(objectOffset == objectHeader.getDataSize()+SynchronisableHeader::getSize());
381    assert(newObjectHeader.getDataSize()>0);
382   
383    origDataPtr += objectOffset;
384    baseDataPtr += objectOffset;
385    newDataPtr += diffedObjectOffset;
386  }
387}
388
389inline void /*Gamestate::*/copyObject( uint8_t*& newData, uint8_t*& origData, uint8_t*& baseData, SynchronisableHeader& objectHeader, std::vector<uint32_t>::iterator& sizes )
390{
391  // Just copy over the whole Object
392  memcpy( newData, origData, objectHeader.getDataSize()+SynchronisableHeader::getSize() );
393  SynchronisableHeader(newData).setDiffed(false);
394 
395  newData += objectHeader.getDataSize()+SynchronisableHeader::getSize();
396  origData += objectHeader.getDataSize()+SynchronisableHeader::getSize();
397
398  sizes += Synchronisable::getSynchronisable(objectHeader.getObjectID())->getNrOfVariables();
399
400}
401
402inline bool findObject(uint8_t*& dataPtr, uint8_t* endPtr, SynchronisableHeader& objectHeader)
403{
404  // Some assertions to make sure the dataPtr is valid (pointing to a SynchronisableHeader)
405  {
406    SynchronisableHeader htemp2(dataPtr);
407    assert(htemp2.getClassID()<500);
408    assert(htemp2.getDataSize()!=0 && htemp2.getDataSize()<1000);
409    assert(htemp2.isDiffed()==false);
410  }
411  uint32_t objectID = objectHeader.getObjectID();
412  while ( dataPtr < endPtr )
413  {
414    SynchronisableHeader htemp(dataPtr);
415    assert( htemp.getDataSize()!=0 );
416    if ( htemp.getObjectID() == objectID )
417    {
418      assert( objectHeader.getClassID() == htemp.getClassID() );
419      assert( objectHeader.getContextID() == htemp.getContextID() );
420      return true;
421    }
422    {
423      if( dataPtr+htemp.getDataSize()+SynchronisableHeader::getSize() < endPtr )
424      {
425        SynchronisableHeader htemp2(dataPtr+htemp.getDataSize()+SynchronisableHeader::getSize());
426        assert(htemp2.getClassID()<500);
427        assert(htemp2.getDataSize()!=0 && htemp2.getDataSize()<1000);
428        assert(htemp2.isDiffed()==false);
429      }
430    }
431    dataPtr += htemp.getDataSize()+SynchronisableHeader::getSize();
432   
433  }
434  assert(dataPtr == endPtr);
435 
436  return false;
437}
438
439Gamestate* Gamestate::diffVariables(Gamestate *base)
440{
441  assert(base); assert(data_ && base->data_);
442  assert(!header_.isCompressed() && !base->header_.isCompressed());
443  assert(!header_.isDiffed());
444  assert( header_.getDataSize() && base->header_.getDataSize() );
445
446
447  // *** first do a raw diff of the two gamestates
448
449  uint8_t *baseDataPtr = GAMESTATE_START(base->data_);
450  uint8_t *origDataPtr = GAMESTATE_START(this->data_);
451  uint8_t *origDataEnd = origDataPtr + header_.getDataSize();
452  uint8_t *baseDataEnd = baseDataPtr + base->header_.getDataSize();
453
454
455  // Allocate new space for diffed gamestate
456  uint32_t newDataSize = header_.getDataSize() + GamestateHeader::getSize() + sizeof(uint32_t)*this->nrOfVariables_;
457  uint8_t *newData = new uint8_t[newDataSize]; // this is the maximum size needed in the worst case
458  uint8_t *destDataPtr = GAMESTATE_START(newData);
459
460  std::vector<uint32_t>::iterator sizesIt = this->sizes_.begin();
461
462  while( origDataPtr < origDataEnd )
463  {
464    //iterate through all objects
465
466    SynchronisableHeader origHeader(origDataPtr);
467
468    // Find (if possible) the current object in the datastream of the old gamestate
469    // Start at the current offset position
470    if(baseDataPtr == baseDataEnd)
471      baseDataPtr = GAMESTATE_START(base->data_);
472    uint8_t* oldBaseDataPtr = baseDataPtr;
473   
474    assert(baseDataPtr < baseDataEnd);
475    assert(destDataPtr < newData + newDataSize);
476    assert(sizesIt != this->sizes_.end());
477   
478    assert(Synchronisable::getSynchronisable(origHeader.getObjectID()));
479    assert(ClassByID(origHeader.getClassID()));
480    assert(origHeader.getDataSize() < 500);
481   
482    if( findObject(baseDataPtr, baseDataEnd, origHeader) )
483    {
484      SynchronisableHeader baseHeader(baseDataPtr);
485      assert(Synchronisable::getSynchronisable(baseHeader.getObjectID()));
486      assert(ClassByID(baseHeader.getClassID()));
487      assert(baseHeader.getDataSize() < 500);
488      if( SynchronisableHeader(baseDataPtr).getDataSize()==origHeader.getDataSize() )
489      {
490//         orxout(verbose, context::packets) << "diffing object in order: " << Synchronisable::getSynchronisable(origHeader.getObjectID())->getIdentifier()->getName() << endl;
491        diffObject(destDataPtr, origDataPtr, baseDataPtr, origHeader, sizesIt);
492      }
493      else
494      {
495//         orxout(verbose, context::packets) << "copy object because of different data sizes (1): " << Synchronisable::getSynchronisable(origHeader.getObjectID())->getIdentifier()->getName() << endl;
496        copyObject(destDataPtr, origDataPtr, baseDataPtr, origHeader, sizesIt);
497        assert(sizesIt != this->sizes_.end() || origDataPtr==origDataEnd);
498      }
499       
500    }
501    else
502    {
503      assert( baseDataPtr == baseDataEnd );
504      baseDataPtr = GAMESTATE_START(base->data_);
505      if( findObject(baseDataPtr, oldBaseDataPtr, origHeader) )
506      {
507        SynchronisableHeader baseHeader(baseDataPtr);
508        assert(Synchronisable::getSynchronisable(baseHeader.getObjectID()));
509        assert(ClassByID(baseHeader.getClassID()));
510        assert(baseHeader.getDataSize() < 500);
511        if( SynchronisableHeader(baseDataPtr).getDataSize()==origHeader.getDataSize() )
512        {
513//           orxout(verbose, context::packets) << "diffing object out of order: " << Synchronisable::getSynchronisable(origHeader.getObjectID())->getIdentifier()->getName() << endl;
514          diffObject(destDataPtr, origDataPtr, baseDataPtr, origHeader, sizesIt);
515        }
516        else
517        {
518//           orxout(verbose, context::packets) << "copy object because of different data sizes (2): " << Synchronisable::getSynchronisable(origHeader.getObjectID())->getIdentifier()->getName() << endl;
519          copyObject(destDataPtr, origDataPtr, baseDataPtr, origHeader, sizesIt);
520          assert(sizesIt != this->sizes_.end() || origDataPtr==origDataEnd);
521        }
522      }
523      else
524      {
525//         orxout(verbose, context::packets) << "copy object: " << Synchronisable::getSynchronisable(origHeader.getObjectID())->getIdentifier()->getName() << endl;
526        assert(baseDataPtr == oldBaseDataPtr);
527        copyObject(destDataPtr, origDataPtr, baseDataPtr, origHeader, sizesIt);
528        assert(sizesIt != this->sizes_.end() || origDataPtr==origDataEnd);
529      }
530    }
531  }
532  assert(sizesIt==this->sizes_.end());
533
534
535  Gamestate *g = new Gamestate(newData, getPeerID());
536  (g->header_) = header_;
537  g->header_.setBaseID( base->getID() );
538  g->header_.setDataSize(destDataPtr - newData - GamestateHeader::getSize());
539  g->flags_=flags_;
540  g->packetDirection_ = packetDirection_;
541  assert(!g->isCompressed());
542  return g;
543}
544
545
546uint32_t Gamestate::calcGamestateSize(uint32_t id, uint8_t mode)
547{
548  uint32_t size = 0;
549  uint32_t nrOfVariables = 0;
550    // get total size of gamestate
551  for(Synchronisable* synchronisable : ObjectList<Synchronisable>()){
552    size+=synchronisable->getSize(id, mode); // size of the actual data of the synchronisable
553    nrOfVariables += synchronisable->getNrOfVariables();
554  }
555//   orxout() << "allocating " << nrOfVariables << " ints" << endl;
556  this->sizes_.reserve(nrOfVariables);
557  return size;
558}
559
560
561} //namespace packet
562} //namespace orxonox
Note: See TracBrowser for help on using the repository browser.