Overhauled Schematic Importing and Exporting #56
@@ -1,7 +1,6 @@
|
|||||||
package StevenDimDoors.mod_pocketDim.helpers;
|
package StevenDimDoors.mod_pocketDim.helpers;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -11,10 +10,6 @@ import java.util.Random;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.nbt.CompressedStreamTools;
|
|
||||||
import net.minecraft.nbt.NBTTagCompound;
|
|
||||||
import net.minecraft.nbt.NBTTagList;
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
|
||||||
import net.minecraft.util.WeightedRandom;
|
import net.minecraft.util.WeightedRandom;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import StevenDimDoors.mod_pocketDim.DDProperties;
|
import StevenDimDoors.mod_pocketDim.DDProperties;
|
||||||
@@ -23,6 +18,7 @@ import StevenDimDoors.mod_pocketDim.DungeonGenerator;
|
|||||||
import StevenDimDoors.mod_pocketDim.LinkData;
|
import StevenDimDoors.mod_pocketDim.LinkData;
|
||||||
import StevenDimDoors.mod_pocketDim.mod_pocketDim;
|
import StevenDimDoors.mod_pocketDim.mod_pocketDim;
|
||||||
import StevenDimDoors.mod_pocketDim.items.itemDimDoor;
|
import StevenDimDoors.mod_pocketDim.items.itemDimDoor;
|
||||||
|
import StevenDimDoors.mod_pocketDim.schematic.Schematic;
|
||||||
import StevenDimDoors.mod_pocketDim.util.WeightedContainer;
|
import StevenDimDoors.mod_pocketDim.util.WeightedContainer;
|
||||||
|
|
||||||
public class DungeonHelper
|
public class DungeonHelper
|
||||||
@@ -383,153 +379,13 @@ public class DungeonHelper
|
|||||||
|
|
||||||
public boolean exportDungeon(World world, int centerX, int centerY, int centerZ, String exportPath)
|
public boolean exportDungeon(World world, int centerX, int centerY, int centerZ, String exportPath)
|
||||||
{
|
{
|
||||||
int xMin, yMin, zMin;
|
|
||||||
int xMax, yMax, zMax;
|
|
||||||
int xStart, yStart, zStart;
|
|
||||||
int xEnd, yEnd, zEnd;
|
|
||||||
|
|
||||||
//Find the smallest bounding box that contains all non-air blocks within a max radius around the player.
|
|
||||||
xMax = yMax = zMax = Integer.MIN_VALUE;
|
|
||||||
xMin = yMin = zMin = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
xStart = centerX - MAX_EXPORT_RADIUS;
|
|
||||||
zStart = centerZ - MAX_EXPORT_RADIUS;
|
|
||||||
yStart = Math.max(centerY - MAX_EXPORT_RADIUS, 0);
|
|
||||||
|
|
||||||
xEnd = centerX + MAX_EXPORT_RADIUS;
|
|
||||||
zEnd = centerZ + MAX_EXPORT_RADIUS;
|
|
||||||
yEnd = Math.min(centerY + MAX_EXPORT_RADIUS, world.getHeight());
|
|
||||||
|
|
||||||
//This could be done more efficiently, but honestly, this is the simplest approach and it
|
|
||||||
//makes it easy for us to verify that the code is correct.
|
|
||||||
for (int y = yStart; y <= yEnd; y++)
|
|
||||||
{
|
|
||||||
for (int z = zStart; z <= zEnd; z++)
|
|
||||||
{
|
|
||||||
for (int x = xStart; x <= xEnd; x++)
|
|
||||||
{
|
|
||||||
if (!world.isAirBlock(x, y, z))
|
|
||||||
{
|
|
||||||
xMax = x > xMax ? x : xMax;
|
|
||||||
zMax = z > zMax ? z : zMax;
|
|
||||||
yMax = y > yMax ? y : yMax;
|
|
||||||
|
|
||||||
xMin = x < xMin ? x : xMin;
|
|
||||||
zMin = z < zMin ? z : zMin;
|
|
||||||
yMin = y < yMin ? y : yMin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Export all the blocks within our selected bounding box
|
|
||||||
short width = (short) (xMax - xMin + 1);
|
|
||||||
short height = (short) (yMax - yMin + 1);
|
|
||||||
short length = (short) (zMax - zMin + 1);
|
|
||||||
|
|
||||||
byte[] blocks = new byte[width * height * length];
|
|
||||||
byte[] addBlocks = null;
|
|
||||||
byte[] blockData = new byte[width * height * length];
|
|
||||||
NBTTagList tileEntities = new NBTTagList();
|
|
||||||
|
|
||||||
for (int y = 0; y < height; y++)
|
|
||||||
{
|
|
||||||
for (int z = 0; z < length; z++)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < width; x++)
|
|
||||||
{
|
|
||||||
int index = y * width * length + z * width + x;
|
|
||||||
int blockID = world.getBlockId(x + xMin, y + yMin, z + zMin);
|
|
||||||
int metadata = world.getBlockMetadata(x + xMin, y + yMin, z + zMin);
|
|
||||||
boolean changed = false;
|
|
||||||
|
|
||||||
if (blockID == properties.DimensionalDoorID)
|
|
||||||
{
|
|
||||||
blockID = Block.doorIron.blockID;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (blockID == properties.WarpDoorID)
|
|
||||||
{
|
|
||||||
blockID = Block.doorWood.blockID;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
//Map fabric of reality and permafabric blocks to standard export IDs
|
|
||||||
if (blockID == properties.FabricBlockID)
|
|
||||||
{
|
|
||||||
blockID = FABRIC_OF_REALITY_EXPORT_ID;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (blockID == properties.PermaFabricBlockID)
|
|
||||||
{
|
|
||||||
blockID = PERMAFABRIC_EXPORT_ID;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save 4096 IDs in an AddBlocks section
|
|
||||||
if (blockID > 255)
|
|
||||||
{
|
|
||||||
if (addBlocks == null)
|
|
||||||
{
|
|
||||||
//Lazily create section
|
|
||||||
addBlocks = new byte[(blocks.length >> 1) + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
addBlocks[index >> 1] = (byte) (((index & 1) == 0) ?
|
|
||||||
addBlocks[index >> 1] & 0xF0 | (blockID >> 8) & 0xF
|
|
||||||
: addBlocks[index >> 1] & 0xF | ((blockID >> 8) & 0xF) << 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks[index] = (byte) blockID;
|
|
||||||
blockData[index] = (byte) metadata;
|
|
||||||
|
|
||||||
//Obtain and export the tile entity of the current block, if any.
|
|
||||||
//Do not obtain a tile entity if the block was changed from its original ID.
|
|
||||||
//I'm not sure if this approach is the most efficient but it works. ~SenseiKiwi
|
|
||||||
TileEntity tileEntity = !changed ? world.getBlockTileEntity(x + xMin, y + yMin, z + zMin) : null;
|
|
||||||
|
|
||||||
if (tileEntity != null)
|
|
||||||
{
|
|
||||||
//Get the tile entity's description as a compound NBT tag
|
|
||||||
NBTTagCompound entityData = new NBTTagCompound();
|
|
||||||
tileEntity.writeToNBT(entityData);
|
|
||||||
//Change the tile entity's location to the schematic coordinate system
|
|
||||||
entityData.setInteger("x", x);
|
|
||||||
entityData.setInteger("y", y);
|
|
||||||
entityData.setInteger("z", z);
|
|
||||||
|
|
||||||
tileEntities.appendTag(entityData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Write NBT tags for schematic file
|
|
||||||
NBTTagCompound schematicTag = new NBTTagCompound("Schematic");
|
|
||||||
|
|
||||||
schematicTag.setShort("Width", width);
|
|
||||||
schematicTag.setShort("Length", length);
|
|
||||||
schematicTag.setShort("Height", height);
|
|
||||||
|
|
||||||
schematicTag.setByteArray("Blocks", blocks);
|
|
||||||
schematicTag.setByteArray("Data", blockData);
|
|
||||||
|
|
||||||
schematicTag.setTag("Entities", new NBTTagList());
|
|
||||||
schematicTag.setTag("TileEntities", tileEntities);
|
|
||||||
schematicTag.setString("Materials", "Alpha");
|
|
||||||
|
|
||||||
if (addBlocks != null)
|
|
||||||
{
|
|
||||||
schematicTag.setByteArray("AddBlocks", addBlocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Write schematic data to a file
|
//Write schematic data to a file
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileOutputStream outputStream = new FileOutputStream(new File(exportPath));
|
short size = (short) 2 * MAX_EXPORT_RADIUS + 1;
|
||||||
CompressedStreamTools.writeCompressed(schematicTag, outputStream);
|
Schematic schematic = Schematic.copyFromWorld(world,
|
||||||
//writeCompressed() probably closes the stream on its own - call close again just in case.
|
centerX - MAX_EXPORT_RADIUS, centerY - MAX_EXPORT_RADIUS, centerZ - MAX_EXPORT_RADIUS, size, size, size, true);
|
||||||
//Closing twice will not throw an exception.
|
schematic.writeToFile(exportPath);
|
||||||
outputStream.close();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package StevenDimDoors.mod_pocketDim.schematic;
|
||||||
|
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import StevenDimDoors.mod_pocketDim.Point3D;
|
||||||
|
|
||||||
|
public class CompactBoundsOperation extends WorldOperation
|
||||||
|
{
|
||||||
|
private int minX;
|
||||||
|
private int minY;
|
||||||
|
private int minZ;
|
||||||
|
private int maxX;
|
||||||
|
private int maxY;
|
||||||
|
private int maxZ;
|
||||||
|
|
||||||
|
public CompactBoundsOperation()
|
||||||
|
{
|
||||||
|
super("CompactBoundsOperation");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean start(World world, int x, int y, int z, int width, int height, int length)
|
||||||
|
{
|
||||||
|
minX = Integer.MAX_VALUE;
|
||||||
|
minY = Integer.MAX_VALUE;
|
||||||
|
minZ = Integer.MAX_VALUE;
|
||||||
|
maxX = x;
|
||||||
|
maxY = y;
|
||||||
|
maxZ = z;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean applyToBlock(World world, int x, int y, int z)
|
||||||
|
{
|
||||||
|
//This could be done more efficiently, but honestly, this is the simplest approach and it
|
||||||
|
//makes it easy for us to verify that the code is correct.
|
||||||
|
if (!world.isAirBlock(x, y, z))
|
||||||
|
{
|
||||||
|
maxX = x > maxX ? x : maxX;
|
||||||
|
maxZ = z > maxZ ? z : maxZ;
|
||||||
|
maxY = y > maxY ? y : maxY;
|
||||||
|
|
||||||
|
minX = x < minX ? x : minX;
|
||||||
|
minZ = z < minZ ? z : minZ;
|
||||||
|
minY = y < minY ? y : minY;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean finish()
|
||||||
|
{
|
||||||
|
if (minX == Integer.MAX_VALUE)
|
||||||
|
{
|
||||||
|
//The whole search space was empty!
|
||||||
|
//Compact the space to a single block.
|
||||||
|
minX = maxX;
|
||||||
|
minY = maxY;
|
||||||
|
minZ = maxZ;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Point3D getMaxCorner()
|
||||||
|
{
|
||||||
|
return new Point3D(maxX, maxY, maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Point3D getMinCorner()
|
||||||
|
{
|
||||||
|
return new Point3D(minX, minY, minZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
183
StevenDimDoors/mod_pocketDim/schematic/Schematic.java
Normal file
183
StevenDimDoors/mod_pocketDim/schematic/Schematic.java
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package StevenDimDoors.mod_pocketDim.schematic;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
|
import net.minecraft.nbt.NBTTagList;
|
||||||
|
import net.minecraft.tileentity.TileEntity;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
public class WorldCopyOperation extends WorldOperation
|
||||||
|
{
|
||||||
|
private int originX;
|
||||||
|
private int originY;
|
||||||
|
private int originZ;
|
||||||
|
private int index;
|
||||||
|
private short[] blockIDs;
|
||||||
|
private byte[] metadata;
|
||||||
|
private NBTTagList tileEntities;
|
||||||
|
|
||||||
|
public WorldCopyOperation()
|
||||||
|
{
|
||||||
|
super("WorldCopyOperation");
|
||||||
|
blockIDs = null;
|
||||||
|
metadata = null;
|
||||||
|
tileEntities = new NBTTagList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean start(World world, int x, int y, int z, int width, int height, int length)
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
originX = x;
|
||||||
|
originY = y;
|
||||||
|
originZ = z;
|
||||||
|
blockIDs = new short[width * height * length];
|
||||||
|
metadata = new byte[width * height * length];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean applyToBlock(World world, int x, int y, int z)
|
||||||
|
{
|
||||||
|
blockIDs[index] = (short) world.getBlockId(x, y, z);
|
||||||
|
metadata[index] = (byte) world.getBlockMetadata(x, y, z);
|
||||||
|
|
||||||
|
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
|
||||||
|
if (tileEntity != null)
|
||||||
|
{
|
||||||
|
//Extract tile entity data
|
||||||
|
NBTTagCompound tileTag = new NBTTagCompound();
|
||||||
|
tileEntity.writeToNBT(tileTag);
|
||||||
|
//Translate the tile entity's position from the world's coordinate system
|
||||||
|
//to the schematic's coordinate system.
|
||||||
|
tileTag.setInteger("x", x - originX);
|
||||||
|
tileTag.setInteger("y", y - originY);
|
||||||
|
tileTag.setInteger("z", z - originZ);
|
||||||
|
tileEntities.appendTag(tileTag);
|
||||||
|
}
|
||||||
|
index++; //This works assuming the loops in WorldOperation are done in YZX order
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short[] getBlockIDs()
|
||||||
|
{
|
||||||
|
return blockIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getMetadata()
|
||||||
|
{
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NBTTagList getTileEntities()
|
||||||
|
{
|
||||||
|
return tileEntities;
|
||||||
|
}
|
||||||
|
}
|
||||||
64
StevenDimDoors/mod_pocketDim/schematic/WorldOperation.java
Normal file
64
StevenDimDoors/mod_pocketDim/schematic/WorldOperation.java
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package StevenDimDoors.mod_pocketDim.schematic;
|
||||||
|
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
public abstract class WorldOperation {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public WorldOperation(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean start(World world, int x, int y, int z, int width, int height, int length)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean applyToBlock(World world, int x, int y, int z);
|
||||||
|
|
||||||
|
protected boolean finish()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int cx, cy, cz;
|
||||||
|
int limitX = x + width;
|
||||||
|
int limitY = y + height;
|
||||||
|
int limitZ = z + length;
|
||||||
|
|
||||||
|
//The order of these loops is important. Don't change it! It's used to avoid calculating
|
||||||
|
//indeces in some schematic operations. The proper order is YZX.
|
||||||
|
for (cy = y; cy < limitY; cy++)
|
||||||
|
{
|
||||||
|
for (cz = z; cz < limitZ; cz++)
|
||||||
|
{
|
||||||
|
for (cx = x; cx < limitX; cx++)
|
||||||
|
{
|
||||||
|
if (!applyToBlock(world, cx, cy, cz))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user