Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core3/src/network/GameStateManager.cc @ 1996

Last change on this file since 1996 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: 19.0 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, (C) 2007
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29//
30// C++ Implementation: GameStateManager
31//
32// Description:
33//
34//
35// Author:  Oliver Scheuss, (C) 2007
36//
37// Copyright: See COPYING file that comes with this distribution
38//
39//
40
41#include "GameStateManager.h"
42
43#include <utility>
44#include <iostream>
45#include <zlib.h>
46#include <assert.h>
47
48#include "core/CoreIncludes.h"
49#include "core/BaseObject.h"
50#include "core/Iterator.h"
51#include "ClientInformation.h"
52#include "Synchronisable.h"
53
54namespace network
55{
56  GameStateManager::GameStateManager(ClientInformation *head) {
57    id_=0;
58    head_=head;
59  }
60
61  GameStateManager::~GameStateManager() {
62  }
63
64  void GameStateManager::update(){
65    cleanup();
66    reference = getSnapshot();
67    COUT(4) << "inserting gamestate: " << reference << std::endl;
68    gameStateMap.insert(std::pair<int, GameState*>(id_, reference));
69    gameStateUsed[id_]=0;
70    printGameStates();
71    return;
72  }
73
74  void GameStateManager::addGameState(GameStateCompressed *gs, int clientID){
75    if(!gs)
76      return;
77    std::map<int, GameStateCompressed*>::iterator it = gameStateQueue.find(clientID);
78    if(it!=gameStateQueue.end()){
79      // delete obsolete gamestate
80      delete[] it->second->data;
81      delete it->second;
82    }
83    gameStateQueue[clientID] = gs;
84    return;
85  }
86
87  void GameStateManager::processGameStates(){
88    std::map<int, GameStateCompressed*>::iterator it;
89    // now push only the most recent gamestates we received (ignore obsolete ones)
90    for(it = gameStateQueue.begin(); it!=gameStateQueue.end(); it++){
91      pushGameState(it->second, it->first);
92    }
93    // now clear the queue
94    gameStateQueue.clear();
95  }
96
97
98  /**
99   * this function is used to keep the memory usage low
100   * it tries to delete all the unused gamestates
101   *
102   *
103   */
104  void GameStateManager::cleanup(){
105    std::map<int,int>::iterator it = gameStateUsed.begin();
106    while(it!=gameStateUsed.end()){
107      if((id_-(*it).first)<KEEP_GAMESTATES)
108        break;
109      if( (*it).second <= 0 ){
110        COUT(5) << "GameStateManager: deleting gamestate with id: " << (*it).first << ", uses: " << (*it).second << std::endl;
111        std::map<int, GameState *>::iterator tempit = gameStateMap.find((*it).first);
112        if( tempit != gameStateMap.end() ){
113          GameState *temp = tempit->second;
114          if(temp){
115            delete[] gameStateMap[(*it).first]->data;
116            delete gameStateMap[(*it).first];
117            gameStateMap.erase((*it).first);
118          }
119        }
120        gameStateUsed.erase(it++);
121        continue;
122      }/*else if(id_-it->first<=KEEP_GAMESTATES){  //as soon as we got a used gamestate break here because we could use newer gamestates in future but only if we do not exceed KEEP_GAMESTATES # of gamestates in cache
123        COUT(4) << "breaking " << std::endl;
124        break;
125      }*/
126      it++;
127    }
128  }
129
130  GameStateCompressed *GameStateManager::popGameState(int clientID) {
131    //why are we searching the same client's gamestate id as we searched in
132    //Server::sendGameState?
133    int gID = head_->findClient(clientID)->getGamestateID();
134    COUT(4) << "G.St.Man: popgamestate: sending gstate_id: " << id_ << " diffed from: " << gID << std::endl;
135//     COUT(3) << "gamestatemap: " << &gameStateMap << std::endl;
136    //chose wheather the next gamestate is the first or not
137    if(gID != GAMESTATEID_INITIAL){
138      // TODO something with the gamestatemap is wrong
139      GameState *client=NULL;
140      std::map<int, GameState*>::iterator it = gameStateMap.find(gID);
141      if(it!=gameStateMap.end())
142        client = it->second;
143      GameState *server = reference;
144      //COUT(4) << "client: " << client << " server: " << server << " gamestatemap: " << &gameStateMap << " size: " << server->size << std::endl;
145      COUT(4) << "client: " << (client!=0 ? client->id : (int)client) << " server: " << server->id << " gamestatemap: " << &gameStateMap << " size: " << server->size << std::endl;
146      if(client)
147        return encode(client, server);
148      else
149        return encode(server);
150    } else {
151      COUT(4) << "we got a GAMESTATEID_INITIAL for clientID: " << clientID << std::endl;
152      GameState *server = reference;
153//       ackGameState(clientID, reference->id);
154      return encode(server);
155      // return an undiffed gamestate and set appropriate flags
156    }
157  }
158
159  bool GameStateManager::pushGameState( GameStateCompressed *gs, int clientID ){
160    GameState *ugs = decompress(gs);
161    delete[] gs->data;
162    delete gs;
163    bool result = loadPartialSnapshot(ugs, clientID);
164    delete[] ugs->data;
165    delete ugs;
166    return result;
167  }
168
169  /**
170  * This function goes through the whole list of synchronisables and
171  * saves all the synchronisables to a flat "list".
172  * @return struct of type gamestate containing the size of the whole gamestate and a pointer linking to the flat list
173  */
174  GameState *GameStateManager::getSnapshot()
175  {
176    //std::cout << "begin getSnapshot" << std::endl;
177    //the size of the gamestate
178    int totalsize=0;
179    int memsize=0;
180    //the size of one specific synchronisable
181    int tempsize=0;
182    // get the start of the Synchronisable list
183    orxonox::ObjectList<Synchronisable>::iterator it;
184    // struct for return value of Synchronisable::getData()
185    syncData sync;
186
187    GameState *retval=new GameState; //return value
188    retval->id=++id_;
189    COUT(4) << "G.ST.Man: producing gamestate with id: " << retval->id << std::endl;
190    // offset of memory functions
191    int offset=0, size=0;
192    // get total size of gamestate
193    for(it = orxonox::ObjectList<Synchronisable>::begin(); it; ++it){
194      size+=it->getSize(); // size of the actual data of the synchronisable
195      size+=3*sizeof(int); // size of datasize, classID and objectID
196    }
197    //retval->data = (unsigned char*)malloc(size);
198    if(size==0)
199      return NULL;
200    retval->data = new unsigned char[size];
201    if(!retval->data){
202      COUT(2) << "GameStateManager: could not allocate memory" << std::endl;
203      return NULL;
204    }
205    memsize=size;
206    // go through all Synchronisables
207    for(it = orxonox::ObjectList<Synchronisable>::begin(); it; ++it){
208      //get size of the synchronisable
209      tempsize=it->getSize();
210      // add place for data and 3 ints (length,classid,objectid)
211      totalsize+=tempsize+3*sizeof(int);
212      // allocate+tempsize additional space
213      if(totalsize > size){
214        COUT(3) << "G.St.Man: need additional memory" << std::endl;
215//         if(tempsize < 1000){
216//           retval->data = (unsigned char *)realloc((void *)retval->data, totalsize+1000);
217//           memsize+=1000;
218//         } else {
219//           retval->data = (unsigned char *)realloc((void *)retval->data, totalsize+1000);
220//           memsize+=tempsize+1000;
221//         }
222//         COUT(5) << "G.St.Man: additional space allocation finished" << std::endl;
223      }
224
225      // run Synchronisable::getData with offset and additional place for 3 ints in between (for ids and length)
226      sync=it->getData((retval->data)+offset+3*sizeof(int));
227      memcpy(retval->data+offset, (void *)&(sync.length), sizeof(int));
228      memcpy(retval->data+offset+sizeof(int), (void *)&(sync.objectID), sizeof(int));
229      memcpy(retval->data+offset+2*sizeof(int), (void *)&(sync.classID), sizeof(int));
230      // increase data pointer
231      offset+=tempsize+3*sizeof(int);
232    }
233    retval->size=totalsize;
234    //#### bugfix
235    retval->diffed = false;
236    retval->complete = true;
237    //std::cout << "end snapShot" << std::endl;
238    COUT(5) << "G.ST.Man: Gamestate size: " << totalsize << std::endl;
239    COUT(5) << "G.ST.Man: 'estimated' Gamestate size: " << size << std::endl;
240    return retval;
241  }
242
243  bool GameStateManager::loadPartialSnapshot(GameState *state, int clientID){
244    if(!state)
245      return false;
246    unsigned char *data=state->data;
247    COUT(4) << "loadSnapshot: loading gs: " << state->id << std::endl;
248    // get the start of the Synchronisable list
249    orxonox::ObjectList<Synchronisable>::iterator it=orxonox::ObjectList<Synchronisable>::begin();
250    syncData sync;
251    /*ClientInformation *client = head_->findClient(clientID);
252    if(client)
253      if(client->getPartialGamestateID()>state->id){
254        COUT(3) << "we received an obsolete partial gamestate" << std::endl;
255        return false;
256      }
257    else;*/
258        //what should we do now ??
259    // loop as long as we have some data ;)
260    while(data < state->data+state->size){
261      // prepare the syncData struct
262      sync.length = *(int *)data;
263      data+=sizeof(int);
264      sync.objectID = *(int*)data;
265      data+=sizeof(int);
266      sync.classID = *(int*)data;
267      if(sync.classID == 0) // TODO: remove this
268        COUT(3) << "received a classid 0" << std::endl;
269      data+=sizeof(int);
270      sync.data = data;
271      data+=sync.length;
272      COUT(5) << "objectID: " << sync.objectID << " classID: " << sync.classID << std::endl;
273      while(it && it->objectID!=sync.objectID)
274        ++it;
275
276
277      if(!it){
278        // the objectaber ich glaub die  does not exist yet
279        COUT(4) << "loadsnapshot: creating new object " << std::endl;
280        //COUT(4) << "loadSnapshot:\tclassid: " << sync.classID << ", name: " << ID((unsigned int) sync.classID)->getName() << std::endl;
281        orxonox::Identifier* id = ID((unsigned int)sync.classID);
282        if(!id){
283          COUT(4) << "We could not identify a new object; classid: " << sync.classID << std::endl;
284          continue;
285        }
286        Synchronisable *no = dynamic_cast<Synchronisable *>(id->fabricate());
287        COUT(4) << "loadpartialsnapshot (generating new object): classid: " << sync.classID << " objectID: " << sync.objectID << " length: " << sync.length << std::endl;
288        no->objectID=sync.objectID;
289        no->classID=sync.classID;
290        it=orxonox::ObjectList<Synchronisable>::end();
291        // update data and create object/entity...
292        if( !no->updateData(sync) )
293          COUT(1) << "We couldn't update the object: " << sync.objectID << std::endl;
294        if( !no->create() )
295          COUT(1) << "We couldn't manifest (create() ) the object: " << sync.objectID << std::endl;
296      }else{
297        // we have our object
298        COUT(4) << "loadpartialsnapshot: we found the appropriate object" << std::endl;
299        if(checkAccess(clientID, sync.objectID)){
300          if(! it->updateData(sync))
301            COUT(1) << "We couldn't update objectID: " \
302              << sync.objectID << "; classID: " << sync.classID << std::endl;
303        }else
304          COUT(4) << "loadPartialSnapshot: no access to change objectID: " << sync.objectID << std::endl;
305      }
306      ++it;
307    }
308    //client->setPartialGamestateID(state->id);
309    return true;
310  }
311
312
313  //##### ADDED FOR TESTING PURPOSE #####
314  GameStateCompressed* GameStateManager::testCompress( GameState* g ) {
315    return compress_( g );
316  }
317
318  GameState* GameStateManager::testDiff( GameState* a, GameState* b ) {
319    return diff( a, b );
320  }
321  //##### END TESTING PURPOSE #####
322
323  GameStateCompressed *GameStateManager::encode(GameState *a, GameState *b) {
324    COUT(5) << "G.St.Man: this will be a DIFFED gamestate" << std::endl;
325    GameState *r = diff(a,b);
326    GameStateCompressed *c = compress_(r);
327    delete[] r->data;
328    delete r;
329    return c;
330  }
331
332  GameStateCompressed *GameStateManager::encode(GameState *a) {
333    COUT(5) << "G.St.Man: encoding gamestate (compress)" << std::endl;
334    a->base_id=GAMESTATEID_INITIAL;
335    return compress_(a);
336    /*GameStateCompressed *g = new GameStateCompressed;
337    g->base_id = a->base_id;
338    g->id = a->id;
339    g->diffed = a->diffed;
340    g->data = a->data;
341    g->normsize = a->size;
342    g->compsize = a->size;
343    return g;*/
344  }
345
346  GameState *GameStateManager::diff(GameState *alt, GameState *neu) {
347    unsigned char *ap = alt->data, *bp = neu->data;
348    int of=0; // pointers offset
349    int dest_length=0;
350    /*if(alt->size>neu->size)
351      dest_length=alt->size;
352    else*/
353      dest_length=neu->size;
354    if(dest_length==0)
355      return NULL;
356    //unsigned char *dp = (unsigned char *)malloc(dest_length*sizeof(unsigned char));
357    unsigned char *dp = new unsigned char[dest_length*sizeof(unsigned char)];
358    while(of<alt->size && of<neu->size){
359      *(dp+of)=*(ap+of)^*(bp+of); // do the xor
360      ++of;
361    }
362    if(alt->size!=neu->size){ // do we have to fill up ?
363      unsigned char n=0;
364      if(alt->size<neu->size){
365        while(of<dest_length){
366          *(dp+of)=n^*(bp+of);
367          of++;
368        }
369      } /*else{
370        while(of<dest_length){
371          *(dp+of)=*(ap+of)^n;
372          of++;
373        }
374      }*/
375    }
376
377    GameState *r = new GameState;
378    r->id = neu->id;
379    r->size = dest_length;
380    r->diffed = true;
381    r->base_id = alt->id;
382    r->data = dp;
383    r->complete = true;
384    return r;
385  }
386
387  GameStateCompressed *GameStateManager::compress_(GameState *a) {
388    //COUT(4) << "G.St.Man: compressing gamestate" << std::endl;
389
390    //COUT(4) << "G.St.Man: a: id: " << a->id << " base_id: " << a->base_id << " size: " << a->size << " diffed: " << a->diffed << std::endl;
391    int size = a->size;
392
393    uLongf buffer = (uLongf)((a->size + 12)*1.01)+1;
394    //COUT(4) << "size: " << size << ", buffer: " << buffer << std::endl;
395    //unsigned char* dest = (unsigned char*)malloc( buffer );
396    if(buffer==0)
397      return NULL;
398    unsigned char *dest = new unsigned char[buffer];
399    //COUT(4) << "dest: " << dest << std::endl;
400    int retval;
401    //std::cout << "before ziped " << buffer << std::endl;
402    retval = compress( dest, &buffer, a->data, (uLong)size );
403    //COUT(4) << "bloablabla aft3er compress" << std::endl;
404    //std::cout << "after ziped " << buffer << std::endl;
405
406    switch ( retval ) {
407      case Z_OK: COUT(5) << "G.St.Man: compress: successfully compressed" << std::endl; break;
408      case Z_MEM_ERROR: COUT(1) << "G.St.Man: compress: not enough memory available in gamestate.compress" << std::endl;
409      return NULL;
410      case Z_BUF_ERROR: COUT(2) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << std::endl;
411      return NULL;
412      case Z_DATA_ERROR: COUT(2) << "G.St.Man: compress: data corrupted in gamestate.compress" << std::endl;
413      return NULL;
414    }
415
416    GameStateCompressed *compressedGamestate = new GameStateCompressed;
417    compressedGamestate->compsize = buffer;
418//     std::cout << "compressedGamestate.compsize = buffer; " << buffer << std::endl;
419    compressedGamestate->normsize = size;
420//     std::cout << "compressedGamestate.normsize = size; " << size << std::endl;
421    compressedGamestate->id = a->id;
422    compressedGamestate->data = dest;
423    compressedGamestate->diffed = a->diffed;
424    compressedGamestate->complete = a->complete;
425    compressedGamestate->base_id = a->base_id;
426    //COUT(5) << "G.St.Man: saved compressed data in GameStateCompressed:" << std::endl;
427    return compressedGamestate;
428  }
429
430  GameState *GameStateManager::decompress(GameStateCompressed *a) {
431    //COUT(4) << "GameStateClient: uncompressing gamestate. id: " << a->id << ", baseid: " << a->base_id << ", normsize: " << a->normsize << ", compsize: " << a->compsize << std::endl;
432    int normsize = a->normsize;
433    int compsize = a->compsize;
434    int bufsize;
435    if(normsize < compsize)
436      bufsize = compsize;
437    else
438      bufsize = normsize;
439//     unsigned char* dest = (unsigned char*)malloc( bufsize );
440    if(bufsize==0)
441      return NULL;
442    unsigned char *dest = new unsigned char[bufsize];
443    int retval;
444    uLongf length=normsize;
445    //std::cout << "gamestateclient" << std::endl;
446    //std::cout << "normsize " << a.normsize << " compsize " << a.compsize << " " << bufsize << std::endl;
447    retval = uncompress( dest, &length, a->data, (uLong)compsize );
448    //std::cout << "length " << length << std::endl;
449    switch ( retval ) {
450      case Z_OK: COUT(5) << "successfully decompressed" << std::endl; break;
451      case Z_MEM_ERROR: COUT(1) << "not enough memory available" << std::endl; return NULL;
452      case Z_BUF_ERROR: COUT(2) << "not enough memory available in the buffer" << std::endl; return NULL;
453      case Z_DATA_ERROR: COUT(2) << "data corrupted (zlib)" << std::endl; return NULL;
454    }
455
456    GameState *gamestate = new GameState;
457    gamestate->id = a->id;
458    gamestate->size = normsize;
459    gamestate->data = dest;
460    gamestate->base_id = a->base_id;
461    gamestate->diffed = a->diffed;
462    gamestate->complete = a->complete;
463
464
465    return gamestate;
466  }
467
468
469  void GameStateManager::ackGameState(int clientID, int gamestateID) {
470    ClientInformation *temp = head_->findClient(clientID);
471    if(temp==0)
472      return;
473    int curid = temp->getGamestateID();
474
475    if(gamestateID == GAMESTATEID_INITIAL){
476      temp->setGameStateID(GAMESTATEID_INITIAL);
477      if(curid!=GAMESTATEID_INITIAL){
478        assert(gameStateUsed.find(curid)!=gameStateUsed.end());
479        --(gameStateUsed.find(curid)->second);
480      }
481      return;
482    }
483    if(curid > gamestateID)
484      // the network packets got messed up
485      return;
486    COUT(4) << "acking gamestate " << gamestateID << " for clientid: " << clientID << " curid: " << curid << std::endl;
487    // decrease usage of gamestate and save it
488//     deleteUnusedGameState(curid);
489    //increase gamestateused
490    std::map<int, int>::iterator it = gameStateUsed.find(curid);
491    if(curid!=GAMESTATEID_INITIAL){
492      if(it!=gameStateUsed.end())
493        --(it->second);
494    }
495    it = gameStateUsed.find(gamestateID);
496    if(it!=gameStateUsed.end()){
497      ++(it->second);
498      temp->setGameStateID(gamestateID);
499    }
500    /*
501    GameState *old = clientGameState[clientID];
502    deleteUnusedGameState(old);
503    clientGameState[clientID]=idGameState[gamestateID];*/
504  }
505
506  bool GameStateManager::printGameStates() {
507    std::map<int, GameState*>::iterator it;
508    COUT(4) << "gamestates: ";
509    for(it = gameStateMap.begin(); it!=gameStateMap.end(); it++){
510      COUT(4) << (*it).first << ":" << (*it).second << " | ";
511    }
512    COUT(4) << std::endl;
513    return true;
514  }
515
516  bool GameStateManager::checkAccess(int clientID, int objectID){
517    // currently we only check, wheter the object is the clients spaceship
518//     return head_->findClient(objectID)->getShipID()==objectID;
519    return true; // TODO: change this
520  }
521
522  void GameStateManager::removeClient(ClientInformation* client){
523    if(!client)
524      return;
525    if(client->getGamestateID()>=0)
526      gameStateUsed[client->getGamestateID()]--;
527    head_->removeClient(client->getID());
528  }
529
530}
Note: See TracBrowser for help on using the repository browser.