Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 5965 was 5929, checked in by rgrieder, 15 years ago

Merged core5 branch back to the trunk.
Key features include clean level unloading and an extended XML event system.

Two important notes:
Delete your keybindings.ini files! * or you will still get parser errors when loading the key bindings.
Delete build_dir/lib/modules/libgamestates.module! * or orxonox won't start.
Best thing to do is to delete the build folder ;)

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