Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/merge/src/network/GameStateClient.cc @ 1306

Last change on this file since 1306 was 1303, checked in by scheusso, 17 years ago

enhanced gamestate loading (abort if data corrupted ;) )

File size: 13.8 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 *      ...
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "GameStateClient.h"
30
31#include <zlib.h>
32
33#include "core/CoreIncludes.h"
34#include "core/BaseObject.h"
35#include "Synchronisable.h"
36
37#define GAMESTATEID_INITIAL -1
38
39namespace network
40{
41  struct GameStateItem{
42    GameState *state;
43    int id;
44  };
45 
46  GameStateClient::GameStateClient() {
47    COUT(5) << "this: " << this << std::endl;
48    last_diff_=0;
49  }
50
51  GameStateClient::~GameStateClient() {
52  }
53
54  bool GameStateClient::pushGameState(GameStateCompressed *compstate) {
55    cleanup();
56    printGameStateMap();
57    GameState *gs, *reference;
58    if(compstate->diffed && compstate->base_id!=GAMESTATEID_INITIAL){
59      std::map<int, GameState*>::iterator it = gameStateMap.find(compstate->base_id);
60      if(it!=gameStateMap.end())
61        reference = (it)->second;
62      else
63        reference = NULL;
64      if(!reference){
65        COUT(4) << "pushGameState: no reference found to diff" << std::endl;
66        delete[] compstate->data;
67        delete compstate;
68        return false;
69      }
70      gs = decode(reference, compstate);
71    }
72    else
73      gs = decode(compstate);
74    if(gs){
75      if (loadSnapshot(gs)){
76        gameStateMap.insert(std::pair<int, GameState*>(gs->id, gs));
77        COUT(4) << "adding decoded gs with id: " << gs->id << " diffed from: " << gs->base_id << std::endl;
78        last_diff_=gs->base_id;
79        return true;
80      }else{
81        COUT(4) << "could not decode gs with id: " << gs->id << " diffed from: " << gs->base_id << std::endl;
82        delete[] gs->data;
83        delete gs;
84        return false;
85      }
86    }
87    COUT(4) << "could not use gamestate sent by server" << std::endl;
88    return false;
89  }
90 
91  GameStateCompressed *GameStateClient::popPartialGameState(){
92    GameState *gs = getPartialSnapshot();
93    GameStateCompressed *cgs = compress_(gs);
94    delete[] gs->data;
95    delete gs;
96    return cgs;
97  }
98 
99
100  /**
101  * This function removes a Synchronisable out of the universe
102  * @param it iterator of the list pointing to the object
103  * @return iterator pointing to the next object in the list
104  */
105  void GameStateClient::removeObject(orxonox::Iterator<Synchronisable> &it) {
106    orxonox::Iterator<Synchronisable> temp=it;
107    ++it;
108    delete  *temp;
109  }
110
111  /**
112  * This function loads a Snapshort of the gamestate into the universe
113  * @param state a GameState struct containing the size of the gamestate and a pointer linking to a flat list (returned by getSnapshot)
114  */
115  bool GameStateClient::loadSnapshot(GameState *state) {
116    unsigned char *data=state->data;
117    COUT(4) << "loadSnapshot: loading gs: " << state->id << std::endl;
118    // get the start of the Synchronisable list
119    orxonox::Iterator<Synchronisable> it=orxonox::ObjectList<Synchronisable>::start();
120    syncData sync;
121    // loop as long as we have some data ;)
122    while(data < state->data+state->size){
123      // prepare the syncData struct
124      sync.length = *(int *)data;
125      data+=sizeof(int);
126      sync.objectID = *(int*)data;
127      data+=sizeof(int);
128      sync.classID = *(int*)data;
129      data+=sizeof(int);
130      sync.data = data;
131      data+=sync.length;
132
133      if(!it || it->objectID!=sync.objectID){
134        // bad luck ;)
135        // delete the synchronisable (obviously seems to be deleted on the server)
136        while(it && it->objectID!=sync.objectID)
137          removeObject(it);
138
139
140        if(!it){
141          //COUT(4) << "loadSnapshot:\tclassid: " << sync.classID << ", name: " << ID((unsigned int) sync.classID)->getName() << std::endl;
142          ///sigsegv may happen here again for some reason
143          ///sigsegv is receved after the COUT(4) above
144          orxonox::Identifier* id = ID((unsigned int)sync.classID);
145          if(!id){
146            COUT(4) << "We could not identify a new object; classid: " << sync.classID << std::endl;
147            return false;
148          }
149          Synchronisable *no = dynamic_cast<Synchronisable *>(id->fabricate());
150          COUT(4) << "loadsnapshot: classid: " << sync.classID << " objectID: " << sync.objectID << " length: " << sync.length << std::endl;
151          no->objectID=sync.objectID;
152          no->classID=sync.classID;
153          // update data and create object/entity...
154          if( !no->updateData(sync) ){
155            COUT(1) << "We couldn't update the object: " << sync.objectID << std::endl;
156            return false;
157          }
158          if( !no->create() )
159            COUT(1) << "We couldn't manifest (create() ) the object: " << sync.objectID << std::endl;
160          it=orxonox::ObjectList<Synchronisable>::end();
161        }
162      } else {
163        // we have our object
164        if(! it->updateData(sync))
165          COUT(1) << "We couldn't update objectID: " \
166          << sync.objectID << "; classID: " << sync.classID << std::endl;
167      }
168      ++it;
169    }
170
171    return true;
172  }
173
174  GameState *GameStateClient::getPartialSnapshot(){
175    //std::cout << "begin getSnapshot" << std::endl;
176    //the size of the gamestate
177    int totalsize=0;
178    int memsize=0;
179    //the size of one specific synchronisable
180    int tempsize=0;
181    // get the start of the Synchronisable list
182    orxonox::Iterator<Synchronisable> it;
183    // struct for return value of Synchronisable::getData()
184    syncData sync;
185
186    GameState *retval=new GameState; //return value
187//     retval->id=reference->id;
188    if(gameStateMap.size()!=0)
189      retval->id=(--gameStateMap.end())->second->id;
190    retval->diffed=false;
191    retval->complete=false;
192    COUT(4) << "G.ST.Client: producing partial gamestate with id: " << retval->id << std::endl;
193    // offset of memory functions
194    int offset=0, size=0;
195    // get total size of gamestate
196    for(it = orxonox::ObjectList<Synchronisable>::start(); it; ++it){
197      if(!it->getBacksync())
198        continue;
199      size+=it->getSize(); // size of the actual data of the synchronisable
200      size+=3*sizeof(int); // size of datasize, classID and objectID
201      COUT(4) << "getpartialsnapshot: size: " << size << std::endl;
202    }
203    //retval->data = (unsigned char*)malloc(size);
204    retval->data = new unsigned char[size];
205    if(!retval->data){
206      COUT(2) << "GameStateClient: could not allocate memory" << std::endl;
207      return NULL;
208    }
209    memsize=size;
210    // go through all Synchronisables
211    for(it = orxonox::ObjectList<Synchronisable>::start(); it; ++it){
212      if(!it->getBacksync())
213        continue;
214      //get size of the synchronisable
215      tempsize=it->getSize();
216      // add place for data and 3 ints (length,classid,objectid)
217      totalsize+=tempsize+3*sizeof(int);
218      // allocate+tempsize additional space
219      if(totalsize > size){
220        COUT(3) << "G.St.Cl: need additional memory" << std::endl;
221      }
222
223      // run Synchronisable::getData with offset and additional place for 3 ints in between (for ids and length)
224      sync=it->getData((retval->data)+offset+3*sizeof(int));
225      memcpy(retval->data+offset, (void *)&(sync.length), sizeof(int));
226      memcpy(retval->data+offset+sizeof(int), (void *)&(sync.objectID), sizeof(int));
227      memcpy(retval->data+offset+2*sizeof(int), (void *)&(sync.classID), sizeof(int));
228      // increase data pointer
229      offset+=tempsize+3*sizeof(int);
230    }
231    retval->size=totalsize;
232    COUT(5) << "G.ST.Cl: Gamestate size: " << totalsize << std::endl;
233    COUT(5) << "G.ST.Cl: 'estimated' Gamestate size: " << size << std::endl;
234    return retval;
235  }
236 
237 
238  GameState *GameStateClient::undiff(GameState *old, GameState *diff) {
239    if(!old || !diff)
240      return NULL;
241    unsigned char *ap = old->data, *bp = diff->data;
242    int of=0; // pointers offset
243    int dest_length=0;
244    if(old->size>=diff->size)
245      dest_length=old->size;
246    else
247      dest_length=diff->size;
248//     unsigned char *dp = (unsigned char *)malloc(dest_length*sizeof(unsigned char));
249    unsigned char *dp = new unsigned char[dest_length*sizeof(unsigned char)];
250    while(of<old->size && of<diff->size){
251      *(dp+of)=*(ap+of)^*(bp+of); // do the xor
252      ++of;
253    }
254    if(old->size!=diff->size){ // do we have to fill up ?
255      unsigned char n=0;
256      if(old->size<diff->size){
257        while(of<dest_length){
258          *(dp+of)=n^*(bp+of);
259          of++;
260        }
261      } else{
262        while(of<dest_length){
263          *(dp+of)=*(ap+of)^n;
264          of++;
265        }
266      }
267    }
268    // should be finished now
269    // FIXME: is it true or false now? (struct has changed, producing warnings)
270    GameState *r = new GameState;
271    r->id = diff->id;
272    r->size = dest_length;
273    r->base_id = old->id;
274    r->diffed = false;
275    r->data = dp;
276    r->complete = true;
277    return r;
278  }
279
280
281
282  GameStateCompressed *GameStateClient::compress_(GameState *a) {
283    if(!a)
284      return NULL;
285    int size = a->size;
286
287    uLongf buffer = (uLongf)((a->size + 12)*1.01)+1;
288    unsigned char *dest = new unsigned char[buffer];
289    int retval;
290    retval = compress( dest, &buffer, a->data, (uLong)size );
291
292    switch ( retval ) {
293      case Z_OK: COUT(5) << "G.St.Cl: compress: successfully compressed" << std::endl; break;
294      case Z_MEM_ERROR: COUT(1) << "G.St.Cl: compress: not enough memory available in gamestate.compress" << std::endl; 
295      return NULL;
296      case Z_BUF_ERROR: COUT(2) << "G.St.Cl: compress: not enough memory available in the buffer in gamestate.compress" << std::endl;
297      return NULL;
298      case Z_DATA_ERROR: COUT(2) << "G.St.Cl: compress: data corrupted in gamestate.compress" << std::endl;
299      return NULL;
300    }
301
302    GameStateCompressed *compressedGamestate = new GameStateCompressed;
303    compressedGamestate->compsize = buffer;
304    compressedGamestate->normsize = size;
305    compressedGamestate->id = a->id;
306    compressedGamestate->data = dest;
307    compressedGamestate->diffed = a->diffed;
308    compressedGamestate->complete = a->complete;
309    compressedGamestate->base_id = a->base_id;
310    return compressedGamestate;
311  }
312 
313 
314  GameState *GameStateClient::decompress(GameStateCompressed *a) {
315    //COUT(4) << "GameStateClient: uncompressing gamestate. id: " << a->id << ", baseid: " << a->base_id << ", normsize: " << a->normsize << ", compsize: " << a->compsize << std::endl;
316    int normsize = a->normsize;
317    int compsize = a->compsize;
318    int bufsize;
319    if(normsize < compsize)
320      bufsize = compsize;
321    else
322      bufsize = normsize;
323//     unsigned char* dest = (unsigned char*)malloc( bufsize );
324    unsigned char *dest = new unsigned char[bufsize];
325    int retval;
326    uLongf length=normsize;
327    //std::cout << "gamestateclient" << std::endl;
328    //std::cout << "normsize " << a.normsize << " compsize " << a.compsize << " " << bufsize << std::endl;
329    retval = uncompress( dest, &length, a->data, (uLong)compsize );
330    //std::cout << "length " << length << std::endl;
331    switch ( retval ) {
332      case Z_OK: COUT(4) << "successfully decompressed" << std::endl; break;
333      case Z_MEM_ERROR: COUT(1) << "not enough memory available" << std::endl; return NULL;
334      case Z_BUF_ERROR: COUT(2) << "not enough memory available in the buffer" << std::endl; return NULL;
335      case Z_DATA_ERROR: COUT(2) << "data corrupted (zlib)" << std::endl; return NULL;
336    }
337
338    GameState *gamestate = new GameState;
339    gamestate->id = a->id;
340    gamestate->size = normsize;
341    gamestate->data = dest;
342    gamestate->base_id = a->base_id;
343    gamestate->diffed = a->diffed;
344    gamestate->complete = a->complete;
345
346
347    return gamestate;
348  }
349
350  GameState *GameStateClient::decode(GameState *old, GameStateCompressed *diff) {
351    COUT(4) << "using diffed gamestate" << std::endl;
352    GameState *t = decode(diff);
353    GameState *r = undiff(old, t);
354    delete[] t->data;
355    delete t;
356    return r;
357  }
358
359  GameState *GameStateClient::decode(GameStateCompressed *x) {
360    GameState *t = decompress(x);
361    delete[] x->data;
362    delete x;
363    return t;
364  }
365 
366  void GameStateClient::cleanup(){
367    std::map<int, GameState*>::iterator temp, it = gameStateMap.begin();
368    while(it!=gameStateMap.end()){
369      if(it->first>=last_diff_)
370        break;
371      // otherwise delete that stuff
372      delete[] (*it).second->data;
373      delete (*it).second;
374      temp=it++;
375      gameStateMap.erase(temp);
376    }
377  }
378
379  void GameStateClient::printGameStateMap(){
380    std::map<int, GameState*>::iterator it;
381    COUT(4) << "gamestates: ";
382    for(it=gameStateMap.begin(); it!=gameStateMap.end(); it++){
383      COUT(4) << it->first << ":" << it->second << "|";
384    }
385    COUT(4) << std::endl;
386   
387  }
388 
389 
390    //##### ADDED FOR TESTING PURPOSE #####
391  GameState* GameStateClient::testDecompress( GameStateCompressed* gc ) {
392    return decompress( gc );
393  }
394 
395  GameState* GameStateClient::testUndiff( GameState* g_old, GameState* g_diffed ) {
396    return undiff( g_old, g_diffed );
397  }
398  //##### ADDED FOR TESTING PURPOSE #####
399 
400 
401}
402
Note: See TracBrowser for help on using the repository browser.