Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gcc43/src/network/GameStateManager.cc @ 1636

Last change on this file since 1636 was 1581, checked in by nicolasc, 16 years ago

well, it compiles..
there are only about 1000 warinig about a deprecated header usage in ogre, and some other uglinesses, but apart from that it builds… and still seg faults…

  • 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.