| 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 | #include <assert.h> | 
|---|
| 47 |  | 
|---|
| 48 | #include "core/CoreIncludes.h" | 
|---|
| 49 | #include "core/BaseObject.h" | 
|---|
| 50 | #include "ClientInformation.h" | 
|---|
| 51 | #include "Synchronisable.h" | 
|---|
| 52 |  | 
|---|
| 53 | namespace network | 
|---|
| 54 | { | 
|---|
| 55 |   GameStateManager::GameStateManager(ClientInformation *head) { | 
|---|
| 56 |     id_=0; | 
|---|
| 57 |     head_=head; | 
|---|
| 58 |   } | 
|---|
| 59 |  | 
|---|
| 60 |   GameStateManager::~GameStateManager() { | 
|---|
| 61 |   } | 
|---|
| 62 |  | 
|---|
| 63 |   void GameStateManager::update(){ | 
|---|
| 64 |     cleanup(); | 
|---|
| 65 |     reference = getSnapshot(); | 
|---|
| 66 |     COUT(4) << "inserting gamestate: " << reference << std::endl; | 
|---|
| 67 |     gameStateMap.insert(std::pair<int, GameState*>(id_, reference)); | 
|---|
| 68 |     gameStateUsed[id_]=0; | 
|---|
| 69 |     printGameStates(); | 
|---|
| 70 |     return; | 
|---|
| 71 |   } | 
|---|
| 72 |    | 
|---|
| 73 |   void GameStateManager::addGameState(GameStateCompressed *gs, int clientID){ | 
|---|
| 74 |     if(!gs) | 
|---|
| 75 |       return; | 
|---|
| 76 |     std::map<int, GameStateCompressed*>::iterator it = gameStateQueue.find(clientID); | 
|---|
| 77 |     if(it!=gameStateQueue.end()){ | 
|---|
| 78 |       // delete obsolete gamestate | 
|---|
| 79 |       delete[] it->second->data; | 
|---|
| 80 |       delete it->second; | 
|---|
| 81 |     } | 
|---|
| 82 |     gameStateQueue[clientID] = gs; | 
|---|
| 83 |     return; | 
|---|
| 84 |   } | 
|---|
| 85 |    | 
|---|
| 86 |   void GameStateManager::processGameStates(){ | 
|---|
| 87 |     std::map<int, GameStateCompressed*>::iterator it; | 
|---|
| 88 |     // now push only the most recent gamestates we received (ignore obsolete ones) | 
|---|
| 89 |     for(it = gameStateQueue.begin(); it!=gameStateQueue.end(); it++){ | 
|---|
| 90 |       pushGameState(it->second, it->first); | 
|---|
| 91 |     } | 
|---|
| 92 |     // now clear the queue | 
|---|
| 93 |     gameStateQueue.clear(); | 
|---|
| 94 |   } | 
|---|
| 95 |    | 
|---|
| 96 |    | 
|---|
| 97 |   /** | 
|---|
| 98 |    * this function is used to keep the memory usage low | 
|---|
| 99 |    * it tries to delete all the unused gamestates | 
|---|
| 100 |    *  | 
|---|
| 101 |    *  | 
|---|
| 102 |    */ | 
|---|
| 103 |   void GameStateManager::cleanup(){ | 
|---|
| 104 |     std::map<int,int>::iterator it = gameStateUsed.begin(); | 
|---|
| 105 |     while(it!=gameStateUsed.end()){ | 
|---|
| 106 |       if((id_-(*it).first)<KEEP_GAMESTATES) | 
|---|
| 107 |         break; | 
|---|
| 108 |       if( (*it).second <= 0 ){ | 
|---|
| 109 |         COUT(5) << "GameStateManager: deleting gamestate with id: " << (*it).first << ", uses: " << (*it).second << std::endl; | 
|---|
| 110 |         std::map<int, GameState *>::iterator tempit = gameStateMap.find((*it).first); | 
|---|
| 111 |         if( tempit != gameStateMap.end() ){ | 
|---|
| 112 |           GameState *temp = tempit->second; | 
|---|
| 113 |           if(temp){ | 
|---|
| 114 |             delete[] gameStateMap[(*it).first]->data; | 
|---|
| 115 |             delete gameStateMap[(*it).first]; | 
|---|
| 116 |             gameStateMap.erase((*it).first); | 
|---|
| 117 |           } | 
|---|
| 118 |         } | 
|---|
| 119 |         gameStateUsed.erase(it++); | 
|---|
| 120 |         continue; | 
|---|
| 121 |       }/*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 | 
|---|
| 122 |         COUT(4) << "breaking " << std::endl; | 
|---|
| 123 |         break; | 
|---|
| 124 |       }*/ | 
|---|
| 125 |       it++; | 
|---|
| 126 |     } | 
|---|
| 127 |   } | 
|---|
| 128 |  | 
|---|
| 129 |   GameStateCompressed *GameStateManager::popGameState(int clientID) { | 
|---|
| 130 |     //why are we searching the same client's gamestate id as we searched in | 
|---|
| 131 |     //Server::sendGameState? | 
|---|
| 132 |     int gID = head_->findClient(clientID)->getGamestateID(); | 
|---|
| 133 |     COUT(4) << "G.St.Man: popgamestate: sending gstate_id: " << id_ << " diffed from: " << gID << std::endl; | 
|---|
| 134 | //     COUT(3) << "gamestatemap: " << &gameStateMap << std::endl; | 
|---|
| 135 |     //chose wheather the next gamestate is the first or not | 
|---|
| 136 |     if(gID != GAMESTATEID_INITIAL){ | 
|---|
| 137 |       // TODO something with the gamestatemap is wrong | 
|---|
| 138 |       GameState *client=NULL; | 
|---|
| 139 |       std::map<int, GameState*>::iterator it = gameStateMap.find(gID); | 
|---|
| 140 |       if(it!=gameStateMap.end()) | 
|---|
| 141 |         client = it->second; | 
|---|
| 142 |       GameState *server = reference; | 
|---|
| 143 |       //COUT(4) << "client: " << client << " server: " << server << " gamestatemap: " << &gameStateMap << " size: " << server->size << std::endl; | 
|---|
| 144 |       COUT(4) << "client: " << (client!=0 ? client->id : (int)client) << " server: " << server->id << " gamestatemap: " << &gameStateMap << " size: " << server->size << std::endl; | 
|---|
| 145 |       if(client) | 
|---|
| 146 |         return encode(client, server); | 
|---|
| 147 |       else | 
|---|
| 148 |         return encode(server); | 
|---|
| 149 |     } else { | 
|---|
| 150 |       COUT(4) << "we got a GAMESTATEID_INITIAL for clientID: " << clientID << std::endl; | 
|---|
| 151 |       GameState *server = reference; | 
|---|
| 152 | //       ackGameState(clientID, reference->id); | 
|---|
| 153 |       return encode(server); | 
|---|
| 154 |       // return an undiffed gamestate and set appropriate flags | 
|---|
| 155 |     } | 
|---|
| 156 |   } | 
|---|
| 157 |    | 
|---|
| 158 |   bool GameStateManager::pushGameState( GameStateCompressed *gs, int clientID ){ | 
|---|
| 159 |     GameState *ugs = decompress(gs); | 
|---|
| 160 |     delete[] gs->data; | 
|---|
| 161 |     delete gs; | 
|---|
| 162 |     bool result = loadPartialSnapshot(ugs, clientID); | 
|---|
| 163 |     delete[] ugs->data; | 
|---|
| 164 |     delete ugs; | 
|---|
| 165 |     return result; | 
|---|
| 166 |   } | 
|---|
| 167 |  | 
|---|
| 168 |   /** | 
|---|
| 169 |   * This function goes through the whole list of synchronisables and | 
|---|
| 170 |   * saves all the synchronisables to a flat "list". | 
|---|
| 171 |   * @return struct of type gamestate containing the size of the whole gamestate and a pointer linking to the flat list | 
|---|
| 172 |   */ | 
|---|
| 173 |   GameState *GameStateManager::getSnapshot() | 
|---|
| 174 |   { | 
|---|
| 175 |     //std::cout << "begin getSnapshot" << std::endl; | 
|---|
| 176 |     //the size of the gamestate | 
|---|
| 177 |     int totalsize=0; | 
|---|
| 178 |     int memsize=0; | 
|---|
| 179 |     //the size of one specific synchronisable | 
|---|
| 180 |     int tempsize=0; | 
|---|
| 181 |     // get the start of the Synchronisable list | 
|---|
| 182 |     orxonox::Iterator<Synchronisable> it; | 
|---|
| 183 |     // struct for return value of Synchronisable::getData() | 
|---|
| 184 |     syncData sync; | 
|---|
| 185 |  | 
|---|
| 186 |     GameState *retval=new GameState; //return value | 
|---|
| 187 |     retval->id=++id_; | 
|---|
| 188 |     COUT(4) << "G.ST.Man: producing gamestate with id: " << retval->id << std::endl; | 
|---|
| 189 |     // offset of memory functions | 
|---|
| 190 |     int offset=0, size=0; | 
|---|
| 191 |     // get total size of gamestate | 
|---|
| 192 |     for(it = orxonox::ObjectList<Synchronisable>::start(); it; ++it){ | 
|---|
| 193 |       size+=it->getSize(); // size of the actual data of the synchronisable | 
|---|
| 194 |       size+=3*sizeof(int); // size of datasize, classID and objectID | 
|---|
| 195 |     } | 
|---|
| 196 |     //retval->data = (unsigned char*)malloc(size); | 
|---|
| 197 |     if(size==0) | 
|---|
| 198 |       return NULL; | 
|---|
| 199 |     retval->data = new unsigned char[size]; | 
|---|
| 200 |     if(!retval->data){ | 
|---|
| 201 |       COUT(2) << "GameStateManager: could not allocate memory" << std::endl; | 
|---|
| 202 |       return NULL; | 
|---|
| 203 |     } | 
|---|
| 204 |     memsize=size; | 
|---|
| 205 |     // go through all Synchronisables | 
|---|
| 206 |     for(it = orxonox::ObjectList<Synchronisable>::start(); it; ++it){ | 
|---|
| 207 |       //get size of the synchronisable | 
|---|
| 208 |       tempsize=it->getSize(); | 
|---|
| 209 |       // add place for data and 3 ints (length,classid,objectid) | 
|---|
| 210 |       totalsize+=tempsize+3*sizeof(int); | 
|---|
| 211 |       // allocate+tempsize additional space | 
|---|
| 212 |       if(totalsize > size){ | 
|---|
| 213 |         COUT(3) << "G.St.Man: need additional memory" << std::endl; | 
|---|
| 214 | //         if(tempsize < 1000){ | 
|---|
| 215 | //           retval->data = (unsigned char *)realloc((void *)retval->data, totalsize+1000); | 
|---|
| 216 | //           memsize+=1000; | 
|---|
| 217 | //         } else { | 
|---|
| 218 | //           retval->data = (unsigned char *)realloc((void *)retval->data, totalsize+1000); | 
|---|
| 219 | //           memsize+=tempsize+1000; | 
|---|
| 220 | //         } | 
|---|
| 221 | //         COUT(5) << "G.St.Man: additional space allocation finished" << std::endl; | 
|---|
| 222 |       } | 
|---|
| 223 |  | 
|---|
| 224 |       // run Synchronisable::getData with offset and additional place for 3 ints in between (for ids and length) | 
|---|
| 225 |       sync=it->getData((retval->data)+offset+3*sizeof(int)); | 
|---|
| 226 |       memcpy(retval->data+offset, (void *)&(sync.length), sizeof(int)); | 
|---|
| 227 |       memcpy(retval->data+offset+sizeof(int), (void *)&(sync.objectID), sizeof(int)); | 
|---|
| 228 |       memcpy(retval->data+offset+2*sizeof(int), (void *)&(sync.classID), sizeof(int)); | 
|---|
| 229 |       // increase data pointer | 
|---|
| 230 |       offset+=tempsize+3*sizeof(int); | 
|---|
| 231 |     } | 
|---|
| 232 |     retval->size=totalsize; | 
|---|
| 233 |     //#### bugfix | 
|---|
| 234 |     retval->diffed = false; | 
|---|
| 235 |     retval->complete = true; | 
|---|
| 236 |     //std::cout << "end snapShot" << std::endl; | 
|---|
| 237 |     COUT(5) << "G.ST.Man: Gamestate size: " << totalsize << std::endl; | 
|---|
| 238 |     COUT(5) << "G.ST.Man: 'estimated' Gamestate size: " << size << std::endl; | 
|---|
| 239 |     return retval; | 
|---|
| 240 |   } | 
|---|
| 241 |  | 
|---|
| 242 |   bool GameStateManager::loadPartialSnapshot(GameState *state, int clientID){ | 
|---|
| 243 |     if(!state) | 
|---|
| 244 |       return false; | 
|---|
| 245 |     unsigned char *data=state->data; | 
|---|
| 246 |     COUT(4) << "loadSnapshot: loading gs: " << state->id << std::endl; | 
|---|
| 247 |     // get the start of the Synchronisable list | 
|---|
| 248 |     orxonox::Iterator<Synchronisable> it=orxonox::ObjectList<Synchronisable>::start(); | 
|---|
| 249 |     syncData sync; | 
|---|
| 250 |     /*ClientInformation *client = head_->findClient(clientID); | 
|---|
| 251 |     if(client) | 
|---|
| 252 |       if(client->getPartialGamestateID()>state->id){ | 
|---|
| 253 |         COUT(3) << "we received an obsolete partial gamestate" << std::endl; | 
|---|
| 254 |         return false; | 
|---|
| 255 |       } | 
|---|
| 256 |     else;*/ | 
|---|
| 257 |         //what should we do now ?? | 
|---|
| 258 |     // loop as long as we have some data ;) | 
|---|
| 259 |     while(data < state->data+state->size){ | 
|---|
| 260 |       // prepare the syncData struct | 
|---|
| 261 |       sync.length = *(int *)data; | 
|---|
| 262 |       data+=sizeof(int); | 
|---|
| 263 |       sync.objectID = *(int*)data; | 
|---|
| 264 |       data+=sizeof(int); | 
|---|
| 265 |       sync.classID = *(int*)data; | 
|---|
| 266 |       if(sync.classID == 0) // TODO: remove this | 
|---|
| 267 |         COUT(3) << "received a classid 0" << std::endl; | 
|---|
| 268 |       data+=sizeof(int); | 
|---|
| 269 |       sync.data = data; | 
|---|
| 270 |       data+=sync.length; | 
|---|
| 271 |       COUT(5) << "objectID: " << sync.objectID << " classID: " << sync.classID << std::endl; | 
|---|
| 272 |       while(it && it->objectID!=sync.objectID) | 
|---|
| 273 |         ++it; | 
|---|
| 274 |  | 
|---|
| 275 |  | 
|---|
| 276 |       if(!it){ | 
|---|
| 277 |         // the objectaber ich glaub die  does not exist yet | 
|---|
| 278 |         COUT(4) << "loadsnapshot: creating new object " << std::endl; | 
|---|
| 279 |         //COUT(4) << "loadSnapshot:\tclassid: " << sync.classID << ", name: " << ID((unsigned int) sync.classID)->getName() << std::endl; | 
|---|
| 280 |         orxonox::Identifier* id = ID((unsigned int)sync.classID); | 
|---|
| 281 |         if(!id){ | 
|---|
| 282 |           COUT(4) << "We could not identify a new object; classid: " << sync.classID << std::endl; | 
|---|
| 283 |           continue; | 
|---|
| 284 |         } | 
|---|
| 285 |         Synchronisable *no = dynamic_cast<Synchronisable *>(id->fabricate()); | 
|---|
| 286 |         COUT(4) << "loadpartialsnapshot (generating new object): classid: " << sync.classID << " objectID: " << sync.objectID << " length: " << sync.length << std::endl; | 
|---|
| 287 |         no->objectID=sync.objectID; | 
|---|
| 288 |         no->classID=sync.classID; | 
|---|
| 289 |         it=orxonox::ObjectList<Synchronisable>::end(); | 
|---|
| 290 |         // update data and create object/entity... | 
|---|
| 291 |         if( !no->updateData(sync) ) | 
|---|
| 292 |           COUT(1) << "We couldn't update the object: " << sync.objectID << std::endl; | 
|---|
| 293 |         if( !no->create() ) | 
|---|
| 294 |           COUT(1) << "We couldn't manifest (create() ) the object: " << sync.objectID << std::endl; | 
|---|
| 295 |       }else{ | 
|---|
| 296 |         // we have our object | 
|---|
| 297 |         COUT(4) << "loadpartialsnapshot: we found the appropriate object" << std::endl; | 
|---|
| 298 |         if(checkAccess(clientID, sync.objectID)){ | 
|---|
| 299 |           if(! it->updateData(sync)) | 
|---|
| 300 |             COUT(1) << "We couldn't update objectID: " \ | 
|---|
| 301 |               << sync.objectID << "; classID: " << sync.classID << std::endl; | 
|---|
| 302 |         }else | 
|---|
| 303 |           COUT(4) << "loadPartialSnapshot: no access to change objectID: " << sync.objectID << std::endl; | 
|---|
| 304 |       } | 
|---|
| 305 |       ++it; | 
|---|
| 306 |     } | 
|---|
| 307 |     //client->setPartialGamestateID(state->id); | 
|---|
| 308 |     return true; | 
|---|
| 309 |   } | 
|---|
| 310 |    | 
|---|
| 311 |    | 
|---|
| 312 |   //##### ADDED FOR TESTING PURPOSE ##### | 
|---|
| 313 |   GameStateCompressed* GameStateManager::testCompress( GameState* g ) { | 
|---|
| 314 |     return compress_( g ); | 
|---|
| 315 |   } | 
|---|
| 316 |  | 
|---|
| 317 |   GameState* GameStateManager::testDiff( GameState* a, GameState* b ) { | 
|---|
| 318 |     return diff( a, b ); | 
|---|
| 319 |   } | 
|---|
| 320 |   //##### END TESTING PURPOSE ##### | 
|---|
| 321 |  | 
|---|
| 322 |   GameStateCompressed *GameStateManager::encode(GameState *a, GameState *b) { | 
|---|
| 323 |     COUT(5) << "G.St.Man: this will be a DIFFED gamestate" << std::endl; | 
|---|
| 324 |     GameState *r = diff(a,b); | 
|---|
| 325 |     GameStateCompressed *c = compress_(r); | 
|---|
| 326 |     delete[] r->data; | 
|---|
| 327 |     delete r; | 
|---|
| 328 |     return c; | 
|---|
| 329 |   } | 
|---|
| 330 |  | 
|---|
| 331 |   GameStateCompressed *GameStateManager::encode(GameState *a) { | 
|---|
| 332 |     COUT(5) << "G.St.Man: encoding gamestate (compress)" << std::endl; | 
|---|
| 333 |     a->base_id=GAMESTATEID_INITIAL; | 
|---|
| 334 |     return compress_(a); | 
|---|
| 335 |     /*GameStateCompressed *g = new GameStateCompressed; | 
|---|
| 336 |     g->base_id = a->base_id; | 
|---|
| 337 |     g->id = a->id; | 
|---|
| 338 |     g->diffed = a->diffed; | 
|---|
| 339 |     g->data = a->data; | 
|---|
| 340 |     g->normsize = a->size; | 
|---|
| 341 |     g->compsize = a->size; | 
|---|
| 342 |     return g;*/ | 
|---|
| 343 |   } | 
|---|
| 344 |  | 
|---|
| 345 |   GameState *GameStateManager::diff(GameState *alt, GameState *neu) { | 
|---|
| 346 |     unsigned char *ap = alt->data, *bp = neu->data; | 
|---|
| 347 |     int of=0; // pointers offset | 
|---|
| 348 |     int dest_length=0; | 
|---|
| 349 |     /*if(alt->size>neu->size) | 
|---|
| 350 |       dest_length=alt->size; | 
|---|
| 351 |     else*/ | 
|---|
| 352 |       dest_length=neu->size; | 
|---|
| 353 |     if(dest_length==0) | 
|---|
| 354 |       return NULL; | 
|---|
| 355 |     //unsigned char *dp = (unsigned char *)malloc(dest_length*sizeof(unsigned char)); | 
|---|
| 356 |     unsigned char *dp = new unsigned char[dest_length*sizeof(unsigned char)]; | 
|---|
| 357 |     while(of<alt->size && of<neu->size){ | 
|---|
| 358 |       *(dp+of)=*(ap+of)^*(bp+of); // do the xor | 
|---|
| 359 |       ++of; | 
|---|
| 360 |     } | 
|---|
| 361 |     if(alt->size!=neu->size){ // do we have to fill up ? | 
|---|
| 362 |       unsigned char n=0; | 
|---|
| 363 |       if(alt->size<neu->size){ | 
|---|
| 364 |         while(of<dest_length){ | 
|---|
| 365 |           *(dp+of)=n^*(bp+of); | 
|---|
| 366 |           of++; | 
|---|
| 367 |         } | 
|---|
| 368 |       } /*else{ | 
|---|
| 369 |         while(of<dest_length){ | 
|---|
| 370 |           *(dp+of)=*(ap+of)^n; | 
|---|
| 371 |           of++; | 
|---|
| 372 |         } | 
|---|
| 373 |       }*/ | 
|---|
| 374 |     } | 
|---|
| 375 |  | 
|---|
| 376 |     GameState *r = new GameState; | 
|---|
| 377 |     r->id = neu->id; | 
|---|
| 378 |     r->size = dest_length; | 
|---|
| 379 |     r->diffed = true; | 
|---|
| 380 |     r->base_id = alt->id; | 
|---|
| 381 |     r->data = dp; | 
|---|
| 382 |     r->complete = true; | 
|---|
| 383 |     return r; | 
|---|
| 384 |   } | 
|---|
| 385 |  | 
|---|
| 386 |   GameStateCompressed *GameStateManager::compress_(GameState *a) { | 
|---|
| 387 |     //COUT(4) << "G.St.Man: compressing gamestate" << std::endl; | 
|---|
| 388 |  | 
|---|
| 389 |     //COUT(4) << "G.St.Man: a: id: " << a->id << " base_id: " << a->base_id << " size: " << a->size << " diffed: " << a->diffed << std::endl; | 
|---|
| 390 |     int size = a->size; | 
|---|
| 391 |  | 
|---|
| 392 |     uLongf buffer = (uLongf)((a->size + 12)*1.01)+1; | 
|---|
| 393 |     //COUT(4) << "size: " << size << ", buffer: " << buffer << std::endl; | 
|---|
| 394 |     //unsigned char* dest = (unsigned char*)malloc( buffer ); | 
|---|
| 395 |     if(buffer==0) | 
|---|
| 396 |       return NULL; | 
|---|
| 397 |     unsigned char *dest = new unsigned char[buffer]; | 
|---|
| 398 |     //COUT(4) << "dest: " << dest << std::endl; | 
|---|
| 399 |     int retval; | 
|---|
| 400 |     //std::cout << "before ziped " << buffer << std::endl; | 
|---|
| 401 |     retval = compress( dest, &buffer, a->data, (uLong)size ); | 
|---|
| 402 |     //COUT(4) << "bloablabla aft3er compress" << std::endl; | 
|---|
| 403 |     //std::cout << "after ziped " << buffer << std::endl; | 
|---|
| 404 |  | 
|---|
| 405 |     switch ( retval ) { | 
|---|
| 406 |       case Z_OK: COUT(5) << "G.St.Man: compress: successfully compressed" << std::endl; break; | 
|---|
| 407 |       case Z_MEM_ERROR: COUT(1) << "G.St.Man: compress: not enough memory available in gamestate.compress" << std::endl;  | 
|---|
| 408 |       return NULL; | 
|---|
| 409 |       case Z_BUF_ERROR: COUT(2) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << std::endl; | 
|---|
| 410 |       return NULL; | 
|---|
| 411 |       case Z_DATA_ERROR: COUT(2) << "G.St.Man: compress: data corrupted in gamestate.compress" << std::endl; | 
|---|
| 412 |       return NULL; | 
|---|
| 413 |     } | 
|---|
| 414 |  | 
|---|
| 415 |     GameStateCompressed *compressedGamestate = new GameStateCompressed; | 
|---|
| 416 |     compressedGamestate->compsize = buffer; | 
|---|
| 417 | //     std::cout << "compressedGamestate.compsize = buffer; " << buffer << std::endl; | 
|---|
| 418 |     compressedGamestate->normsize = size; | 
|---|
| 419 | //     std::cout << "compressedGamestate.normsize = size; " << size << std::endl; | 
|---|
| 420 |     compressedGamestate->id = a->id; | 
|---|
| 421 |     compressedGamestate->data = dest; | 
|---|
| 422 |     compressedGamestate->diffed = a->diffed; | 
|---|
| 423 |     compressedGamestate->complete = a->complete; | 
|---|
| 424 |     compressedGamestate->base_id = a->base_id; | 
|---|
| 425 |     //COUT(5) << "G.St.Man: saved compressed data in GameStateCompressed:" << std::endl; | 
|---|
| 426 |     return compressedGamestate; | 
|---|
| 427 |   } | 
|---|
| 428 |    | 
|---|
| 429 |   GameState *GameStateManager::decompress(GameStateCompressed *a) { | 
|---|
| 430 |     //COUT(4) << "GameStateClient: uncompressing gamestate. id: " << a->id << ", baseid: " << a->base_id << ", normsize: " << a->normsize << ", compsize: " << a->compsize << std::endl; | 
|---|
| 431 |     int normsize = a->normsize; | 
|---|
| 432 |     int compsize = a->compsize; | 
|---|
| 433 |     int bufsize; | 
|---|
| 434 |     if(normsize < compsize) | 
|---|
| 435 |       bufsize = compsize; | 
|---|
| 436 |     else | 
|---|
| 437 |       bufsize = normsize; | 
|---|
| 438 | //     unsigned char* dest = (unsigned char*)malloc( bufsize ); | 
|---|
| 439 |     if(bufsize==0) | 
|---|
| 440 |       return NULL; | 
|---|
| 441 |     unsigned char *dest = new unsigned char[bufsize]; | 
|---|
| 442 |     int retval; | 
|---|
| 443 |     uLongf length=normsize; | 
|---|
| 444 |     //std::cout << "gamestateclient" << std::endl; | 
|---|
| 445 |     //std::cout << "normsize " << a.normsize << " compsize " << a.compsize << " " << bufsize << std::endl; | 
|---|
| 446 |     retval = uncompress( dest, &length, a->data, (uLong)compsize ); | 
|---|
| 447 |     //std::cout << "length " << length << std::endl; | 
|---|
| 448 |     switch ( retval ) { | 
|---|
| 449 |       case Z_OK: COUT(5) << "successfully decompressed" << std::endl; break; | 
|---|
| 450 |       case Z_MEM_ERROR: COUT(1) << "not enough memory available" << std::endl; return NULL; | 
|---|
| 451 |       case Z_BUF_ERROR: COUT(2) << "not enough memory available in the buffer" << std::endl; return NULL; | 
|---|
| 452 |       case Z_DATA_ERROR: COUT(2) << "data corrupted (zlib)" << std::endl; return NULL; | 
|---|
| 453 |     } | 
|---|
| 454 |  | 
|---|
| 455 |     GameState *gamestate = new GameState; | 
|---|
| 456 |     gamestate->id = a->id; | 
|---|
| 457 |     gamestate->size = normsize; | 
|---|
| 458 |     gamestate->data = dest; | 
|---|
| 459 |     gamestate->base_id = a->base_id; | 
|---|
| 460 |     gamestate->diffed = a->diffed; | 
|---|
| 461 |     gamestate->complete = a->complete; | 
|---|
| 462 |  | 
|---|
| 463 |  | 
|---|
| 464 |     return gamestate; | 
|---|
| 465 |   } | 
|---|
| 466 |    | 
|---|
| 467 |  | 
|---|
| 468 |   void GameStateManager::ackGameState(int clientID, int gamestateID) { | 
|---|
| 469 |     ClientInformation *temp = head_->findClient(clientID); | 
|---|
| 470 |     if(temp==0) | 
|---|
| 471 |       return; | 
|---|
| 472 |     int curid = temp->getGamestateID(); | 
|---|
| 473 |      | 
|---|
| 474 |     if(gamestateID == GAMESTATEID_INITIAL){ | 
|---|
| 475 |       temp->setGameStateID(GAMESTATEID_INITIAL); | 
|---|
| 476 |       if(curid!=GAMESTATEID_INITIAL){ | 
|---|
| 477 |         assert(gameStateUsed.find(curid)!=gameStateUsed.end()); | 
|---|
| 478 |         --(gameStateUsed.find(curid)->second); | 
|---|
| 479 |       } | 
|---|
| 480 |       return; | 
|---|
| 481 |     } | 
|---|
| 482 |     if(curid > gamestateID) | 
|---|
| 483 |       // the network packets got messed up  | 
|---|
| 484 |       return; | 
|---|
| 485 |     COUT(4) << "acking gamestate " << gamestateID << " for clientid: " << clientID << " curid: " << curid << std::endl; | 
|---|
| 486 |     // decrease usage of gamestate and save it | 
|---|
| 487 | //     deleteUnusedGameState(curid); | 
|---|
| 488 |     //increase gamestateused | 
|---|
| 489 |     std::map<int, int>::iterator it = gameStateUsed.find(curid); | 
|---|
| 490 |     if(curid!=GAMESTATEID_INITIAL){ | 
|---|
| 491 |       if(it!=gameStateUsed.end()) | 
|---|
| 492 |         --(it->second); | 
|---|
| 493 |     } | 
|---|
| 494 |     it = gameStateUsed.find(gamestateID); | 
|---|
| 495 |     if(it!=gameStateUsed.end()){ | 
|---|
| 496 |       ++(it->second); | 
|---|
| 497 |       temp->setGameStateID(gamestateID); | 
|---|
| 498 |     } | 
|---|
| 499 |     /* | 
|---|
| 500 |     GameState *old = clientGameState[clientID]; | 
|---|
| 501 |     deleteUnusedGameState(old); | 
|---|
| 502 |     clientGameState[clientID]=idGameState[gamestateID];*/ | 
|---|
| 503 |   } | 
|---|
| 504 |  | 
|---|
| 505 |   bool GameStateManager::printGameStates() { | 
|---|
| 506 |     std::map<int, GameState*>::iterator it; | 
|---|
| 507 |     COUT(4) << "gamestates: "; | 
|---|
| 508 |     for(it = gameStateMap.begin(); it!=gameStateMap.end(); it++){ | 
|---|
| 509 |       COUT(4) << (*it).first << ":" << (*it).second << " | "; | 
|---|
| 510 |     } | 
|---|
| 511 |     COUT(4) << std::endl; | 
|---|
| 512 |     return true; | 
|---|
| 513 |   } | 
|---|
| 514 |    | 
|---|
| 515 |   bool GameStateManager::checkAccess(int clientID, int objectID){ | 
|---|
| 516 |     // currently we only check, wheter the object is the clients spaceship | 
|---|
| 517 | //     return head_->findClient(objectID)->getShipID()==objectID; | 
|---|
| 518 |     return true; // TODO: change this | 
|---|
| 519 |   } | 
|---|
| 520 |    | 
|---|
| 521 |   void GameStateManager::removeClient(ClientInformation* client){ | 
|---|
| 522 |     if(!client) | 
|---|
| 523 |       return; | 
|---|
| 524 |     if(client->getGamestateID()>=0) | 
|---|
| 525 |       gameStateUsed[client->getGamestateID()]--; | 
|---|
| 526 |     head_->removeClient(client->getID()); | 
|---|
| 527 |   } | 
|---|
| 528 |  | 
|---|
| 529 | } | 
|---|