Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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