#include "../../Platform/stdafx.h" #include "../../IO/Files/File.h" #include "../../IO/Streams/InputOutputStream.h" #include "../../Headers/net.minecraft.world.entity.h" #include "../../Headers/net.minecraft.world.level.h" #include "../../Headers/net.minecraft.world.level.chunk.h" #include "../../Headers/net.minecraft.world.level.tile.entity.h" #include "../../Headers/net.minecraft.world.level.storage.h" #include "../../IO/Files/FileHeader.h" #include "OldChunkStorage.h" #if defined(_WIN32) namespace { inline void* OldChunkStorageTlsGetValue(OldChunkStorage::TlsKey key) { return TlsGetValue(key); } inline void OldChunkStorageTlsSetValue(OldChunkStorage::TlsKey key, void* value) { TlsSetValue(key, value); } } // namespace OldChunkStorage::TlsKey OldChunkStorage::tlsIdx = TlsAlloc(); #else namespace { pthread_key_t CreateOldChunkStorageTlsKey() { pthread_key_t key; const int result = pthread_key_create(&key, nullptr); assert(result == 0); return key; } inline void* OldChunkStorageTlsGetValue(pthread_key_t key) { return pthread_getspecific(key); } inline void OldChunkStorageTlsSetValue(pthread_key_t key, void* value) { pthread_setspecific(key, value); } } // namespace OldChunkStorage::TlsKey OldChunkStorage::tlsIdx = CreateOldChunkStorageTlsKey(); #endif OldChunkStorage::ThreadStorage* OldChunkStorage::tlsDefault = NULL; OldChunkStorage::ThreadStorage::ThreadStorage() { blockData = byteArray(Level::CHUNK_TILE_COUNT); dataData = byteArray(Level::HALF_CHUNK_TILE_COUNT); skyLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); blockLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); } OldChunkStorage::ThreadStorage::~ThreadStorage() { delete[] blockData.data; delete[] dataData.data; delete[] skyLightData.data; delete[] blockLightData.data; } void OldChunkStorage::CreateNewThreadStorage() { ThreadStorage* tls = new ThreadStorage(); if (tlsDefault == NULL) { tlsDefault = tls; } OldChunkStorageTlsSetValue(tlsIdx, tls); } void OldChunkStorage::UseDefaultThreadStorage() { OldChunkStorageTlsSetValue(tlsIdx, tlsDefault); } void OldChunkStorage::ReleaseThreadStorage() { ThreadStorage* tls = static_cast(OldChunkStorageTlsGetValue(tlsIdx)); if (tls == tlsDefault) return; delete tls; } OldChunkStorage::OldChunkStorage(File dir, bool create) { this->dir = dir; this->create = create; } File OldChunkStorage::getFile(int x, int z) { wchar_t name[MAX_PATH_SIZE]; wchar_t path1[MAX_PATH_SIZE]; wchar_t path2[MAX_PATH_SIZE]; wchar_t xRadix36[64]; wchar_t zRadix36[64]; #if (defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ || \ defined __linux__) assert(0); // need a gcc verison of _itow ? #else _itow(x, xRadix36, 36); _itow(z, zRadix36, 36); swprintf(name, MAX_PATH_SIZE, L"c.%ls.%ls.dat", xRadix36, zRadix36); _itow(x & 63, path1, 36); _itow(z & 63, path2, 36); #endif // sprintf(file,"%s\\%s",dir,path1); File file(dir, std::wstring(path1)); if (!file.exists()) { if (create) file.mkdir(); else { return File(L""); } } // strcat(file,"\\"); // strcat(file,path2); file = File(file, std::wstring(path2)); if (!file.exists()) { if (create) file.mkdir(); else { return File(L""); } } // strcat(file,"\\"); // strcat(file,name); // sprintf(file,"%s\\%s",file,name); file = File(file, std::wstring(name)); if (!file.exists()) { if (!create) { return File(L""); } } return file; } LevelChunk* OldChunkStorage::load(Level* level, int x, int z) { File file = getFile(x, z); if (!file.getPath().empty() && file.exists()) { // 4J - removed try/catch // try { // System.out.println("Loading chunk "+x+", "+z); FileInputStream fis = FileInputStream(file); CompoundTag* tag = NbtIo::readCompressed(&fis); if (!tag->contains(L"Level")) { char buf[256]; sprintf(buf, "Chunk file at %d, %d is missing level data, skipping\n", x, z); app.DebugPrintf(buf); return NULL; } if (!tag->getCompound(L"Level")->contains(L"Blocks")) { char buf[256]; sprintf(buf, "Chunk file at %d, %d is missing block data, skipping\n", x, z); app.DebugPrintf(buf); return NULL; } LevelChunk* levelChunk = OldChunkStorage::load(level, tag->getCompound(L"Level")); if (!levelChunk->isAt(x, z)) { char buf[256]; sprintf(buf, "Chunk fileat %d, %d is in the wrong location; relocating. " "Expected %d, %d, got %d, %d\n", x, z, x, z, levelChunk->x, levelChunk->z); app.DebugPrintf(buf); tag->putInt(L"xPos", x); tag->putInt(L"zPos", z); levelChunk = OldChunkStorage::load(level, tag->getCompound(L"Level")); } return levelChunk; // } catch (Exception e) { // e.printStackTrace(); // } } return NULL; } void OldChunkStorage::save(Level* level, LevelChunk* levelChunk) { level->checkSession(); File file = getFile(levelChunk->x, levelChunk->z); if (file.exists()) { LevelData* levelData = level->getLevelData(); levelData->setSizeOnDisk(levelData->getSizeOnDisk() - file.length()); } // 4J - removed try/catch // try { // char tmpFileName[MAX_PATH_SIZE]; // sprintf(tmpFileName,"%s\\%s",dir,"tmp_chunk.dat"); File tmpFile(dir, L"tmp_chunk.dat"); // System.out.println("Saving chunk "+levelChunk.x+", // "+levelChunk.z); FileOutputStream fos = FileOutputStream(tmpFile); CompoundTag* tag = new CompoundTag(); CompoundTag* levelData = new CompoundTag(); tag->put(L"Level", levelData); OldChunkStorage::save(levelChunk, level, levelData); NbtIo::writeCompressed(tag, &fos); fos.close(); if (file.exists()) { // DeleteFile(file); file._delete(); } // MoveFile(tmpFile,file); tmpFile.renameTo(file); LevelData* levelInfo = level->getLevelData(); levelInfo->setSizeOnDisk(levelInfo->getSizeOnDisk() + file.length()); // } catch (Exception e) { // e.printStackTrace(); // } } bool OldChunkStorage::saveEntities(LevelChunk* lc, Level* level, CompoundTag* tag) { // If we saved and it had no entities, and nothing has been added since skip // this one if (!lc->lastSaveHadEntities) return false; lc->lastSaveHadEntities = false; ListTag* entityTags = new ListTag(); #ifdef _ENTITIES_RW_SECTION EnterCriticalRWSection(&lc->m_csEntities, true); #else EnterCriticalSection(&lc->m_csEntities); #endif for (int i = 0; i < lc->ENTITY_BLOCKS_LENGTH; i++) { AUTO_VAR(itEnd, lc->entityBlocks[i]->end()); for (std::vector >::iterator it = lc->entityBlocks[i]->begin(); it != itEnd; it++) { std::shared_ptr e = *it; lc->lastSaveHadEntities = true; CompoundTag* teTag = new CompoundTag(); if (e->save(teTag)) { entityTags->add(teTag); } } } #ifdef _ENTITIES_RW_SECTION LeaveCriticalRWSection(&lc->m_csEntities, true); #else LeaveCriticalSection(&lc->m_csEntities); #endif tag->put(L"Entities", entityTags); return lc->lastSaveHadEntities; } void OldChunkStorage::save(LevelChunk* lc, Level* level, DataOutputStream* dos) { dos->writeShort(SAVE_FILE_VERSION_NUMBER); dos->writeInt(lc->x); dos->writeInt(lc->z); dos->writeLong(level->getTime()); PIXBeginNamedEvent(0, "Getting block data"); lc->writeCompressedBlockData(dos); PIXEndNamedEvent(); PIXBeginNamedEvent(0, "Getting data data"); lc->writeCompressedDataData(dos); PIXEndNamedEvent(); PIXBeginNamedEvent(0, "Getting sky and block light data"); lc->writeCompressedSkyLightData(dos); lc->writeCompressedBlockLightData(dos); PIXEndNamedEvent(); dos->write(lc->heightmap); dos->writeShort(lc->terrainPopulated); dos->write(lc->getBiomes()); PIXBeginNamedEvent(0, "Saving entities"); CompoundTag* tag = new CompoundTag(); #ifndef SPLIT_SAVES saveEntities(lc, level, tag); #endif PIXBeginNamedEvent(0, "Saving tile entities"); ListTag* tileEntityTags = new ListTag(); AUTO_VAR(itEnd, lc->tileEntities.end()); for (std::unordered_map, TilePosKeyHash, TilePosKeyEq>::iterator it = lc->tileEntities.begin(); it != itEnd; it++) { std::shared_ptr te = it->second; CompoundTag* teTag = new CompoundTag(); te->save(teTag); tileEntityTags->add(teTag); } tag->put(L"TileEntities", tileEntityTags); PIXEndNamedEvent(); PIXBeginNamedEvent(0, "Saving tile tick data"); std::vector* ticksInChunk = level->fetchTicksInChunk(lc, false); if (ticksInChunk != NULL) { __int64 levelTime = level->getTime(); ListTag* tickTags = new ListTag(); for (int i = 0; i < ticksInChunk->size(); i++) { TickNextTickData td = ticksInChunk->at(i); CompoundTag* teTag = new CompoundTag(); teTag->putInt(L"i", td.tileId); teTag->putInt(L"x", td.x); teTag->putInt(L"y", td.y); teTag->putInt(L"z", td.z); teTag->putInt(L"t", (int)(td.m_delay - levelTime)); tickTags->add(teTag); } tag->put(L"TileTicks", tickTags); } delete ticksInChunk; PIXEndNamedEvent(); NbtIo::write(tag, dos); delete tag; PIXEndNamedEvent(); } void OldChunkStorage::save(LevelChunk* lc, Level* level, CompoundTag* tag) { level->checkSession(); tag->putInt(L"xPos", lc->x); tag->putInt(L"zPos", lc->z); tag->putLong(L"LastUpdate", level->getTime()); // 4J - changes here for new storage. Now have static storage for getting // lighting data for block, data, and sky & block lighting. This wasn't // required in the original version as we could just reference the // information in the level itself, but with our new storage system the full // data doesn't normally exist & so getSkyLightData/getBlockLightData etc. // need somewhere to output this data. Making this static so that we aren't // dynamically allocating memory in the server thread when writing chunks as // this causes serious stalling on the main thread. Will be fine so long as // we only actually create tags for once chunk at a time. // 4J Stu - As we now save on multiple threads, the static data has been // moved to TLS ThreadStorage* tls = static_cast(OldChunkStorageTlsGetValue(tlsIdx)); PIXBeginNamedEvent(0, "Getting block data"); // static byteArray blockData = byteArray(32768); lc->getBlockData(tls->blockData); tag->putByteArray(L"Blocks", tls->blockData); PIXEndNamedEvent(); PIXBeginNamedEvent(0, "Getting data data"); // static byteArray dataData = byteArray(16384); lc->getDataData(tls->dataData); tag->putByteArray(L"Data", tls->dataData); PIXEndNamedEvent(); PIXBeginNamedEvent(0, "Getting sky and block light data"); // static byteArray skyLightData = byteArray(16384); // static byteArray blockLightData = byteArray(16384); lc->getSkyLightData(tls->skyLightData); lc->getBlockLightData(tls->blockLightData); tag->putByteArray(L"SkyLight", tls->skyLightData); tag->putByteArray(L"BlockLight", tls->blockLightData); PIXEndNamedEvent(); tag->putByteArray(L"HeightMap", lc->heightmap); tag->putShort( L"TerrainPopulatedFlags", lc->terrainPopulated); // 4J - changed from "TerrainPopulated" to // "TerrainPopulatedFlags" as now stores a // bitfield, java stores a bool tag->putByteArray(L"Biomes", lc->getBiomes()); PIXBeginNamedEvent(0, "Saving entities"); #ifndef SPLIT_SAVES saveEntities(lc, level, tag); #endif PIXBeginNamedEvent(0, "Saving tile entities"); ListTag* tileEntityTags = new ListTag(); AUTO_VAR(itEnd, lc->tileEntities.end()); for (std::unordered_map, TilePosKeyHash, TilePosKeyEq>::iterator it = lc->tileEntities.begin(); it != itEnd; it++) { std::shared_ptr te = it->second; CompoundTag* teTag = new CompoundTag(); te->save(teTag); tileEntityTags->add(teTag); } tag->put(L"TileEntities", tileEntityTags); PIXEndNamedEvent(); PIXBeginNamedEvent(0, "Saving tile tick data"); std::vector* ticksInChunk = level->fetchTicksInChunk(lc, false); if (ticksInChunk != NULL) { __int64 levelTime = level->getTime(); ListTag* tickTags = new ListTag(); for (int i = 0; i < ticksInChunk->size(); i++) { TickNextTickData td = ticksInChunk->at(i); CompoundTag* teTag = new CompoundTag(); teTag->putInt(L"i", td.tileId); teTag->putInt(L"x", td.x); teTag->putInt(L"y", td.y); teTag->putInt(L"z", td.z); teTag->putInt(L"t", (int)(td.m_delay - levelTime)); tickTags->add(teTag); } tag->put(L"TileTicks", tickTags); } delete ticksInChunk; PIXEndNamedEvent(); PIXEndNamedEvent(); } void OldChunkStorage::loadEntities(LevelChunk* lc, Level* level, CompoundTag* tag) { ListTag* entityTags = (ListTag*)tag->getList(L"Entities"); if (entityTags != NULL) { for (int i = 0; i < entityTags->size(); i++) { CompoundTag* teTag = entityTags->get(i); std::shared_ptr te = EntityIO::loadStatic(teTag, level); lc->lastSaveHadEntities = true; if (te != NULL) { lc->addEntity(te); } } } ListTag* tileEntityTags = (ListTag*)tag->getList(L"TileEntities"); if (tileEntityTags != NULL) { for (int i = 0; i < tileEntityTags->size(); i++) { CompoundTag* teTag = tileEntityTags->get(i); std::shared_ptr te = TileEntity::loadStatic(teTag); if (te != NULL) { lc->addTileEntity(te); } } } } LevelChunk* OldChunkStorage::load(Level* level, DataInputStream* dis) { short version = dis->readShort(); int x = dis->readInt(); int z = dis->readInt(); int time = dis->readLong(); LevelChunk* levelChunk = new LevelChunk(level, x, z); levelChunk->readCompressedBlockData(dis); levelChunk->readCompressedDataData(dis); levelChunk->readCompressedSkyLightData(dis); levelChunk->readCompressedBlockLightData(dis); dis->readFully(levelChunk->heightmap); levelChunk->terrainPopulated = dis->readShort(); // 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 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 before this flag was added. if ((levelChunk->terrainPopulated & LevelChunk::sTerrainPopulatedAllNeighbours) == LevelChunk::sTerrainPopulatedAllNeighbours) { levelChunk->terrainPopulated |= LevelChunk::sTerrainPostPostProcessed; } dis->readFully(levelChunk->biomes); CompoundTag* tag = NbtIo::read(dis); loadEntities(levelChunk, level, tag); if (tag->contains(L"TileTicks")) { ListTag* tileTicks = (ListTag*)tag->getList(L"TileTicks"); if (tileTicks != NULL) { for (int i = 0; i < tileTicks->size(); i++) { CompoundTag* teTag = tileTicks->get(i); level->forceAddTileTick( teTag->getInt(L"x"), teTag->getInt(L"y"), teTag->getInt(L"z"), teTag->getInt(L"i"), teTag->getInt(L"t")); } } } delete tag; return levelChunk; } LevelChunk* OldChunkStorage::load(Level* level, CompoundTag* tag) { int x = tag->getInt(L"xPos"); int z = tag->getInt(L"zPos"); LevelChunk* levelChunk = new LevelChunk(level, x, z); // 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 we need to // free up the data in the tag once we are done levelChunk->setBlockData(tag->getByteArray(L"Blocks")); delete[] tag->getByteArray(L"Blocks").data; // levelChunk->blocks = tag->getByteArray(L"Blocks"); // 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 we need to // free up the data in the tag once we are done levelChunk->setDataData(tag->getByteArray(L"Data")); delete[] tag->getByteArray(L"Data").data; // 4J - changed to use our new methods for accessing lighting levelChunk->setSkyLightData(tag->getByteArray(L"SkyLight")); levelChunk->setBlockLightData(tag->getByteArray(L"BlockLight")); // 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 setSkyLightData/setBlockLightData take a copy of the data so we need // to delete the local one now delete[] tag->getByteArray(L"SkyLight").data; delete[] tag->getByteArray(L"BlockLight").data; // levelChunk->skyLight = new DataLayer(tag->getByteArray(L"SkyLight"), //level->depthBits); levelChunk->blockLight = new //DataLayer(tag->getByteArray(L"BlockLight"), level->depthBits); delete[] levelChunk->heightmap.data; levelChunk->heightmap = tag->getByteArray(L"HeightMap"); // 4J - TerrainPopulated was a bool (java), then changed to be a byte // bitfield, then replaced with TerrainPopulatedShort to store a wider // bitfield if (tag->get(L"TerrainPopulated")) { // Java bool type or byte bitfield levelChunk->terrainPopulated = tag->getByte(L"TerrainPopulated"); if (levelChunk->terrainPopulated >= 1) levelChunk->terrainPopulated = LevelChunk::sTerrainPopulatedAllNeighbours | LevelChunk::sTerrainPostPostProcessed; // Convert from old bool // type to new bitfield } else { // New style short levelChunk->terrainPopulated = tag->getShort(L"TerrainPopulatedFlags"); // 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 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 before this flag // was added. if ((levelChunk->terrainPopulated & LevelChunk::sTerrainPopulatedAllNeighbours) == LevelChunk::sTerrainPopulatedAllNeighbours) { levelChunk->terrainPopulated |= LevelChunk::sTerrainPostPostProcessed; } } #if 0 // 4J - removed - we shouldn't need this any more if (!levelChunk->data->isValid()) { levelChunk->data = new DataLayer(LevelChunk::BLOCKS_LENGTH, level->depthBits); // 4J - BLOCKS_LENGTH was levelChunk->blocks.length } #endif // 4J removed - we shouldn't need this any more #if 0 if (levelChunk->heightmap.data == NULL || !levelChunk->skyLight->isValid()) { static int chunksUpdated = 0; delete [] levelChunk->heightmap.data; levelChunk->heightmap = byteArray(16 * 16); delete levelChunk->skyLight; levelChunk->skyLight = new DataLayer(levelChunk->blocks.length, level->depthBits); levelChunk->recalcHeightmap(); } if (!levelChunk->blockLight->isValid()) { delete levelChunk->blockLight; levelChunk->blockLight = new DataLayer(levelChunk->blocks.length, level->depthBits); levelChunk->recalcBlockLights(); } #endif if (tag->contains(L"Biomes")) { levelChunk->setBiomes(tag->getByteArray(L"Biomes")); } loadEntities(levelChunk, level, tag); if (tag->contains(L"TileTicks")) { ListTag* tileTicks = (ListTag*)tag->getList(L"TileTicks"); if (tileTicks != NULL) { for (int i = 0; i < tileTicks->size(); i++) { CompoundTag* teTag = tileTicks->get(i); level->forceAddTileTick( teTag->getInt(L"x"), teTag->getInt(L"y"), teTag->getInt(L"z"), teTag->getInt(L"i"), teTag->getInt(L"t")); } } } return levelChunk; } void OldChunkStorage::tick() {} void OldChunkStorage::flush() {} void OldChunkStorage::saveEntities(Level* level, LevelChunk* levelChunk) {}