DimDoors/StevenDimDoors/mod_pocketDim/schematic/Schematic.java
SenseiKiwi 210c791af4 Created Schematic Class
Separated part of the dungeon-exporting logic into a new class:
Schematic. Also created a few classes to implement important operations.
Some parts of the original exporting logic are missing now, such as
mapping certain blocks to standard IDs. That will be reimplemented in
the future. Importing schematics is not supported yet. I need to test
what's done so far.
2013-07-27 06:52:30 -04:00

183 lines
5.6 KiB
Java

package StevenDimDoors.mod_pocketDim.schematic;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import StevenDimDoors.mod_pocketDim.Point3D;
public class Schematic {
protected short width;
protected short height;
protected short length;
protected short[] blocks;
protected byte[] metadata;
protected NBTTagList tileEntities = new NBTTagList();
protected Schematic(short width, short height, short length, short[] blocks, byte[] metadata, NBTTagList tileEntities)
{
this.width = width;
this.height = height;
this.length = length;
this.blocks = blocks;
this.metadata = metadata;
this.tileEntities = tileEntities;
}
private int calculateIndex(int x, int y, int z)
{
if (x < 0 || x >= width)
throw new IndexOutOfBoundsException("x must be non-negative and less than width");
if (y < 0 || y >= height)
throw new IndexOutOfBoundsException("y must be non-negative and less than height");
if (z < 0 || z >= length)
throw new IndexOutOfBoundsException("z must be non-negative and less than length");
return (y * width * length + z * width + x);
}
public short getBlockID(int x, int y, int z)
{
return blocks[calculateIndex(x, y, z)];
}
public byte getBlockMetadata(int x, int y, int z)
{
return metadata[calculateIndex(x, y, z)];
}
public NBTTagList getTileEntities()
{
return (NBTTagList) tileEntities.copy();
}
public static Schematic readFromFile()
{
throw new UnsupportedOperationException();
}
public static Schematic copyFromWorld(World world, int x, int y, int z, short width, short height, short length, boolean doCompactBounds)
{
if (doCompactBounds)
{
//Adjust the vertical bounds to reasonable values if necessary
int worldHeight = world.getHeight();
int fixedY = (y < 0) ? 0 : y;
int fixedHeight = height + y - fixedY;
if (fixedHeight + fixedY >= worldHeight)
{
fixedHeight = worldHeight - fixedY;
}
//Compact the area to be copied to remove empty borders
CompactBoundsOperation compactor = new CompactBoundsOperation();
compactor.apply(world, x, fixedY, z, width, fixedHeight, length);
Point3D minCorner = compactor.getMinCorner();
Point3D maxCorner = compactor.getMaxCorner();
short compactWidth = (short) (maxCorner.getX() - minCorner.getX() + 1);
short compactHeight = (short) (maxCorner.getY() - minCorner.getY() + 1);
short compactLength = (short) (maxCorner.getZ() - minCorner.getZ() + 1);
return copyFromWorld(world, minCorner.getX(), minCorner.getY(), minCorner.getZ(),
compactWidth, compactHeight, compactLength);
}
else
{
return copyFromWorld(world, x, y, z, width, height, length);
}
}
private static Schematic copyFromWorld(World world, int x, int y, int z, short width, short height, short length)
{
//Short and sweet ^_^
WorldCopyOperation copier = new WorldCopyOperation();
copier.apply(world, x, y, z, width, height, length);
return new Schematic(width, height, length, copier.getBlockIDs(), copier.getMetadata(), copier.getTileEntities());
}
private static boolean encodeBlockIDs(short[] blocks, byte[] lowBits, byte[] highBits)
{
int index;
int length = blocks.length - (blocks.length & 1);
boolean hasHighBits = false;
for (index = 0; index < length; index += 2)
{
highBits[index >> 1] = (byte) (((blocks[index] >> 8) & 0x0F) + ((blocks[index + 1] >> 4) & 0xF0));
hasHighBits |= (highBits[index >> 1] != 0);
}
if (index < blocks.length)
{
highBits[index >> 1] = (byte) ((blocks[index] >> 8) & 0x0F);
hasHighBits |= (highBits[index >> 1] != 0);
}
return hasHighBits;
}
public NBTTagCompound writeToNBT()
{
return writeToNBT(true);
}
private NBTTagCompound writeToNBT(boolean copyTileEntities)
{
//This is the main storage function. Schematics are really compressed NBT tags, so if we can generate
//the tags, most of the work is done. All the other storage functions will rely on this one.
NBTTagCompound schematicTag = new NBTTagCompound("Schematic");
schematicTag.setShort("Width", width);
schematicTag.setShort("Length", length);
schematicTag.setShort("Height", height);
schematicTag.setTag("Entities", new NBTTagList());
schematicTag.setString("Materials", "Alpha");
byte[] lowBytes = new byte[blocks.length];
byte[] highBytes = new byte[(blocks.length >> 1) + 1];
boolean hasExtendedIDs = encodeBlockIDs(blocks, lowBytes, highBytes);
schematicTag.setByteArray("Blocks", lowBytes);
schematicTag.setByteArray("Data", metadata);
if (hasExtendedIDs)
{
schematicTag.setByteArray("AddBlocks", highBytes);
}
if (copyTileEntities)
{
//Used when the result of this function will be passed outside this class.
//Avoids exposing the private field to external modifications.
schematicTag.setTag("TileEntities", (NBTTagList) tileEntities.copy());
}
else
{
//Used when the result of this function is for internal use.
//It's more efficient not to copy the tags unless it's needed.
schematicTag.setTag("TileEntities", tileEntities);
}
return schematicTag;
}
public void writeToFile(String schematicPath) throws IOException
{
writeToFile(new File(schematicPath));
}
public void writeToFile(File schematicFile) throws IOException
{
FileOutputStream outputStream = new FileOutputStream(schematicFile);
CompressedStreamTools.writeCompressed(writeToNBT(false), outputStream);
//writeCompressed() probably closes the stream on its own - call close again just in case.
//Closing twice will not throw an exception.
outputStream.close();
}
}