Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/merge/src/network/GameStateManager.cc @ 1283

Last change on this file since 1283 was 1282, checked in by scheusso, 17 years ago

made gamestatemanager::cleanup a bit safer ;)

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