Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network3/src/network/GameStateManager.cc @ 1234

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

took out some mallocs and replaced them with new

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