Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

we have compression enabled and working now, hurray

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