Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/network/GameStateClient.cc @ 1663

Last change on this file since 1663 was 1625, checked in by rgrieder, 16 years ago

merged hud branch back to trunk

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