Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/questsystem5/src/network/packet/Gamestate.cc @ 2916

Last change on this file since 2916 was 2908, checked in by dafrick, 16 years ago

Reverted to revision 2906 (because I'm too stupid to merge correctly, 2nd try will follow shortly. ;))

  • Property svn:eol-style set to native
File size: 14.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 <enet/enet.h>
31#include <zlib.h>
32#include <cassert>
33#include "../GamestateHandler.h"
34#include "../synchronisable/Synchronisable.h"
35#include "../TrafficControl.h"
36#include "core/Core.h"
37#include "core/CoreIncludes.h"
38#include "core/Iterator.h"
39
40
41
42
43namespace orxonox {
44
45namespace packet {
46
47#define GAMESTATE_START(data) (data + GamestateHeader::getSize())
48
49#define PACKET_FLAG_GAMESTATE  ENET_PACKET_FLAG_RELIABLE
50
51
52Gamestate::Gamestate()
53{
54  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
55  header_ = 0;
56}
57
58Gamestate::Gamestate(uint8_t *data, unsigned int clientID):
59    Packet(data, clientID)
60{
61  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
62  header_ = new GamestateHeader(data_);
63}
64
65Gamestate::Gamestate(uint8_t *data)
66{
67  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
68  data_=data;
69  header_ = new GamestateHeader(data_);
70}
71
72Gamestate::Gamestate(const Gamestate& g) :
73    Packet( *(Packet*)&g )
74{
75  flags_ = flags_ | PACKET_FLAG_GAMESTATE;
76  header_ = new GamestateHeader(data_);
77}
78
79
80Gamestate::~Gamestate()
81{
82}
83
84bool Gamestate::collectData(int id, uint8_t mode)
85{
86  assert(this->header_==0); // make sure the header didn't exist before
87  uint32_t tempsize=0, currentsize=0;
88  assert(data_==0);
89  uint32_t size = calcGamestateSize(id, mode);
90
91  COUT(4) << "G.ST.Man: producing gamestate with id: " << id << std::endl;
92  if(size==0)
93    return false;
94  data_ = new uint8_t[size + GamestateHeader::getSize()];
95  if(!data_){
96    COUT(2) << "GameStateManager: could not allocate memory" << std::endl;
97    return false;
98  }
99 
100  // create the header object
101  header_ = new GamestateHeader(data_);
102
103  //start collect data synchronisable by synchronisable
104  uint8_t *mem=data_;
105  mem += GamestateHeader::getSize();
106  ObjectList<Synchronisable>::iterator it;
107  for(it = ObjectList<Synchronisable>::begin(); it; ++it){
108   
109    tempsize=it->getSize(id, mode);
110#ifndef NDEBUG
111    if(currentsize+tempsize > size){
112      assert(0); // if we don't use multithreading this part shouldn't be neccessary
113      // start allocate additional memory
114      COUT(3) << "G.St.Man: need additional memory" << std::endl;
115      ObjectList<Synchronisable>::iterator temp = it;
116      uint32_t addsize=tempsize;
117      while(++temp)
118        addsize+=temp->getSize(id, mode);
119      data_ = (uint8_t *)realloc(data_, GamestateHeader::getSize() + currentsize + addsize);
120      if(!data_)
121        return false;
122      size = currentsize+addsize;
123    }// stop allocate additional memory
124#endif
125
126    if ( it->doSync( id, mode ) )
127      dataMap_.push_back( obj(it->getObjectID(), it->getCreatorID(), tempsize, mem-data_) );
128    if(!it->getData(mem, id, mode))
129      return false; // mem pointer gets automatically increased because of call by reference
130    // increase size counter by size of current synchronisable
131    currentsize+=tempsize;
132  }
133
134
135  //start write gamestate header
136  header_->setDataSize( currentsize );
137  header_->setID( id );
138  header_->setDiffed( false );
139  header_->setComplete( true );
140  header_->setCompressed( false );
141  //stop write gamestate header
142
143  COUT(5) << "G.ST.Man: Gamestate size: " << currentsize << std::endl;
144  COUT(5) << "G.ST.Man: 'estimated' (and corrected) Gamestate size: " << size << std::endl;
145  return true;
146}
147
148bool Gamestate::spreadData(uint8_t mode)
149{
150  COUT(4) << "processing gamestate with id " << header_->getID() << endl;
151  assert(data_);
152  assert(!header_->isCompressed());
153  assert(!header_->isDiffed());
154  uint8_t *mem=data_+GamestateHeader::getSize();
155  Synchronisable *s;
156
157  // update the data of the objects we received
158  while(mem < data_+GamestateHeader::getSize()+header_->getDataSize()){
159    SynchronisableHeader objectheader(mem);
160
161    s = Synchronisable::getSynchronisable( objectheader.getObjectID() );
162    if(!s)
163    {
164      if (!Core::isMaster())
165      {
166        Synchronisable::fabricate(mem, mode);
167      }
168      else
169      {
170        mem += objectheader.getDataSize();
171      }
172    }
173    else
174    {
175      bool b = s->updateData(mem, mode);
176      assert(b);
177    }
178  }
179
180   // In debug mode, check first, whether there are no duplicate objectIDs
181#ifndef NDEBUG
182  ObjectList<Synchronisable>::iterator it;
183  for (it = ObjectList<Synchronisable>::begin(); it != ObjectList<Synchronisable>::end(); ++it) {
184    if (it->getObjectID() == OBJECTID_UNKNOWN) {
185      if (it->objectMode_ != 0x0) {
186        COUT(0) << "Found object with OBJECTID_UNKNOWN on the client with objectMode != 0x0!" << std::endl;
187        COUT(0) << "Possible reason for this error: Client created a synchronized object without the Server's approval." << std::endl;
188        COUT(0) << "Objects class: " << it->getIdentifier()->getName() << std::endl;
189        assert(false);
190      }
191    }
192    else {
193      ObjectList<Synchronisable>::iterator it2;
194      for (it2 = ObjectList<Synchronisable>::begin(); it2 != ObjectList<Synchronisable>::end(); ++it2) {
195        if (it->getObjectID() == it2->getObjectID() && *it != *it2) {
196           COUT(0) << "Found duplicate objectIDs on the client!" << std::endl
197                   << "Are you sure you don't create a Sychnronisable objcect with 'new' \
198                       that doesn't have objectMode = 0x0?" << std::endl;
199           assert(false);
200        }
201      }
202    }
203  }
204#endif
205
206  return true;
207}
208
209uint32_t Gamestate::getSize() const
210{
211  assert(data_);
212  if(header_->isCompressed())
213    return header_->getCompSize()+GamestateHeader::getSize();
214  else
215  {
216    return header_->getDataSize()+GamestateHeader::getSize();
217  }
218}
219
220bool Gamestate::operator==(packet::Gamestate gs){
221  uint8_t *d1 = data_+GamestateHeader::getSize();
222  uint8_t *d2 = gs.data_+GamestateHeader::getSize();
223  assert(!isCompressed());
224  assert(!gs.isCompressed());
225  while(d1<data_+header_->getDataSize())
226  {
227    if(*d1!=*d2)
228      return false;
229    d1++;
230    d2++;
231  }
232  return true;
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
322Gamestate *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::doSelection(unsigned int clientID, unsigned int targetSize){
360  assert(data_);
361  std::list<obj>::iterator it;
362
363  // allocate memory for new data
364  uint8_t *gdata = new uint8_t[header_->getDataSize()+GamestateHeader::getSize()];
365  // create a gamestate out of it
366  Gamestate *gs = new Gamestate(gdata);
367  uint8_t *newdata = gdata + GamestateHeader::getSize();
368  uint8_t *origdata = GAMESTATE_START(data_);
369
370  //copy the GamestateHeader
371  assert(gs->header_);
372  *(gs->header_) = *header_;
373
374  uint32_t objectOffset;
375  unsigned int objectsize, destsize=0;
376  // TODO: Why is this variable not used?
377  //Synchronisable *object;
378
379  //call TrafficControl
380  TrafficControl::getInstance()->processObjectList( clientID, header_->getID(), &dataMap_ );
381
382  //copy in the zeros
383  for(it=dataMap_.begin(); it!=dataMap_.end();){
384    SynchronisableHeader oldobjectheader(origdata);
385    SynchronisableHeader newobjectheader(newdata);
386    if ( (*it).objSize == 0 )
387    {
388      ++it;
389      continue;
390    }
391    objectsize = oldobjectheader.getDataSize();
392    objectOffset=SynchronisableHeader::getSize(); //skip the size and the availableData variables in the objectheader
393    if ( (*it).objID == oldobjectheader.getObjectID() ){
394      memcpy(newdata, origdata, objectsize);
395      assert(newobjectheader.isDataAvailable()==true);
396      ++it;
397    }else{
398      newobjectheader = oldobjectheader;
399      newobjectheader.setDataAvailable(false);
400      memset(newdata+objectOffset, 0, objectsize-objectOffset);
401    }
402    newdata += objectsize;
403    origdata += objectsize;
404    destsize += objectsize;
405  }
406#ifndef NDEBUG
407  uint32_t origsize = destsize;
408  while ( origsize < header_->getDataSize() )
409  {
410    SynchronisableHeader oldobjectheader(origdata);
411    objectsize = oldobjectheader.getDataSize();
412    origdata += objectsize;
413    origsize += objectsize;
414  }
415  assert(origsize==header_->getDataSize());
416  assert(destsize!=0);
417#endif
418  gs->header_->setDataSize( destsize );
419  return gs;
420}
421
422
423Gamestate *Gamestate::undiff(Gamestate *base)
424{
425  assert(this && base);assert(data_);
426  assert(header_->isDiffed());
427  assert(!header_->isCompressed() && !base->header_->isCompressed());
428  uint8_t *basep = GAMESTATE_START(base->data_);
429  uint8_t *gs = GAMESTATE_START(this->data_);
430  uint32_t of=0; // pointers offset
431  uint32_t dest_length=0;
432  dest_length=header_->getDataSize();
433  if(dest_length==0)
434    return NULL;
435  uint8_t *ndata = new uint8_t[dest_length*sizeof(uint8_t)+GamestateHeader::getSize()];
436  uint8_t *dest = ndata + GamestateHeader::getSize();
437  while(of < base->header_->getDataSize() && of < header_->getDataSize()){
438    *(dest+of)=*(basep+of)^*(gs+of); // do the xor
439    ++of;
440  }
441  if(base->header_->getDataSize()!=header_->getDataSize()){
442    uint8_t n=0;
443    if(base->header_->getDataSize() < header_->getDataSize()){
444      while(of < dest_length){
445        *(dest+of)=n^*(gs+of);
446        of++;
447      }
448    }
449  }
450  Gamestate *g = new Gamestate(ndata, getClientID());
451  assert(g->header_);
452  *(g->header_) = *header_;
453  g->header_->setDiffed( false );
454  g->flags_=flags_;
455  g->packetDirection_ = packetDirection_;
456  assert(!g->isDiffed());
457  assert(!g->isCompressed());
458  return g;
459}
460
461
462uint32_t Gamestate::calcGamestateSize(int32_t id, uint8_t mode)
463{
464  uint32_t size=0;
465    // get the start of the Synchronisable list
466  ObjectList<Synchronisable>::iterator it;
467    // get total size of gamestate
468  for(it = ObjectList<Synchronisable>::begin(); it; ++it)
469    size+=it->getSize(id, mode); // size of the actual data of the synchronisable
470  return size;
471}
472
473} //namespace packet
474} //namespace orxonox
Note: See TracBrowser for help on using the repository browser.