Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1494 was 1494, checked in by rgrieder, 16 years ago
  • set the svn:eol-style property to all files so, that where ever you check out, you'll get the right line endings (had to change every file with mixed endings to windows in order to set the property)
  • Property svn:eol-style set to native
File size: 18.5 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(4) << "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    if(size==0)
196      return NULL;
197    retval->data = new unsigned char[size];
198    if(!retval->data){
199      COUT(2) << "GameStateManager: could not allocate memory" << std::endl;
200      return NULL;
201    }
202    memsize=size;
203    // go through all Synchronisables
204    for(it = orxonox::ObjectList<Synchronisable>::start(); it; ++it){
205      //get size of the synchronisable
206      tempsize=it->getSize();
207      // add place for data and 3 ints (length,classid,objectid)
208      totalsize+=tempsize+3*sizeof(int);
209      // allocate+tempsize additional space
210      if(totalsize > size){
211        COUT(3) << "G.St.Man: need additional memory" << std::endl;
212//         if(tempsize < 1000){
213//           retval->data = (unsigned char *)realloc((void *)retval->data, totalsize+1000);
214//           memsize+=1000;
215//         } else {
216//           retval->data = (unsigned char *)realloc((void *)retval->data, totalsize+1000);
217//           memsize+=tempsize+1000;
218//         }
219//         COUT(5) << "G.St.Man: additional space allocation finished" << std::endl;
220      }
221
222      // run Synchronisable::getData with offset and additional place for 3 ints in between (for ids and length)
223      sync=it->getData((retval->data)+offset+3*sizeof(int));
224      memcpy(retval->data+offset, (void *)&(sync.length), sizeof(int));
225      memcpy(retval->data+offset+sizeof(int), (void *)&(sync.objectID), sizeof(int));
226      memcpy(retval->data+offset+2*sizeof(int), (void *)&(sync.classID), sizeof(int));
227      // increase data pointer
228      offset+=tempsize+3*sizeof(int);
229    }
230    retval->size=totalsize;
231    //#### bugfix
232    retval->diffed = false;
233    retval->complete = true;
234    //std::cout << "end snapShot" << std::endl;
235    COUT(5) << "G.ST.Man: Gamestate size: " << totalsize << std::endl;
236    COUT(5) << "G.ST.Man: 'estimated' Gamestate size: " << size << std::endl;
237    return retval;
238  }
239
240  bool GameStateManager::loadPartialSnapshot(GameState *state, int clientID){
241    if(!state)
242      return false;
243    unsigned char *data=state->data;
244    COUT(4) << "loadSnapshot: loading gs: " << state->id << std::endl;
245    // get the start of the Synchronisable list
246    orxonox::Iterator<Synchronisable> it=orxonox::ObjectList<Synchronisable>::start();
247    syncData sync;
248    /*ClientInformation *client = head_->findClient(clientID);
249    if(client)
250      if(client->getPartialGamestateID()>state->id){
251        COUT(3) << "we received an obsolete partial gamestate" << std::endl;
252        return false;
253      }
254    else;*/
255        //what should we do now ??
256    // loop as long as we have some data ;)
257    while(data < state->data+state->size){
258      // prepare the syncData struct
259      sync.length = *(int *)data;
260      data+=sizeof(int);
261      sync.objectID = *(int*)data;
262      data+=sizeof(int);
263      sync.classID = *(int*)data;
264      if(sync.classID == 0) // TODO: remove this
265        COUT(3) << "received a classid 0" << std::endl;
266      data+=sizeof(int);
267      sync.data = data;
268      data+=sync.length;
269      COUT(4) << "objectID: " << sync.objectID << " classID: " << sync.classID << std::endl;
270      while(it && it->objectID!=sync.objectID)
271        ++it;
272
273
274      if(!it){
275        // the objectaber ich glaub die  does not exist yet
276        COUT(4) << "loadsnapshot: creating new object " << std::endl;
277        //COUT(4) << "loadSnapshot:\tclassid: " << sync.classID << ", name: " << ID((unsigned int) sync.classID)->getName() << std::endl;
278        orxonox::Identifier* id = ID((unsigned int)sync.classID);
279        if(!id){
280          COUT(4) << "We could not identify a new object; classid: " << sync.classID << std::endl;
281          continue;
282        }
283        Synchronisable *no = dynamic_cast<Synchronisable *>(id->fabricate());
284        COUT(4) << "loadpartialsnapshot (generating new object): classid: " << sync.classID << " objectID: " << sync.objectID << " length: " << sync.length << std::endl;
285        no->objectID=sync.objectID;
286        no->classID=sync.classID;
287        it=orxonox::ObjectList<Synchronisable>::end();
288        // update data and create object/entity...
289        if( !no->updateData(sync) )
290          COUT(1) << "We couldn't update the object: " << sync.objectID << std::endl;
291        if( !no->create() )
292          COUT(1) << "We couldn't manifest (create() ) the object: " << sync.objectID << std::endl;
293      }else{
294        // we have our object
295        COUT(4) << "loadpartialsnapshot: we found the appropriate object" << std::endl;
296        if(checkAccess(clientID, sync.objectID)){
297          if(! it->updateData(sync))
298            COUT(1) << "We couldn't update objectID: " \
299              << sync.objectID << "; classID: " << sync.classID << std::endl;
300        }else
301          COUT(4) << "loadPartialSnapshot: no access to change objectID: " << sync.objectID << std::endl;
302      }
303      ++it;
304    }
305    //client->setPartialGamestateID(state->id);
306    return true;
307  }
308 
309 
310  //##### ADDED FOR TESTING PURPOSE #####
311  GameStateCompressed* GameStateManager::testCompress( GameState* g ) {
312    return compress_( g );
313  }
314
315  GameState* GameStateManager::testDiff( GameState* a, GameState* b ) {
316    return diff( a, b );
317  }
318  //##### END TESTING PURPOSE #####
319
320  GameStateCompressed *GameStateManager::encode(GameState *a, GameState *b) {
321    COUT(4) << "G.St.Man: this will be a DIFFED gamestate" << std::endl;
322    GameState *r = diff(a,b);
323    GameStateCompressed *c = compress_(r);
324    delete[] r->data;
325    delete r;
326    return c;
327  }
328
329  GameStateCompressed *GameStateManager::encode(GameState *a) {
330    COUT(5) << "G.St.Man: encoding gamestate (compress)" << std::endl;
331    a->base_id=GAMESTATEID_INITIAL;
332    return compress_(a);
333    /*GameStateCompressed *g = new GameStateCompressed;
334    g->base_id = a->base_id;
335    g->id = a->id;
336    g->diffed = a->diffed;
337    g->data = a->data;
338    g->normsize = a->size;
339    g->compsize = a->size;
340    return g;*/
341  }
342
343  GameState *GameStateManager::diff(GameState *alt, GameState *neu) {
344    unsigned char *ap = alt->data, *bp = neu->data;
345    int of=0; // pointers offset
346    int dest_length=0;
347    /*if(alt->size>neu->size)
348      dest_length=alt->size;
349    else*/
350      dest_length=neu->size;
351    if(dest_length==0)
352      return NULL;
353    //unsigned char *dp = (unsigned char *)malloc(dest_length*sizeof(unsigned char));
354    unsigned char *dp = new unsigned char[dest_length*sizeof(unsigned char)];
355    while(of<alt->size && of<neu->size){
356      *(dp+of)=*(ap+of)^*(bp+of); // do the xor
357      ++of;
358    }
359    if(alt->size!=neu->size){ // do we have to fill up ?
360      unsigned char n=0;
361      if(alt->size<neu->size){
362        while(of<dest_length){
363          *(dp+of)=n^*(bp+of);
364          of++;
365        }
366      } /*else{
367        while(of<dest_length){
368          *(dp+of)=*(ap+of)^n;
369          of++;
370        }
371      }*/
372    }
373
374    GameState *r = new GameState;
375    r->id = neu->id;
376    r->size = dest_length;
377    r->diffed = true;
378    r->base_id = alt->id;
379    r->data = dp;
380    r->complete = true;
381    return r;
382  }
383
384  GameStateCompressed *GameStateManager::compress_(GameState *a) {
385    //COUT(4) << "G.St.Man: compressing gamestate" << std::endl;
386
387    //COUT(4) << "G.St.Man: a: id: " << a->id << " base_id: " << a->base_id << " size: " << a->size << " diffed: " << a->diffed << std::endl;
388    int size = a->size;
389
390    uLongf buffer = (uLongf)((a->size + 12)*1.01)+1;
391    //COUT(4) << "size: " << size << ", buffer: " << buffer << std::endl;
392    //unsigned char* dest = (unsigned char*)malloc( buffer );
393    if(buffer==0)
394      return NULL;
395    unsigned char *dest = new unsigned char[buffer];
396    //COUT(4) << "dest: " << dest << std::endl;
397    int retval;
398    //std::cout << "before ziped " << buffer << std::endl;
399    retval = compress( dest, &buffer, a->data, (uLong)size );
400    //COUT(4) << "bloablabla aft3er compress" << std::endl;
401    //std::cout << "after ziped " << buffer << std::endl;
402
403    switch ( retval ) {
404      case Z_OK: COUT(5) << "G.St.Man: compress: successfully compressed" << std::endl; break;
405      case Z_MEM_ERROR: COUT(1) << "G.St.Man: compress: not enough memory available in gamestate.compress" << std::endl; 
406      return NULL;
407      case Z_BUF_ERROR: COUT(2) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << std::endl;
408      return NULL;
409      case Z_DATA_ERROR: COUT(2) << "G.St.Man: compress: data corrupted in gamestate.compress" << std::endl;
410      return NULL;
411    }
412
413    GameStateCompressed *compressedGamestate = new GameStateCompressed;
414    compressedGamestate->compsize = buffer;
415//     std::cout << "compressedGamestate.compsize = buffer; " << buffer << std::endl;
416    compressedGamestate->normsize = size;
417//     std::cout << "compressedGamestate.normsize = size; " << size << std::endl;
418    compressedGamestate->id = a->id;
419    compressedGamestate->data = dest;
420    compressedGamestate->diffed = a->diffed;
421    compressedGamestate->complete = a->complete;
422    compressedGamestate->base_id = a->base_id;
423    //COUT(5) << "G.St.Man: saved compressed data in GameStateCompressed:" << std::endl;
424    return compressedGamestate;
425  }
426 
427  GameState *GameStateManager::decompress(GameStateCompressed *a) {
428    //COUT(4) << "GameStateClient: uncompressing gamestate. id: " << a->id << ", baseid: " << a->base_id << ", normsize: " << a->normsize << ", compsize: " << a->compsize << std::endl;
429    int normsize = a->normsize;
430    int compsize = a->compsize;
431    int bufsize;
432    if(normsize < compsize)
433      bufsize = compsize;
434    else
435      bufsize = normsize;
436//     unsigned char* dest = (unsigned char*)malloc( bufsize );
437    if(bufsize==0)
438      return NULL;
439    unsigned char *dest = new unsigned char[bufsize];
440    int retval;
441    uLongf length=normsize;
442    //std::cout << "gamestateclient" << std::endl;
443    //std::cout << "normsize " << a.normsize << " compsize " << a.compsize << " " << bufsize << std::endl;
444    retval = uncompress( dest, &length, a->data, (uLong)compsize );
445    //std::cout << "length " << length << std::endl;
446    switch ( retval ) {
447      case Z_OK: COUT(5) << "successfully decompressed" << std::endl; break;
448      case Z_MEM_ERROR: COUT(1) << "not enough memory available" << std::endl; return NULL;
449      case Z_BUF_ERROR: COUT(2) << "not enough memory available in the buffer" << std::endl; return NULL;
450      case Z_DATA_ERROR: COUT(2) << "data corrupted (zlib)" << std::endl; return NULL;
451    }
452
453    GameState *gamestate = new GameState;
454    gamestate->id = a->id;
455    gamestate->size = normsize;
456    gamestate->data = dest;
457    gamestate->base_id = a->base_id;
458    gamestate->diffed = a->diffed;
459    gamestate->complete = a->complete;
460
461
462    return gamestate;
463  }
464 
465
466  void GameStateManager::ackGameState(int clientID, int gamestateID) {
467    ClientInformation *temp = head_->findClient(clientID);
468    if(temp==0)
469      return;
470    int curid = temp->getGamestateID();
471   
472    if(gamestateID == GAMESTATEID_INITIAL){
473      temp->setGameStateID(GAMESTATEID_INITIAL);
474      if(curid!=GAMESTATEID_INITIAL)
475        --(gameStateUsed.find(curid)->second);
476      return;
477    }
478    if(curid > gamestateID)
479      // the network packets got messed up
480      return;
481    COUT(4) << "acking gamestate " << gamestateID << " for clientid: " << clientID << " curid: " << curid << std::endl;
482    // decrease usage of gamestate and save it
483//     deleteUnusedGameState(curid);
484    //increase gamestateused
485    if(curid!=GAMESTATEID_INITIAL)
486      --(gameStateUsed.find(curid)->second);
487    ++(gameStateUsed.find(gamestateID)->second);
488    temp->setGameStateID(gamestateID);
489    /*
490    GameState *old = clientGameState[clientID];
491    deleteUnusedGameState(old);
492    clientGameState[clientID]=idGameState[gamestateID];*/
493  }
494
495  bool GameStateManager::printGameStates() {
496    std::map<int, GameState*>::iterator it;
497    COUT(4) << "gamestates: ";
498    for(it = gameStateMap.begin(); it!=gameStateMap.end(); it++){
499      COUT(4) << (*it).first << ":" << (*it).second << " | ";
500    }
501    COUT(4) << std::endl;
502    return true;
503  }
504 
505  bool GameStateManager::checkAccess(int clientID, int objectID){
506    // currently we only check, wheter the object is the clients spaceship
507//     return head_->findClient(objectID)->getShipID()==objectID;
508    return true; // TODO: change this
509  }
510 
511  void GameStateManager::removeClient(ClientInformation* client){
512    if(!client)
513      return;
514    if(client->getGamestateID()>=0)
515      gameStateUsed[client->getGamestateID()]--;
516    head_->removeClient(client->getID());
517  }
518
519}
Note: See TracBrowser for help on using the repository browser.