Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation/src/network/packet/Gamestate.cc @ 2655

Last change on this file since 2655 was 2655, checked in by scheusso, 15 years ago

finally got rid of these structs in our network datastream
this means we should now be able to play with gcc and msvc compiled versions together

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