Improved Schematic Class

Improved the Schematic class. Added support for reading schematic files.
That functionality still needs to be integrated and tested. Made minor
changes to WorldOperation and its derived classes. Also fixed a few bugs
in Schematic's export functions that were found during testing. The
exporting code still doesn't do all the changes (e.g. block
standardization) that had been implemented before.
This commit is contained in:
SenseiKiwi 2013-07-27 17:29:55 -04:00
parent 210c791af4
commit 77efb63a5f
5 changed files with 212 additions and 36 deletions

View file

@ -18,7 +18,7 @@ public class CompactBoundsOperation extends WorldOperation
}
@Override
protected boolean start(World world, int x, int y, int z, int width, int height, int length)
protected boolean initialize(World world, int x, int y, int z, int width, int height, int length)
{
minX = Integer.MAX_VALUE;
minY = Integer.MAX_VALUE;

View file

@ -0,0 +1,21 @@
package StevenDimDoors.mod_pocketDim.schematic;
public class InvalidSchematicException extends Exception {
private static final long serialVersionUID = -1011044077455149932L;
public InvalidSchematicException()
{
super();
}
public InvalidSchematicException(String message)
{
super(message);
}
public InvalidSchematicException(String message, Throwable cause)
{
super(message, cause);
}
}

View file

@ -1,15 +1,23 @@
package StevenDimDoors.mod_pocketDim.schematic;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import StevenDimDoors.mod_pocketDim.Point3D;
/**
* Represents an MC schematic and provides functions for loading, storing, and manipulating schematics.
* This functionality has no dependencies to Dimensional Doors.
*/
public class Schematic {
protected short width;
@ -57,9 +65,115 @@ public class Schematic {
return (NBTTagList) tileEntities.copy();
}
public static Schematic readFromFile()
public static Schematic readFromFile(String schematicPath) throws FileNotFoundException, InvalidSchematicException
{
throw new UnsupportedOperationException();
return readFromFile(new File(schematicPath));
}
public static Schematic readFromFile(File schematicFile) throws FileNotFoundException, InvalidSchematicException
{
return readFromStream(new FileInputStream(schematicFile));
}
public static Schematic readFromResource(String resourcePath) throws InvalidSchematicException
{
//We need an instance of a class in the mod to retrieve a resource
Schematic empty = new Schematic((short) 0, (short) 0, (short) 0, null, null, null);
InputStream schematicStream = empty.getClass().getResourceAsStream(resourcePath);
return readFromStream(schematicStream);
}
private static Schematic readFromStream(InputStream schematicStream) throws InvalidSchematicException
{
short width;
short height;
short length;
int volume;
int pairs;
byte[] metadata = null; //block metadata
byte[] lowBits = null; //first 8 bits of the block IDs
byte[] highBits = null; //additional 4 bits of the block IDs
short[] blocks = null; //list of combined block IDs
NBTTagList tileEntities = null; //storage for tile entities in NBT form
NBTTagCompound schematicTag; //the NBT data extracted from the schematic file
boolean hasExtendedBlockIDs; //indicates whether the schematic contains extended block IDs
try
{
try
{
schematicTag = CompressedStreamTools.readCompressed(schematicStream);
schematicStream.close(); //readCompressed() probably closes the stream anyway, but close again to be sure.
}
catch (Exception ex)
{
throw new InvalidSchematicException("The schematic could not be decoded.");
}
//load size of schematic to generate
width = schematicTag.getShort("Width");
height = schematicTag.getShort("Height");
length = schematicTag.getShort("Length");
volume = width * length * height;
if (width < 0)
throw new InvalidSchematicException("The schematic cannot have a negative width.");
if (height < 0)
throw new InvalidSchematicException("The schematic cannot have a negative height.");
if (length < 0)
throw new InvalidSchematicException("The schematic cannot have a negative length.");
//load block info
lowBits = schematicTag.getByteArray("Blocks");
highBits = schematicTag.getByteArray("AddBlocks");
metadata = schematicTag.getByteArray("Data");
hasExtendedBlockIDs = (highBits.length != 0);
if (volume != lowBits.length)
throw new InvalidSchematicException("The schematic has data for fewer blocks than its dimensions indicate.");
if (volume != metadata.length)
throw new InvalidSchematicException("The schematic has metadata for fewer blocks than its dimensions indicate.");
if (volume > 2 * highBits.length && highBits.length != 0)
throw new InvalidSchematicException("The schematic has extended block IDs for fewer blocks than its dimensions indicate.");
blocks = new short[volume];
if (hasExtendedBlockIDs)
{
//Combine the split block IDs into a single value
pairs = volume - (volume & 1);
int index;
for (index = 0; index < pairs; index += 2)
{
blocks[index] = (short) (((highBits[index >> 1] & 0x0F) << 8) + lowBits[index]);
blocks[index + 1] = (short) (((highBits[index >> 1] & 0xF0) << 4) + lowBits[index + 1]);
}
if (index < volume)
{
blocks[index] = lowBits[index >> 1];
}
}
else
{
//Copy the blockIDs
for (int index = 0; index < volume; index++)
blocks[index] = lowBits[index];
}
//Get the list of tile entities
tileEntities = schematicTag.getTagList("TileEntities");
return new Schematic(width, height, length, blocks, metadata, tileEntities);
}
catch (InvalidSchematicException ex)
{
//Throw the exception again to pass it to the caller.
throw ex;
}
catch (Exception ex)
{
throw new InvalidSchematicException("An unexpected error occurred while trying to decode the schematic.", ex);
}
}
public static Schematic copyFromWorld(World world, int x, int y, int z, short width, short height, short length, boolean doCompactBounds)
@ -118,6 +232,10 @@ public class Schematic {
highBits[index >> 1] = (byte) ((blocks[index] >> 8) & 0x0F);
hasHighBits |= (highBits[index >> 1] != 0);
}
for (index = 0; index < blocks.length; index++)
{
lowBits[index] = (byte) (blocks[index] & 0xFF);
}
return hasHighBits;
}
@ -140,16 +258,16 @@ public class Schematic {
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);
byte[] lowBits = new byte[blocks.length];
byte[] highBits = new byte[(blocks.length >> 1) + 1];
boolean hasExtendedIDs = encodeBlockIDs(blocks, lowBits, highBits);
schematicTag.setByteArray("Blocks", lowBytes);
schematicTag.setByteArray("Blocks", lowBits);
schematicTag.setByteArray("Data", metadata);
if (hasExtendedIDs)
{
schematicTag.setByteArray("AddBlocks", highBytes);
schematicTag.setByteArray("AddBlocks", highBits);
}
if (copyTileEntities)
@ -180,4 +298,40 @@ public class Schematic {
//Closing twice will not throw an exception.
outputStream.close();
}
public void copyToWorld(World world, int x, int y, int z)
{
int index;
int count;
int dx, dy, dz;
//Copy blocks and metadata into the world
index = 0;
for (dy = 0; dy < height; dy++)
{
for (dz = 0; dz < length; dz++)
{
for (dx = 0; dx < width; dx++)
{
world.setBlock(x + dx, y + dy, z + dz, blocks[index], metadata[index], 3);
index++;
}
}
}
//Copy tile entities into the world
count = tileEntities.tagCount();
for (index = 0; index < count; index++)
{
NBTTagCompound tileTag = (NBTTagCompound) tileEntities.tagAt(index);
//Rewrite its location to be in world coordinates
dx = tileTag.getInteger("x") + x;
dy = tileTag.getInteger("y") + y;
dz = tileTag.getInteger("z") + z;
tileTag.setInteger("x", dx);
tileTag.setInteger("y", dy);
tileTag.setInteger("z", dz);
//Load the tile entity and put it in the world
world.setBlockTileEntity(dx, dy, dz, TileEntity.createAndLoadEntity(tileTag));
}
}
}

View file

@ -20,11 +20,11 @@ public class WorldCopyOperation extends WorldOperation
super("WorldCopyOperation");
blockIDs = null;
metadata = null;
tileEntities = new NBTTagList();
tileEntities = null;
}
@Override
protected boolean start(World world, int x, int y, int z, int width, int height, int length)
protected boolean initialize(World world, int x, int y, int z, int width, int height, int length)
{
index = 0;
originX = x;
@ -32,6 +32,7 @@ public class WorldCopyOperation extends WorldOperation
originZ = z;
blockIDs = new short[width * height * length];
metadata = new byte[width * height * length];
tileEntities = new NBTTagList();
return true;
}

View file

@ -11,7 +11,7 @@ public abstract class WorldOperation {
this.name = name;
}
protected boolean start(World world, int x, int y, int z, int width, int height, int length)
protected boolean initialize(World world, int x, int y, int z, int width, int height, int length)
{
return true;
}
@ -25,7 +25,7 @@ public abstract class WorldOperation {
public boolean apply(World world, int x, int y, int z, int width, int height, int length)
{
if (!start(world, x, y, z, width, height, length))
if (!initialize(world, x, y, z, width, height, length))
return false;
int cx, cy, cz;