Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network3/src/network/GameStateManager.cc @ 1245

Last change on this file since 1245 was 1245, checked in by scheusso, 16 years ago

different enhancements in input/network handling and synchronisation

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