the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 628 lines 19 kB view raw
1#include "stdafx.h" 2#include "File.h" 3#include "InputOutputStream.h" 4#include "net.minecraft.world.entity.h" 5#include "net.minecraft.world.level.h" 6#include "net.minecraft.world.level.chunk.h" 7#include "net.minecraft.world.level.tile.entity.h" 8#include "net.minecraft.world.level.storage.h" 9#include "FileHeader.h" 10#include "OldChunkStorage.h" 11DWORD OldChunkStorage::tlsIdx = 0; 12OldChunkStorage::ThreadStorage *OldChunkStorage::tlsDefault = NULL; 13 14OldChunkStorage::ThreadStorage::ThreadStorage() 15{ 16 blockData = byteArray(Level::CHUNK_TILE_COUNT); 17 dataData = byteArray(Level::HALF_CHUNK_TILE_COUNT); 18 skyLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); 19 blockLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); 20} 21 22OldChunkStorage::ThreadStorage::~ThreadStorage() 23{ 24 delete [] blockData.data; 25 delete [] dataData.data; 26 delete [] skyLightData.data; 27 delete [] blockLightData.data; 28} 29 30void OldChunkStorage::CreateNewThreadStorage() 31{ 32 ThreadStorage *tls = new ThreadStorage(); 33 if(tlsDefault == NULL ) 34 { 35 tlsIdx = TlsAlloc(); 36 tlsDefault = tls; 37 } 38 TlsSetValue(tlsIdx, tls); 39} 40 41void OldChunkStorage::UseDefaultThreadStorage() 42{ 43 TlsSetValue(tlsIdx, tlsDefault); 44} 45 46void OldChunkStorage::ReleaseThreadStorage() 47{ 48 ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); 49 if( tls == tlsDefault ) return; 50 51 delete tls; 52} 53 54OldChunkStorage::OldChunkStorage(File dir, bool create) 55{ 56 this->dir = dir; 57 this->create = create; 58} 59 60File OldChunkStorage::getFile(int x, int z) 61{ 62 wchar_t name[MAX_PATH_SIZE]; 63 wchar_t path1[MAX_PATH_SIZE]; 64 wchar_t path2[MAX_PATH_SIZE]; 65 66 wchar_t xRadix36[64]; 67 wchar_t zRadix36[64]; 68#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ ) 69 assert(0); // need a gcc verison of _itow ? 70#else 71 _itow(x,xRadix36,36); 72 _itow(z,zRadix36,36); 73 swprintf(name,MAX_PATH_SIZE,L"c.%ls.%ls.dat",xRadix36,zRadix36); 74 _itow(x & 63,path1,36); 75 _itow(z & 63,path2,36); 76#endif 77 //sprintf(file,"%s\\%s",dir,path1); 78 File file( dir, wstring( path1 ) ); 79 if( !file.exists() ) 80 { 81 if(create) file.mkdir(); 82 else 83 { 84 return File(L""); 85 } 86 } 87 88 //strcat(file,"\\"); 89 //strcat(file,path2); 90 file = File( file, wstring( path2 ) ); 91 if( !file.exists() ) 92 { 93 if(create) file.mkdir(); 94 else 95 { 96 return File(L""); 97 } 98 } 99 100 //strcat(file,"\\"); 101 //strcat(file,name); 102 //sprintf(file,"%s\\%s",file,name); 103 file = File( file, wstring( name ) ); 104 if ( !file.exists() ) 105 { 106 if (!create) 107 { 108 return File(L""); 109 } 110 } 111 return file; 112} 113 114LevelChunk *OldChunkStorage::load(Level *level, int x, int z) 115{ 116 File file = getFile(x, z); 117 if (!file.getPath().empty() && file.exists()) 118 { 119 // 4J - removed try/catch 120 // try { 121 // System.out.println("Loading chunk "+x+", "+z); 122 FileInputStream fis = FileInputStream(file); 123 CompoundTag *tag = NbtIo::readCompressed(&fis); 124 if (!tag->contains(L"Level")) 125 { 126 char buf[256]; 127 sprintf(buf,"Chunk file at %d, %d is missing level data, skipping\n",x,z); 128 app.DebugPrintf(buf); 129 return NULL; 130 } 131 if (!tag->getCompound(L"Level")->contains(L"Blocks")) 132 { 133 char buf[256]; 134 sprintf(buf,"Chunk file at %d, %d is missing block data, skipping\n",x,z); 135 app.DebugPrintf(buf); 136 return NULL; 137 } 138 LevelChunk *levelChunk = OldChunkStorage::load(level, tag->getCompound(L"Level")); 139 if (!levelChunk->isAt(x, z)) 140 { 141 char buf[256]; 142 sprintf(buf,"Chunk fileat %d, %d is in the wrong location; relocating. Expected %d, %d, got %d, %d\n", 143 x, z, x, z, levelChunk->x, levelChunk->z); 144 app.DebugPrintf(buf); 145 tag->putInt(L"xPos", x); 146 tag->putInt(L"zPos", z); 147 levelChunk = OldChunkStorage::load(level, tag->getCompound(L"Level")); 148 } 149 150 return levelChunk; 151 // } catch (Exception e) { 152 // e.printStackTrace(); 153 // } 154 } 155 return NULL; 156} 157 158void OldChunkStorage::save(Level *level, LevelChunk *levelChunk) 159{ 160 level->checkSession(); 161 File file = getFile(levelChunk->x, levelChunk->z); 162 if (file.exists()) 163 { 164 LevelData *levelData = level->getLevelData(); 165 levelData->setSizeOnDisk( levelData->getSizeOnDisk() - file.length() ); 166 } 167 168 // 4J - removed try/catch 169 // try { 170 //char tmpFileName[MAX_PATH_SIZE]; 171 //sprintf(tmpFileName,"%s\\%s",dir,"tmp_chunk.dat"); 172 File tmpFile( dir, L"tmp_chunk.dat" ); 173 // System.out.println("Saving chunk "+levelChunk.x+", "+levelChunk.z); 174 175 FileOutputStream fos = FileOutputStream(tmpFile); 176 CompoundTag *tag = new CompoundTag(); 177 CompoundTag *levelData = new CompoundTag(); 178 tag->put(L"Level", levelData); 179 OldChunkStorage::save(levelChunk, level, levelData); 180 NbtIo::writeCompressed(tag, &fos); 181 fos.close(); 182 183 if (file.exists()) 184 { 185 //DeleteFile(file); 186 file._delete(); 187 } 188 //MoveFile(tmpFile,file); 189 tmpFile.renameTo( file ); 190 191 LevelData *levelInfo = level->getLevelData(); 192 levelInfo->setSizeOnDisk(levelInfo->getSizeOnDisk() + file.length() ); 193 // } catch (Exception e) { 194 // e.printStackTrace(); 195 // } 196} 197 198bool OldChunkStorage::saveEntities(LevelChunk *lc, Level *level, CompoundTag *tag) 199{ 200 // If we saved and it had no entities, and nothing has been added since skip this one 201 if(!lc->lastSaveHadEntities) return false; 202 203 lc->lastSaveHadEntities = false; 204 ListTag<CompoundTag> *entityTags = new ListTag<CompoundTag>(); 205 206#ifdef _ENTITIES_RW_SECTION 207 EnterCriticalRWSection(&lc->m_csEntities, true); 208#else 209 EnterCriticalSection(&lc->m_csEntities); 210#endif 211 for (int i = 0; i < lc->ENTITY_BLOCKS_LENGTH; i++) 212 { 213 AUTO_VAR(itEnd, lc->entityBlocks[i]->end()); 214 for( vector<shared_ptr<Entity> >::iterator it = lc->entityBlocks[i]->begin(); it != itEnd; it++ ) 215 { 216 shared_ptr<Entity> e = *it; 217 lc->lastSaveHadEntities = true; 218 CompoundTag *teTag = new CompoundTag(); 219 if (e->save(teTag)) 220 { 221 entityTags->add(teTag); 222 } 223 224 } 225 } 226#ifdef _ENTITIES_RW_SECTION 227 LeaveCriticalRWSection(&lc->m_csEntities, true); 228#else 229 LeaveCriticalSection(&lc->m_csEntities); 230#endif 231 232 tag->put(L"Entities", entityTags); 233 234 return lc->lastSaveHadEntities; 235} 236 237void OldChunkStorage::save(LevelChunk *lc, Level *level, DataOutputStream *dos) 238{ 239 dos->writeShort(SAVE_FILE_VERSION_NUMBER); 240 dos->writeInt(lc->x); 241 dos->writeInt(lc->z); 242 dos->writeLong(level->getGameTime()); 243 dos->writeLong(lc->inhabitedTime); 244 245 PIXBeginNamedEvent(0,"Getting block data"); 246 lc->writeCompressedBlockData(dos); 247 PIXEndNamedEvent(); 248 249 PIXBeginNamedEvent(0,"Getting data data"); 250 lc->writeCompressedDataData(dos); 251 PIXEndNamedEvent(); 252 253 PIXBeginNamedEvent(0,"Getting sky and block light data"); 254 lc->writeCompressedSkyLightData(dos); 255 lc->writeCompressedBlockLightData(dos); 256 PIXEndNamedEvent(); 257 258 dos->write(lc->heightmap); 259 dos->writeShort(lc->terrainPopulated); 260 dos->write(lc->getBiomes()); 261 262 PIXBeginNamedEvent(0,"Saving entities"); 263 CompoundTag *tag = new CompoundTag(); 264#ifndef SPLIT_SAVES 265 saveEntities(lc, level, tag); 266#endif 267 268 PIXBeginNamedEvent(0,"Saving tile entities"); 269 ListTag<CompoundTag> *tileEntityTags = new ListTag<CompoundTag>(); 270 271 AUTO_VAR(itEnd, lc->tileEntities.end()); 272 for( unordered_map<TilePos, shared_ptr<TileEntity>, TilePosKeyHash, TilePosKeyEq>::iterator it = lc->tileEntities.begin(); 273 it != itEnd; it++) 274 { 275 shared_ptr<TileEntity> te = it->second; 276 CompoundTag *teTag = new CompoundTag(); 277 te->save(teTag); 278 tileEntityTags->add(teTag); 279 } 280 tag->put(L"TileEntities", tileEntityTags); 281 PIXEndNamedEvent(); 282 283 PIXBeginNamedEvent(0,"Saving tile tick data"); 284 vector<TickNextTickData > *ticksInChunk = level->fetchTicksInChunk(lc, false); 285 if (ticksInChunk != NULL) 286 { 287 __int64 levelTime = level->getGameTime(); 288 289 ListTag<CompoundTag> *tickTags = new ListTag<CompoundTag>(); 290 for( int i = 0; i < ticksInChunk->size(); i++ ) 291 { 292 TickNextTickData td = ticksInChunk->at(i); 293 CompoundTag *teTag = new CompoundTag(); 294 teTag->putInt(L"i", td.tileId); 295 teTag->putInt(L"x", td.x); 296 teTag->putInt(L"y", td.y); 297 teTag->putInt(L"z", td.z); 298 teTag->putInt(L"t", (int) (td.m_delay - levelTime)); 299 300 tickTags->add(teTag); 301 } 302 tag->put(L"TileTicks", tickTags); 303 } 304 delete ticksInChunk; 305 PIXEndNamedEvent(); 306 307 NbtIo::write(tag,dos); 308 delete tag; 309 PIXEndNamedEvent(); 310} 311 312void OldChunkStorage::save(LevelChunk *lc, Level *level, CompoundTag *tag) 313{ 314 level->checkSession(); 315 tag->putInt(L"xPos", lc->x); 316 tag->putInt(L"zPos", lc->z); 317 tag->putLong(L"LastUpdate", level->getGameTime()); 318 tag->putLong(L"InhabitedTime", lc->inhabitedTime); 319 // 4J - changes here for new storage. Now have static storage for getting lighting data for block, data, and sky & block lighting. This 320 // wasn't required in the original version as we could just reference the information in the level itself, but with our new storage system 321 // the full data doesn't normally exist & so getSkyLightData/getBlockLightData etc. need somewhere to output this data. Making this static so 322 // that we aren't dynamically allocating memory in the server thread when writing chunks as this causes serious stalling on the main thread. 323 // Will be fine so long as we only actually create tags for once chunk at a time. 324 325 // 4J Stu - As we now save on multiple threads, the static data has been moved to TLS 326 ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); 327 328 PIXBeginNamedEvent(0,"Getting block data"); 329 //static byteArray blockData = byteArray(32768); 330 lc->getBlockData(tls->blockData); 331 tag->putByteArray(L"Blocks", tls->blockData); 332 PIXEndNamedEvent(); 333 334 PIXBeginNamedEvent(0,"Getting data data"); 335 //static byteArray dataData = byteArray(16384); 336 lc->getDataData(tls->dataData); 337 tag->putByteArray(L"Data", tls->dataData); 338 PIXEndNamedEvent(); 339 340 PIXBeginNamedEvent(0,"Getting sky and block light data"); 341 //static byteArray skyLightData = byteArray(16384); 342 //static byteArray blockLightData = byteArray(16384); 343 lc->getSkyLightData(tls->skyLightData); 344 lc->getBlockLightData(tls->blockLightData); 345 tag->putByteArray(L"SkyLight", tls->skyLightData); 346 tag->putByteArray(L"BlockLight", tls->blockLightData); 347 PIXEndNamedEvent(); 348 349 tag->putByteArray(L"HeightMap", lc->heightmap); 350 tag->putShort(L"TerrainPopulatedFlags", lc->terrainPopulated); // 4J - changed from "TerrainPopulated" to "TerrainPopulatedFlags" as now stores a bitfield, java stores a bool 351 tag->putByteArray(L"Biomes", lc->getBiomes()); 352 353 PIXBeginNamedEvent(0,"Saving entities"); 354#ifndef SPLIT_SAVES 355 saveEntities(lc, level, tag); 356#endif 357 358 PIXBeginNamedEvent(0,"Saving tile entities"); 359 ListTag<CompoundTag> *tileEntityTags = new ListTag<CompoundTag>(); 360 361 AUTO_VAR(itEnd, lc->tileEntities.end()); 362 for( unordered_map<TilePos, shared_ptr<TileEntity>, TilePosKeyHash, TilePosKeyEq>::iterator it = lc->tileEntities.begin(); 363 it != itEnd; it++) 364 { 365 shared_ptr<TileEntity> te = it->second; 366 CompoundTag *teTag = new CompoundTag(); 367 te->save(teTag); 368 tileEntityTags->add(teTag); 369 } 370 tag->put(L"TileEntities", tileEntityTags); 371 PIXEndNamedEvent(); 372 373 PIXBeginNamedEvent(0,"Saving tile tick data"); 374 vector<TickNextTickData > *ticksInChunk = level->fetchTicksInChunk(lc, false); 375 if (ticksInChunk != NULL) 376 { 377 __int64 levelTime = level->getGameTime(); 378 379 ListTag<CompoundTag> *tickTags = new ListTag<CompoundTag>(); 380 for( int i = 0; i < ticksInChunk->size(); i++ ) 381 { 382 TickNextTickData td = ticksInChunk->at(i); 383 CompoundTag *teTag = new CompoundTag(); 384 teTag->putInt(L"i", td.tileId); 385 teTag->putInt(L"x", td.x); 386 teTag->putInt(L"y", td.y); 387 teTag->putInt(L"z", td.z); 388 teTag->putInt(L"t", (int) (td.m_delay - levelTime)); 389 teTag->putInt(L"p", td.priorityTilt); 390 391 tickTags->add(teTag); 392 } 393 tag->put(L"TileTicks", tickTags); 394 } 395 delete ticksInChunk; 396 PIXEndNamedEvent(); 397 PIXEndNamedEvent(); 398} 399 400void OldChunkStorage::loadEntities(LevelChunk *lc, Level *level, CompoundTag *tag) 401{ 402 ListTag<CompoundTag> *entityTags = (ListTag<CompoundTag> *) tag->getList(L"Entities"); 403 if (entityTags != NULL) 404 { 405 for (int i = 0; i < entityTags->size(); i++) 406 { 407 CompoundTag *teTag = entityTags->get(i); 408 shared_ptr<Entity> te = EntityIO::loadStatic(teTag, level); 409 lc->lastSaveHadEntities = true; 410 if (te != NULL) 411 { 412 lc->addEntity(te); 413 } 414 } 415 } 416 417 ListTag<CompoundTag> *tileEntityTags = (ListTag<CompoundTag> *) tag->getList(L"TileEntities"); 418 if (tileEntityTags != NULL) 419 { 420 for (int i = 0; i < tileEntityTags->size(); i++) 421 { 422 CompoundTag *teTag = tileEntityTags->get(i); 423 shared_ptr<TileEntity> te = TileEntity::loadStatic(teTag); 424 if (te != NULL) 425 { 426 lc->addTileEntity(te); 427 } 428 } 429 } 430} 431 432LevelChunk *OldChunkStorage::load(Level *level, DataInputStream *dis) 433{ 434 PIXBeginNamedEvent(0,"Loading chunk"); 435 short version = dis->readShort(); 436 int x = dis->readInt(); 437 int z = dis->readInt(); 438 int time = dis->readLong(); 439 440 LevelChunk *levelChunk = new LevelChunk(level, x, z); 441 442 if (version >= SAVE_FILE_VERSION_CHUNK_INHABITED_TIME) 443 { 444 levelChunk->inhabitedTime = dis->readLong(); 445 } 446 447 levelChunk->readCompressedBlockData(dis); 448 levelChunk->readCompressedDataData(dis); 449 levelChunk->readCompressedSkyLightData(dis); 450 levelChunk->readCompressedBlockLightData(dis); 451 452 dis->readFully(levelChunk->heightmap); 453 454 levelChunk->terrainPopulated = dis->readShort(); 455 // If all neighbours have been post-processed, then we should have done the post-post-processing now. Check that this is set as if it isn't then we won't be able 456 // to send network data for chunks, and we won't ever try and set it again as all the directional flags are now already set - should only be an issue for old maps 457 // before this flag was added. 458 if( ( levelChunk->terrainPopulated & LevelChunk::sTerrainPopulatedAllNeighbours ) == LevelChunk::sTerrainPopulatedAllNeighbours ) 459 { 460 levelChunk->terrainPopulated |= LevelChunk::sTerrainPostPostProcessed; 461 } 462 463#ifndef _CONTENT_PACKAGE 464 if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_EnableBiomeOverride)) 465 { 466 // Read the biome data from the stream, but don't use it 467 byteArray dummyBiomes(levelChunk->biomes.length); 468 dis->readFully(dummyBiomes); 469 delete [] dummyBiomes.data; 470 } 471 else 472#endif 473 { 474 dis->readFully(levelChunk->biomes); 475 } 476 477 CompoundTag *tag = NbtIo::read(dis); 478 479 loadEntities(levelChunk, level, tag); 480 481 if (tag->contains(L"TileTicks")) 482 { 483 PIXBeginNamedEvent(0,"Loading TileTicks"); 484 ListTag<CompoundTag> *tileTicks = (ListTag<CompoundTag> *) tag->getList(L"TileTicks"); 485 486 if (tileTicks != NULL) 487 { 488 for (int i = 0; i < tileTicks->size(); i++) 489 { 490 CompoundTag *teTag = tileTicks->get(i); 491 492 level->forceAddTileTick(teTag->getInt(L"x"), teTag->getInt(L"y"), teTag->getInt(L"z"), teTag->getInt(L"i"), teTag->getInt(L"t"), teTag->getInt(L"p")); 493 } 494 } 495 PIXEndNamedEvent(); 496 } 497 498 delete tag; 499 500 PIXEndNamedEvent(); 501 502 return levelChunk; 503} 504 505LevelChunk *OldChunkStorage::load(Level *level, CompoundTag *tag) 506{ 507 int x = tag->getInt(L"xPos"); 508 int z = tag->getInt(L"zPos"); 509 510 LevelChunk *levelChunk = new LevelChunk(level, x, z); 511 // 4J - the original code uses the data in the tag directly, but this is now just used as a source when creating the compressed data, so 512 // we need to free up the data in the tag once we are done 513 levelChunk->setBlockData(tag->getByteArray(L"Blocks")); 514 delete [] tag->getByteArray(L"Blocks").data; 515 // levelChunk->blocks = tag->getByteArray(L"Blocks"); 516 517 // 4J - the original code uses the data in the tag directly, but this is now just used as a source when creating the compressed data, so 518 // we need to free up the data in the tag once we are done 519 levelChunk->setDataData(tag->getByteArray(L"Data")); 520 delete [] tag->getByteArray(L"Data").data; 521 522 // 4J - changed to use our new methods for accessing lighting 523 levelChunk->setSkyLightData(tag->getByteArray(L"SkyLight")); 524 levelChunk->setBlockLightData(tag->getByteArray(L"BlockLight")); 525 526 // In the original code (commented out below) constructing DataLayers from these arrays uses the data directly and so it doesn't need deleted. The new 527 // setSkyLightData/setBlockLightData take a copy of the data so we need to delete the local one now 528 delete [] tag->getByteArray(L"SkyLight").data; 529 delete [] tag->getByteArray(L"BlockLight").data; 530 531 // levelChunk->skyLight = new DataLayer(tag->getByteArray(L"SkyLight"), level->depthBits); 532 // levelChunk->blockLight = new DataLayer(tag->getByteArray(L"BlockLight"), level->depthBits); 533 534 delete [] levelChunk->heightmap.data; 535 levelChunk->heightmap = tag->getByteArray(L"HeightMap"); 536 // 4J - TerrainPopulated was a bool (java), then changed to be a byte bitfield, then replaced with TerrainPopulatedShort to store a wider bitfield 537 if( tag->get(L"TerrainPopulated") ) 538 { 539 // Java bool type or byte bitfield 540 levelChunk->terrainPopulated = tag->getByte(L"TerrainPopulated"); 541 if( levelChunk->terrainPopulated >= 1 ) levelChunk->terrainPopulated = LevelChunk::sTerrainPopulatedAllNeighbours | LevelChunk::sTerrainPostPostProcessed; // Convert from old bool type to new bitfield 542 } 543 else 544 { 545 // New style short 546 levelChunk->terrainPopulated = tag->getShort(L"TerrainPopulatedFlags"); 547 // If all neighbours have been post-processed, then we should have done the post-post-processing now. Check that this is set as if it isn't then we won't be able 548 // to send network data for chunks, and we won't ever try and set it again as all the directional flags are now already set - should only be an issue for old maps 549 // before this flag was added. 550 if( ( levelChunk->terrainPopulated & LevelChunk::sTerrainPopulatedAllNeighbours ) == LevelChunk::sTerrainPopulatedAllNeighbours ) 551 { 552 levelChunk->terrainPopulated |= LevelChunk::sTerrainPostPostProcessed; 553 } 554 } 555 556#if 0 557 // 4J - removed - we shouldn't need this any more 558 if (!levelChunk->data->isValid()) 559 { 560 levelChunk->data = new DataLayer(LevelChunk::BLOCKS_LENGTH, level->depthBits); // 4J - BLOCKS_LENGTH was levelChunk->blocks.length 561 } 562#endif 563 564 // 4J removed - we shouldn't need this any more 565#if 0 566 if (levelChunk->heightmap.data == NULL || !levelChunk->skyLight->isValid()) 567 { 568 static int chunksUpdated = 0; 569 delete [] levelChunk->heightmap.data; 570 levelChunk->heightmap = byteArray(16 * 16); 571 delete levelChunk->skyLight; 572 levelChunk->skyLight = new DataLayer(levelChunk->blocks.length, level->depthBits); 573 levelChunk->recalcHeightmap(); 574 } 575 576 if (!levelChunk->blockLight->isValid()) 577 { 578 delete levelChunk->blockLight; 579 levelChunk->blockLight = new DataLayer(levelChunk->blocks.length, level->depthBits); 580 levelChunk->recalcBlockLights(); 581 } 582#endif 583 584#ifndef _CONTENT_PACKAGE 585 if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_EnableBiomeOverride)) 586 { 587 // Do nothing 588 } 589 else 590#endif 591 { 592 if (tag->contains(L"Biomes")) 593 { 594 levelChunk->setBiomes(tag->getByteArray(L"Biomes")); 595 } 596 } 597 598 loadEntities(levelChunk, level, tag); 599 600 if (tag->contains(L"TileTicks")) 601 { 602 ListTag<CompoundTag> *tileTicks = (ListTag<CompoundTag> *) tag->getList(L"TileTicks"); 603 604 if (tileTicks != NULL) 605 { 606 for (int i = 0; i < tileTicks->size(); i++) 607 { 608 CompoundTag *teTag = tileTicks->get(i); 609 610 level->forceAddTileTick(teTag->getInt(L"x"), teTag->getInt(L"y"), teTag->getInt(L"z"), teTag->getInt(L"i"), teTag->getInt(L"t"), teTag->getInt(L"p")); 611 } 612 } 613 } 614 615 return levelChunk; 616} 617 618void OldChunkStorage::tick() 619{ 620} 621 622void OldChunkStorage::flush() 623{ 624} 625 626void OldChunkStorage::saveEntities(Level *level, LevelChunk *levelChunk) 627{ 628}