Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network2/src/network/GameStateManager.cc @ 1150

Last change on this file since 1150 was 1098, checked in by rgrieder, 17 years ago

merged network branch into new network2 branch (from trunk)

File size: 11.2 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 "ClientInformation.h"
49#include "Synchronisable.h"
50
51namespace network
52{
53  GameStateManager::GameStateManager(ClientInformation *head) {
54    id=0;
55    head_=head;
56  }
57
58  GameStateManager::~GameStateManager() {
59  }
60
61  void GameStateManager::update(){
62    cleanup();
63    reference = getSnapshot(id);
64    gameStateMap.insert(std::pair<int, GameState*>(id, reference));
65    gameStateUsed[id]=0;
66    ++id;
67    return;
68  }
69 
70 
71  /**
72   * this function is used to keep the memory usage low
73   * it tries to delete all the unused gamestates
74   *
75   *
76   */
77  void GameStateManager::cleanup(){
78    std::map<int,int>::iterator it = gameStateUsed.begin();
79    while(it!=gameStateUsed.end()){
80      if( (*it).second <= 0 ){
81        free(gameStateMap[(*it).first]->data);
82        delete gameStateMap[(*it).first];
83        gameStateMap.erase((*it).first);
84        gameStateUsed.erase(it++);
85      }else  //as soon as we got a used gamestate break here because we could use newer gamestates in future
86        break;
87    }
88  }
89
90  GameStateCompressed *GameStateManager::popGameState(int clientID) {
91    //why are we searching the same client's gamestate id as we searched in
92    //Server::sendGameState?
93    int gID = head_->findClient(clientID)->getGamestateID();
94    COUT(4) << "G.St.Man: popgamestate: sending gstate_id: " << id << " diffed from: " << gID << " (not diffed yet)" << std::endl;
95   
96    //chose wheather the next gamestate is the first or not
97    if(gID != GAMESTATEID_INITIAL){
98      GameState *client = gameStateMap[gID];
99      GameState *server = reference;
100      //head_->findClient(clientID)->setGamestateID(id);
101      return encode(client, server);
102    } else {
103      GameState *server = reference;
104      //head_->findClient(clientID)->setGamestateID(id);
105      return encode(server);
106      // return an undiffed gamestate and set appropriate flags
107    }
108  }
109
110  /**
111  * This function goes through the whole list of synchronisables and
112  * saves all the synchronisables to a flat "list".
113  * @return struct of type gamestate containing the size of the whole gamestate and a pointer linking to the flat list
114  */
115  GameState *GameStateManager::getSnapshot(int id)
116  {
117    //std::cout << "begin getSnapshot" << std::endl;
118    //the size of the gamestate
119    int totalsize=0;
120    int memsize=1000;
121    //the size of one specific synchronisable
122    int tempsize=0;
123    // get the start of the Synchronisable list
124    orxonox::Iterator<Synchronisable> it;
125    // struct for return value of Synchronisable::getData()
126    syncData sync;
127
128    GameState *retval=new GameState; //return value
129    retval->id=id++;
130    COUT(4) << "G.ST.Man: producing gamestate with id: " << retval->id << std::endl;
131    // reserve a little memory and increase it later on
132    COUT(5) << "G.ST.Man: mallocing: " << memsize << std::endl;
133    retval->data = (unsigned char*)malloc(memsize);
134    COUT(5) << "G.ST.Man: malloced: " << memsize << std::endl;
135
136    // offset of memory functions
137    int offset=0;
138    // go through all Synchronisables
139    for(it = orxonox::ObjectList<Synchronisable>::start(); it; ++it){
140      //std::cout << "begin inner loop" << std::endl;
141      //std::cout << "gamestatemanager: in for loop" << std::endl;
142      //get size of the synchronisable
143      tempsize=it->getSize();
144      //COUT(5) << "size of temp gamestate: " << tempsize << std::endl;
145      //COUT(2) << "size of synchronisable: " << tempsize << std::endl;
146      // add place for data and 3 ints (length,classid,objectid)
147      totalsize+=tempsize+3*sizeof(int);
148      //std::cout << "totalsize: " << totalsize << std::endl;
149      //COUT(5) << "G.St.Man: current totalsize=" << totalsize << std::endl;
150      //COUT(5) << "G.St.Man: current it->classID=" << it->classID << " it->objectID=" << it->objectID << std::endl;
151      // allocate additional space
152      if((totalsize+tempsize) > memsize){
153        COUT(5) << "G.St.Man: need additional memory" << std::endl;
154        if(tempsize < 1000){
155          retval->data = (unsigned char *)realloc((void *)retval->data, totalsize+1000);
156          memsize+=1000;
157        } else {
158          retval->data = (unsigned char *)realloc((void *)retval->data, totalsize+1000);
159          memsize+=tempsize+1000;
160        }
161        COUT(5) << "G.St.Man: additional space allocation finished" << std::endl;
162      }
163
164      // run Synchronisable::getData with offset and additional place for 3 ints in between (for ids and length)
165      sync=it->getData((retval->data)+offset+3*sizeof(int));
166      memcpy(retval->data+offset, (void *)&sync.length, sizeof(int));
167      //*(retval->data+offset)=sync.length;
168      memcpy(retval->data+offset+sizeof(int), (void *)&sync.objectID, sizeof(int));
169      //*(retval->data+offset+sizeof(int))=sync.objectID;
170      memcpy(retval->data+offset+2*sizeof(int), (void *)&sync.classID, sizeof(int));
171      //*(retval->data+offset+2*sizeof(int))=sync.classID;
172      // increase data pointer
173      offset+=tempsize+3*sizeof(int);
174      //std::cout << "end inner loop" << std::endl;
175    }
176    retval->size=totalsize;
177    //#### bugfix
178    retval->diffed = false;
179    //std::cout << "end snapShot" << std::endl;
180    COUT(5) << "G.ST.Man: Gamestate size: " << totalsize << std::endl;
181    return retval;
182  }
183
184  //##### ADDED FOR TESTING PURPOSE #####
185  GameStateCompressed* GameStateManager::testCompress( GameState* g ) {
186    return compress_( g );
187  }
188
189  GameState* GameStateManager::testDiff( GameState* a, GameState* b ) {
190    return diff( a, b );
191  }
192  //##### END TESTING PURPOSE #####
193
194  GameStateCompressed *GameStateManager::encode(GameState *a, GameState *b) {
195    COUT(5) << "G.St.Man: this will be a DIFFED gamestate" << std::endl;
196    //GameState r = diff(a,b);
197    //r.diffed = true;
198    GameState *r = b;
199    r->diffed = false;
200    //return compress_(r);
201    GameStateCompressed *g = new GameStateCompressed;
202    g->base_id = b->base_id;
203    g->id = b->id;
204    g->diffed = b->diffed;
205    g->data = b->data;
206    g->normsize = b->size;
207    g->compsize = b->size;
208    return g;
209  }
210
211  GameStateCompressed *GameStateManager::encode(GameState *a) {
212    COUT(5) << "G.St.Man: this will be a not diffed gamestate" << std::endl;
213    a->diffed=false;
214    GameStateCompressed *g = new GameStateCompressed;
215    g->base_id = a->base_id;
216    g->id = a->id;
217    g->diffed = a->diffed;
218    g->data = a->data;
219    g->normsize = a->size;
220    g->compsize = a->size;
221    return g;
222  }
223
224  GameState *GameStateManager::diff(GameState *a, GameState *b) {
225    unsigned char *ap = a->data, *bp = b->data;
226    int of=0; // pointers offset
227    int dest_length=0;
228    if(a->size>=b->size)
229      dest_length=a->size;
230    else
231      dest_length=b->size;
232    unsigned char *dp = (unsigned char *)malloc(dest_length*sizeof(unsigned char));
233    while(of<a->size && of<b->size){
234      *(dp+of)=*(ap+of)^*(bp+of); // do the xor
235      ++of;
236    }
237    if(a->size!=b->size){ // do we have to fill up ?
238      unsigned char n=0;
239      if(a->size<b->size){
240        while(of<dest_length){
241          *(dp+of)=n^*(bp+of);
242          of++;
243        }
244      } else{
245        while(of<dest_length){
246          *(dp+of)=*(ap+of)^n;
247          of++;
248        }
249      }
250    }
251
252    GameState *r = new GameState;
253    r->id = b->id;
254    r->size = dest_length;
255    r->diffed = true;
256    r->base_id = a->id;
257    r->data = dp;
258    return r;
259  }
260
261  GameStateCompressed *GameStateManager::compress_(GameState *a) {
262    //COUT(4) << "G.St.Man: compressing gamestate" << std::endl;
263
264    //COUT(4) << "G.St.Man: a: id: " << a->id << " base_id: " << a->base_id << " size: " << a->size << " diffed: " << a->diffed << std::endl;
265    int size = a->size;
266
267    uLongf buffer = (uLongf)((a->size + 12)*1.01)+1;
268    //COUT(4) << "size: " << size << ", buffer: " << buffer << std::endl;
269    unsigned char* dest = (unsigned char*)malloc( buffer );
270    //COUT(4) << "dest: " << dest << std::endl;
271    int retval;
272    //std::cout << "before ziped " << buffer << std::endl;
273    retval = compress( dest, &buffer, a->data, (uLong)size );
274    //COUT(4) << "bloablabla aft3er compress" << std::endl;
275    //std::cout << "after ziped " << buffer << std::endl;
276
277    switch ( retval ) {
278      case Z_OK: COUT(5) << "G.St.Man: compress: successfully compressed" << std::endl; break;
279      case Z_MEM_ERROR: COUT(1) << "G.St.Man: compress: not enough memory available in gamestate.compress" << std::endl; 
280      return NULL;
281      case Z_BUF_ERROR: COUT(2) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << std::endl;
282      return NULL;
283      case Z_DATA_ERROR: COUT(2) << "G.St.Man: compress: data corrupted in gamestate.compress" << std::endl;
284      return NULL;
285    }
286
287    GameStateCompressed *compressedGamestate = new GameStateCompressed;
288    compressedGamestate->compsize = buffer;
289//     std::cout << "compressedGamestate.compsize = buffer; " << buffer << std::endl;
290    compressedGamestate->normsize = size;
291//     std::cout << "compressedGamestate.normsize = size; " << size << std::endl;
292    compressedGamestate->id = a->id;
293    compressedGamestate->data = dest;
294    compressedGamestate->diffed = a->diffed;
295    compressedGamestate->base_id = a->base_id;
296    //COUT(5) << "G.St.Man: saved compressed data in GameStateCompressed:" << std::endl;
297    return compressedGamestate;
298  }
299
300  void GameStateManager::ackGameState(int clientID, int gamestateID) {
301    ClientInformation *temp = head_->findClient(clientID);
302    int curid = temp->getID();
303    // decrease usage of gamestate and save it
304    deleteUnusedGameState(curid);
305    //increase gamestateused
306    ++gameStateUsed.find(gamestateID)->second;
307    temp->setGamestateID(gamestateID);
308    /*
309    GameState *old = clientGameState[clientID];
310    deleteUnusedGameState(old);
311    clientGameState[clientID]=idGameState[gamestateID];*/
312  }
313
314  bool GameStateManager::deleteUnusedGameState(int gamestateID) {
315    int used = --(gameStateUsed.find(gamestateID)->second);
316    if(id-gamestateID>KEEP_GAMESTATES && used==0){
317      // delete gamestate
318      delete gameStateMap.find(gamestateID)->second;
319      gameStateMap.erase(gamestateID);
320      return true;
321    }
322    return false;
323  }
324
325}
Note: See TracBrowser for help on using the repository browser.