diff options
Diffstat (limited to 'Minecraft.Client/Common/GameRules/ConsoleSchematicFile.cpp')
| -rw-r--r-- | Minecraft.Client/Common/GameRules/ConsoleSchematicFile.cpp | 1020 |
1 files changed, 1020 insertions, 0 deletions
diff --git a/Minecraft.Client/Common/GameRules/ConsoleSchematicFile.cpp b/Minecraft.Client/Common/GameRules/ConsoleSchematicFile.cpp new file mode 100644 index 00000000..4a4e27b2 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/ConsoleSchematicFile.cpp @@ -0,0 +1,1020 @@ +#include "stdafx.h" +#include <vector> +#include "..\..\..\Minecraft.World\com.mojang.nbt.h" +#include "..\..\..\Minecraft.World\System.h" +#include "ConsoleSchematicFile.h" +#include "..\..\..\Minecraft.World\InputOutputStream.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\..\..\Minecraft.World\compression.h" + +ConsoleSchematicFile::ConsoleSchematicFile() +{ + m_xSize = m_ySize = m_zSize = 0; + m_refCount = 1; + m_data.data = NULL; +} + +ConsoleSchematicFile::~ConsoleSchematicFile() +{ + app.DebugPrintf("Deleting schematic file\n"); + if(m_data.data != NULL) delete [] m_data.data; +} + +void ConsoleSchematicFile::save(DataOutputStream *dos) +{ + if(dos != NULL) + { + 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 BYTE[ 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 != NULL) + { + // 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 != NULL) + { + delete [] m_data.data; + m_data.data = NULL; + } + + 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 // + CompoundTag *tag = NbtIo::read(dis); + ListTag<CompoundTag> *tileEntityTags = (ListTag<CompoundTag> *) tag->getList(L"TileEntities"); + if (tileEntityTags != NULL) + { + for (int i = 0; i < tileEntityTags->size(); i++) + { + CompoundTag *teTag = tileEntityTags->get(i); + shared_ptr<TileEntity> te = TileEntity::loadStatic(teTag); + + if(te == NULL) + { +#ifndef _CONTENT_PACKAGE + app.DebugPrintf("ConsoleSchematicFile has read a NULL tile entity\n"); + __debugbreak(); +#endif + } + else + { + m_tileEntities.push_back(te); + } + } + } + ListTag<CompoundTag> *entityTags = (ListTag<CompoundTag> *) tag->getList(L"Entities"); + if (entityTags != NULL) + { + for (int i = 0; i < entityTags->size(); i++) + { + CompoundTag *eTag = entityTags->get(i); + eINSTANCEOF type = EntityIO::getType(eTag->getString(L"id")); + ListTag<DoubleTag> *pos = (ListTag<DoubleTag> *) eTag->getList(L"Pos"); + + double x = pos->get(0)->data; + double y = pos->get(1)->data; + double z = 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( pair<Vec3 *, CompoundTag *>(Vec3::newPermanent(x,y,z),(CompoundTag *)eTag->copy())); + } + } + delete tag; + } +} + +void ConsoleSchematicFile::save_tags(DataOutputStream *dos) +{ + CompoundTag *tag = new CompoundTag(); + + ListTag<CompoundTag> *tileEntityTags = new ListTag<CompoundTag>(); + tag->put(L"TileEntities", tileEntityTags); + + for (AUTO_VAR(it, m_tileEntities.begin()); it != m_tileEntities.end(); it++) + { + CompoundTag *cTag = new CompoundTag(); + (*it)->save(cTag); + tileEntityTags->add(cTag); + } + + ListTag<CompoundTag> *entityTags = new ListTag<CompoundTag>(); + tag->put(L"Entities", entityTags); + + for (AUTO_VAR(it, m_entities.begin()); it != m_entities.end(); it++) + entityTags->add( (CompoundTag *)(*it).second->copy() ); + + NbtIo::write(tag,dos); + delete tag; +} + +__int64 ConsoleSchematicFile::applyBlocksAndData(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot) +{ + int xStart = max(destinationBox->x0, (double)chunk->x*16); + int xEnd = min(destinationBox->x1, (double)((xStart>>4)<<4) + 16); + + int yStart = destinationBox->y0; + int yEnd = destinationBox->y1; + if(yEnd > Level::maxBuildHeight) yEnd = Level::maxBuildHeight; + + int zStart = max(destinationBox->z0, (double)chunk->z*16); + int zEnd = min(destinationBox->z1, (double)((zStart>>4)<<4) + 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; + chunk->recalcHeightmapOnly(); + PIXBeginNamedEvent(0,"Setting Data data"); + chunk->setDataData(dataData); + PIXEndNamedEvent(); + delete dataData.data; + + // 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 ConsoleSchematicFile::applyLighting(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot) +{ + int xStart = max(destinationBox->x0, (double)chunk->x*16); + int xEnd = min(destinationBox->x1, (double)((xStart>>4)<<4) + 16); + + int yStart = destinationBox->y0; + int yEnd = destinationBox->y1; + if(yEnd > Level::maxBuildHeight) yEnd = Level::maxBuildHeight; + + int zStart = max(destinationBox->z0, (double)chunk->z*16); + int zEnd = min(destinationBox->z1, (double)((zStart>>4)<<4) + 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_VAR(it, m_tileEntities.begin()); it != m_tileEntities.end();++it) + { + shared_ptr<TileEntity> 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 = Vec3::newTemp(targetX,targetY,targetZ); + if( chunkBox->containsIncludingLowerBound(pos) ) + { + shared_ptr<TileEntity> teCopy = chunk->getTileEntity( (int)targetX & 15, (int)targetY & 15, (int)targetZ & 15 ); + + if ( teCopy != NULL ) + { + 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_VAR(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 = Vec3::newTemp(targetX+0.01,targetY+0.01,targetZ+0.01); + if( !chunkBox->containsIncludingLowerBound(pos) ) + { + ++it; + continue; + } + + CompoundTag *eTag = it->second; + shared_ptr<Entity> e = EntityIO::loadStatic(eTag, NULL); + + if( e->GetType() == eTYPE_PAINTING ) + { + shared_ptr<Painting> painting = dynamic_pointer_cast<Painting>(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 ) + { + shared_ptr<ItemFrame> frame = dynamic_pointer_cast<ItemFrame>(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 != NULL) dos->writeInt(XBOX_SCHEMATIC_CURRENT_VERSION); + + if(dos != NULL) dos->writeByte(compressionType); + + //Write xSize + if(dos != NULL) dos->writeInt(xSize); + + //Write ySize + if(dos != NULL) dos->writeInt(ySize); + + //Write zSize + if(dos != NULL) 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 != NULL) dos->writeInt(inputSize); + if(dos != NULL) dos->write(buffer); + delete [] buffer.data; + + CompoundTag tag; + ListTag<CompoundTag> *tileEntitiesTag = new ListTag<CompoundTag>(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++) + { + vector<shared_ptr<TileEntity> > *tileEntities = getTileEntitiesInRegion(level->getChunk(xc, zc), xStart, yStart, zStart, xStart + xSize, yStart + ySize, zStart + zSize); + for(AUTO_VAR(it, tileEntities->begin()); it != tileEntities->end(); ++it) + { + shared_ptr<TileEntity> te = *it; + CompoundTag *teTag = new CompoundTag(); + shared_ptr<TileEntity> 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 = AABB::newTemp(xStart,yStart,zStart,xEnd,yEnd,zEnd); + vector<shared_ptr<Entity> > *entities = level->getEntities(nullptr, bb); + ListTag<CompoundTag> *entitiesTag = new ListTag<CompoundTag>(L"entities"); + + for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) + { + shared_ptr<Entity> e = *it; + + bool mobCanBeSaved = false; + if(bSaveMobs) + { + if( ( e->GetType() & eTYPE_MONSTER ) || ( e->GetType() & eTYPE_WATERANIMAL ) || ( e->GetType() & eTYPE_ANIMAL ) || + ( e->GetType() == eTYPE_CHICKEN ) || ( e->GetType() == eTYPE_WOLF ) || ( e->GetType() == eTYPE_VILLAGER) || ( e->GetType() == eTYPE_MUSHROOMCOW ) ) + { + mobCanBeSaved = true; + } + } + if(mobCanBeSaved || e->GetType() == eTYPE_MINECART || e->GetType() == eTYPE_BOAT || e->GetType() == eTYPE_PAINTING || e->GetType() == eTYPE_ITEM_FRAME) + { + CompoundTag *eTag = new CompoundTag(); + if( e->save(eTag) ) + { + ListTag<DoubleTag> *pos = (ListTag<DoubleTag> *) eTag->getList(L"Pos"); + + pos->get(0)->data -= xStart; + pos->get(1)->data -= yStart; + pos->get(2)->data -= zStart; + + if( e->GetType() == eTYPE_PAINTING || e->GetType() == eTYPE_ITEM_FRAME ) + { + ((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 != NULL) 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 = min(y1, compressedHeight); + bHasLower = true; + } + if(y1 >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + upperY0 = 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 = min(y1, compressedHeight); + bHasLower = true; + } + if(y1 >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + upperY0 = 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; + } +} + +vector<shared_ptr<TileEntity> > *ConsoleSchematicFile::getTileEntitiesInRegion(LevelChunk *chunk, int x0, int y0, int z0, int x1, int y1, int z1) +{ + vector<shared_ptr<TileEntity> > *result = new vector<shared_ptr<TileEntity> >; + for (AUTO_VAR(it, chunk->tileEntities.begin()); it != chunk->tileEntities.end(); ++it) + { + shared_ptr<TileEntity> 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; +} |
