Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core3/src/network/GameStateClient.cc @ 1958

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

Again some heavy changes in ObjectList and Iterator:
there are now two types of iterators:

Iterator<ClassName> can iterate through any objectlist, either given by ObjectList<AnyClassName>::begin() or anyidentifier→getObjects()→begin(). Important note Iterator<ClassName> uses dynamic_cast.
And yes, it's possible to do this: Iterator<WorldEntity> it = ObjectList<SpaceShip>::begin()

ObjectList<ClassName>::iterator is the second iterator - it uses the ObjectList in a templated manner and therefore doesn't need dynamic_cast. But the only thing you can do is iterating through exactly the right ObjectList: ObjectList<ClassName>::iterator it = ObjectList<ClassName>::begin(). Anything else fails.

Those changes bring, at my system, something around +12% FPS compared with trunk and +25% FPS compared with the last revision of core3. Although I have to admit the FPS gain is only that high because iterating through objects is the main thing we're doing ingame right now. It would look totally different with physics, sound, AI, scripts, triggers and so on.

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