diff --git a/StevenDimDoors/mod_pocketDim/commands/CommandCreateDungeonRift.java b/StevenDimDoors/mod_pocketDim/commands/CommandCreateDungeonRift.java index 84f2c06a..281df903 100644 --- a/StevenDimDoors/mod_pocketDim/commands/CommandCreateDungeonRift.java +++ b/StevenDimDoors/mod_pocketDim/commands/CommandCreateDungeonRift.java @@ -1,13 +1,17 @@ package StevenDimDoors.mod_pocketDim.commands; import java.io.File; +import java.io.FileNotFoundException; import java.util.Collection; import net.minecraft.entity.player.EntityPlayer; +import StevenDimDoors.mod_pocketDim.DDProperties; import StevenDimDoors.mod_pocketDim.DungeonGenerator; import StevenDimDoors.mod_pocketDim.LinkData; +import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic; import StevenDimDoors.mod_pocketDim.helpers.DungeonHelper; import StevenDimDoors.mod_pocketDim.helpers.dimHelper; +import StevenDimDoors.mod_pocketDim.schematic.InvalidSchematicException; public class CommandCreateDungeonRift extends DDCommandBase { @@ -81,6 +85,25 @@ public class CommandCreateDungeonRift extends DDCommandBase link = dimHelper.instance.createPocket(link, true, true); dimHelper.dimList.get(link.destDimID).dungeonGenerator = result; 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 { diff --git a/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java b/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java new file mode 100644 index 00000000..b364cbff --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java @@ -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 mapping = getAssignedToStandardIDMapping(properties); + + for (Entry 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 mapping = getAssignedToStandardIDMapping(properties); + + for (Entry 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 getAssignedToStandardIDMapping(DDProperties properties) + { + //If we ever need this broadly or support other mods, this should be moved to a separate class + TreeMap mapping = new TreeMap(); + 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)); + } +} diff --git a/StevenDimDoors/mod_pocketDim/dungeon/ModBlockFilter.java b/StevenDimDoors/mod_pocketDim/dungeon/ModBlockFilter.java new file mode 100644 index 00000000..c0ab0990 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/dungeon/ModBlockFilter.java @@ -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; + } +} diff --git a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java index f9cfa8df..93903399 100644 --- a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java +++ b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java @@ -17,8 +17,8 @@ import StevenDimDoors.mod_pocketDim.DimData; import StevenDimDoors.mod_pocketDim.DungeonGenerator; import StevenDimDoors.mod_pocketDim.LinkData; import StevenDimDoors.mod_pocketDim.mod_pocketDim; +import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic; import StevenDimDoors.mod_pocketDim.items.itemDimDoor; -import StevenDimDoors.mod_pocketDim.schematic.Schematic; import StevenDimDoors.mod_pocketDim.util.WeightedContainer; public class DungeonHelper @@ -383,9 +383,10 @@ public class DungeonHelper try { 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); - schematic.writeToFile(exportPath); + dungeon.ApplyExportFilters(properties); + dungeon.writeToFile(exportPath); return true; } catch(Exception e) diff --git a/StevenDimDoors/mod_pocketDim/schematic/CompoundFilter.java b/StevenDimDoors/mod_pocketDim/schematic/CompoundFilter.java new file mode 100644 index 00000000..2de3ec6e --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/CompoundFilter.java @@ -0,0 +1,58 @@ +package StevenDimDoors.mod_pocketDim.schematic; + +import java.util.ArrayList; + +public class CompoundFilter extends SchematicFilter { + + private ArrayList filters; + + public CompoundFilter() + { + super("CompoundFilter"); + filters = new ArrayList(); + } + + 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; + } +} diff --git a/StevenDimDoors/mod_pocketDim/schematic/ReplacementFilter.java b/StevenDimDoors/mod_pocketDim/schematic/ReplacementFilter.java new file mode 100644 index 00000000..ba993047 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/ReplacementFilter.java @@ -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; + } +} diff --git a/StevenDimDoors/mod_pocketDim/schematic/Schematic.java b/StevenDimDoors/mod_pocketDim/schematic/Schematic.java index a7baeb90..4f00dab6 100644 --- a/StevenDimDoors/mod_pocketDim/schematic/Schematic.java +++ b/StevenDimDoors/mod_pocketDim/schematic/Schematic.java @@ -7,11 +7,14 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +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.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import StevenDimDoors.mod_pocketDim.Point3D; /** @@ -26,7 +29,7 @@ public class Schematic { protected short[] blocks; 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) { @@ -37,8 +40,20 @@ public class Schematic { this.metadata = metadata; this.tileEntities = tileEntities; } + + 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; + } - private int calculateIndex(int x, int y, int z) + protected 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"); @@ -83,7 +98,7 @@ public class Schematic { return readFromStream(schematicStream); } - private static Schematic readFromStream(InputStream schematicStream) throws InvalidSchematicException + public static Schematic readFromStream(InputStream schematicStream) throws InvalidSchematicException { short width; short height; @@ -165,7 +180,8 @@ public class Schematic { //Get the list of tile entities 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) { @@ -246,7 +262,13 @@ public class Schematic { 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 //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(); } + public boolean applyFilter(SchematicFilter filter) + { + return filter.apply(this, this.blocks, this.metadata); + } + 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. @@ -317,8 +344,8 @@ public class Schematic { { for (dx = 0; dx < width; dx++) { - //Don't cause block updates! - world.setBlock(x + dx, y + dy, z + dz, blocks[index], metadata[index], 2); + //In the future, we might want to make this more efficient by building whole chunks at a time + setBlockDirectly(world, x + dx, y + dy, z + dz, blocks[index], metadata[index]); index++; } } @@ -339,4 +366,38 @@ public class Schematic { 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(); + } + } } diff --git a/StevenDimDoors/mod_pocketDim/schematic/SchematicFilter.java b/StevenDimDoors/mod_pocketDim/schematic/SchematicFilter.java new file mode 100644 index 00000000..db3b3746 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/SchematicFilter.java @@ -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; + } +}