Overhauled Schematic Importing and Exporting #56
@@ -18,7 +18,7 @@ public class CompactBoundsOperation extends WorldOperation
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
minX = Integer.MAX_VALUE;
|
||||||
minY = Integer.MAX_VALUE;
|
minY = Integer.MAX_VALUE;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
package StevenDimDoors.mod_pocketDim.schematic;
|
package StevenDimDoors.mod_pocketDim.schematic;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
import net.minecraft.nbt.CompressedStreamTools;
|
import net.minecraft.nbt.CompressedStreamTools;
|
||||||
import net.minecraft.nbt.NBTTagCompound;
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
import net.minecraft.nbt.NBTTagList;
|
import net.minecraft.nbt.NBTTagList;
|
||||||
|
import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import StevenDimDoors.mod_pocketDim.Point3D;
|
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 {
|
public class Schematic {
|
||||||
|
|
||||||
protected short width;
|
protected short width;
|
||||||
@@ -57,9 +65,115 @@ public class Schematic {
|
|||||||
return (NBTTagList) tileEntities.copy();
|
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)
|
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);
|
highBits[index >> 1] = (byte) ((blocks[index] >> 8) & 0x0F);
|
||||||
hasHighBits |= (highBits[index >> 1] != 0);
|
hasHighBits |= (highBits[index >> 1] != 0);
|
||||||
}
|
}
|
||||||
|
for (index = 0; index < blocks.length; index++)
|
||||||
|
{
|
||||||
|
lowBits[index] = (byte) (blocks[index] & 0xFF);
|
||||||
|
}
|
||||||
return hasHighBits;
|
return hasHighBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,16 +258,16 @@ public class Schematic {
|
|||||||
schematicTag.setTag("Entities", new NBTTagList());
|
schematicTag.setTag("Entities", new NBTTagList());
|
||||||
schematicTag.setString("Materials", "Alpha");
|
schematicTag.setString("Materials", "Alpha");
|
||||||
|
|
||||||
byte[] lowBytes = new byte[blocks.length];
|
byte[] lowBits = new byte[blocks.length];
|
||||||
byte[] highBytes = new byte[(blocks.length >> 1) + 1];
|
byte[] highBits = new byte[(blocks.length >> 1) + 1];
|
||||||
boolean hasExtendedIDs = encodeBlockIDs(blocks, lowBytes, highBytes);
|
boolean hasExtendedIDs = encodeBlockIDs(blocks, lowBits, highBits);
|
||||||
|
|
||||||
schematicTag.setByteArray("Blocks", lowBytes);
|
schematicTag.setByteArray("Blocks", lowBits);
|
||||||
schematicTag.setByteArray("Data", metadata);
|
schematicTag.setByteArray("Data", metadata);
|
||||||
|
|
||||||
if (hasExtendedIDs)
|
if (hasExtendedIDs)
|
||||||
{
|
{
|
||||||
schematicTag.setByteArray("AddBlocks", highBytes);
|
schematicTag.setByteArray("AddBlocks", highBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copyTileEntities)
|
if (copyTileEntities)
|
||||||
@@ -180,4 +298,40 @@ public class Schematic {
|
|||||||
//Closing twice will not throw an exception.
|
//Closing twice will not throw an exception.
|
||||||
outputStream.close();
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ public class WorldCopyOperation extends WorldOperation
|
|||||||
super("WorldCopyOperation");
|
super("WorldCopyOperation");
|
||||||
blockIDs = null;
|
blockIDs = null;
|
||||||
metadata = null;
|
metadata = null;
|
||||||
tileEntities = new NBTTagList();
|
tileEntities = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
index = 0;
|
||||||
originX = x;
|
originX = x;
|
||||||
@@ -32,6 +32,7 @@ public class WorldCopyOperation extends WorldOperation
|
|||||||
originZ = z;
|
originZ = z;
|
||||||
blockIDs = new short[width * height * length];
|
blockIDs = new short[width * height * length];
|
||||||
metadata = new byte[width * height * length];
|
metadata = new byte[width * height * length];
|
||||||
|
tileEntities = new NBTTagList();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public abstract class WorldOperation {
|
|||||||
this.name = name;
|
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;
|
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)
|
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;
|
return false;
|
||||||
|
|
||||||
int cx, cy, cz;
|
int cx, cy, cz;
|
||||||
|
|||||||
Reference in New Issue
Block a user