Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Merged buildsystem3 containing buildsystem2 containing Adi's buildsystem branch back to the trunk.
Please update the media directory if you were not using buildsystem3 before.

  • Property svn:eol-style set to native
File size: 15.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, (C) 2008
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "Gamestate.h"
30#include "../GamestateHandler.h"
31#include "../synchronisable/Synchronisable.h"
32#include "../TrafficControl.h"
33#include "core/Core.h"
34#include "core/CoreIncludes.h"
35#include "core/Iterator.h"
36
37#include <zlib.h>
38#include <cassert>
39
40
41
42namespace orxonox {
43
44namespace packet {
45
46#define GAMESTATE_START(data) (data + GamestateHeader::getSize())
47
48#define PACKET_FLAG_GAMESTATE  ENET_PACKET_FLAG_RELIABLE
49
50// Gamestate::Gamestate()
51// {
52//   flags_ = flags_ | PACKET_FLAG_GAMESTATE;
53// }
54
55Gamestate::Gamestate()
56{
57  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
58  header_ = 0;
59}
60
61Gamestate::Gamestate(uint8_t *data, unsigned int clientID):
62    Packet(data, clientID)
63{
64  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
65  header_ = new GamestateHeader(data_);
66}
67
68Gamestate::Gamestate(uint8_t *data)
69{
70  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
71  data_=data;
72  header_ = new GamestateHeader(data_);
73}
74
75Gamestate::Gamestate(const Gamestate& g) :
76    Packet( *(Packet*)&g )
77{
78  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
79  header_ = new GamestateHeader(data_);
80}
81
82
83Gamestate::~Gamestate()
84{
85}
86
87bool Gamestate::collectData(int id, uint8_t mode)
88{
89  assert(this->header_==0); // make sure the header didn't exist before
90  uint32_t tempsize=0, currentsize=0;
91  assert(data_==0);
92  uint32_t size = calcGamestateSize(id, mode);
93
94  COUT(4) << "G.ST.Man: producing gamestate with id: " << id << std::endl;
95  if(size==0)
96    return false;
97  data_ = new uint8_t[size + GamestateHeader::getSize()];
98  if(!data_){
99    COUT(2) << "GameStateManager: could not allocate memory" << std::endl;
100    return false;
101  }
102 
103  // create the header object
104  header_ = new GamestateHeader(data_);
105
106  //start collect data synchronisable by synchronisable
107  uint8_t *mem=data_;
108  mem += GamestateHeader::getSize();
109  ObjectList<Synchronisable>::iterator it;
110  for(it = ObjectList<Synchronisable>::begin(); it; ++it){
111   
112    tempsize=it->getSize(id, mode);
113#ifndef NDEBUG
114    if(currentsize+tempsize > size){
115      assert(0); // if we don't use multithreading this part shouldn't be neccessary
116      // start allocate additional memory
117      COUT(3) << "G.St.Man: need additional memory" << std::endl;
118      ObjectList<Synchronisable>::iterator temp = it;
119      uint32_t addsize=tempsize;
120      while(++temp)
121        addsize+=temp->getSize(id, mode);
122      data_ = (uint8_t *)realloc(data_, GamestateHeader::getSize() + currentsize + addsize);
123      if(!data_)
124        return false;
125      size = currentsize+addsize;
126    }// stop allocate additional memory
127#endif
128
129    //if(it->doSelection(id))
130    if ( it->doSync( id, mode ) )
131      dataMap_.push_back( obj(it->getObjectID(), it->getCreatorID(), tempsize, mem-data_) );
132//     dataMap_[mem-data_]=(*it);  // save the mem location of the synchronisable data
133    if(!it->getData(mem, id, mode))
134      return false; // mem pointer gets automatically increased because of call by reference
135    // increase size counter by size of current synchronisable
136    currentsize+=tempsize;
137  }
138
139
140  //start write gamestate header
141  header_->setDataSize( currentsize );
142  header_->setID( id );
143  header_->setDiffed( false );
144  header_->setComplete( true );
145  header_->setCompressed( false );
146  //stop write gamestate header
147
148  COUT(5) << "G.ST.Man: Gamestate size: " << currentsize << std::endl;
149  COUT(5) << "G.ST.Man: 'estimated' (and corrected) Gamestate size: " << size << std::endl;
150  return true;
151}
152
153bool Gamestate::spreadData(uint8_t mode)
154{
155  COUT(4) << "processing gamestate with id " << header_->getID() << endl;
156  assert(data_);
157  assert(!header_->isCompressed());
158  assert(!header_->isDiffed());
159  uint8_t *mem=data_+GamestateHeader::getSize();
160    // get the start of the Synchronisable list
161  //ObjectList<Synchronisable>::iterator it=ObjectList<Synchronisable>::begin();
162  Synchronisable *s;
163
164  // update the data of the objects we received
165  while(mem < data_+GamestateHeader::getSize()+header_->getDataSize()){
166    SynchronisableHeader objectheader(mem);
167
168    s = Synchronisable::getSynchronisable( objectheader.getObjectID() );
169    if(!s)
170    {
171      if (!Core::isMaster())
172      {
173        Synchronisable::fabricate(mem, mode);
174      }
175      else
176      {
177        mem += objectheader.getDataSize();
178      }
179//         COUT(0) << "could not fabricate synchronisable: " << objectheader->objectID << " classid: " << objectheader->classID << " creator: " << objectheader->creatorID << endl;
180//       else
181//         COUT(0) << "fabricated: " << objectheader->objectID << " classid: " << objectheader->classID << " creator: "  << objectheader->creatorID << endl;
182    }
183    else
184    {
185      bool b = s->updateData(mem, mode);
186//      if(!b)
187//        COUT(0) << "data could not be updated" << endl;
188      assert(b);
189    }
190  }
191
192   // In debug mode, check first, whether there are no duplicate objectIDs
193#ifndef NDEBUG
194  ObjectList<Synchronisable>::iterator it;
195  for (it = ObjectList<Synchronisable>::begin(); it != ObjectList<Synchronisable>::end(); ++it) {
196    if (it->getObjectID() == OBJECTID_UNKNOWN) {
197      if (it->objectMode_ != 0x0) {
198        COUT(0) << "Found object with OBJECTID_UNKNOWN on the client with objectMode != 0x0!" << std::endl;
199        COUT(0) << "Possible reason for this error: Client created a synchronized object without the Server's approval." << std::endl;
200        COUT(0) << "Objects class: " << it->getIdentifier()->getName() << std::endl;
201        assert(false);
202      }
203    }
204    else {
205      ObjectList<Synchronisable>::iterator it2;
206      for (it2 = ObjectList<Synchronisable>::begin(); it2 != ObjectList<Synchronisable>::end(); ++it2) {
207        if (it->getObjectID() == it2->getObjectID() && *it != *it2) {
208           COUT(0) << "Found duplicate objectIDs on the client!" << std::endl
209                   << "Are you sure you don't create a Sychnronisable objcect with 'new' \
210                       that doesn't have objectMode = 0x0?" << std::endl;
211           assert(false);
212        }
213      }
214    }
215  }
216#endif
217
218  return true;
219}
220
221uint32_t Gamestate::getSize() const
222{
223  assert(data_);
224  if(header_->isCompressed())
225    return header_->getCompSize()+GamestateHeader::getSize();
226  else
227  {
228    return header_->getDataSize()+GamestateHeader::getSize();
229  }
230}
231
232bool Gamestate::operator==(packet::Gamestate gs){
233  uint8_t *d1 = data_+GamestateHeader::getSize();
234  uint8_t *d2 = gs.data_+GamestateHeader::getSize();
235  assert(!isCompressed());
236  assert(!gs.isCompressed());
237  while(d1<data_+header_->getDataSize())
238  {
239    if(*d1!=*d2)
240      return false;
241    d1++;
242    d2++;
243  }
244  return true;
245}
246
247bool Gamestate::process()
248{
249  return GamestateHandler::addGamestate(this, getClientID());
250}
251
252
253
254bool Gamestate::compressData()
255{
256  assert(data_);
257  assert(!header_->isCompressed());
258  uLongf buffer = (uLongf)(((header_->getDataSize() + 12)*1.01)+1);
259  if(buffer==0)
260    return false;
261
262  uint8_t *ndata = new uint8_t[buffer+GamestateHeader::getSize()];
263  uint8_t *dest = ndata + GamestateHeader::getSize();
264  //unsigned char *dest = new unsigned char[buffer];
265  uint8_t *source = data_ + GamestateHeader::getSize();
266  int retval;
267  retval = compress( dest, &buffer, source, (uLong)(header_->getDataSize()) );
268  switch ( retval ) {
269    case Z_OK: COUT(5) << "G.St.Man: compress: successfully compressed" << std::endl; break;
270    case Z_MEM_ERROR: COUT(1) << "G.St.Man: compress: not enough memory available in gamestate.compress" << std::endl; return false;
271    case Z_BUF_ERROR: COUT(2) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << std::endl; return false;
272    case Z_DATA_ERROR: COUT(2) << "G.St.Man: compress: data corrupted in gamestate.compress" << std::endl; return false;
273  }
274
275  //copy and modify header
276  GamestateHeader *temp = header_;
277  header_ = new GamestateHeader(ndata, temp);
278  delete temp;
279  //delete old data
280  delete[] data_;
281  //save new data
282  data_ = ndata;
283  header_->setCompSize( buffer );
284  header_->setCompressed( true );
285  COUT(5) << "gamestate compress datasize: " << header_->getDataSize() << " compsize: " << header_->getCompSize() << std::endl;
286  return true;
287}
288bool Gamestate::decompressData()
289{
290  assert(data_);
291  assert(header_->isCompressed());
292  COUT(4) << "GameStateClient: uncompressing gamestate. id: " << header_->getID() << ", baseid: " << header_->getBaseID() << ", datasize: " << header_->getDataSize() << ", compsize: " << header_->getCompSize() << std::endl;
293  uint32_t datasize = header_->getDataSize();
294  uint32_t compsize = header_->getCompSize();
295  uint32_t bufsize;
296//  assert(compsize<=datasize);
297  bufsize = datasize;
298  assert(bufsize!=0);
299  uint8_t *ndata = new uint8_t[bufsize + GamestateHeader::getSize()];
300  uint8_t *dest = ndata + GamestateHeader::getSize();
301  uint8_t *source = data_ + GamestateHeader::getSize();
302  int retval;
303  uLongf length=bufsize;
304  retval = uncompress( dest, &length, source, (uLong)compsize );
305  switch ( retval ) {
306    case Z_OK: COUT(5) << "successfully decompressed" << std::endl; break;
307    case Z_MEM_ERROR: COUT(1) << "not enough memory available" << std::endl; return false;
308    case Z_BUF_ERROR: COUT(2) << "not enough memory available in the buffer" << std::endl; return false;
309    case Z_DATA_ERROR: COUT(2) << "data corrupted (zlib)" << std::endl; return false;
310  }
311
312  //copy over the header
313  GamestateHeader *temp = header_;
314  header_ = new GamestateHeader( data_, header_ );
315  delete temp;
316
317  if (this->bDataENetAllocated_){
318    // Memory was allocated by ENet. --> We let it be since enet_packet_destroy will
319    // deallocated it anyway. So data and packet stay together.
320    this->bDataENetAllocated_ = false;
321  }
322  else{
323    // We allocated the memory in the first place (unlikely). So we destroy the old data
324    // and overwrite it with the new decompressed data.
325    delete[] this->data_;
326  }
327
328  //set new pointers
329  data_ = ndata;
330  header_->setCompressed( false );
331  assert(header_->getDataSize()==datasize);
332  assert(header_->getCompSize()==compsize);
333  return true;
334}
335
336Gamestate *Gamestate::diff(Gamestate *base)
337{
338  assert(data_);
339  assert(!header_->isCompressed());
340  assert(!header_->isDiffed());
341  GamestateHeader diffHeader(base->data_);
342  //unsigned char *basep = base->getGs()/*, *gs = getGs()*/;
343  uint8_t *basep = GAMESTATE_START(base->data_), *gs = GAMESTATE_START(this->data_);
344  uint32_t of=0; // pointers offset
345  uint32_t dest_length=0;
346  dest_length=header_->getDataSize();
347  if(dest_length==0)
348    return NULL;
349  uint8_t *ndata = new uint8_t[dest_length*sizeof(uint8_t)+GamestateHeader::getSize()];
350  uint8_t *dest = ndata + GamestateHeader::getSize();
351  while(of < diffHeader.getDataSize() && of < header_->getDataSize()){
352    *(dest+of)=*(basep+of)^*(gs+of); // do the xor
353    ++of;
354  }
355  if(diffHeader.getDataSize()!=header_->getDataSize()){
356    uint8_t n=0;
357    if(diffHeader.getDataSize() < header_->getDataSize()){
358      while(of<dest_length){
359        *(dest+of)=n^*(gs+of);
360        of++;
361      }
362    }
363  }
364
365  Gamestate *g = new Gamestate(ndata, getClientID());
366  *(g->header_) = *header_;
367  g->header_->setDiffed( true );
368  g->header_->setBaseID( base->getID() );
369  g->flags_=flags_;
370  g->packetDirection_ = packetDirection_;
371  return g;
372}
373
374Gamestate* Gamestate::doSelection(unsigned int clientID, unsigned int targetSize){
375  assert(data_);
376  std::list<obj>::iterator it;
377
378  // allocate memory for new data
379  uint8_t *gdata = new uint8_t[header_->getDataSize()+GamestateHeader::getSize()];
380  // create a gamestate out of it
381  Gamestate *gs = new Gamestate(gdata);
382  uint8_t *newdata = gdata + GamestateHeader::getSize();
383  uint8_t *origdata = GAMESTATE_START(data_);
384
385  //copy the GamestateHeader
386  assert(gs->header_);
387  *(gs->header_) = *header_;
388
389  uint32_t objectOffset;
390  unsigned int objectsize, destsize=0;
391  // TODO: Why is this variable not used?
392  //Synchronisable *object;
393
394  //call TrafficControl
395  TrafficControl::getInstance()->processObjectList( clientID, header_->getID(), &dataMap_ );
396
397  //copy in the zeros
398  for(it=dataMap_.begin(); it!=dataMap_.end();){
399//    if((*it).objSize==0)
400//      continue;
401//    if(it->second->getSize(HEADER->id)==0) // merged from objecthierarchy2, doesn't work anymore; TODO: change this
402//      continue;                            // merged from objecthierarchy2, doesn't work anymore; TODO: change this
403    SynchronisableHeader oldobjectheader(origdata);
404    SynchronisableHeader newobjectheader(newdata);
405    if ( (*it).objSize == 0 )
406    {
407      ++it;
408      continue;
409    }
410//     object = Synchronisable::getSynchronisable( (*it).objID );
411//     assert(object->objectID == oldobjectheader->objectID);
412    objectsize = oldobjectheader.getDataSize();
413    objectOffset=SynchronisableHeader::getSize(); //skip the size and the availableData variables in the objectheader
414    if ( (*it).objID == oldobjectheader.getObjectID() ){
415      memcpy(newdata, origdata, objectsize);
416      assert(newobjectheader.isDataAvailable()==true);
417      ++it;
418    }else{
419      newobjectheader = oldobjectheader;
420      newobjectheader.setDataAvailable(false);
421      memset(newdata+objectOffset, 0, objectsize-objectOffset);
422    }
423    newdata += objectsize;
424    origdata += objectsize;
425    destsize += objectsize;
426  }
427#ifndef NDEBUG
428  uint32_t origsize = destsize;
429  while ( origsize < header_->getDataSize() )
430  {
431    SynchronisableHeader oldobjectheader(origdata);
432    objectsize = oldobjectheader.getDataSize();
433    origdata += objectsize;
434    origsize += objectsize;
435  }
436  assert(origsize==header_->getDataSize());
437  assert(destsize!=0);
438#endif
439  gs->header_->setDataSize( destsize );
440  return gs;
441}
442
443
444Gamestate *Gamestate::undiff(Gamestate *base)
445{
446  assert(this && base);assert(data_);
447  assert(header_->isDiffed());
448  assert(!header_->isCompressed() && !base->header_->isCompressed());
449  //unsigned char *basep = base->getGs()/*, *gs = getGs()*/;
450  uint8_t *basep = GAMESTATE_START(base->data_);
451  uint8_t *gs = GAMESTATE_START(this->data_);
452  uint32_t of=0; // pointers offset
453  uint32_t dest_length=0;
454  dest_length=header_->getDataSize();
455  if(dest_length==0)
456    return NULL;
457  uint8_t *ndata = new uint8_t[dest_length*sizeof(uint8_t)+GamestateHeader::getSize()];
458  uint8_t *dest = ndata + GamestateHeader::getSize();
459  while(of < base->header_->getDataSize() && of < header_->getDataSize()){
460    *(dest+of)=*(basep+of)^*(gs+of); // do the xor
461    ++of;
462  }
463  if(base->header_->getDataSize()!=header_->getDataSize()){
464    uint8_t n=0;
465    if(base->header_->getDataSize() < header_->getDataSize()){
466      while(of < dest_length){
467        *(dest+of)=n^*(gs+of);
468        of++;
469      }
470    }
471  }
472  Gamestate *g = new Gamestate(ndata, getClientID());
473  assert(g->header_);
474  *(g->header_) = *header_;
475  g->header_->setDiffed( false );
476  g->flags_=flags_;
477  g->packetDirection_ = packetDirection_;
478  assert(!g->isDiffed());
479  assert(!g->isCompressed());
480  return g;
481}
482
483
484uint32_t Gamestate::calcGamestateSize(int32_t id, uint8_t mode)
485{
486  uint32_t size=0;
487    // get the start of the Synchronisable list
488  ObjectList<Synchronisable>::iterator it;
489    // get total size of gamestate
490  for(it = ObjectList<Synchronisable>::begin(); it; ++it)
491    size+=it->getSize(id, mode); // size of the actual data of the synchronisable
492  return size;
493}
494
495} //namespace packet
496} //namespace orxonox
Note: See TracBrowser for help on using the repository browser.