Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

reverted some changes from previous version

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