Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/network/packet/Gamestate.cc @ 1751

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

merged network branch back into trunk

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#include "core/Iterator.h"
33
34#include <zlib.h>
35#include <assert.h>
36
37
38
39namespace network {
40
41namespace packet {
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::ObjectList<Synchronisable>::iterator it;
80  for(it = orxonox::ObjectList<Synchronisable>::begin(); 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::ObjectList<Synchronisable>::iterator 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::ObjectList<Synchronisable>::iterator it=orxonox::ObjectList<Synchronisable>::begin();
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::ObjectList<Synchronisable>::iterator it;
363    // get total size of gamestate
364  for(it = orxonox::ObjectList<Synchronisable>::begin(); 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::ObjectList<Synchronisable>::iterator &it) {
376    orxonox::ObjectList<Synchronisable>::iterator 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.