| [1711] | 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) 2008 | 
|---|
|  | 24 | *   Co-authors: | 
|---|
|  | 25 | *      ... | 
|---|
|  | 26 | * | 
|---|
|  | 27 | */ | 
|---|
|  | 28 |  | 
|---|
| [1701] | 29 | #include "Gamestate.h" | 
|---|
|  | 30 | #include "network/ClientInformation.h" | 
|---|
| [1705] | 31 | #include "network/GamestateHandler.h" | 
|---|
| [1763] | 32 | #include "core/CoreIncludes.h" | 
|---|
| [1751] | 33 | #include "core/Iterator.h" | 
|---|
| [1701] | 34 |  | 
|---|
|  | 35 | #include <zlib.h> | 
|---|
|  | 36 | #include <assert.h> | 
|---|
|  | 37 |  | 
|---|
|  | 38 |  | 
|---|
|  | 39 |  | 
|---|
|  | 40 | namespace network { | 
|---|
|  | 41 |  | 
|---|
|  | 42 | namespace packet { | 
|---|
|  | 43 |  | 
|---|
| [1751] | 44 | #define GAMESTATE_START(data) (data + sizeof(GamestateHeader)) | 
|---|
| [1701] | 45 | #define GAMESTATE_HEADER(data) ((GamestateHeader *)data) | 
|---|
|  | 46 | #define HEADER GAMESTATE_HEADER(data_) | 
|---|
| [1740] | 47 |  | 
|---|
| [2031] | 48 |  | 
|---|
| [1907] | 49 | #define PACKET_FLAG_GAMESTATE  ENET_PACKET_FLAG_RELIABLE | 
|---|
| [2031] | 50 |  | 
|---|
| [1701] | 51 | Gamestate::Gamestate() | 
|---|
|  | 52 | { | 
|---|
| [1907] | 53 | flags_ = flags_ | PACKET_FLAG_GAMESTATE; | 
|---|
| [1701] | 54 | } | 
|---|
|  | 55 |  | 
|---|
| [1907] | 56 | Gamestate::Gamestate(uint8_t *data, unsigned int clientID): | 
|---|
| [1711] | 57 | Packet(data, clientID) | 
|---|
| [1701] | 58 | { | 
|---|
| [1907] | 59 | flags_ = flags_ | PACKET_FLAG_GAMESTATE; | 
|---|
| [1701] | 60 | } | 
|---|
|  | 61 |  | 
|---|
| [1907] | 62 | Gamestate::Gamestate(uint8_t *data) | 
|---|
|  | 63 | { | 
|---|
|  | 64 | flags_ = flags_ | PACKET_FLAG_GAMESTATE; | 
|---|
|  | 65 | data_=data; | 
|---|
|  | 66 | } | 
|---|
| [1701] | 67 |  | 
|---|
| [1907] | 68 |  | 
|---|
| [1701] | 69 | Gamestate::~Gamestate() | 
|---|
|  | 70 | { | 
|---|
|  | 71 | } | 
|---|
|  | 72 |  | 
|---|
|  | 73 | bool Gamestate::collectData(int id, int mode) | 
|---|
|  | 74 | { | 
|---|
|  | 75 | int tempsize=0, currentsize=0; | 
|---|
| [1751] | 76 | assert(data_==0); | 
|---|
|  | 77 | int size = calcGamestateSize(id, mode); | 
|---|
| [1740] | 78 |  | 
|---|
| [1701] | 79 | COUT(4) << "G.ST.Man: producing gamestate with id: " << id << std::endl; | 
|---|
|  | 80 | if(size==0) | 
|---|
|  | 81 | return false; | 
|---|
|  | 82 | data_ = new unsigned char[size + sizeof(GamestateHeader)]; | 
|---|
|  | 83 | if(!data_){ | 
|---|
|  | 84 | COUT(2) << "GameStateManager: could not allocate memory" << std::endl; | 
|---|
|  | 85 | return false; | 
|---|
|  | 86 | } | 
|---|
| [2031] | 87 |  | 
|---|
| [1907] | 88 | #ifndef NDEBUG | 
|---|
|  | 89 | std::list<Synchronisable*> slist; | 
|---|
|  | 90 | std::list<Synchronisable*>::iterator iit; | 
|---|
|  | 91 | #endif | 
|---|
| [1701] | 92 | //start collect data synchronisable by synchronisable | 
|---|
| [1907] | 93 | uint8_t *mem=data_; | 
|---|
| [1701] | 94 | mem+=sizeof(GamestateHeader); | 
|---|
| [1747] | 95 | orxonox::ObjectList<Synchronisable>::iterator it; | 
|---|
|  | 96 | for(it = orxonox::ObjectList<Synchronisable>::begin(); it; ++it){ | 
|---|
| [1907] | 97 | tempsize=it->getSize(id, mode); | 
|---|
| [1740] | 98 |  | 
|---|
| [1701] | 99 | if(currentsize+tempsize > size){ | 
|---|
|  | 100 | // start allocate additional memory | 
|---|
|  | 101 | COUT(3) << "G.St.Man: need additional memory" << std::endl; | 
|---|
| [1747] | 102 | orxonox::ObjectList<Synchronisable>::iterator temp = it; | 
|---|
| [1701] | 103 | int addsize=tempsize; | 
|---|
|  | 104 | while(++temp) | 
|---|
| [1907] | 105 | addsize+=temp->getSize(id, mode); | 
|---|
|  | 106 | data_ = (uint8_t *)realloc(data_, sizeof(GamestateHeader) + currentsize + addsize); | 
|---|
| [1701] | 107 | if(!data_) | 
|---|
|  | 108 | return false; | 
|---|
|  | 109 | size = currentsize+addsize; | 
|---|
|  | 110 | }// stop allocate additional memory | 
|---|
|  | 111 |  | 
|---|
| [1907] | 112 | #ifndef NDEBUG | 
|---|
|  | 113 | for(iit=slist.begin(); iit!=slist.end(); iit++) | 
|---|
|  | 114 | assert((*iit)!=*it); | 
|---|
|  | 115 | slist.push_back(*it); | 
|---|
|  | 116 | #endif | 
|---|
| [2031] | 117 |  | 
|---|
| [1907] | 118 | //if(it->doSelection(id)) | 
|---|
|  | 119 | dataMap_[mem-data_]=(*it);  // save the mem location of the synchronisable data | 
|---|
| [1751] | 120 | if(!it->getData(mem, id, mode)) | 
|---|
| [1701] | 121 | return false; // mem pointer gets automatically increased because of call by reference | 
|---|
|  | 122 | // increase size counter by size of current synchronisable | 
|---|
|  | 123 | currentsize+=tempsize; | 
|---|
|  | 124 | } | 
|---|
| [1740] | 125 |  | 
|---|
|  | 126 |  | 
|---|
| [1701] | 127 | //start write gamestate header | 
|---|
| [1710] | 128 | HEADER->packetType = ENUM::Gamestate; | 
|---|
| [1740] | 129 | assert( *(ENUM::Type *)(data_) == ENUM::Gamestate); | 
|---|
| [1907] | 130 | HEADER->datasize = currentsize; | 
|---|
| [1701] | 131 | HEADER->id = id; | 
|---|
|  | 132 | HEADER->diffed = false; | 
|---|
|  | 133 | HEADER->complete = true; | 
|---|
| [1715] | 134 | HEADER->compressed = false; | 
|---|
| [1701] | 135 | //stop write gamestate header | 
|---|
| [1740] | 136 |  | 
|---|
| [1701] | 137 | COUT(5) << "G.ST.Man: Gamestate size: " << currentsize << std::endl; | 
|---|
|  | 138 | COUT(5) << "G.ST.Man: 'estimated' (and corrected) Gamestate size: " << size << std::endl; | 
|---|
|  | 139 | return true; | 
|---|
|  | 140 | } | 
|---|
|  | 141 |  | 
|---|
|  | 142 | bool Gamestate::spreadData(int mode) | 
|---|
|  | 143 | { | 
|---|
| [1751] | 144 | assert(data_); | 
|---|
|  | 145 | assert(!HEADER->compressed); | 
|---|
|  | 146 | assert(!HEADER->diffed); | 
|---|
| [1907] | 147 | uint8_t *mem=data_+sizeof(GamestateHeader); | 
|---|
| [1701] | 148 | // get the start of the Synchronisable list | 
|---|
| [1907] | 149 | //orxonox::ObjectList<Synchronisable>::iterator it=orxonox::ObjectList<Synchronisable>::begin(); | 
|---|
|  | 150 | Synchronisable *s; | 
|---|
| [1740] | 151 |  | 
|---|
| [1907] | 152 | // update the data of the objects we received | 
|---|
|  | 153 | while(mem < data_+sizeof(GamestateHeader)+HEADER->datasize){ | 
|---|
|  | 154 | synchronisableHeader *objectheader = (synchronisableHeader*)mem; | 
|---|
| [1701] | 155 |  | 
|---|
| [1907] | 156 | s = Synchronisable::getSynchronisable( objectheader->objectID ); | 
|---|
|  | 157 | if(!s) | 
|---|
| [1701] | 158 | { | 
|---|
| [2035] | 159 | Synchronisable::fabricate(mem, mode); | 
|---|
| [1701] | 160 | } | 
|---|
| [1907] | 161 | else | 
|---|
|  | 162 | { | 
|---|
|  | 163 | bool b = s->updateData(mem, mode); | 
|---|
| [2035] | 164 | assert(b); | 
|---|
| [1907] | 165 | //if(!s->updateData(mem, mode)) | 
|---|
|  | 166 | //return false; | 
|---|
|  | 167 | } | 
|---|
| [1701] | 168 | } | 
|---|
|  | 169 |  | 
|---|
|  | 170 | return true; | 
|---|
|  | 171 | } | 
|---|
|  | 172 |  | 
|---|
| [1907] | 173 |  | 
|---|
|  | 174 |  | 
|---|
| [1705] | 175 | int Gamestate::getID(){ | 
|---|
|  | 176 | return HEADER->id; | 
|---|
|  | 177 | } | 
|---|
|  | 178 |  | 
|---|
| [1701] | 179 | unsigned int Gamestate::getSize() const | 
|---|
|  | 180 | { | 
|---|
| [1711] | 181 | assert(data_); | 
|---|
| [1715] | 182 | if(HEADER->compressed) | 
|---|
| [1701] | 183 | return HEADER->compsize+sizeof(GamestateHeader); | 
|---|
|  | 184 | else | 
|---|
|  | 185 | { | 
|---|
| [1907] | 186 | return HEADER->datasize+sizeof(GamestateHeader); | 
|---|
| [1701] | 187 | } | 
|---|
|  | 188 | } | 
|---|
|  | 189 |  | 
|---|
| [1751] | 190 | bool Gamestate::operator==(packet::Gamestate gs){ | 
|---|
| [1907] | 191 | uint8_t *d1 = data_+sizeof(GamestateHeader); | 
|---|
|  | 192 | uint8_t *d2 = gs.data_+sizeof(GamestateHeader); | 
|---|
| [1751] | 193 | assert(!isCompressed()); | 
|---|
|  | 194 | assert(!gs.isCompressed()); | 
|---|
| [1907] | 195 | while(d1<data_+HEADER->datasize) | 
|---|
| [1751] | 196 | { | 
|---|
|  | 197 | if(*d1!=*d2) | 
|---|
|  | 198 | return false; | 
|---|
|  | 199 | d1++; | 
|---|
|  | 200 | d2++; | 
|---|
|  | 201 | } | 
|---|
|  | 202 | return true; | 
|---|
|  | 203 | } | 
|---|
|  | 204 |  | 
|---|
| [1701] | 205 | bool Gamestate::process() | 
|---|
|  | 206 | { | 
|---|
| [1705] | 207 | return GamestateHandler::addGamestate(this, getClientID()); | 
|---|
| [1701] | 208 | } | 
|---|
|  | 209 |  | 
|---|
| [1907] | 210 |  | 
|---|
|  | 211 |  | 
|---|
| [1701] | 212 | bool Gamestate::compressData() | 
|---|
|  | 213 | { | 
|---|
|  | 214 | assert(HEADER); | 
|---|
| [1751] | 215 | assert(!HEADER->compressed); | 
|---|
| [1907] | 216 | uLongf buffer = (uLongf)(((HEADER->datasize + 12)*1.01)+1); | 
|---|
| [1701] | 217 | if(buffer==0) | 
|---|
|  | 218 | return false; | 
|---|
| [1740] | 219 |  | 
|---|
| [1907] | 220 | uint8_t *ndata = new uint8_t[buffer+sizeof(GamestateHeader)]; | 
|---|
|  | 221 | uint8_t *dest = GAMESTATE_START(ndata); | 
|---|
| [1751] | 222 | //unsigned char *dest = new unsigned char[buffer]; | 
|---|
| [1907] | 223 | uint8_t *source = GAMESTATE_START(data_); | 
|---|
| [1701] | 224 | int retval; | 
|---|
| [1907] | 225 | retval = compress( dest, &buffer, source, (uLong)(HEADER->datasize) ); | 
|---|
| [1701] | 226 | switch ( retval ) { | 
|---|
|  | 227 | case Z_OK: COUT(5) << "G.St.Man: compress: successfully compressed" << std::endl; break; | 
|---|
| [1751] | 228 | case Z_MEM_ERROR: COUT(1) << "G.St.Man: compress: not enough memory available in gamestate.compress" << std::endl; return false; | 
|---|
|  | 229 | case Z_BUF_ERROR: COUT(2) << "G.St.Man: compress: not enough memory available in the buffer in gamestate.compress" << std::endl; return false; | 
|---|
|  | 230 | case Z_DATA_ERROR: COUT(2) << "G.St.Man: compress: data corrupted in gamestate.compress" << std::endl; return false; | 
|---|
| [1701] | 231 | } | 
|---|
| [1751] | 232 | #ifndef NDEBUG | 
|---|
|  | 233 | //decompress and compare the start and the decompressed data | 
|---|
| [1907] | 234 | uint8_t *rdata = new uint8_t[HEADER->datasize+sizeof(GamestateHeader)]; | 
|---|
|  | 235 | uint8_t *d2 = GAMESTATE_START(rdata); | 
|---|
|  | 236 | uLongf length2 = HEADER->datasize; | 
|---|
| [1751] | 237 | uncompress(d2, &length2, dest, buffer); | 
|---|
| [1907] | 238 | for(unsigned int i=0; i<HEADER->datasize; i++){ | 
|---|
| [1751] | 239 | assert(*(source+i)==*(d2+i)); | 
|---|
|  | 240 | } | 
|---|
|  | 241 | delete[] rdata; | 
|---|
|  | 242 | #endif | 
|---|
| [1701] | 243 |  | 
|---|
|  | 244 | //copy and modify header | 
|---|
| [1751] | 245 | #ifndef NDEBUG | 
|---|
| [1907] | 246 | HEADER->crc32 = calcCRC(data_+sizeof(GamestateHeader), HEADER->datasize); | 
|---|
| [1751] | 247 | #endif | 
|---|
| [1701] | 248 | *GAMESTATE_HEADER(ndata) = *HEADER; | 
|---|
|  | 249 | //delete old data | 
|---|
|  | 250 | delete[] data_; | 
|---|
|  | 251 | //save new data | 
|---|
|  | 252 | data_ = ndata; | 
|---|
| [1751] | 253 | HEADER->compsize = buffer; | 
|---|
|  | 254 | HEADER->compressed = true; | 
|---|
| [1730] | 255 | assert(HEADER->compressed); | 
|---|
| [2041] | 256 | COUT(4) << "gamestate compress datasize: " << HEADER->datasize << " compsize: " << HEADER->compsize << std::endl; | 
|---|
| [1701] | 257 | return true; | 
|---|
|  | 258 | } | 
|---|
|  | 259 | bool Gamestate::decompressData() | 
|---|
|  | 260 | { | 
|---|
| [1751] | 261 | assert(HEADER); | 
|---|
| [1715] | 262 | assert(HEADER->compressed); | 
|---|
| [2041] | 263 | COUT(4) << "GameStateClient: uncompressing gamestate. id: " << HEADER->id << ", baseid: " << HEADER->base_id << ", datasize: " << HEADER->datasize << ", compsize: " << HEADER->compsize << std::endl; | 
|---|
| [1907] | 264 | unsigned int datasize = HEADER->datasize; | 
|---|
| [1751] | 265 | unsigned int compsize = HEADER->compsize; | 
|---|
|  | 266 | unsigned int bufsize; | 
|---|
| [1990] | 267 | //  assert(compsize<=datasize); | 
|---|
| [1907] | 268 | bufsize = datasize; | 
|---|
| [1751] | 269 | assert(bufsize!=0); | 
|---|
| [1907] | 270 | uint8_t *ndata = new uint8_t[bufsize + sizeof(GamestateHeader)]; | 
|---|
|  | 271 | uint8_t *dest = ndata + sizeof(GamestateHeader); | 
|---|
|  | 272 | uint8_t *source = data_ + sizeof(GamestateHeader); | 
|---|
| [1701] | 273 | int retval; | 
|---|
| [1751] | 274 | uLongf length=bufsize; | 
|---|
|  | 275 | retval = uncompress( dest, &length, source, (uLong)compsize ); | 
|---|
| [1701] | 276 | switch ( retval ) { | 
|---|
|  | 277 | case Z_OK: COUT(5) << "successfully decompressed" << std::endl; break; | 
|---|
|  | 278 | case Z_MEM_ERROR: COUT(1) << "not enough memory available" << std::endl; return false; | 
|---|
|  | 279 | case Z_BUF_ERROR: COUT(2) << "not enough memory available in the buffer" << std::endl; return false; | 
|---|
|  | 280 | case Z_DATA_ERROR: COUT(2) << "data corrupted (zlib)" << std::endl; return false; | 
|---|
|  | 281 | } | 
|---|
| [1751] | 282 | #ifndef NDEBUG | 
|---|
| [1907] | 283 | assert(HEADER->crc32==calcCRC(ndata+sizeof(GamestateHeader), HEADER->datasize)); | 
|---|
| [1751] | 284 | #endif | 
|---|
| [1752] | 285 |  | 
|---|
| [1701] | 286 | //copy over the header | 
|---|
|  | 287 | *GAMESTATE_HEADER(ndata) = *HEADER; | 
|---|
| [2070] | 288 |  | 
|---|
|  | 289 | if (this->bDataENetAllocated_){ | 
|---|
|  | 290 | // Memory was allocated by ENet. --> We let it be since enet_packet_destroy will | 
|---|
|  | 291 | // deallocated it anyway. So data and packet stay together. | 
|---|
|  | 292 | this->bDataENetAllocated_ = false; | 
|---|
|  | 293 | } | 
|---|
|  | 294 | else{ | 
|---|
|  | 295 | // We allocated the memory in the first place (unlikely). So we destroy the old data | 
|---|
|  | 296 | // and overwrite it with the new decompressed data. | 
|---|
|  | 297 | delete[] this->data_; | 
|---|
|  | 298 | } | 
|---|
|  | 299 |  | 
|---|
| [1751] | 300 | //set new pointers | 
|---|
| [1701] | 301 | data_ = ndata; | 
|---|
| [1751] | 302 | HEADER->compressed = false; | 
|---|
| [1907] | 303 | assert(HEADER->datasize==datasize); | 
|---|
| [1751] | 304 | assert(HEADER->compsize==compsize); | 
|---|
| [1701] | 305 | return true; | 
|---|
|  | 306 | } | 
|---|
|  | 307 |  | 
|---|
|  | 308 | Gamestate *Gamestate::diff(Gamestate *base) | 
|---|
|  | 309 | { | 
|---|
| [1751] | 310 | assert(HEADER); | 
|---|
|  | 311 | assert(!HEADER->compressed); | 
|---|
|  | 312 | assert(!HEADER->diffed); | 
|---|
| [1701] | 313 | //unsigned char *basep = base->getGs()/*, *gs = getGs()*/; | 
|---|
| [1907] | 314 | uint8_t *basep = GAMESTATE_START(base->data_), *gs = GAMESTATE_START(this->data_); | 
|---|
| [1701] | 315 | unsigned int of=0; // pointers offset | 
|---|
|  | 316 | unsigned int dest_length=0; | 
|---|
| [1907] | 317 | dest_length=HEADER->datasize; | 
|---|
| [1701] | 318 | if(dest_length==0) | 
|---|
|  | 319 | return NULL; | 
|---|
| [1907] | 320 | uint8_t *ndata = new uint8_t[dest_length*sizeof(uint8_t)+sizeof(GamestateHeader)]; | 
|---|
|  | 321 | uint8_t *dest = ndata + sizeof(GamestateHeader); | 
|---|
|  | 322 | while(of < GAMESTATE_HEADER(base->data_)->datasize && of < HEADER->datasize){ | 
|---|
| [1701] | 323 | *(dest+of)=*(basep+of)^*(gs+of); // do the xor | 
|---|
|  | 324 | ++of; | 
|---|
|  | 325 | } | 
|---|
| [1907] | 326 | if(GAMESTATE_HEADER(base->data_)->datasize!=HEADER->datasize){ | 
|---|
|  | 327 | uint8_t n=0; | 
|---|
|  | 328 | if(GAMESTATE_HEADER(base->data_)->datasize < HEADER->datasize){ | 
|---|
| [1701] | 329 | while(of<dest_length){ | 
|---|
|  | 330 | *(dest+of)=n^*(gs+of); | 
|---|
|  | 331 | of++; | 
|---|
|  | 332 | } | 
|---|
|  | 333 | } | 
|---|
|  | 334 | } | 
|---|
|  | 335 |  | 
|---|
| [1715] | 336 | *GAMESTATE_HEADER(ndata) = *HEADER; | 
|---|
|  | 337 | GAMESTATE_HEADER(ndata)->diffed = true; | 
|---|
| [1751] | 338 | GAMESTATE_HEADER(ndata)->base_id = base->getID(); | 
|---|
|  | 339 | Gamestate *g = new Gamestate(ndata, getClientID()); | 
|---|
|  | 340 | g->flags_=flags_; | 
|---|
|  | 341 | g->packetDirection_ = packetDirection_; | 
|---|
| [1701] | 342 | return g; | 
|---|
|  | 343 | } | 
|---|
|  | 344 |  | 
|---|
| [1907] | 345 | Gamestate* Gamestate::doSelection(unsigned int clientID){ | 
|---|
|  | 346 | assert(data_); | 
|---|
|  | 347 | std::map<unsigned int, Synchronisable *>::iterator it; | 
|---|
| [2031] | 348 |  | 
|---|
| [1907] | 349 | // allocate memory for new data | 
|---|
|  | 350 | uint8_t *gdata = new uint8_t[HEADER->datasize+sizeof(GamestateHeader)]; | 
|---|
|  | 351 | // create a gamestate out of it | 
|---|
|  | 352 | Gamestate *gs = new Gamestate(gdata); | 
|---|
|  | 353 | uint8_t *newdata = gdata + sizeof(GamestateHeader); | 
|---|
|  | 354 | uint8_t *origdata = GAMESTATE_START(data_); | 
|---|
| [2031] | 355 |  | 
|---|
| [1907] | 356 | //copy the GamestateHeader | 
|---|
|  | 357 | *(GamestateHeader*)gdata = *HEADER; | 
|---|
| [2031] | 358 |  | 
|---|
| [1907] | 359 | synchronisableHeader *oldobjectheader, *newobjectheader; | 
|---|
|  | 360 | unsigned int objectOffset; | 
|---|
| [2031] | 361 |  | 
|---|
| [1907] | 362 | //copy in the zeros | 
|---|
|  | 363 | for(it=dataMap_.begin(); it!=dataMap_.end(); it++){ | 
|---|
|  | 364 | oldobjectheader = (synchronisableHeader*)origdata; | 
|---|
|  | 365 | newobjectheader = (synchronisableHeader*)newdata; | 
|---|
|  | 366 | unsigned int objectsize = oldobjectheader->size; | 
|---|
|  | 367 | assert(it->second->objectID==oldobjectheader->objectID); | 
|---|
|  | 368 | *newobjectheader = *oldobjectheader; | 
|---|
| [2062] | 369 | objectOffset=sizeof(uint8_t)+sizeof(bool); //skip the size and the availableData variables in the objectheader | 
|---|
| [1907] | 370 | if(it->second->doSelection(HEADER->id)){ | 
|---|
|  | 371 | newobjectheader->dataAvailable=true; //TODO: probably not neccessary | 
|---|
|  | 372 | while(objectOffset<objectsize){ | 
|---|
|  | 373 | *(newdata + objectOffset)=*(origdata + objectOffset);    // copy the data | 
|---|
|  | 374 | objectOffset++; | 
|---|
|  | 375 | } | 
|---|
|  | 376 | }else{ | 
|---|
|  | 377 | newobjectheader->dataAvailable=false; | 
|---|
|  | 378 | while(objectOffset<objectsize){ | 
|---|
|  | 379 | *(newdata+objectOffset)=0;    // set to 0 | 
|---|
|  | 380 | objectOffset++; | 
|---|
|  | 381 | } | 
|---|
|  | 382 | assert(objectOffset==objectsize); | 
|---|
|  | 383 | } | 
|---|
|  | 384 | newdata += objectsize; | 
|---|
|  | 385 | origdata += objectsize; | 
|---|
|  | 386 | } | 
|---|
|  | 387 | return gs; | 
|---|
|  | 388 | } | 
|---|
|  | 389 |  | 
|---|
|  | 390 |  | 
|---|
|  | 391 | Gamestate* Gamestate::intelligentDiff(Gamestate *base, unsigned int clientID){ | 
|---|
|  | 392 | // asserts | 
|---|
|  | 393 | assert(data_); | 
|---|
|  | 394 | assert(base->data_); | 
|---|
|  | 395 | assert(!GAMESTATE_HEADER(base->data_)->diffed); | 
|---|
|  | 396 | assert(!GAMESTATE_HEADER(base->data_)->compressed); | 
|---|
|  | 397 | assert(!HEADER->compressed); | 
|---|
|  | 398 | assert(!HEADER->diffed); | 
|---|
| [2031] | 399 |  | 
|---|
| [1907] | 400 | //preparations | 
|---|
|  | 401 | std::map<unsigned int, Synchronisable *>::iterator it; | 
|---|
|  | 402 | uint8_t *origdata, *basedata, *destdata, *ndata; | 
|---|
|  | 403 | unsigned int objectOffset, streamOffset=0;    //data offset | 
|---|
| [2031] | 404 | unsigned int minsize = (HEADER->datasize < GAMESTATE_HEADER(base->data_)->datasize) ? HEADER->datasize : GAMESTATE_HEADER(base->data_)->datasize; | 
|---|
| [1907] | 405 | synchronisableHeader *origheader; | 
|---|
|  | 406 | synchronisableHeader *destheader; | 
|---|
| [2031] | 407 |  | 
|---|
| [1907] | 408 | origdata = GAMESTATE_START(this->data_); | 
|---|
|  | 409 | basedata = GAMESTATE_START(base->data_); | 
|---|
|  | 410 | ndata = new uint8_t[HEADER->datasize + sizeof(GamestateHeader)]; | 
|---|
|  | 411 | destdata = ndata + sizeof(GamestateHeader); | 
|---|
| [2031] | 412 |  | 
|---|
| [1907] | 413 | // do the diff | 
|---|
|  | 414 | for(it=dataMap_.begin(); it!=dataMap_.end(); it++){ | 
|---|
|  | 415 | assert(streamOffset<HEADER->datasize); | 
|---|
|  | 416 | bool sendData = it->second->doSelection(HEADER->id); | 
|---|
|  | 417 | origheader = (synchronisableHeader *)(origdata+streamOffset); | 
|---|
|  | 418 | destheader = (synchronisableHeader *)(destdata+streamOffset); | 
|---|
| [2031] | 419 |  | 
|---|
| [1907] | 420 | //copy and partially diff the object header | 
|---|
|  | 421 | assert(sizeof(synchronisableHeader)==3*sizeof(unsigned int)+sizeof(bool)); | 
|---|
|  | 422 | *(uint32_t*)destdata = *(uint32_t*)origdata; //size (do not diff) | 
|---|
|  | 423 | *(bool*)(destdata+sizeof(uint32_t)) = sendData; | 
|---|
|  | 424 | if(sendData){ | 
|---|
|  | 425 | *(uint32_t*)(destdata+sizeof(uint32_t)+sizeof(bool)) = *(uint32_t*)(basedata+sizeof(uint32_t)+sizeof(bool)) ^ *(uint32_t*)(origdata+sizeof(uint32_t)+sizeof(bool)); //objectid (diff it) | 
|---|
|  | 426 | *(uint32_t*)(destdata+2*sizeof(uint32_t)+sizeof(bool)) = *(uint32_t*)(basedata+2*sizeof(uint32_t)+sizeof(bool)) ^ *(uint32_t*)(origdata+2*sizeof(uint32_t)+sizeof(bool)); //classid (diff it) | 
|---|
|  | 427 | }else{ | 
|---|
|  | 428 | *(uint32_t*)(destdata+sizeof(uint32_t)+sizeof(bool)) = 0; | 
|---|
| [2031] | 429 | *(uint32_t*)(destdata+2*sizeof(uint32_t)+sizeof(bool)) = 0; | 
|---|
| [1907] | 430 | } | 
|---|
|  | 431 | objectOffset=sizeof(synchronisableHeader); | 
|---|
|  | 432 | streamOffset+=sizeof(synchronisableHeader); | 
|---|
| [2031] | 433 |  | 
|---|
| [1907] | 434 | //now handle the object data or fill with zeros | 
|---|
|  | 435 | while(objectOffset<origheader->size ){ | 
|---|
| [2031] | 436 |  | 
|---|
| [1907] | 437 | if(sendData && streamOffset<minsize) | 
|---|
|  | 438 | *(destdata+objectOffset)=*(basedata+objectOffset)^*(origdata+objectOffset); // do the xor | 
|---|
|  | 439 | else if(sendData) | 
|---|
|  | 440 | *(destdata+objectOffset)=((uint8_t)0)^*(origdata+objectOffset); // xor with 0 (basestream is too short) | 
|---|
|  | 441 | else | 
|---|
|  | 442 | *(destdata+objectOffset)=0; // set to 0 because this object should not be transfered | 
|---|
| [2031] | 443 |  | 
|---|
| [1907] | 444 | objectOffset++; | 
|---|
|  | 445 | streamOffset++; | 
|---|
|  | 446 | } | 
|---|
|  | 447 | destdata+=objectOffset; | 
|---|
|  | 448 | origdata+=objectOffset; | 
|---|
|  | 449 | basedata+=objectOffset; | 
|---|
|  | 450 | } | 
|---|
| [2031] | 451 |  | 
|---|
| [1907] | 452 | //copy over the gamestate header and set the diffed flag | 
|---|
|  | 453 | *(GamestateHeader *)ndata = *HEADER; //copy over the header | 
|---|
|  | 454 | Gamestate *gs = new Gamestate(ndata); | 
|---|
|  | 455 | GAMESTATE_HEADER(ndata)->diffed=true; | 
|---|
|  | 456 | return gs; | 
|---|
|  | 457 | } | 
|---|
|  | 458 |  | 
|---|
|  | 459 | Gamestate* Gamestate::intelligentUnDiff(Gamestate *base){ | 
|---|
|  | 460 | // asserts | 
|---|
|  | 461 | assert(data_); | 
|---|
|  | 462 | assert(base->data_); | 
|---|
|  | 463 | assert(!GAMESTATE_HEADER(base->data_)->diffed); | 
|---|
|  | 464 | assert(!GAMESTATE_HEADER(base->data_)->compressed); | 
|---|
|  | 465 | assert(!HEADER->compressed); | 
|---|
|  | 466 | assert(HEADER->diffed); | 
|---|
| [2031] | 467 |  | 
|---|
| [1907] | 468 | //preparations | 
|---|
|  | 469 | std::map<unsigned int, Synchronisable *>::iterator it; | 
|---|
|  | 470 | uint8_t *origdata, *basedata, *destdata, *ndata; | 
|---|
|  | 471 | unsigned int objectOffset, streamOffset=0;    //data offset | 
|---|
| [2031] | 472 | unsigned int minsize = (HEADER->datasize < GAMESTATE_HEADER(base->data_)->datasize) ? HEADER->datasize : GAMESTATE_HEADER(base->data_)->datasize; | 
|---|
| [1907] | 473 | synchronisableHeader *origheader; | 
|---|
|  | 474 | synchronisableHeader *destheader; | 
|---|
| [2031] | 475 |  | 
|---|
| [1907] | 476 | origdata = GAMESTATE_START(this->data_); | 
|---|
|  | 477 | basedata = GAMESTATE_START(base->data_); | 
|---|
|  | 478 | ndata = new uint8_t[HEADER->datasize + sizeof(GamestateHeader)]; | 
|---|
|  | 479 | destdata = ndata + sizeof(GamestateHeader); | 
|---|
| [2031] | 480 |  | 
|---|
| [1907] | 481 | // do the undiff | 
|---|
|  | 482 | for(it=dataMap_.begin(); it!=dataMap_.end(); it++){ | 
|---|
|  | 483 | assert(streamOffset<HEADER->datasize); | 
|---|
|  | 484 | origheader = (synchronisableHeader *)(origdata+streamOffset); | 
|---|
|  | 485 | destheader = (synchronisableHeader *)(destdata+streamOffset); | 
|---|
|  | 486 | bool sendData; | 
|---|
| [2031] | 487 |  | 
|---|
| [1907] | 488 | //copy and partially diff the object header | 
|---|
|  | 489 | assert(sizeof(synchronisableHeader)==3*sizeof(unsigned int)+sizeof(bool)); | 
|---|
|  | 490 | *(unsigned int*)destdata = *(unsigned int*)origdata; //size (do not diff) | 
|---|
|  | 491 | *(bool*)(destdata+sizeof(unsigned int)) = *(bool*)(origdata+sizeof(unsigned int)); | 
|---|
|  | 492 | sendData = *(bool*)(origdata+sizeof(unsigned int)); | 
|---|
|  | 493 | if(sendData){ | 
|---|
|  | 494 | *(unsigned int*)(destdata+sizeof(unsigned int)+sizeof(bool)) = *(unsigned int*)(basedata+sizeof(unsigned int)+sizeof(bool)) ^ *(unsigned int*)(origdata+sizeof(unsigned int)+sizeof(bool)); //objectid (diff it) | 
|---|
|  | 495 | *(unsigned int*)(destdata+2*sizeof(unsigned int)+sizeof(bool)) = *(unsigned int*)(basedata+2*sizeof(unsigned int)+sizeof(bool)) ^ *(unsigned int*)(origdata+2*sizeof(unsigned int)+sizeof(bool)); //classid (diff it) | 
|---|
|  | 496 | }else{ | 
|---|
|  | 497 | *(unsigned int*)(destdata+sizeof(unsigned int)+sizeof(bool)) = 0; | 
|---|
| [2031] | 498 | *(unsigned int*)(destdata+2*sizeof(unsigned int)+sizeof(bool)) = 0; | 
|---|
| [1907] | 499 | } | 
|---|
|  | 500 | objectOffset=sizeof(synchronisableHeader); | 
|---|
|  | 501 | streamOffset+=sizeof(synchronisableHeader); | 
|---|
| [2031] | 502 |  | 
|---|
| [1907] | 503 | //now handle the object data or fill with zeros | 
|---|
|  | 504 | while(objectOffset<origheader->size ){ | 
|---|
| [2031] | 505 |  | 
|---|
| [1907] | 506 | if(sendData && streamOffset<minsize) | 
|---|
|  | 507 | *(destdata+objectOffset)=*(basedata+objectOffset)^*(origdata+objectOffset); // do the xor | 
|---|
|  | 508 | else if(sendData) | 
|---|
|  | 509 | *(destdata+objectOffset)=((unsigned char)0)^*(origdata+objectOffset); // xor with 0 (basestream is too short) | 
|---|
|  | 510 | else | 
|---|
|  | 511 | *(destdata+objectOffset)=0; // set to 0 because this object should not be transfered | 
|---|
| [2031] | 512 |  | 
|---|
| [1907] | 513 | objectOffset++; | 
|---|
|  | 514 | streamOffset++; | 
|---|
|  | 515 | } | 
|---|
|  | 516 | destdata+=objectOffset; | 
|---|
|  | 517 | origdata+=objectOffset; | 
|---|
|  | 518 | basedata+=objectOffset; | 
|---|
|  | 519 | } | 
|---|
| [2031] | 520 |  | 
|---|
| [1907] | 521 | //copy over the gamestate header and set the diffed flag | 
|---|
|  | 522 | *(GamestateHeader *)ndata = *HEADER; //copy over the header | 
|---|
|  | 523 | Gamestate *gs = new Gamestate(ndata); | 
|---|
|  | 524 | GAMESTATE_HEADER(ndata)->diffed=false; | 
|---|
|  | 525 | return gs; | 
|---|
|  | 526 | } | 
|---|
|  | 527 |  | 
|---|
| [1701] | 528 | Gamestate *Gamestate::undiff(Gamestate *base) | 
|---|
|  | 529 | { | 
|---|
| [1751] | 530 | assert(this && base);assert(HEADER); | 
|---|
|  | 531 | assert(HEADER->diffed); | 
|---|
| [1715] | 532 | assert(!HEADER->compressed && !GAMESTATE_HEADER(base->data_)->compressed); | 
|---|
| [1701] | 533 | //unsigned char *basep = base->getGs()/*, *gs = getGs()*/; | 
|---|
| [1907] | 534 | uint8_t *basep = GAMESTATE_START(base->data_); | 
|---|
|  | 535 | uint8_t *gs = GAMESTATE_START(this->data_); | 
|---|
| [1701] | 536 | unsigned int of=0; // pointers offset | 
|---|
|  | 537 | unsigned int dest_length=0; | 
|---|
| [1907] | 538 | dest_length=HEADER->datasize; | 
|---|
| [1701] | 539 | if(dest_length==0) | 
|---|
|  | 540 | return NULL; | 
|---|
| [1907] | 541 | uint8_t *ndata = new uint8_t[dest_length*sizeof(uint8_t)+sizeof(GamestateHeader)]; | 
|---|
|  | 542 | uint8_t *dest = ndata + sizeof(GamestateHeader); | 
|---|
|  | 543 | while(of < GAMESTATE_HEADER(base->data_)->datasize && of < HEADER->datasize){ | 
|---|
| [1701] | 544 | *(dest+of)=*(basep+of)^*(gs+of); // do the xor | 
|---|
|  | 545 | ++of; | 
|---|
|  | 546 | } | 
|---|
| [1907] | 547 | if(GAMESTATE_HEADER(base->data_)->datasize!=HEADER->datasize){ | 
|---|
|  | 548 | uint8_t n=0; | 
|---|
|  | 549 | if(GAMESTATE_HEADER(base->data_)->datasize < HEADER->datasize){ | 
|---|
| [1701] | 550 | while(of < dest_length){ | 
|---|
|  | 551 | *(dest+of)=n^*(gs+of); | 
|---|
|  | 552 | of++; | 
|---|
|  | 553 | } | 
|---|
|  | 554 | } | 
|---|
|  | 555 | } | 
|---|
| [1715] | 556 | *GAMESTATE_HEADER(ndata) = *HEADER; | 
|---|
|  | 557 | GAMESTATE_HEADER(ndata)->diffed = false; | 
|---|
| [1751] | 558 | Gamestate *g = new Gamestate(ndata, getClientID()); | 
|---|
|  | 559 | g->flags_=flags_; | 
|---|
|  | 560 | g->packetDirection_ = packetDirection_; | 
|---|
|  | 561 | assert(!g->isDiffed()); | 
|---|
|  | 562 | assert(!g->isCompressed()); | 
|---|
| [1701] | 563 | return g; | 
|---|
|  | 564 | } | 
|---|
|  | 565 |  | 
|---|
|  | 566 |  | 
|---|
| [1751] | 567 | unsigned int Gamestate::calcGamestateSize(unsigned int id, int mode) | 
|---|
| [1701] | 568 | { | 
|---|
|  | 569 | int size=0; | 
|---|
|  | 570 | // get the start of the Synchronisable list | 
|---|
| [1747] | 571 | orxonox::ObjectList<Synchronisable>::iterator it; | 
|---|
| [1701] | 572 | // get total size of gamestate | 
|---|
| [1747] | 573 | for(it = orxonox::ObjectList<Synchronisable>::begin(); it; ++it) | 
|---|
| [1907] | 574 | size+=it->getSize(id, mode); // size of the actual data of the synchronisable | 
|---|
| [1701] | 575 | //  size+=sizeof(GamestateHeader); | 
|---|
|  | 576 | return size; | 
|---|
|  | 577 | } | 
|---|
|  | 578 |  | 
|---|
|  | 579 | /** | 
|---|
|  | 580 | * This function removes a Synchronisable out of the universe | 
|---|
|  | 581 | * @param it iterator of the list pointing to the object | 
|---|
|  | 582 | * @return iterator pointing to the next object in the list | 
|---|
|  | 583 | */ | 
|---|
| [1747] | 584 | void Gamestate::removeObject(orxonox::ObjectList<Synchronisable>::iterator &it) { | 
|---|
|  | 585 | orxonox::ObjectList<Synchronisable>::iterator temp=it; | 
|---|
| [1701] | 586 | ++it; | 
|---|
|  | 587 | delete  *temp; | 
|---|
|  | 588 | } | 
|---|
|  | 589 |  | 
|---|
| [1712] | 590 | bool Gamestate::isDiffed(){ | 
|---|
|  | 591 | return HEADER->diffed; | 
|---|
|  | 592 | } | 
|---|
| [1740] | 593 |  | 
|---|
| [1751] | 594 | bool Gamestate::isCompressed(){ | 
|---|
|  | 595 | return HEADER->compressed; | 
|---|
|  | 596 | } | 
|---|
| [1752] | 597 |  | 
|---|
| [1712] | 598 | int Gamestate::getBaseID(){ | 
|---|
|  | 599 | return HEADER->base_id; | 
|---|
|  | 600 | } | 
|---|
| [1701] | 601 | } | 
|---|
|  | 602 |  | 
|---|
|  | 603 | } | 
|---|