Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

we have a new gamestate concept now: dont transmit synchronisable header of objects, that dont get updated that tick. transmit objectids of deleted objects to delete them on the client too

  • Property svn:eol-style set to native
File size: 11.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
58
59Gamestate::~Gamestate()
60{
61}
62
63bool Gamestate::collectData(int id, int mode)
64{
65  int tempsize=0, currentsize=0;
66  assert(data_==0);
67  int size = calcGamestateSize(id, mode);
68
69  COUT(4) << "G.ST.Man: producing gamestate with id: " << id << std::endl;
70  if(size==0)
71    return false;
72  data_ = new unsigned char[size + sizeof(GamestateHeader)];
73  if(!data_){
74    COUT(2) << "GameStateManager: could not allocate memory" << std::endl;
75    return false;
76  }
77
78  //start collect data synchronisable by synchronisable
79  unsigned char *mem=data_;
80  mem+=sizeof(GamestateHeader);
81  orxonox::ObjectList<Synchronisable>::iterator it;
82  for(it = orxonox::ObjectList<Synchronisable>::begin(); it; ++it){
83    tempsize=it->getSize(id, mode);
84
85    if(currentsize+tempsize > size){
86      // start allocate additional memory
87      COUT(3) << "G.St.Man: need additional memory" << std::endl;
88      orxonox::ObjectList<Synchronisable>::iterator temp = it;
89      int addsize=tempsize;
90      while(++temp)
91        addsize+=temp->getSize(id, mode);
92      data_ = (unsigned char *)realloc(data_, sizeof(GamestateHeader) + currentsize + addsize);
93      if(!data_)
94        return false;
95      size = currentsize+addsize;
96    }// stop allocate additional memory
97
98    if(!it->getData(mem, id, mode))
99      return false; // mem pointer gets automatically increased because of call by reference
100    // increase size counter by size of current synchronisable
101    currentsize+=tempsize;
102  }
103
104
105  //start write gamestate header
106  HEADER->packetType = ENUM::Gamestate;
107  assert( *(ENUM::Type *)(data_) == ENUM::Gamestate);
108  HEADER->datasize = currentsize;
109  HEADER->id = id;
110  HEADER->diffed = false;
111  HEADER->complete = true;
112  HEADER->compressed = false;
113  //stop write gamestate header
114
115  COUT(5) << "G.ST.Man: Gamestate size: " << currentsize << std::endl;
116  COUT(5) << "G.ST.Man: 'estimated' (and corrected) Gamestate size: " << size << std::endl;
117  return true;
118}
119
120bool Gamestate::spreadData(int mode)
121{
122  assert(data_);
123  assert(!HEADER->compressed);
124  assert(!HEADER->diffed);
125  unsigned int size, objectID, classID;
126  unsigned char *mem=data_+sizeof(GamestateHeader);
127    // get the start of the Synchronisable list
128  //orxonox::ObjectList<Synchronisable>::iterator it=orxonox::ObjectList<Synchronisable>::begin();
129  Synchronisable *s;
130
131  // update the data of the objects we received
132  while(mem < data_+sizeof(GamestateHeader)+HEADER->datasize){
133    size = *(unsigned int *)mem;
134    objectID = *(unsigned int*)(mem+sizeof(unsigned int));
135    classID = *(unsigned int*)(mem+2*sizeof(unsigned int));
136
137    s = Synchronisable::getSynchronisable( objectID );
138    if(!s)
139    {
140      s = Synchronisable::fabricate(mem, mode);
141      if(!s)
142        return false;
143    }
144    else
145    {
146      if(!s->updateData(mem, mode))
147        return false;
148    }
149  }
150
151  return true;
152}
153
154
155
156int Gamestate::getID(){
157  return HEADER->id;
158}
159
160unsigned int Gamestate::getSize() const
161{
162  assert(data_);
163  if(HEADER->compressed)
164    return HEADER->compsize+sizeof(GamestateHeader);
165  else
166  {
167    return HEADER->datasize+sizeof(GamestateHeader);
168  }
169}
170
171bool Gamestate::operator==(packet::Gamestate gs){
172  unsigned char *d1 = data_+sizeof(GamestateHeader);
173  unsigned char *d2 = gs.data_+sizeof(GamestateHeader);
174  assert(!isCompressed());
175  assert(!gs.isCompressed());
176  while(d1<data_+HEADER->datasize)
177  {
178    if(*d1!=*d2)
179      return false;
180    d1++;
181    d2++;
182  }
183  return true;
184}
185
186bool Gamestate::process()
187{
188  return GamestateHandler::addGamestate(this, getClientID());
189}
190
191bool Gamestate::compressData()
192{
193  assert(HEADER);
194  assert(!HEADER->compressed);
195  uLongf buffer = (uLongf)(((HEADER->datasize + 12)*1.01)+1);
196  if(buffer==0)
197    return false;
198
199  unsigned char *ndata = new unsigned char[buffer+sizeof(GamestateHeader)];
200  unsigned char *dest = GAMESTATE_START(ndata);
201  //unsigned char *dest = new unsigned char[buffer];
202  unsigned char *source = GAMESTATE_START(data_);
203  int retval;
204  retval = compress( dest, &buffer, source, (uLong)(HEADER->datasize) );
205  switch ( retval ) {
206    case Z_OK: COUT(5) << "G.St.Man: compress: successfully compressed" << std::endl; break;
207    case Z_MEM_ERROR: COUT(1) << "G.St.Man: compress: not enough memory available in gamestate.compress" << std::endl; return false;
208    case Z_BUF_ERROR: COUT(2) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << std::endl; return false;
209    case Z_DATA_ERROR: COUT(2) << "G.St.Man: compress: data corrupted in gamestate.compress" << std::endl; return false;
210  }
211#ifndef NDEBUG
212  //decompress and compare the start and the decompressed data
213  unsigned char *rdata = new unsigned char[HEADER->datasize+sizeof(GamestateHeader)];
214  unsigned char *d2 = GAMESTATE_START(rdata);
215  uLongf length2 = HEADER->datasize;
216  uncompress(d2, &length2, dest, buffer);
217  for(unsigned int i=0; i<HEADER->datasize; i++){
218    assert(*(source+i)==*(d2+i));
219  }
220  delete[] rdata;
221#endif
222
223  //copy and modify header
224#ifndef NDEBUG
225  HEADER->crc32 = calcCRC(data_+sizeof(GamestateHeader), HEADER->datasize);
226#endif
227  *GAMESTATE_HEADER(ndata) = *HEADER;
228  //delete old data
229  delete[] data_;
230  //save new data
231  data_ = ndata;
232  HEADER->compsize = buffer;
233  HEADER->compressed = true;
234  assert(HEADER->compressed);
235  COUT(3) << "gamestate compress datasize: " << HEADER->datasize << " compsize: " << HEADER->compsize << std::endl;
236  return true;
237}
238bool Gamestate::decompressData()
239{
240  assert(HEADER);
241  assert(HEADER->compressed);
242  COUT(3) << "GameStateClient: uncompressing gamestate. id: " << HEADER->id << ", baseid: " << HEADER->base_id << ", datasize: " << HEADER->datasize << ", compsize: " << HEADER->compsize << std::endl;
243  unsigned int datasize = HEADER->datasize;
244  unsigned int compsize = HEADER->compsize;
245  unsigned int bufsize;
246  assert(compsize<=datasize);
247  bufsize = datasize;
248  assert(bufsize!=0);
249  unsigned char *ndata = new unsigned char[bufsize + sizeof(GamestateHeader)];
250  unsigned char *dest = ndata + sizeof(GamestateHeader);
251  unsigned char *source = data_ + sizeof(GamestateHeader);
252  int retval;
253  uLongf length=bufsize;
254  retval = uncompress( dest, &length, source, (uLong)compsize );
255  switch ( retval ) {
256    case Z_OK: COUT(5) << "successfully decompressed" << std::endl; break;
257    case Z_MEM_ERROR: COUT(1) << "not enough memory available" << std::endl; return false;
258    case Z_BUF_ERROR: COUT(2) << "not enough memory available in the buffer" << std::endl; return false;
259    case Z_DATA_ERROR: COUT(2) << "data corrupted (zlib)" << std::endl; return false;
260  }
261#ifndef NDEBUG
262  assert(HEADER->crc32==calcCRC(ndata+sizeof(GamestateHeader), HEADER->datasize));
263#endif
264
265  //copy over the header
266  *GAMESTATE_HEADER(ndata) = *HEADER;
267  //delete old (compressed data)
268  delete[] data_;
269  //set new pointers
270  data_ = ndata;
271  HEADER->compressed = false;
272  assert(HEADER->datasize==datasize);
273  assert(HEADER->compsize==compsize);
274  return true;
275}
276
277Gamestate *Gamestate::diff(Gamestate *base)
278{
279  assert(HEADER);
280  assert(!HEADER->compressed);
281  assert(!HEADER->diffed);
282  //unsigned char *basep = base->getGs()/*, *gs = getGs()*/;
283  unsigned char *basep = GAMESTATE_START(base->data_), *gs = GAMESTATE_START(this->data_);
284  unsigned int of=0; // pointers offset
285  unsigned int dest_length=0;
286  dest_length=HEADER->datasize;
287  if(dest_length==0)
288    return NULL;
289  unsigned char *ndata = new unsigned char[dest_length*sizeof(unsigned char)+sizeof(GamestateHeader)];
290  unsigned char *dest = ndata + sizeof(GamestateHeader);
291  while(of < GAMESTATE_HEADER(base->data_)->datasize && of < HEADER->datasize){
292    *(dest+of)=*(basep+of)^*(gs+of); // do the xor
293    ++of;
294  }
295  if(GAMESTATE_HEADER(base->data_)->datasize!=HEADER->datasize){
296    unsigned char n=0;
297    if(GAMESTATE_HEADER(base->data_)->datasize < HEADER->datasize){
298      while(of<dest_length){
299        *(dest+of)=n^*(gs+of);
300        of++;
301      }
302    }
303  }
304
305  *GAMESTATE_HEADER(ndata) = *HEADER;
306  GAMESTATE_HEADER(ndata)->diffed = true;
307  GAMESTATE_HEADER(ndata)->base_id = base->getID();
308  Gamestate *g = new Gamestate(ndata, getClientID());
309  g->flags_=flags_;
310  g->packetDirection_ = packetDirection_;
311  return g;
312}
313
314Gamestate *Gamestate::undiff(Gamestate *base)
315{
316  assert(this && base);assert(HEADER);
317  assert(HEADER->diffed);
318  assert(!HEADER->compressed && !GAMESTATE_HEADER(base->data_)->compressed);
319  //unsigned char *basep = base->getGs()/*, *gs = getGs()*/;
320  unsigned char *basep = GAMESTATE_START(base->data_);
321  unsigned char *gs = GAMESTATE_START(this->data_);
322  unsigned int of=0; // pointers offset
323  unsigned int dest_length=0;
324  dest_length=HEADER->datasize;
325  if(dest_length==0)
326    return NULL;
327  unsigned char *ndata = new unsigned char[dest_length*sizeof(unsigned char)+sizeof(GamestateHeader)];
328  unsigned char *dest = ndata + sizeof(GamestateHeader);
329  while(of < GAMESTATE_HEADER(base->data_)->datasize && of < HEADER->datasize){
330    *(dest+of)=*(basep+of)^*(gs+of); // do the xor
331    ++of;
332  }
333  if(GAMESTATE_HEADER(base->data_)->datasize!=HEADER->datasize){
334    unsigned char n=0;
335    if(GAMESTATE_HEADER(base->data_)->datasize < HEADER->datasize){
336      while(of < dest_length){
337        *(dest+of)=n^*(gs+of);
338        of++;
339      }
340    }
341  }
342  *GAMESTATE_HEADER(ndata) = *HEADER;
343  GAMESTATE_HEADER(ndata)->diffed = false;
344  Gamestate *g = new Gamestate(ndata, getClientID());
345  g->flags_=flags_;
346  g->packetDirection_ = packetDirection_;
347  assert(!g->isDiffed());
348  assert(!g->isCompressed());
349  return g;
350}
351
352
353unsigned int Gamestate::calcGamestateSize(unsigned int id, int mode)
354{
355  int size=0;
356    // get the start of the Synchronisable list
357  orxonox::ObjectList<Synchronisable>::iterator it;
358    // get total size of gamestate
359  for(it = orxonox::ObjectList<Synchronisable>::begin(); it; ++it)
360    size+=it->getSize(id, mode); // size of the actual data of the synchronisable
361//  size+=sizeof(GamestateHeader);
362  return size;
363}
364
365/**
366 * This function removes a Synchronisable out of the universe
367 * @param it iterator of the list pointing to the object
368 * @return iterator pointing to the next object in the list
369 */
370  void Gamestate::removeObject(orxonox::ObjectList<Synchronisable>::iterator &it) {
371    orxonox::ObjectList<Synchronisable>::iterator temp=it;
372    ++it;
373    delete  *temp;
374  }
375
376  bool Gamestate::isDiffed(){
377    return HEADER->diffed;
378  }
379
380  bool Gamestate::isCompressed(){
381    return HEADER->compressed;
382  }
383
384  int Gamestate::getBaseID(){
385    return HEADER->base_id;
386  }
387}
388
389}
Note: See TracBrowser for help on using the repository browser.