Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

enabled prediction for WE (synchronise velocity/acceleration)

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 << " uint: " << (unsigned int)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.