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