Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1284 was 1264, checked in by scheusso, 17 years ago

merge network3 and camera branch into merge branch

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