Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/network/GameStateManager.cc @ 1994

Last change on this file since 1994 was 1638, checked in by rgrieder, 16 years ago

merged input branch into gui test branch (was about time)
svn save (it's still a mess and CMLs haven't been updated)
I'll have to create a special project to create the tolua_bind files for tolua itself anyway..

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