Overhauled Schematic Importing and Exporting #56

Merged
SenseiKiwi merged 8 commits from master into master 2013-07-30 22:29:38 +00:00
8 changed files with 446 additions and 10 deletions
Showing only changes of commit 877678c945 - Show all commits

View File

@@ -1,13 +1,17 @@
package StevenDimDoors.mod_pocketDim.commands; package StevenDimDoors.mod_pocketDim.commands;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.util.Collection; import java.util.Collection;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import StevenDimDoors.mod_pocketDim.DDProperties;
import StevenDimDoors.mod_pocketDim.DungeonGenerator; import StevenDimDoors.mod_pocketDim.DungeonGenerator;
import StevenDimDoors.mod_pocketDim.LinkData; import StevenDimDoors.mod_pocketDim.LinkData;
import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic;
import StevenDimDoors.mod_pocketDim.helpers.DungeonHelper; import StevenDimDoors.mod_pocketDim.helpers.DungeonHelper;
import StevenDimDoors.mod_pocketDim.helpers.dimHelper; import StevenDimDoors.mod_pocketDim.helpers.dimHelper;
import StevenDimDoors.mod_pocketDim.schematic.InvalidSchematicException;
public class CommandCreateDungeonRift extends DDCommandBase public class CommandCreateDungeonRift extends DDCommandBase
{ {
@@ -81,6 +85,25 @@ public class CommandCreateDungeonRift extends DDCommandBase
link = dimHelper.instance.createPocket(link, true, true); link = dimHelper.instance.createPocket(link, true, true);
dimHelper.dimList.get(link.destDimID).dungeonGenerator = result; dimHelper.dimList.get(link.destDimID).dungeonGenerator = result;
sender.sendChatToPlayer("Created a rift to \"" + getSchematicName(result) + "\" dungeon (Dimension ID = " + link.destDimID + ")."); sender.sendChatToPlayer("Created a rift to \"" + getSchematicName(result) + "\" dungeon (Dimension ID = " + link.destDimID + ").");
/*try {
DungeonSchematic dungeon;
if ((new File(result.schematicPath)).exists())
{
dungeon = DungeonSchematic.readFromFile(result.schematicPath);
}
else
{
dungeon = DungeonSchematic.readFromResource(result.schematicPath);
}
dungeon.ApplyImportFilters(DDProperties.instance());
dungeon.copyToWorld(sender.worldObj, x, y, z);
} catch (InvalidSchematicException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}*/
} }
else else
{ {

View File

@@ -0,0 +1,112 @@
package StevenDimDoors.mod_pocketDim.dungeon;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import net.minecraft.world.World;
import StevenDimDoors.mod_pocketDim.DDProperties;
import StevenDimDoors.mod_pocketDim.schematic.CompoundFilter;
import StevenDimDoors.mod_pocketDim.schematic.InvalidSchematicException;
import StevenDimDoors.mod_pocketDim.schematic.ReplacementFilter;
import StevenDimDoors.mod_pocketDim.schematic.Schematic;
public class DungeonSchematic extends Schematic {
private static final short MAX_VANILLA_BLOCK_ID = 158;
private static final short STANDARD_FABRIC_OF_REALITY_ID = 1973;
private static final short STANDARD_ETERNAL_FABRIC_ID = 220;
private static final short[] MOD_BLOCK_FILTER_EXCEPTIONS = new short[] {
STANDARD_FABRIC_OF_REALITY_ID,
STANDARD_ETERNAL_FABRIC_ID
};
private DungeonSchematic(Schematic source)
{
super(source);
}
private DungeonSchematic()
{
//Used to create a dummy instance for readFromResource()
super((short) 0, (short) 0, (short) 0, null, null, null);
}
public static DungeonSchematic readFromFile(String schematicPath) throws FileNotFoundException, InvalidSchematicException
{
return readFromFile(new File(schematicPath));
}
public static DungeonSchematic readFromFile(File schematicFile) throws FileNotFoundException, InvalidSchematicException
{
return readFromStream(new FileInputStream(schematicFile));
}
public static DungeonSchematic readFromResource(String resourcePath) throws InvalidSchematicException
{
//We need an instance of a class in the mod to retrieve a resource
DungeonSchematic empty = new DungeonSchematic();
InputStream schematicStream = empty.getClass().getResourceAsStream(resourcePath);
return readFromStream(schematicStream);
}
public static DungeonSchematic readFromStream(InputStream schematicStream) throws InvalidSchematicException
{
return new DungeonSchematic(Schematic.readFromStream(schematicStream));
}
public void ApplyImportFilters(DDProperties properties)
{
//Filter out mod blocks except some of our own
CompoundFilter standardizer = new CompoundFilter();
standardizer.addFilter(new ModBlockFilter(MAX_VANILLA_BLOCK_ID, MOD_BLOCK_FILTER_EXCEPTIONS,
(short) properties.FabricBlockID, (byte) 0));
//Also convert standard DD block IDs to local versions
Map<Short, Short> mapping = getAssignedToStandardIDMapping(properties);
for (Entry<Short, Short> entry : mapping.entrySet())
{
if (entry.getKey() != entry.getValue())
{
standardizer.addFilter(new ReplacementFilter(entry.getValue(), entry.getKey()));
}
}
standardizer.apply(this, this.blocks, this.metadata);
}
public void ApplyExportFilters(DDProperties properties)
{
//Check if some block IDs assigned by Forge differ from our standard IDs
//If so, change the IDs to standard values
CompoundFilter standardizer = new CompoundFilter();
Map<Short, Short> mapping = getAssignedToStandardIDMapping(properties);
for (Entry<Short, Short> entry : mapping.entrySet())
{
if (entry.getKey() != entry.getValue())
{
standardizer.addFilter(new ReplacementFilter(entry.getKey(), entry.getValue()));
}
}
standardizer.apply(this, this.blocks, this.metadata);
}
private Map<Short, Short> getAssignedToStandardIDMapping(DDProperties properties)
{
//If we ever need this broadly or support other mods, this should be moved to a separate class
TreeMap<Short, Short> mapping = new TreeMap<Short, Short>();
mapping.put((short) properties.FabricBlockID, STANDARD_FABRIC_OF_REALITY_ID);
mapping.put((short) properties.PermaFabricBlockID, STANDARD_ETERNAL_FABRIC_ID);
return mapping;
}
public static DungeonSchematic copyFromWorld(World world, int x, int y, int z, short width, short height, short length, boolean doCompactBounds)
{
return new DungeonSchematic(Schematic.copyFromWorld(world, x, y, z, width, height, length, doCompactBounds));
}
}

View File

@@ -0,0 +1,51 @@
package StevenDimDoors.mod_pocketDim.dungeon;
import net.minecraft.block.Block;
import StevenDimDoors.mod_pocketDim.schematic.SchematicFilter;
public class ModBlockFilter extends SchematicFilter {
private short maxVanillaBlockID;
private short[] exceptions;
private short replacementBlockID;
private byte replacementMetadata;
public ModBlockFilter(short maxVanillaBlockID, short[] exceptions, short replacementBlockID, byte replacementMetadata)
{
super("ModBlockFilter");
this.maxVanillaBlockID = maxVanillaBlockID;
this.exceptions = exceptions;
this.replacementBlockID = replacementBlockID;
this.replacementMetadata = replacementMetadata;
}
@Override
protected boolean applyToBlock(int index, short[] blocks, byte[] metadata)
{
int k;
short currentID = blocks[index];
if (currentID > maxVanillaBlockID || (currentID != 0 && Block.blocksList[currentID] == null))
{
//This might be a mod block. Check if an exception exists.
for (k = 0; k < exceptions.length; k++)
{
if (currentID == exceptions[k])
{
//Exception found, not considered a mod block
return false;
}
}
//No matching exception found. Replace the block.
blocks[index] = replacementBlockID;
metadata[index] = replacementMetadata;
return true;
}
return false;
}
@Override
protected boolean terminates()
{
return false;
}
}

View File

@@ -17,8 +17,8 @@ import StevenDimDoors.mod_pocketDim.DimData;
import StevenDimDoors.mod_pocketDim.DungeonGenerator; 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.dungeon.DungeonSchematic;
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,9 +383,10 @@ public class DungeonHelper
try try
{ {
short size = (short) 2 * MAX_EXPORT_RADIUS + 1; short size = (short) 2 * MAX_EXPORT_RADIUS + 1;
Schematic schematic = Schematic.copyFromWorld(world, DungeonSchematic dungeon = DungeonSchematic.copyFromWorld(world,
centerX - MAX_EXPORT_RADIUS, centerY - MAX_EXPORT_RADIUS, centerZ - MAX_EXPORT_RADIUS, size, size, size, true); centerX - MAX_EXPORT_RADIUS, centerY - MAX_EXPORT_RADIUS, centerZ - MAX_EXPORT_RADIUS, size, size, size, true);
schematic.writeToFile(exportPath); dungeon.ApplyExportFilters(properties);
dungeon.writeToFile(exportPath);
return true; return true;
} }
catch(Exception e) catch(Exception e)

View File

@@ -0,0 +1,58 @@
package StevenDimDoors.mod_pocketDim.schematic;
import java.util.ArrayList;
public class CompoundFilter extends SchematicFilter {
private ArrayList<SchematicFilter> filters;
public CompoundFilter()
{
super("CompoundFilter");
filters = new ArrayList<SchematicFilter>();
}
public void addFilter(SchematicFilter filter)
{
filters.add(filter);
}
@Override
protected boolean initialize(Schematic schematic, short[] blocks, byte[] metadata)
{
for (SchematicFilter filter : filters)
{
if (!filter.initialize(schematic, blocks, metadata))
{
return false;
}
}
return !filters.isEmpty();
}
@Override
protected boolean finish()
{
for (SchematicFilter filter : filters)
{
if (!filter.finish())
{
return false;
}
}
return true;
}
@Override
protected boolean applyToBlock(int index, short[] blocks, byte[] metadata)
{
for (SchematicFilter filter : filters)
{
if (!filter.applyToBlock(index, blocks, metadata))
{
return !filter.terminates();
}
}
return true;
}
}

View File

@@ -0,0 +1,75 @@
package StevenDimDoors.mod_pocketDim.schematic;
public class ReplacementFilter extends SchematicFilter {
private short targetBlock;
private byte targetMetadata;
private boolean matchMetadata;
private short replacementBlock;
private byte replacementMetadata;
private boolean changeMetadata;
public ReplacementFilter(short targetBlock, byte targetMetadata, short replacementBlock, byte replacementMetadata)
{
super("ReplacementFilter");
this.targetBlock = targetBlock;
this.targetMetadata = targetMetadata;
this.matchMetadata = true;
this.replacementBlock = replacementBlock;
this.replacementMetadata = replacementMetadata;
this.changeMetadata = true;
}
public ReplacementFilter(short targetBlock, short replacementBlock, byte replacementMetadata)
{
super("ReplacementFilter");
this.targetBlock = targetBlock;
this.matchMetadata = false;
this.replacementBlock = replacementBlock;
this.replacementMetadata = replacementMetadata;
this.changeMetadata = true;
}
public ReplacementFilter(short targetBlock, byte targetMetadata, short replacementBlock)
{
super("ReplacementFilter");
this.targetBlock = targetBlock;
this.targetMetadata = targetMetadata;
this.matchMetadata = true;
this.replacementBlock = replacementBlock;
this.changeMetadata = false;
}
public ReplacementFilter(short targetBlock, short replacementBlock)
{
super("ReplacementFilter");
this.targetBlock = targetBlock;
this.matchMetadata = false;
this.replacementBlock = replacementBlock;
this.changeMetadata = false;
}
@Override
protected boolean applyToBlock(int index, short[] blocks, byte[] metadata)
{
if (blocks[index] == targetBlock)
{
if ((matchMetadata && metadata[index] == targetMetadata) || !matchMetadata)
{
blocks[index] = replacementBlock;
if (changeMetadata)
{
metadata[index] = replacementMetadata;
}
return true;
}
}
return false;
}
@Override
protected boolean terminates()
{
return false;
}
}

View File

@@ -7,11 +7,14 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import net.minecraft.block.Block;
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.tileentity.TileEntity;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import StevenDimDoors.mod_pocketDim.Point3D; import StevenDimDoors.mod_pocketDim.Point3D;
/** /**
@@ -26,7 +29,7 @@ public class Schematic {
protected short[] blocks; protected short[] blocks;
protected byte[] metadata; protected byte[] metadata;
protected NBTTagList tileEntities = new NBTTagList(); protected NBTTagList tileEntities;
protected Schematic(short width, short height, short length, short[] blocks, byte[] metadata, NBTTagList tileEntities) protected Schematic(short width, short height, short length, short[] blocks, byte[] metadata, NBTTagList tileEntities)
{ {
@@ -38,7 +41,19 @@ public class Schematic {
this.tileEntities = tileEntities; this.tileEntities = tileEntities;
} }
private int calculateIndex(int x, int y, int z) protected Schematic(Schematic source)
{
//Shallow copy constructor - critical for code reuse in derived classes since
//source's fields will be inaccessible if the derived class is in another package.
this.width = source.width;
this.height = source.height;
this.length = source.length;
this.blocks = source.blocks;
this.metadata = source.metadata;
this.tileEntities = source.tileEntities;
}
protected int calculateIndex(int x, int y, int z)
{ {
if (x < 0 || x >= width) if (x < 0 || x >= width)
throw new IndexOutOfBoundsException("x must be non-negative and less than width"); throw new IndexOutOfBoundsException("x must be non-negative and less than width");
@@ -83,7 +98,7 @@ public class Schematic {
return readFromStream(schematicStream); return readFromStream(schematicStream);
} }
private static Schematic readFromStream(InputStream schematicStream) throws InvalidSchematicException public static Schematic readFromStream(InputStream schematicStream) throws InvalidSchematicException
{ {
short width; short width;
short height; short height;
@@ -165,7 +180,8 @@ public class Schematic {
//Get the list of tile entities //Get the list of tile entities
tileEntities = schematicTag.getTagList("TileEntities"); tileEntities = schematicTag.getTagList("TileEntities");
return new Schematic(width, height, length, blocks, metadata, tileEntities); Schematic result = new Schematic(width, height, length, blocks, metadata, tileEntities);
return result;
} }
catch (InvalidSchematicException ex) catch (InvalidSchematicException ex)
{ {
@@ -246,7 +262,13 @@ public class Schematic {
return writeToNBT(true); return writeToNBT(true);
} }
private NBTTagCompound writeToNBT(boolean copyTileEntities) protected NBTTagCompound writeToNBT(boolean copyTileEntities)
{
return writeToNBT(width, height, length, blocks, metadata, tileEntities, copyTileEntities);
}
protected static NBTTagCompound writeToNBT(short width, short height, short length, short[] blocks, byte[] metadata,
NBTTagList tileEntities, boolean copyTileEntities)
{ {
//This is the main storage function. Schematics are really compressed NBT tags, so if we can generate //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. //the tags, most of the work is done. All the other storage functions will rely on this one.
@@ -301,6 +323,11 @@ public class Schematic {
outputStream.close(); outputStream.close();
} }
public boolean applyFilter(SchematicFilter filter)
{
return filter.apply(this, this.blocks, this.metadata);
}
public void copyToWorld(World world, int x, int y, int z) public void copyToWorld(World world, int x, int y, int z)
{ {
//This isn't implemented as a WorldOperation because it doesn't quite fit the structure of those operations. //This isn't implemented as a WorldOperation because it doesn't quite fit the structure of those operations.
@@ -317,8 +344,8 @@ public class Schematic {
{ {
for (dx = 0; dx < width; dx++) for (dx = 0; dx < width; dx++)
{ {
//Don't cause block updates! //In the future, we might want to make this more efficient by building whole chunks at a time
world.setBlock(x + dx, y + dy, z + dz, blocks[index], metadata[index], 2); setBlockDirectly(world, x + dx, y + dy, z + dz, blocks[index], metadata[index]);
index++; index++;
} }
} }
@@ -339,4 +366,38 @@ public class Schematic {
world.setBlockTileEntity(dx, dy, dz, TileEntity.createAndLoadEntity(tileTag)); world.setBlockTileEntity(dx, dy, dz, TileEntity.createAndLoadEntity(tileTag));
} }
} }
protected static void setBlockDirectly(World world, int x, int y, int z, int blockID, int metadata)
{
if (blockID != 0 && Block.blocksList[blockID] == null)
{
return;
}
int cX = x >> 4;
int cZ = z >> 4;
int cY = y >> 4;
Chunk chunk;
int localX = (x % 16) < 0 ? (x % 16) + 16 : (x % 16);
int localZ = (z % 16) < 0 ? (z % 16) + 16 : (z % 16);
ExtendedBlockStorage extBlockStorage;
try
{
chunk = world.getChunkFromChunkCoords(cX, cZ);
extBlockStorage = chunk.getBlockStorageArray()[cY];
if (extBlockStorage == null)
{
extBlockStorage = new ExtendedBlockStorage(cY << 4, !world.provider.hasNoSky);
chunk.getBlockStorageArray()[cY] = extBlockStorage;
}
extBlockStorage.setExtBlockID(localX, y & 15, localZ, blockID);
extBlockStorage.setExtBlockMetadata(localX, y & 15, localZ, metadata);
}
catch(Exception e)
{
e.printStackTrace();
}
}
} }

View File

@@ -0,0 +1,55 @@
package StevenDimDoors.mod_pocketDim.schematic;
public class SchematicFilter {
private String name;
protected SchematicFilter(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public boolean apply(Schematic schematic, short[] blocks, byte[] metadata)
{
if (!initialize(schematic, blocks, metadata))
return false;
for (int index = 0; index < blocks.length; index++)
{
if (!applyToBlock(index, blocks, metadata) && terminates())
return false;
}
return finish();
}
protected boolean initialize(Schematic schematic, short[] blocks, byte[] metadata)
{
return true;
}
protected boolean applyToBlock(int index, short[] blocks, byte[] metadata)
{
return true;
}
protected boolean finish()
{
return true;
}
protected boolean terminates()
{
return true;
}
public String toString()
{
return name;
}
}