Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1480 was 1360, checked in by rgrieder, 17 years ago

merged merge branch back to trunk

File size: 17.7 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    /*ClientInformation *client = head_->findClient(clientID);
224    if(client)
225      if(client->getPartialGamestateID()>state->id){
226        COUT(3) << "we received an obsolete partial gamestate" << std::endl;
227        return false;
228      }
229    else;*/
230        //what should we do now ??
231    // loop as long as we have some data ;)
232    while(data < state->data+state->size){
233      // prepare the syncData struct
234      sync.length = *(int *)data;
235      data+=sizeof(int);
236      sync.objectID = *(int*)data;
237      data+=sizeof(int);
238      sync.classID = *(int*)data;
239      data+=sizeof(int);
240      sync.data = data;
241      data+=sync.length;
242      COUT(4) << "objectID: " << sync.objectID << " classID: " << sync.classID << std::endl;
243      while(it && it->objectID!=sync.objectID)
244        ++it;
245
246
247      if(!it){
248        // the object does not exist yet
249        COUT(4) << "loadsnapshot: creating new object " << std::endl;
250        //COUT(4) << "loadSnapshot:\tclassid: " << sync.classID << ", name: " << ID((unsigned int) sync.classID)->getName() << std::endl;
251        orxonox::Identifier* id = ID((unsigned int)sync.classID);
252        if(!id){
253          COUT(4) << "We could not identify a new object; classid: " << sync.classID << std::endl;
254          continue;
255        }
256        Synchronisable *no = dynamic_cast<Synchronisable *>(id->fabricate());
257        COUT(4) << "loadpartialsnapshot (generating new object): classid: " << sync.classID << " objectID: " << sync.objectID << " length: " << sync.length << std::endl;
258        no->objectID=sync.objectID;
259        no->classID=sync.classID;
260        it=orxonox::ObjectList<Synchronisable>::end();
261        // update data and create object/entity...
262        if( !no->updateData(sync) )
263          COUT(1) << "We couldn't update the object: " << sync.objectID << std::endl;
264        if( !no->create() )
265          COUT(1) << "We couldn't manifest (create() ) the object: " << sync.objectID << std::endl;
266      }else{
267        // we have our object
268        COUT(4) << "loadpartialsnapshot: we found the appropriate object" << std::endl;
269        if(checkAccess(clientID, sync.objectID)){
270          if(! it->updateData(sync))
271            COUT(1) << "We couldn't update objectID: " \
272              << sync.objectID << "; classID: " << sync.classID << std::endl;
273        }else
274          COUT(4) << "loadPartialSnapshot: no access to change objectID: " << sync.objectID << std::endl;
275      }
276      ++it;
277    }
278    //client->setPartialGamestateID(state->id);
279    return true;
280  }
281 
282 
283  //##### ADDED FOR TESTING PURPOSE #####
284  GameStateCompressed* GameStateManager::testCompress( GameState* g ) {
285    return compress_( g );
286  }
287
288  GameState* GameStateManager::testDiff( GameState* a, GameState* b ) {
289    return diff( a, b );
290  }
291  //##### END TESTING PURPOSE #####
292
293  GameStateCompressed *GameStateManager::encode(GameState *a, GameState *b) {
294    COUT(4) << "G.St.Man: this will be a DIFFED gamestate" << std::endl;
295    GameState *r = diff(a,b);
296    GameStateCompressed *c = compress_(r);
297    delete[] r->data;
298    delete r;
299    return c;
300  }
301
302  GameStateCompressed *GameStateManager::encode(GameState *a) {
303    COUT(5) << "G.St.Man: encoding gamestate (compress)" << std::endl;
304    a->base_id=GAMESTATEID_INITIAL;
305    return compress_(a);
306    /*GameStateCompressed *g = new GameStateCompressed;
307    g->base_id = a->base_id;
308    g->id = a->id;
309    g->diffed = a->diffed;
310    g->data = a->data;
311    g->normsize = a->size;
312    g->compsize = a->size;
313    return g;*/
314  }
315
316  GameState *GameStateManager::diff(GameState *a, GameState *b) {
317    unsigned char *ap = a->data, *bp = b->data;
318    int of=0; // pointers offset
319    int dest_length=0;
320    if(a->size>=b->size)
321      dest_length=a->size;
322    else
323      dest_length=b->size;
324    //unsigned char *dp = (unsigned char *)malloc(dest_length*sizeof(unsigned char));
325    unsigned char *dp = new unsigned char[dest_length*sizeof(unsigned char)];
326    while(of<a->size && of<b->size){
327      *(dp+of)=*(ap+of)^*(bp+of); // do the xor
328      ++of;
329    }
330    if(a->size!=b->size){ // do we have to fill up ?
331      unsigned char n=0;
332      if(a->size<b->size){
333        while(of<dest_length){
334          *(dp+of)=n^*(bp+of);
335          of++;
336        }
337      } else{
338        while(of<dest_length){
339          *(dp+of)=*(ap+of)^n;
340          of++;
341        }
342      }
343    }
344
345    GameState *r = new GameState;
346    r->id = b->id;
347    r->size = dest_length;
348    r->diffed = true;
349    r->base_id = a->id;
350    r->data = dp;
351    r->complete = true;
352    return r;
353  }
354
355  GameStateCompressed *GameStateManager::compress_(GameState *a) {
356    //COUT(4) << "G.St.Man: compressing gamestate" << std::endl;
357
358    //COUT(4) << "G.St.Man: a: id: " << a->id << " base_id: " << a->base_id << " size: " << a->size << " diffed: " << a->diffed << std::endl;
359    int size = a->size;
360
361    uLongf buffer = (uLongf)((a->size + 12)*1.01)+1;
362    //COUT(4) << "size: " << size << ", buffer: " << buffer << std::endl;
363    //unsigned char* dest = (unsigned char*)malloc( buffer );
364    unsigned char *dest = new unsigned char[buffer];
365    //COUT(4) << "dest: " << dest << std::endl;
366    int retval;
367    //std::cout << "before ziped " << buffer << std::endl;
368    retval = compress( dest, &buffer, a->data, (uLong)size );
369    //COUT(4) << "bloablabla aft3er compress" << std::endl;
370    //std::cout << "after ziped " << buffer << std::endl;
371
372    switch ( retval ) {
373      case Z_OK: COUT(5) << "G.St.Man: compress: successfully compressed" << std::endl; break;
374      case Z_MEM_ERROR: COUT(1) << "G.St.Man: compress: not enough memory available in gamestate.compress" << std::endl; 
375      return NULL;
376      case Z_BUF_ERROR: COUT(2) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << std::endl;
377      return NULL;
378      case Z_DATA_ERROR: COUT(2) << "G.St.Man: compress: data corrupted in gamestate.compress" << std::endl;
379      return NULL;
380    }
381
382    GameStateCompressed *compressedGamestate = new GameStateCompressed;
383    compressedGamestate->compsize = buffer;
384//     std::cout << "compressedGamestate.compsize = buffer; " << buffer << std::endl;
385    compressedGamestate->normsize = size;
386//     std::cout << "compressedGamestate.normsize = size; " << size << std::endl;
387    compressedGamestate->id = a->id;
388    compressedGamestate->data = dest;
389    compressedGamestate->diffed = a->diffed;
390    compressedGamestate->complete = a->complete;
391    compressedGamestate->base_id = a->base_id;
392    //COUT(5) << "G.St.Man: saved compressed data in GameStateCompressed:" << std::endl;
393    return compressedGamestate;
394  }
395 
396  GameState *GameStateManager::decompress(GameStateCompressed *a) {
397    //COUT(4) << "GameStateClient: uncompressing gamestate. id: " << a->id << ", baseid: " << a->base_id << ", normsize: " << a->normsize << ", compsize: " << a->compsize << std::endl;
398    int normsize = a->normsize;
399    int compsize = a->compsize;
400    int bufsize;
401    if(normsize < compsize)
402      bufsize = compsize;
403    else
404      bufsize = normsize;
405//     unsigned char* dest = (unsigned char*)malloc( bufsize );
406    unsigned char *dest = new unsigned char[bufsize];
407    int retval;
408    uLongf length=normsize;
409    //std::cout << "gamestateclient" << std::endl;
410    //std::cout << "normsize " << a.normsize << " compsize " << a.compsize << " " << bufsize << std::endl;
411    retval = uncompress( dest, &length, a->data, (uLong)compsize );
412    //std::cout << "length " << length << std::endl;
413    switch ( retval ) {
414      case Z_OK: COUT(5) << "successfully decompressed" << std::endl; break;
415      case Z_MEM_ERROR: COUT(1) << "not enough memory available" << std::endl; return NULL;
416      case Z_BUF_ERROR: COUT(2) << "not enough memory available in the buffer" << std::endl; return NULL;
417      case Z_DATA_ERROR: COUT(2) << "data corrupted (zlib)" << std::endl; return NULL;
418    }
419
420    GameState *gamestate = new GameState;
421    gamestate->id = a->id;
422    gamestate->size = normsize;
423    gamestate->data = dest;
424    gamestate->base_id = a->base_id;
425    gamestate->diffed = a->diffed;
426    gamestate->complete = a->complete;
427
428
429    return gamestate;
430  }
431 
432
433  void GameStateManager::ackGameState(int clientID, int gamestateID) {
434    ClientInformation *temp = head_->findClient(clientID);
435    if(temp==0)
436      return;
437    int curid = temp->getGamestateID();
438    if(curid > gamestateID)
439      // the network packets got messed up
440      return;
441    COUT(4) << "acking gamestate " << gamestateID << " for clientid: " << clientID << " curid: " << curid << std::endl;
442    // decrease usage of gamestate and save it
443//     deleteUnusedGameState(curid);
444    //increase gamestateused
445    if(curid!=GAMESTATEID_INITIAL)
446      --(gameStateUsed.find(curid)->second);
447    ++(gameStateUsed.find(gamestateID)->second);
448    temp->setGamestateID(gamestateID);
449    /*
450    GameState *old = clientGameState[clientID];
451    deleteUnusedGameState(old);
452    clientGameState[clientID]=idGameState[gamestateID];*/
453  }
454
455  bool GameStateManager::printGameStates() {
456    std::map<int, GameState*>::iterator it;
457    COUT(4) << "gamestates: ";
458    for(it = gameStateMap.begin(); it!=gameStateMap.end(); it++){
459      COUT(4) << (*it).first << ":" << (*it).second << " | ";
460    }
461    COUT(4) << std::endl;
462    return true;
463  }
464 
465  bool GameStateManager::checkAccess(int clientID, int objectID){
466    // currently we only check, wheter the object is the clients spaceship
467//     return head_->findClient(objectID)->getShipID()==objectID;
468    return true; // TODO: change this
469  }
470 
471  void GameStateManager::removeClient(ClientInformation* client){
472    if(!client)
473      return;
474    if(client->getGamestateID()>=0)
475      gameStateUsed[client->getGamestateID()]--;
476    head_->removeClient(client->getID());
477  }
478
479}
Note: See TracBrowser for help on using the repository browser.