Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network/src/network/GameStateClient.cc @ 1446

Last change on this file since 1446 was 1446, checked in by landauf, 16 years ago

merged console branch into network branch

after several heavy troubles it compiles, but there is still a bug I couldn't fix: orxonox crashes as soon as one presses a key after opening the console… maybe someone else sees the problem?

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