Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/network/GameStateManager.cc @ 1704

Last change on this file since 1704 was 1639, checked in by rgrieder, 16 years ago

merged nico's fixes for gcc 4.3 back to trunk.
I'm not going to delete branch yet.

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