Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network/src/network/GameStateManager.cc @ 1431

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

another one bites the dust: solved that problem with zeros in the gamestate

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