Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1827 was 1827, checked in by scheusso, 16 years ago

another new gamestate concept ;)

  • server does an individual object composition (to be sent) for every client
  • atm objects have sync frequencies and are priorized after their frequency (not clienbased yet)
  • after highlevel diff (object composition) a lowlevel diff is done
  • afterwards compression

→ 65 to 80 percent less data to be transmitted

  • Property svn:eol-style set to native
File size: 18.7 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 "network/ClientInformation.h"
31#include "network/GamestateHandler.h"
32#include "core/CoreIncludes.h"
33#include "core/Iterator.h"
34
35#include <zlib.h>
36#include <assert.h>
37
38
39
40namespace network {
41
42namespace packet {
43
44#define GAMESTATE_START(data) (data + sizeof(GamestateHeader))
45#define GAMESTATE_HEADER(data) ((GamestateHeader *)data)
46#define HEADER GAMESTATE_HEADER(data_)
47
48 
49Gamestate::Gamestate()
50{
51}
52
53Gamestate::Gamestate(unsigned char *data, int clientID):
54    Packet(data, clientID)
55{
56}
57
58Gamestate::Gamestate(unsigned char *data)
59{
60  data_=data;
61}
62
63
64Gamestate::~Gamestate()
65{
66}
67
68bool Gamestate::collectData(int id, int mode)
69{
70  int tempsize=0, currentsize=0;
71  assert(data_==0);
72  int size = calcGamestateSize(id, mode);
73
74  COUT(4) << "G.ST.Man: producing gamestate with id: " << id << std::endl;
75  if(size==0)
76    return false;
77  data_ = new unsigned char[size + sizeof(GamestateHeader)];
78  if(!data_){
79    COUT(2) << "GameStateManager: could not allocate memory" << std::endl;
80    return false;
81  }
82
83  //start collect data synchronisable by synchronisable
84  unsigned char *mem=data_;
85  mem+=sizeof(GamestateHeader);
86  orxonox::ObjectList<Synchronisable>::iterator it;
87  for(it = orxonox::ObjectList<Synchronisable>::begin(); it; ++it){
88    tempsize=it->getSize(id, mode);
89
90    if(currentsize+tempsize > size){
91      // start allocate additional memory
92      COUT(3) << "G.St.Man: need additional memory" << std::endl;
93      orxonox::ObjectList<Synchronisable>::iterator temp = it;
94      int addsize=tempsize;
95      while(++temp)
96        addsize+=temp->getSize(id, mode);
97      data_ = (unsigned char *)realloc(data_, sizeof(GamestateHeader) + currentsize + addsize);
98      if(!data_)
99        return false;
100      size = currentsize+addsize;
101    }// stop allocate additional memory
102
103    //if(it->doSelection(id))
104    dataMap_[mem-data_]=(*it);  // save the mem location of the synchronisable data
105    if(!it->getData(mem, id, mode))
106      return false; // mem pointer gets automatically increased because of call by reference
107    // increase size counter by size of current synchronisable
108    currentsize+=tempsize;
109  }
110
111
112  //start write gamestate header
113  HEADER->packetType = ENUM::Gamestate;
114  assert( *(ENUM::Type *)(data_) == ENUM::Gamestate);
115  HEADER->datasize = currentsize;
116  HEADER->id = id;
117  HEADER->diffed = false;
118  HEADER->complete = true;
119  HEADER->compressed = false;
120  //stop write gamestate header
121
122  COUT(5) << "G.ST.Man: Gamestate size: " << currentsize << std::endl;
123  COUT(5) << "G.ST.Man: 'estimated' (and corrected) Gamestate size: " << size << std::endl;
124  return true;
125}
126
127bool Gamestate::spreadData(int mode)
128{
129  assert(data_);
130  assert(!HEADER->compressed);
131  assert(!HEADER->diffed);
132  unsigned char *mem=data_+sizeof(GamestateHeader);
133    // get the start of the Synchronisable list
134  //orxonox::ObjectList<Synchronisable>::iterator it=orxonox::ObjectList<Synchronisable>::begin();
135  Synchronisable *s;
136
137  // update the data of the objects we received
138  while(mem < data_+sizeof(GamestateHeader)+HEADER->datasize){
139    synchronisableHeader *objectheader = (synchronisableHeader*)mem;
140
141    s = Synchronisable::getSynchronisable( objectheader->objectID );
142    if(!s)
143    {
144      s = Synchronisable::fabricate(mem, mode);
145      if(!s)
146        return false;
147    }
148    else
149    {
150      if(!s->updateData(mem, mode))
151        return false;
152    }
153  }
154
155  return true;
156}
157
158
159
160int Gamestate::getID(){
161  return HEADER->id;
162}
163
164unsigned int Gamestate::getSize() const
165{
166  assert(data_);
167  if(HEADER->compressed)
168    return HEADER->compsize+sizeof(GamestateHeader);
169  else
170  {
171    return HEADER->datasize+sizeof(GamestateHeader);
172  }
173}
174
175bool Gamestate::operator==(packet::Gamestate gs){
176  unsigned char *d1 = data_+sizeof(GamestateHeader);
177  unsigned char *d2 = gs.data_+sizeof(GamestateHeader);
178  assert(!isCompressed());
179  assert(!gs.isCompressed());
180  while(d1<data_+HEADER->datasize)
181  {
182    if(*d1!=*d2)
183      return false;
184    d1++;
185    d2++;
186  }
187  return true;
188}
189
190bool Gamestate::process()
191{
192  return GamestateHandler::addGamestate(this, getClientID());
193}
194
195
196
197bool Gamestate::compressData()
198{
199  assert(HEADER);
200  assert(!HEADER->compressed);
201  uLongf buffer = (uLongf)(((HEADER->datasize + 12)*1.01)+1);
202  if(buffer==0)
203    return false;
204
205  unsigned char *ndata = new unsigned char[buffer+sizeof(GamestateHeader)];
206  unsigned char *dest = GAMESTATE_START(ndata);
207  //unsigned char *dest = new unsigned char[buffer];
208  unsigned char *source = GAMESTATE_START(data_);
209  int retval;
210  retval = compress( dest, &buffer, source, (uLong)(HEADER->datasize) );
211  switch ( retval ) {
212    case Z_OK: COUT(5) << "G.St.Man: compress: successfully compressed" << std::endl; break;
213    case Z_MEM_ERROR: COUT(1) << "G.St.Man: compress: not enough memory available in gamestate.compress" << std::endl; return false;
214    case Z_BUF_ERROR: COUT(2) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << std::endl; return false;
215    case Z_DATA_ERROR: COUT(2) << "G.St.Man: compress: data corrupted in gamestate.compress" << std::endl; return false;
216  }
217#ifndef NDEBUG
218  //decompress and compare the start and the decompressed data
219  unsigned char *rdata = new unsigned char[HEADER->datasize+sizeof(GamestateHeader)];
220  unsigned char *d2 = GAMESTATE_START(rdata);
221  uLongf length2 = HEADER->datasize;
222  uncompress(d2, &length2, dest, buffer);
223  for(unsigned int i=0; i<HEADER->datasize; i++){
224    assert(*(source+i)==*(d2+i));
225  }
226  delete[] rdata;
227#endif
228
229  //copy and modify header
230#ifndef NDEBUG
231  HEADER->crc32 = calcCRC(data_+sizeof(GamestateHeader), HEADER->datasize);
232#endif
233  *GAMESTATE_HEADER(ndata) = *HEADER;
234  //delete old data
235  delete[] data_;
236  //save new data
237  data_ = ndata;
238  HEADER->compsize = buffer;
239  HEADER->compressed = true;
240  assert(HEADER->compressed);
241  COUT(3) << "gamestate compress datasize: " << HEADER->datasize << " compsize: " << HEADER->compsize << std::endl;
242  return true;
243}
244bool Gamestate::decompressData()
245{
246  assert(HEADER);
247  assert(HEADER->compressed);
248  COUT(3) << "GameStateClient: uncompressing gamestate. id: " << HEADER->id << ", baseid: " << HEADER->base_id << ", datasize: " << HEADER->datasize << ", compsize: " << HEADER->compsize << std::endl;
249  unsigned int datasize = HEADER->datasize;
250  unsigned int compsize = HEADER->compsize;
251  unsigned int bufsize;
252  assert(compsize<=datasize);
253  bufsize = datasize;
254  assert(bufsize!=0);
255  unsigned char *ndata = new unsigned char[bufsize + sizeof(GamestateHeader)];
256  unsigned char *dest = ndata + sizeof(GamestateHeader);
257  unsigned char *source = data_ + sizeof(GamestateHeader);
258  int retval;
259  uLongf length=bufsize;
260  retval = uncompress( dest, &length, source, (uLong)compsize );
261  switch ( retval ) {
262    case Z_OK: COUT(5) << "successfully decompressed" << std::endl; break;
263    case Z_MEM_ERROR: COUT(1) << "not enough memory available" << std::endl; return false;
264    case Z_BUF_ERROR: COUT(2) << "not enough memory available in the buffer" << std::endl; return false;
265    case Z_DATA_ERROR: COUT(2) << "data corrupted (zlib)" << std::endl; return false;
266  }
267#ifndef NDEBUG
268  assert(HEADER->crc32==calcCRC(ndata+sizeof(GamestateHeader), HEADER->datasize));
269#endif
270
271  //copy over the header
272  *GAMESTATE_HEADER(ndata) = *HEADER;
273  //delete old (compressed data)
274  delete[] data_;
275  //set new pointers
276  data_ = ndata;
277  HEADER->compressed = false;
278  assert(HEADER->datasize==datasize);
279  assert(HEADER->compsize==compsize);
280  return true;
281}
282
283Gamestate *Gamestate::diff(Gamestate *base)
284{
285  assert(HEADER);
286  assert(!HEADER->compressed);
287  assert(!HEADER->diffed);
288  //unsigned char *basep = base->getGs()/*, *gs = getGs()*/;
289  unsigned char *basep = GAMESTATE_START(base->data_), *gs = GAMESTATE_START(this->data_);
290  unsigned int of=0; // pointers offset
291  unsigned int dest_length=0;
292  dest_length=HEADER->datasize;
293  if(dest_length==0)
294    return NULL;
295  unsigned char *ndata = new unsigned char[dest_length*sizeof(unsigned char)+sizeof(GamestateHeader)];
296  unsigned char *dest = ndata + sizeof(GamestateHeader);
297  while(of < GAMESTATE_HEADER(base->data_)->datasize && of < HEADER->datasize){
298    *(dest+of)=*(basep+of)^*(gs+of); // do the xor
299    ++of;
300  }
301  if(GAMESTATE_HEADER(base->data_)->datasize!=HEADER->datasize){
302    unsigned char n=0;
303    if(GAMESTATE_HEADER(base->data_)->datasize < HEADER->datasize){
304      while(of<dest_length){
305        *(dest+of)=n^*(gs+of);
306        of++;
307      }
308    }
309  }
310
311  *GAMESTATE_HEADER(ndata) = *HEADER;
312  GAMESTATE_HEADER(ndata)->diffed = true;
313  GAMESTATE_HEADER(ndata)->base_id = base->getID();
314  Gamestate *g = new Gamestate(ndata, getClientID());
315  g->flags_=flags_;
316  g->packetDirection_ = packetDirection_;
317  return g;
318}
319
320Gamestate* Gamestate::doSelection(unsigned int clientID){
321  assert(data_);
322  std::map<unsigned int, Synchronisable *>::iterator it;
323 
324  Gamestate *gs = new Gamestate(*this);
325  unsigned char *ndata = gs->data_ + sizeof(GamestateHeader);
326  synchronisableHeader *objectheader;
327  unsigned int objectOffset;
328 
329  //copy in the zeros
330  for(it=dataMap_.begin(); it!=dataMap_.end(); it++){
331    objectheader = (synchronisableHeader*)ndata;
332    unsigned int objectsize = objectheader->size;
333    assert(it->second->objectID==objectheader->objectID);
334    objectOffset=sizeof(unsigned int)+sizeof(bool); //skip the size and the availableDate variables in the objectheader
335    if(!it->second->doSelection(HEADER->id)){
336      while(objectOffset<objectsize){
337        objectheader->dataAvailable=false;
338        *(ndata+objectOffset)=0;    // set to 0
339        objectOffset++;
340      }
341      assert(objectOffset==objectsize);
342    }
343    ndata+=objectsize;
344  }
345  return gs;
346}
347
348
349Gamestate* Gamestate::intelligentDiff(Gamestate *base, unsigned int clientID){
350  // asserts
351  assert(data_);
352  assert(base->data_);
353  assert(!GAMESTATE_HEADER(base->data_)->diffed);
354  assert(!GAMESTATE_HEADER(base->data_)->compressed);
355  assert(!HEADER->compressed);
356  assert(!HEADER->diffed);
357 
358  //preparations
359  std::map<unsigned int, Synchronisable *>::iterator it;
360  unsigned char *origdata, *basedata, *destdata, *ndata;
361  unsigned int objectOffset, streamOffset=0;    //data offset
362  unsigned int minsize = (HEADER->datasize < GAMESTATE_HEADER(base->data_)->datasize) ? HEADER->datasize : GAMESTATE_HEADER(base->data_)->datasize; 
363  synchronisableHeader *origheader;
364  synchronisableHeader *destheader;
365 
366  origdata = GAMESTATE_START(this->data_);
367  basedata = GAMESTATE_START(base->data_);
368  ndata = new unsigned char[HEADER->datasize + sizeof(GamestateHeader)];
369  destdata = ndata + sizeof(GamestateHeader);
370 
371  // do the diff
372  for(it=dataMap_.begin(); it!=dataMap_.end(); it++){
373    assert(streamOffset<HEADER->datasize);
374    bool sendData = it->second->doSelection(HEADER->id);
375    origheader = (synchronisableHeader *)(origdata+streamOffset);
376    destheader = (synchronisableHeader *)(destdata+streamOffset);
377   
378    //copy and partially diff the object header
379    assert(sizeof(synchronisableHeader)==3*sizeof(unsigned int)+sizeof(bool));
380    *(unsigned int*)destdata = *(unsigned int*)origdata; //size (do not diff)
381    *(bool*)(destdata+sizeof(unsigned int)) = sendData;
382    if(sendData){
383      *(unsigned int*)(destdata+sizeof(unsigned int)+sizeof(bool)) = *(unsigned int*)(basedata+sizeof(unsigned int)+sizeof(bool)) ^ *(unsigned int*)(origdata+sizeof(unsigned int)+sizeof(bool)); //objectid (diff it)
384      *(unsigned int*)(destdata+2*sizeof(unsigned int)+sizeof(bool)) = *(unsigned int*)(basedata+2*sizeof(unsigned int)+sizeof(bool)) ^ *(unsigned int*)(origdata+2*sizeof(unsigned int)+sizeof(bool)); //classid (diff it)
385    }else{
386      *(unsigned int*)(destdata+sizeof(unsigned int)+sizeof(bool)) = 0;
387      *(unsigned int*)(destdata+2*sizeof(unsigned int)+sizeof(bool)) = 0; 
388    }
389    objectOffset=sizeof(synchronisableHeader);
390    streamOffset+=sizeof(synchronisableHeader);
391   
392    //now handle the object data or fill with zeros
393    while(objectOffset<origheader->size ){
394     
395      if(sendData && streamOffset<minsize)
396        *(destdata+objectOffset)=*(basedata+objectOffset)^*(origdata+objectOffset); // do the xor
397      else if(sendData)
398        *(destdata+objectOffset)=((unsigned char)0)^*(origdata+objectOffset); // xor with 0 (basestream is too short)
399      else
400        *(destdata+objectOffset)=0; // set to 0 because this object should not be transfered
401     
402      objectOffset++;
403      streamOffset++;
404    }
405    destdata+=objectOffset;
406    origdata+=objectOffset;
407    basedata+=objectOffset;
408  }
409 
410  //copy over the gamestate header and set the diffed flag
411  *(GamestateHeader *)ndata = *HEADER; //copy over the header
412  Gamestate *gs = new Gamestate(ndata);
413  GAMESTATE_HEADER(ndata)->diffed=true;
414  return gs;
415}
416
417Gamestate* Gamestate::intelligentUnDiff(Gamestate *base){
418  // asserts
419  assert(data_);
420  assert(base->data_);
421  assert(!GAMESTATE_HEADER(base->data_)->diffed);
422  assert(!GAMESTATE_HEADER(base->data_)->compressed);
423  assert(!HEADER->compressed);
424  assert(HEADER->diffed);
425 
426  //preparations
427  std::map<unsigned int, Synchronisable *>::iterator it;
428  unsigned char *origdata, *basedata, *destdata, *ndata;
429  unsigned int objectOffset, streamOffset=0;    //data offset
430  unsigned int minsize = (HEADER->datasize < GAMESTATE_HEADER(base->data_)->datasize) ? HEADER->datasize : GAMESTATE_HEADER(base->data_)->datasize; 
431  synchronisableHeader *origheader;
432  synchronisableHeader *destheader;
433 
434  origdata = GAMESTATE_START(this->data_);
435  basedata = GAMESTATE_START(base->data_);
436  ndata = new unsigned char[HEADER->datasize + sizeof(GamestateHeader)];
437  destdata = ndata + sizeof(GamestateHeader);
438 
439  // do the undiff
440  for(it=dataMap_.begin(); it!=dataMap_.end(); it++){
441    assert(streamOffset<HEADER->datasize);
442    origheader = (synchronisableHeader *)(origdata+streamOffset);
443    destheader = (synchronisableHeader *)(destdata+streamOffset);
444    bool sendData;
445   
446    //copy and partially diff the object header
447    assert(sizeof(synchronisableHeader)==3*sizeof(unsigned int)+sizeof(bool));
448    *(unsigned int*)destdata = *(unsigned int*)origdata; //size (do not diff)
449    *(bool*)(destdata+sizeof(unsigned int)) = *(bool*)(origdata+sizeof(unsigned int));
450    sendData = *(bool*)(origdata+sizeof(unsigned int));
451    if(sendData){
452      *(unsigned int*)(destdata+sizeof(unsigned int)+sizeof(bool)) = *(unsigned int*)(basedata+sizeof(unsigned int)+sizeof(bool)) ^ *(unsigned int*)(origdata+sizeof(unsigned int)+sizeof(bool)); //objectid (diff it)
453      *(unsigned int*)(destdata+2*sizeof(unsigned int)+sizeof(bool)) = *(unsigned int*)(basedata+2*sizeof(unsigned int)+sizeof(bool)) ^ *(unsigned int*)(origdata+2*sizeof(unsigned int)+sizeof(bool)); //classid (diff it)
454    }else{
455      *(unsigned int*)(destdata+sizeof(unsigned int)+sizeof(bool)) = 0;
456      *(unsigned int*)(destdata+2*sizeof(unsigned int)+sizeof(bool)) = 0; 
457    }
458    objectOffset=sizeof(synchronisableHeader);
459    streamOffset+=sizeof(synchronisableHeader);
460   
461    //now handle the object data or fill with zeros
462    while(objectOffset<origheader->size ){
463     
464      if(sendData && streamOffset<minsize)
465        *(destdata+objectOffset)=*(basedata+objectOffset)^*(origdata+objectOffset); // do the xor
466      else if(sendData)
467        *(destdata+objectOffset)=((unsigned char)0)^*(origdata+objectOffset); // xor with 0 (basestream is too short)
468      else
469        *(destdata+objectOffset)=0; // set to 0 because this object should not be transfered
470     
471      objectOffset++;
472      streamOffset++;
473    }
474    destdata+=objectOffset;
475    origdata+=objectOffset;
476    basedata+=objectOffset;
477  }
478 
479  //copy over the gamestate header and set the diffed flag
480  *(GamestateHeader *)ndata = *HEADER; //copy over the header
481  Gamestate *gs = new Gamestate(ndata);
482  GAMESTATE_HEADER(ndata)->diffed=false;
483  return gs;
484}
485
486Gamestate *Gamestate::undiff(Gamestate *base)
487{
488  assert(this && base);assert(HEADER);
489  assert(HEADER->diffed);
490  assert(!HEADER->compressed && !GAMESTATE_HEADER(base->data_)->compressed);
491  //unsigned char *basep = base->getGs()/*, *gs = getGs()*/;
492  unsigned char *basep = GAMESTATE_START(base->data_);
493  unsigned char *gs = GAMESTATE_START(this->data_);
494  unsigned int of=0; // pointers offset
495  unsigned int dest_length=0;
496  dest_length=HEADER->datasize;
497  if(dest_length==0)
498    return NULL;
499  unsigned char *ndata = new unsigned char[dest_length*sizeof(unsigned char)+sizeof(GamestateHeader)];
500  unsigned char *dest = ndata + sizeof(GamestateHeader);
501  while(of < GAMESTATE_HEADER(base->data_)->datasize && of < HEADER->datasize){
502    *(dest+of)=*(basep+of)^*(gs+of); // do the xor
503    ++of;
504  }
505  if(GAMESTATE_HEADER(base->data_)->datasize!=HEADER->datasize){
506    unsigned char n=0;
507    if(GAMESTATE_HEADER(base->data_)->datasize < HEADER->datasize){
508      while(of < dest_length){
509        *(dest+of)=n^*(gs+of);
510        of++;
511      }
512    }
513  }
514  *GAMESTATE_HEADER(ndata) = *HEADER;
515  GAMESTATE_HEADER(ndata)->diffed = false;
516  Gamestate *g = new Gamestate(ndata, getClientID());
517  g->flags_=flags_;
518  g->packetDirection_ = packetDirection_;
519  assert(!g->isDiffed());
520  assert(!g->isCompressed());
521  return g;
522}
523
524
525unsigned int Gamestate::calcGamestateSize(unsigned int id, int mode)
526{
527  int size=0;
528    // get the start of the Synchronisable list
529  orxonox::ObjectList<Synchronisable>::iterator it;
530    // get total size of gamestate
531  for(it = orxonox::ObjectList<Synchronisable>::begin(); it; ++it)
532    size+=it->getSize(id, mode); // size of the actual data of the synchronisable
533//  size+=sizeof(GamestateHeader);
534  return size;
535}
536
537/**
538 * This function removes a Synchronisable out of the universe
539 * @param it iterator of the list pointing to the object
540 * @return iterator pointing to the next object in the list
541 */
542  void Gamestate::removeObject(orxonox::ObjectList<Synchronisable>::iterator &it) {
543    orxonox::ObjectList<Synchronisable>::iterator temp=it;
544    ++it;
545    delete  *temp;
546  }
547
548  bool Gamestate::isDiffed(){
549    return HEADER->diffed;
550  }
551
552  bool Gamestate::isCompressed(){
553    return HEADER->compressed;
554  }
555
556  int Gamestate::getBaseID(){
557    return HEADER->base_id;
558  }
559}
560
561}
Note: See TracBrowser for help on using the repository browser.