Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1411 was 1360, checked in by rgrieder, 17 years ago

merged merge branch back to trunk

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