#include "../../Minecraft.World/Platform/stdafx.h" #include #include "../../Minecraft.World/Headers/com.mojang.nbt.h" #include "../../Minecraft.World/Platform/System.h" #include "ConsoleSchematicFile.h" #include "../../Minecraft.World/IO/Streams/InputOutputStream.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.chunk.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.entity.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.item.h" #include "../../Minecraft.World/Headers/net.minecraft.world.phys.h" #include "../../Minecraft.World/IO/Streams/Compression.h" ConsoleSchematicFile::ConsoleSchematicFile() { m_xSize = m_ySize = m_zSize = 0; m_refCount = 1; m_data.data = nullptr; } ConsoleSchematicFile::~ConsoleSchematicFile() { app.DebugPrintf("Deleting schematic file\n"); if (m_data.data != nullptr) delete[] m_data.data; } void ConsoleSchematicFile::save(DataOutputStream* dos) { if (dos != nullptr) { dos->writeInt(XBOX_SCHEMATIC_CURRENT_VERSION); dos->writeByte(APPROPRIATE_COMPRESSION_TYPE); dos->writeInt(m_xSize); dos->writeInt(m_ySize); dos->writeInt(m_zSize); byteArray ba(new std::uint8_t[m_data.length], m_data.length); Compression::getCompression()->CompressLZXRLE( ba.data, &ba.length, m_data.data, m_data.length); dos->writeInt(ba.length); dos->write(ba); save_tags(dos); delete[] ba.data; } } void ConsoleSchematicFile::load(DataInputStream* dis) { if (dis != nullptr) { // VERSION CHECK // int version = dis->readInt(); Compression::ECompressionTypes compressionType = Compression::eCompressionType_LZXRLE; if (version > XBOX_SCHEMATIC_ORIGINAL_VERSION) // Or later versions { compressionType = (Compression::ECompressionTypes)dis->readByte(); } if (version > XBOX_SCHEMATIC_CURRENT_VERSION) assert(false && "Unrecognised schematic version!!"); m_xSize = dis->readInt(); m_ySize = dis->readInt(); m_zSize = dis->readInt(); int compressedSize = dis->readInt(); byteArray compressedBuffer(compressedSize); dis->readFully(compressedBuffer); if (m_data.data != nullptr) { delete[] m_data.data; m_data.data = nullptr; } if (compressionType == Compression::eCompressionType_None) { m_data = compressedBuffer; } else { unsigned int outputSize = m_xSize * m_ySize * m_zSize * 3 / 2; m_data = byteArray(outputSize); switch (compressionType) { case Compression::eCompressionType_RLE: Compression::getCompression()->DecompressRLE( m_data.data, &m_data.length, compressedBuffer.data, compressedSize); break; case APPROPRIATE_COMPRESSION_TYPE: Compression::getCompression()->DecompressLZXRLE( m_data.data, &m_data.length, compressedBuffer.data, compressedSize); break; default: app.DebugPrintf( "Unrecognized compression type for Schematic file " "(%d)\n", (int)compressionType); Compression::getCompression()->SetDecompressionType( (Compression::ECompressionTypes)compressionType); Compression::getCompression()->DecompressLZXRLE( m_data.data, &m_data.length, compressedBuffer.data, compressedSize); Compression::getCompression()->SetDecompressionType( APPROPRIATE_COMPRESSION_TYPE); }; delete[] compressedBuffer.data; } // READ TAGS // // 4jcraft, fixed cast of templated List to get the tag list // and cast it to CompoundTag inside the loop CompoundTag* tag = NbtIo::read(dis); ListTag* tileEntityTags = tag->getList(L"TileEntities"); if (tileEntityTags != nullptr) { for (int i = 0; i < tileEntityTags->size(); i++) { CompoundTag* teTag = (CompoundTag*)tileEntityTags->get(i); std::shared_ptr te = TileEntity::loadStatic(teTag); if (te == nullptr) { #ifndef _CONTENT_PACKAGE app.DebugPrintf( "ConsoleSchematicFile has read a nullptr tile " "entity\n"); __debugbreak(); #endif } else { m_tileEntities.push_back(te); } } } // 4jcraft, fixed cast of templated List to get the tag list // and cast it to CompoundTag inside the loop ListTag* entityTags = tag->getList(L"Entities"); if (entityTags != nullptr) { for (int i = 0; i < entityTags->size(); i++) { CompoundTag* eTag = (CompoundTag*)entityTags->get(i); eINSTANCEOF type = EntityIO::getType(eTag->getString(L"id")); // 4jcraft, same here ListTag* pos = eTag->getList(L"Pos"); double x = ((DoubleTag*)pos->get(0))->data; double y = ((DoubleTag*)pos->get(1))->data; double z = ((DoubleTag*)pos->get(2))->data; if (type == eTYPE_PAINTING || type == eTYPE_ITEM_FRAME) { x = ((IntTag*)eTag->get(L"TileX"))->data; y = ((IntTag*)eTag->get(L"TileY"))->data; z = ((IntTag*)eTag->get(L"TileZ"))->data; } #ifdef _DEBUG // app.DebugPrintf(1,"Loaded entity type %d at // (%f,%f,%f)\n",(int)type,x,y,z); #endif m_entities.push_back(std::pair( Vec3(x, y, z), (CompoundTag*)eTag->copy())); } } delete tag; } } void ConsoleSchematicFile::save_tags(DataOutputStream* dos) { CompoundTag* tag = new CompoundTag(); ListTag* tileEntityTags = new ListTag(); tag->put(L"TileEntities", tileEntityTags); for (auto it = m_tileEntities.begin(); it != m_tileEntities.end(); it++) { CompoundTag* cTag = new CompoundTag(); (*it)->save(cTag); tileEntityTags->add(cTag); } ListTag* entityTags = new ListTag(); tag->put(L"Entities", entityTags); for (auto it = m_entities.begin(); it != m_entities.end(); it++) entityTags->add((CompoundTag*)(*it).second->copy()); NbtIo::write(tag, dos); delete tag; } int64_t ConsoleSchematicFile::applyBlocksAndData(LevelChunk* chunk, AABB* chunkBox, AABB* destinationBox, ESchematicRotation rot) { int xStart = std::max(destinationBox->x0, (double)chunk->x * 16); // 4jcraft changed from (xStart>>4)<<4 to (xStart & ~15) int xEnd = std::min(destinationBox->x1, (double)((xStart & ~15) + 16)); int yStart = destinationBox->y0; int yEnd = destinationBox->y1; if (yEnd > Level::maxBuildHeight) yEnd = Level::maxBuildHeight; int zStart = std::max(destinationBox->z0, (double)chunk->z * 16); int zEnd = std::min(destinationBox->z1, (double)(zStart & ~15) + 16); #ifdef _DEBUG app.DebugPrintf("Range is (%d,%d,%d) to (%d,%d,%d)\n", xStart, yStart, zStart, xEnd - 1, yEnd - 1, zEnd - 1); #endif int rowBlocksIncluded = (yEnd - yStart) * (zEnd - zStart); int blocksIncluded = (xEnd - xStart) * rowBlocksIncluded; int rowBlockCount = getYSize() * getZSize(); int totalBlockCount = getXSize() * rowBlockCount; byteArray blockData = byteArray(Level::CHUNK_TILE_COUNT); PIXBeginNamedEvent(0, "Getting block data"); chunk->getBlockData(blockData); PIXEndNamedEvent(); byteArray dataData = byteArray(Level::HALF_CHUNK_TILE_COUNT); PIXBeginNamedEvent(0, "Getting Data data"); chunk->getDataData(dataData); PIXEndNamedEvent(); // Ignore light data int blockLightP = -1; int skyLightP = -1; if (rot == eSchematicRot_90 || rot == eSchematicRot_180 || rot == eSchematicRot_270) { int schematicXRow = 0; int schematicZRow = 0; int blocksP = 0; int dataP = 0; for (int x = xStart; x < xEnd; ++x) { int x0 = x - chunk->x * 16; int x1 = x0 + 1; for (int z = zStart; z < zEnd; ++z) { int z0 = z - chunk->z * 16; int z1 = z0 + 1; chunkCoordToSchematicCoord(destinationBox, x, z, rot, schematicXRow, schematicZRow); blocksP = (schematicXRow * rowBlockCount) + (schematicZRow * getYSize()); dataP = totalBlockCount + (blocksP) / 2; ConsoleSchematicFile::setBlocksAndData( chunk, blockData, dataData, m_data, x0, yStart, z0, x1, yEnd, z1, blocksP, dataP, blockLightP, skyLightP); } } } else if (rot == eSchematicRot_0) { // The initial pointer offsets for the different data types int schematicXRow = xStart - destinationBox->x0; int schematicZRow = zStart - destinationBox->z0; int blocksP = (schematicXRow * rowBlockCount) + (schematicZRow * getYSize()); int dataP = totalBlockCount + (schematicXRow * rowBlockCount + (schematicZRow * getYSize())) / 2; for (int x = xStart; x < xEnd; ++x) { int x0 = x - chunk->x * 16; int x1 = x0 + 1; int z0 = zStart - chunk->z * 16; int z1 = zEnd - chunk->z * 16; ConsoleSchematicFile::setBlocksAndData( chunk, blockData, dataData, m_data, x0, yStart, z0, x1, yEnd, z1, blocksP, dataP, blockLightP, skyLightP); // update all pointer positions // For z start to z end // Set blocks and data // increment z by the right amount blocksP += (rowBlockCount - rowBlocksIncluded); dataP += (rowBlockCount - rowBlocksIncluded) / 2; } } else { app.DebugPrintf( "ERROR: Rotation of block and data not implemented!!\n"); } // 4J Stu - Hack for ME pack to replace sand with end stone in schematics // for(int i = 0; i < blockData.length; ++i) //{ // if(blockData[i] == Tile::sand_Id || blockData[i] == Tile::sandStone_Id) // { // blockData[i] = Tile::whiteStone_Id; // } //} PIXBeginNamedEvent(0, "Setting Block data"); chunk->setBlockData(blockData); PIXEndNamedEvent(); delete[] blockData.data; // 4jcraft changed to array delete chunk->recalcHeightmapOnly(); PIXBeginNamedEvent(0, "Setting Data data"); chunk->setDataData(dataData); PIXEndNamedEvent(); delete[] dataData.data; // 4jcraft, same here // A basic pass through to roughly do the lighting. At this point of // post-processing, we don't have all the neighbouring chunks loaded in, so // any lighting here should be things that won't propagate out of this // chunk. for (int xx = xStart; xx < xEnd; xx++) for (int y = yStart; y < yEnd; y++) for (int zz = zStart; zz < zEnd; zz++) { int x = xx - chunk->x * 16; int z = zz - chunk->z * 16; chunk->setBrightness(LightLayer::Block, x, y, z, 0); if (chunk->getTile(x, y, z)) { chunk->setBrightness(LightLayer::Sky, x, y, z, 0); } else { if (chunk->isSkyLit(x, y, z)) { chunk->setBrightness(LightLayer::Sky, x, y, z, 15); } else { chunk->setBrightness(LightLayer::Sky, x, y, z, 0); } } } return blocksIncluded; } // At the point that this is called, we have all the neighbouring chunks loaded // in (and generally post-processed, apart from this lighting pass), so we can // do the sort of lighting that might propagate out of the chunk. int64_t ConsoleSchematicFile::applyLighting(LevelChunk* chunk, AABB* chunkBox, AABB* destinationBox, ESchematicRotation rot) { int xStart = std::max(destinationBox->x0, (double)chunk->x * 16); // 4jcraft changed >>4<<4 to & ~15 int xEnd = std::min(destinationBox->x1, (double)(xStart & ~15) + 16); int yStart = destinationBox->y0; int yEnd = destinationBox->y1; if (yEnd > Level::maxBuildHeight) yEnd = Level::maxBuildHeight; int zStart = std::max(destinationBox->z0, (double)chunk->z * 16); int zEnd = std::min(destinationBox->z1, (double)(zStart & ~15) + 16); int rowBlocksIncluded = (yEnd - yStart) * (zEnd - zStart); int blocksIncluded = (xEnd - xStart) * rowBlocksIncluded; // Now actually do a checkLight on blocks that might need it, which should // more accurately put everything in place for (int xx = xStart; xx < xEnd; xx++) for (int y = yStart; y < yEnd; y++) for (int zz = zStart; zz < zEnd; zz++) { int x = xx - chunk->x * 16; int z = zz - chunk->z * 16; if (y <= chunk->getHeightmap(x, z)) { chunk->level->checkLight(LightLayer::Sky, xx, y, zz, true); } if (Tile::lightEmission[chunk->getTile(x, y, z)]) { // Note that this lighting passes a rootOnlyEmissive flag of // true, which means that only the location xx/y/zz is // considered as possibly being a source of emissive light, // not other tiles that we might encounter whilst // propagating the light from the start location. If we // don't do this, and Do encounter another emissive source // in the radius of influence that the first light source // had, then we'll start also lighting from that tile but // won't actually be able to progatate that second light // fully since checkLight only has a finite radius of 17 // from the start position that it can light. Then when we // do a checkLight on the second light later, it won't // bother doing anything because the light level at the // location of the tile itself will be correct. chunk->level->checkLight(LightLayer::Block, xx, y, zz, true, true); } } return blocksIncluded; } void ConsoleSchematicFile::chunkCoordToSchematicCoord(AABB* destinationBox, int chunkX, int chunkZ, ESchematicRotation rot, int& schematicX, int& schematicZ) { switch (rot) { case eSchematicRot_90: // schematicX decreases as chunkZ increases // schematicZ increases as chunkX increases schematicX = chunkZ - destinationBox->z0; schematicZ = (destinationBox->x1 - 1 - destinationBox->x0) - (chunkX - destinationBox->x0); break; case eSchematicRot_180: // schematicX decreases as chunkX increases // schematicZ decreases as chunkZ increases schematicX = (destinationBox->x1 - 1 - destinationBox->x0) - (chunkX - destinationBox->x0); schematicZ = (destinationBox->z1 - 1 - destinationBox->z0) - (chunkZ - destinationBox->z0); break; case eSchematicRot_270: // schematicX increases as chunkZ increases // shcematicZ decreases as chunkX increases schematicX = (destinationBox->z1 - 1 - destinationBox->z0) - (chunkZ - destinationBox->z0); schematicZ = chunkX - destinationBox->x0; break; case eSchematicRot_0: default: // schematicX increases as chunkX increases // schematicZ increases as chunkZ increases schematicX = chunkX - destinationBox->x0; schematicZ = chunkZ - destinationBox->z0; break; }; } void ConsoleSchematicFile::schematicCoordToChunkCoord( AABB* destinationBox, double schematicX, double schematicZ, ESchematicRotation rot, double& chunkX, double& chunkZ) { switch (rot) { case eSchematicRot_90: // schematicX decreases as chunkZ increases // schematicZ increases as chunkX increases chunkX = (destinationBox->x1 - 1 - schematicZ); chunkZ = schematicX + destinationBox->z0; break; case eSchematicRot_180: // schematicX decreases as chunkX increases // schematicZ decreases as chunkZ increases chunkX = (destinationBox->x1 - 1 - schematicX); chunkZ = (destinationBox->z1 - 1 - schematicZ); break; case eSchematicRot_270: // schematicX increases as chunkZ increases // shcematicZ decreases as chunkX increases chunkX = schematicZ + destinationBox->x0; chunkZ = (destinationBox->z1 - 1 - schematicX); break; case eSchematicRot_0: default: // schematicX increases as chunkX increases // schematicZ increases as chunkZ increases chunkX = schematicX + destinationBox->x0; chunkZ = schematicZ + destinationBox->z0; break; }; } void ConsoleSchematicFile::applyTileEntities(LevelChunk* chunk, AABB* chunkBox, AABB* destinationBox, ESchematicRotation rot) { for (auto it = m_tileEntities.begin(); it != m_tileEntities.end(); ++it) { std::shared_ptr te = *it; double targetX = te->x; double targetY = te->y + destinationBox->y0; double targetZ = te->z; schematicCoordToChunkCoord(destinationBox, te->x, te->z, rot, targetX, targetZ); Vec3 pos(targetX, targetY, targetZ); if (chunkBox->containsIncludingLowerBound(pos)) { std::shared_ptr teCopy = chunk->getTileEntity( (int)targetX & 15, (int)targetY & 15, (int)targetZ & 15); if (teCopy != nullptr) { CompoundTag* teData = new CompoundTag(); te->save(teData); teCopy->load(teData); delete teData; // Adjust the tileEntity position to world coords from schematic // co-ords teCopy->x = targetX; teCopy->y = targetY; teCopy->z = targetZ; // Remove the current tile entity // chunk->removeTileEntity( (int)targetX & 15, (int)targetY & // 15, (int)targetZ & 15 ); } else { teCopy = te->clone(); // Adjust the tileEntity position to world coords from schematic // co-ords teCopy->x = targetX; teCopy->y = targetY; teCopy->z = targetZ; chunk->addTileEntity(teCopy); } teCopy->setChanged(); } } for (auto it = m_entities.begin(); it != m_entities.end();) { Vec3 source = it->first; double targetX = source.x; double targetY = source.y + destinationBox->y0; double targetZ = source.z; schematicCoordToChunkCoord(destinationBox, source.x, source.z, rot, targetX, targetZ); // Add 0.01 as the AABB::contains function returns false if a value is // <= the lower bound Vec3 pos(targetX + 0.01, targetY + 0.01, targetZ + 0.01); if (!chunkBox->containsIncludingLowerBound(pos)) { ++it; continue; } CompoundTag* eTag = it->second; std::shared_ptr e = EntityIO::loadStatic(eTag, nullptr); if (e->GetType() == eTYPE_PAINTING) { std::shared_ptr painting = std::dynamic_pointer_cast(e); double tileX = painting->xTile; double tileZ = painting->zTile; schematicCoordToChunkCoord(destinationBox, painting->xTile, painting->zTile, rot, tileX, tileZ); painting->yTile += destinationBox->y0; painting->xTile = tileX; painting->zTile = tileZ; painting->setDir(painting->dir); } else if (e->GetType() == eTYPE_ITEM_FRAME) { std::shared_ptr frame = std::dynamic_pointer_cast(e); double tileX = frame->xTile; double tileZ = frame->zTile; schematicCoordToChunkCoord(destinationBox, frame->xTile, frame->zTile, rot, tileX, tileZ); frame->yTile += destinationBox->y0; frame->xTile = tileX; frame->zTile = tileZ; frame->setDir(frame->dir); } else { e->absMoveTo(targetX, targetY, targetZ, e->yRot, e->xRot); } #ifdef _DEBUG app.DebugPrintf("Adding entity type %d at (%f,%f,%f)\n", e->GetType(), e->x, e->y, e->z); #endif e->setLevel(chunk->level); e->resetSmallId(); e->setDespawnProtected(); // default to being protected against // despawning chunk->level->addEntity(e); // 4J Stu - Until we can copy every type of entity, remove them from // this vector This means that the entities will only exist in the first // use of the schematic that is processed // it = m_entities.erase(it); ++it; } } void ConsoleSchematicFile::generateSchematicFile( DataOutputStream* dos, Level* level, int xStart, int yStart, int zStart, int xEnd, int yEnd, int zEnd, bool bSaveMobs, Compression::ECompressionTypes compressionType) { assert(xEnd > xStart); assert(yEnd > yStart); assert(zEnd > zStart); // 4J Stu - Enforce even numbered positions to start with to avoid problems // with half-bytes in data // We want the start to be even if (xStart > 0 && xStart % 2 != 0) xStart -= 1; else if (xStart < 0 && xStart % 2 != 0) xStart -= 1; if (yStart < 0) yStart = 0; else if (yStart > 0 && yStart % 2 != 0) yStart -= 1; if (zStart > 0 && zStart % 2 != 0) zStart -= 1; else if (zStart < 0 && zStart % 2 != 0) zStart -= 1; // We want the end to be odd to have a total size that is even if (xEnd > 0 && xEnd % 2 == 0) xEnd += 1; else if (xEnd < 0 && xEnd % 2 == 0) xEnd += 1; if (yEnd > Level::maxBuildHeight) yEnd = Level::maxBuildHeight; else if (yEnd > 0 && yEnd % 2 == 0) yEnd += 1; else if (yEnd < 0 && yEnd % 2 == 0) yEnd += 1; if (zEnd > 0 && zEnd % 2 == 0) zEnd += 1; else if (zEnd < 0 && zEnd % 2 == 0) zEnd += 1; int xSize = xEnd - xStart + 1; int ySize = yEnd - yStart + 1; int zSize = zEnd - zStart + 1; app.DebugPrintf( "Generating schematic file for area (%d,%d,%d) to (%d,%d,%d), " "%dx%dx%d\n", xStart, yStart, zStart, xEnd, yEnd, zEnd, xSize, ySize, zSize); if (dos != nullptr) dos->writeInt(XBOX_SCHEMATIC_CURRENT_VERSION); if (dos != nullptr) dos->writeByte(compressionType); // Write xSize if (dos != nullptr) dos->writeInt(xSize); // Write ySize if (dos != nullptr) dos->writeInt(ySize); // Write zSize if (dos != nullptr) dos->writeInt(zSize); // byteArray rawBuffer = level->getBlocksAndData(xStart, yStart, zStart, // xSize, ySize, zSize, false); int xRowSize = ySize * zSize; int blockCount = xSize * xRowSize; byteArray result(blockCount * 3 / 2); // Position pointers into the data when not ordered by chunk int p = 0; int dataP = blockCount; int blockLightP = -1; int skyLightP = -1; int y0 = yStart; int y1 = yStart + ySize; if (y0 < 0) y0 = 0; if (y1 > Level::maxBuildHeight) y1 = Level::maxBuildHeight; // Every x is a whole row for (int xPos = xStart; xPos < xStart + xSize; ++xPos) { int xc = xPos >> 4; int x0 = xPos - xc * 16; if (x0 < 0) x0 = 0; int x1 = x0 + 1; if (x1 > 16) x1 = 16; for (int zPos = zStart; zPos < zStart + zSize;) { int zc = zPos >> 4; int z0 = zStart - zc * 16; int z1 = zStart + zSize - zc * 16; if (z0 < 0) z0 = 0; if (z1 > 16) z1 = 16; getBlocksAndData(level->getChunk(xc, zc), &result, x0, y0, z0, x1, y1, z1, p, dataP, blockLightP, skyLightP); zPos += (z1 - z0); } } #ifndef _CONTENT_PACKAGE if (p != blockCount) __debugbreak(); #endif // We don't know how this will compress - just make a fixed length buffer to // initially decompress into Some small sets of blocks can end up // compressing into something bigger than their source unsigned int inputSize = blockCount * 3 / 2; unsigned char* ucTemp = new unsigned char[inputSize]; switch (compressionType) { case Compression::eCompressionType_LZXRLE: Compression::getCompression()->CompressLZXRLE( ucTemp, &inputSize, result.data, (unsigned int)result.length); break; case Compression::eCompressionType_RLE: Compression::getCompression()->CompressRLE( ucTemp, &inputSize, result.data, (unsigned int)result.length); break; case Compression::eCompressionType_None: default: memcpy(ucTemp, result.data, inputSize); break; }; delete[] result.data; byteArray buffer = byteArray(ucTemp, inputSize); if (dos != nullptr) dos->writeInt(inputSize); if (dos != nullptr) dos->write(buffer); delete[] buffer.data; CompoundTag tag; ListTag* tileEntitiesTag = new ListTag(L"tileEntities"); int xc0 = xStart >> 4; int zc0 = zStart >> 4; int xc1 = (xStart + xSize - 1) >> 4; int zc1 = (zStart + zSize - 1) >> 4; for (int xc = xc0; xc <= xc1; xc++) { for (int zc = zc0; zc <= zc1; zc++) { std::vector >* tileEntities = getTileEntitiesInRegion(level->getChunk(xc, zc), xStart, yStart, zStart, xStart + xSize, yStart + ySize, zStart + zSize); for (auto it = tileEntities->begin(); it != tileEntities->end(); ++it) { std::shared_ptr te = *it; CompoundTag* teTag = new CompoundTag(); std::shared_ptr teCopy = te->clone(); // Adjust the tileEntity position to schematic coords from world // co-ords teCopy->x -= xStart; teCopy->y -= yStart; teCopy->z -= zStart; teCopy->save(teTag); tileEntitiesTag->add(teTag); } delete tileEntities; } } tag.put(L"TileEntities", tileEntitiesTag); AABB bb(xStart, yStart, zStart, xEnd, yEnd, zEnd); std::vector >* entities = level->getEntities(nullptr, &bb); ListTag* entitiesTag = new ListTag(L"entities"); for (auto it = entities->begin(); it != entities->end(); ++it) { std::shared_ptr e = *it; bool mobCanBeSaved = false; if (bSaveMobs) { if (e->instanceof(eTYPE_MONSTER) || e->instanceof(eTYPE_WATERANIMAL) || e->instanceof(eTYPE_ANIMAL) || (e->GetType() == eTYPE_VILLAGER)) // 4J-JEV: All these are derived from eTYPE_ANIMAL and true // implicitly. //|| ( e->GetType() == eTYPE_CHICKEN ) || ( e->GetType() == // eTYPE_WOLF ) || ( e->GetType() == eTYPE_MUSHROOMCOW ) ) { mobCanBeSaved = true; } } // 4J-JEV: Changed to check for instances of minecarts and // hangingEntities instead of just eTYPE_PAINTING, eTYPE_ITEM_FRAME and // eTYPE_MINECART if (mobCanBeSaved || e->instanceof(eTYPE_MINECART) || e->GetType() == eTYPE_BOAT || e->instanceof(eTYPE_HANGING_ENTITY)) { CompoundTag* eTag = new CompoundTag(); if (e->save(eTag)) { ListTag* pos = (ListTag*)eTag->getList(L"Pos"); pos->get(0)->data -= xStart; pos->get(1)->data -= yStart; pos->get(2)->data -= zStart; if (e->instanceof(eTYPE_HANGING_ENTITY)) { ((IntTag*)eTag->get(L"TileX"))->data -= xStart; ((IntTag*)eTag->get(L"TileY"))->data -= yStart; ((IntTag*)eTag->get(L"TileZ"))->data -= zStart; } entitiesTag->add(eTag); } } } tag.put(L"Entities", entitiesTag); if (dos != nullptr) NbtIo::write(&tag, dos); } void ConsoleSchematicFile::getBlocksAndData(LevelChunk* chunk, byteArray* data, int x0, int y0, int z0, int x1, int y1, int z1, int& blocksP, int& dataP, int& blockLightP, int& skyLightP) { // 4J Stu - Needs updated to work with higher worlds, should still work with // non-optimised version below // int xs = x1 - x0; // int ys = y1 - y0; // int zs = z1 - z0; // if (xs * ys * zs == LevelChunk::BLOCKS_LENGTH) //{ // byteArray blockData = byteArray(data->data + blocksP, // Level::CHUNK_TILE_COUNT); chunk->getBlockData(blockData); // blocksP += blockData.length; // byteArray dataData = byteArray(data->data + dataP, 16384); // chunk->getBlockLightData(dataData); // dataP += dataData.length; // byteArray blockLightData = byteArray(data->data + blockLightP, 16384); // chunk->getBlockLightData(blockLightData); // blockLightP += blockLightData.length; // byteArray skyLightData = byteArray(data->data + skyLightP, 16384); // chunk->getSkyLightData(skyLightData); // skyLightP += skyLightData.length; // return; //} bool bHasLower, bHasUpper; bHasLower = bHasUpper = false; int lowerY0, lowerY1, upperY0, upperY1; lowerY0 = upperY0 = y0; lowerY1 = upperY1 = y1; int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; if (y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { lowerY0 = y0; lowerY1 = std::min(y1, compressedHeight); bHasLower = true; } if (y1 >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { upperY0 = std::max(y0, compressedHeight) - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; upperY1 = y1 - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; bHasUpper = true; } byteArray blockData = byteArray(Level::CHUNK_TILE_COUNT); chunk->getBlockData(blockData); for (int x = x0; x < x1; x++) for (int z = z0; z < z1; z++) { if (bHasLower) { int slot = x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0; int len = lowerY1 - lowerY0; System::arraycopy(blockData, slot, data, blocksP, len); blocksP += len; } if (bHasUpper) { int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES; int len = upperY1 - upperY0; System::arraycopy(blockData, slot, data, blocksP, len); blocksP += len; } } delete blockData.data; byteArray dataData = byteArray(Level::CHUNK_TILE_COUNT); chunk->getDataData(dataData); for (int x = x0; x < x1; x++) for (int z = z0; z < z1; z++) { if (bHasLower) { int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; int len = (lowerY1 - lowerY0) / 2; System::arraycopy(dataData, slot, data, dataP, len); dataP += len; } if (bHasUpper) { int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES) >> 1; int len = (upperY1 - upperY0) / 2; System::arraycopy(dataData, slot, data, dataP, len); dataP += len; } } delete dataData.data; // 4J Stu - Allow ignoring light data if (blockLightP > -1) { byteArray blockLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); chunk->getBlockLightData(blockLightData); for (int x = x0; x < x1; x++) for (int z = z0; z < z1; z++) { if (bHasLower) { int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; int len = (lowerY1 - lowerY0) / 2; System::arraycopy(blockLightData, slot, data, blockLightP, len); blockLightP += len; } if (bHasUpper) { int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) >> 1) + (Level::COMPRESSED_CHUNK_SECTION_TILES / 2); int len = (upperY1 - upperY0) / 2; System::arraycopy(blockLightData, slot, data, blockLightP, len); blockLightP += len; } } delete blockLightData.data; } // 4J Stu - Allow ignoring light data if (skyLightP > -1) { byteArray skyLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); chunk->getSkyLightData(skyLightData); for (int x = x0; x < x1; x++) for (int z = z0; z < z1; z++) { if (bHasLower) { int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; int len = (lowerY1 - lowerY0) / 2; System::arraycopy(skyLightData, slot, data, skyLightP, len); skyLightP += len; } if (bHasUpper) { int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) >> 1) + (Level::COMPRESSED_CHUNK_SECTION_TILES / 2); int len = (upperY1 - upperY0) / 2; System::arraycopy(skyLightData, slot, data, skyLightP, len); skyLightP += len; } } delete skyLightData.data; } return; } void ConsoleSchematicFile::setBlocksAndData( LevelChunk* chunk, byteArray blockData, byteArray dataData, byteArray inputData, int x0, int y0, int z0, int x1, int y1, int z1, int& blocksP, int& dataP, int& blockLightP, int& skyLightP) { bool bHasLower, bHasUpper; bHasLower = bHasUpper = false; int lowerY0, lowerY1, upperY0, upperY1; lowerY0 = upperY0 = y0; lowerY1 = upperY1 = y1; int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; if (y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { lowerY0 = y0; lowerY1 = std::min(y1, compressedHeight); bHasLower = true; } if (y1 >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { upperY0 = std::max(y0, compressedHeight) - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; upperY1 = y1 - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; bHasUpper = true; } PIXBeginNamedEvent(0, "Applying block data"); for (int x = x0; x < x1; x++) for (int z = z0; z < z1; z++) { if (bHasLower) { int slot = x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0; int len = lowerY1 - lowerY0; System::arraycopy(inputData, blocksP, &blockData, slot, len); blocksP += len; } if (bHasUpper) { int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES; int len = upperY1 - upperY0; System::arraycopy(inputData, blocksP, &blockData, slot, len); blocksP += len; } } PIXEndNamedEvent(); PIXBeginNamedEvent(0, "Applying Data data"); for (int x = x0; x < x1; x++) for (int z = z0; z < z1; z++) { if (bHasLower) { int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; int len = (lowerY1 - lowerY0) / 2; System::arraycopy(inputData, dataP, &dataData, slot, len); dataP += len; } if (bHasUpper) { int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES) >> 1; int len = (upperY1 - upperY0) / 2; System::arraycopy(inputData, dataP, &dataData, slot, len); dataP += len; } } PIXEndNamedEvent(); // 4J Stu - Allow ignoring light data if (blockLightP > -1) { byteArray blockLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); chunk->getBlockLightData(blockLightData); for (int x = x0; x < x1; x++) for (int z = z0; z < z1; z++) { if (bHasLower) { int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; int len = (lowerY1 - lowerY0) / 2; System::arraycopy(inputData, blockLightP, &blockLightData, slot, len); blockLightP += len; } if (bHasUpper) { int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) >> 1) + (Level::COMPRESSED_CHUNK_SECTION_TILES / 2); int len = (upperY1 - upperY0) / 2; System::arraycopy(inputData, blockLightP, &blockLightData, slot, len); blockLightP += len; } } chunk->setBlockLightData(blockLightData); delete blockLightData.data; } // 4J Stu - Allow ignoring light data if (skyLightP > -1) { byteArray skyLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); chunk->getSkyLightData(skyLightData); for (int x = x0; x < x1; x++) for (int z = z0; z < z1; z++) { if (bHasLower) { int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; int len = (lowerY1 - lowerY0) / 2; System::arraycopy(inputData, skyLightP, &skyLightData, slot, len); skyLightP += len; } if (bHasUpper) { int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + (Level::COMPRESSED_CHUNK_SECTION_TILES / 2); int len = (upperY1 - upperY0) / 2; System::arraycopy(inputData, skyLightP, &skyLightData, slot, len); skyLightP += len; } } chunk->setSkyLightData(skyLightData); delete skyLightData.data; } } std::vector >* ConsoleSchematicFile::getTileEntitiesInRegion(LevelChunk* chunk, int x0, int y0, int z0, int x1, int y1, int z1) { std::vector >* result = new std::vector >; for (auto it = chunk->tileEntities.begin(); it != chunk->tileEntities.end(); ++it) { std::shared_ptr te = it->second; if (te->x >= x0 && te->y >= y0 && te->z >= z0 && te->x < x1 && te->y < y1 && te->z < z1) { result->push_back(te); } } return result; }