Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

compressing and diffing works now (also shipCache on client). server can't move while client connected. preparations for synchronise frequency of synchronisables

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