From 2f3c6769c8b2eda7e1568221722ee309b775121a Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Sat, 2 Nov 2019 07:15:56 +0100 Subject: [PATCH] Legacy clipboards --- .../boydti/fawe/jnbt/SchematicStreamer.java | 429 ---------------- .../boydti/fawe/jnbt/streamer/InfoReader.java | 6 +- .../fawe/jnbt/streamer/IntValueReader.java | 6 +- .../boydti/fawe/jnbt/streamer/LazyReader.java | 3 +- .../fawe/jnbt/streamer/LongValueReader.java | 6 +- .../boydti/fawe/jnbt/streamer/ReaderType.java | 7 - .../fawe/jnbt/streamer/StreamDelegate.java | 4 +- .../fawe/jnbt/streamer/StreamReader.java | 4 +- .../fawe/jnbt/streamer/ValueReader.java | 14 +- .../clipboard/DiskOptimizedClipboard.java | 13 +- .../fawe/object/schematic/Schematic.java | 16 +- .../extent/clipboard/io/SchematicReader.java | 471 +++++++++++++++++- .../clipboard/io/SpongeSchematicReader.java | 225 ++------- .../world/registry/LegacyMapper.java | 7 +- 14 files changed, 556 insertions(+), 655 deletions(-) delete mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java delete mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ReaderType.java diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java deleted file mode 100644 index 5ca39aebb..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java +++ /dev/null @@ -1,429 +0,0 @@ -package com.boydti.fawe.jnbt; - -import com.boydti.fawe.Fawe; -import com.boydti.fawe.config.Settings; -import com.boydti.fawe.object.FaweInputStream; -import com.boydti.fawe.object.FaweOutputStream; -import com.boydti.fawe.object.clipboard.CPUOptimizedClipboard; -import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; -import com.boydti.fawe.object.clipboard.LinearClipboard; -import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard; -import com.boydti.fawe.object.io.FastByteArrayOutputStream; -import com.boydti.fawe.object.io.FastByteArraysInputStream; - -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.NBTInputStream; -import com.sk89q.jnbt.StringTag; -import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; -import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.registry.state.PropertyKey; -import com.sk89q.worldedit.util.Direction; -import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; -import com.sk89q.worldedit.world.block.BlockCategories; -import com.sk89q.worldedit.world.block.BlockID; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypeSwitch; -import com.sk89q.worldedit.world.block.BlockTypeSwitchBuilder; -import com.sk89q.worldedit.world.entity.EntityType; -import com.sk89q.worldedit.world.entity.EntityTypes; -import com.sk89q.worldedit.world.registry.BlockMaterial; -import com.sk89q.worldedit.world.registry.LegacyMapper; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; - -import java.io.Closeable; -import java.io.IOException; -import java.util.UUID; -import java.util.function.BiConsumer; - -// TODO FIXME -public class SchematicStreamer implements Closeable { - private final UUID uuid; - private final NBTInputStream input; - private FastByteArrayOutputStream idOut = new FastByteArrayOutputStream(); - private FastByteArrayOutputStream dataOut = new FastByteArrayOutputStream(); - private FastByteArrayOutputStream addOut; - - private FaweOutputStream ids; - private FaweOutputStream datas; - private FaweOutputStream adds; - - public SchematicStreamer(NBTInputStream stream, UUID uuid) { - this.input = stream; - this.uuid = uuid; - clipboard = new BlockArrayClipboard(new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(0, 0, 0)), fc); - } - - public void addBlockReaders() throws IOException { -// NBTStreamReader idInit = new NBTStreamReader() { -// @Override -// public void accept(Integer length, Integer type) { -// ids = new FaweOutputStream(new LZ4BlockOutputStream(idOut)); -// } -// }; -// NBTStreamReader dataInit = new NBTStreamReader() { -// @Override -// public void accept(Integer length, Integer type) { -// datas = new FaweOutputStream(new LZ4BlockOutputStream(dataOut)); -// } -// }; -// NBTStreamReader addInit = new NBTStreamReader() { -// @Override -// public void accept(Integer length, Integer type) { -// addOut = new FastByteArrayOutputStream(); -// adds = new FaweOutputStream(new LZ4BlockOutputStream(addOut)); -// } -// }; -// -// addReader("Schematic.Blocks", NBTStreamer.ReadType.INFO, idInit); -// addReader("Schematic.Data", NBTStreamer.ReadType.INFO, dataInit); -// addReader("Schematic.AddBlocks", NBTStreamer.ReadType.INFO, addInit); -// addReader("Schematic.Blocks", NBTStreamer.ReadType.ELEM, new ByteReader() { -// @Override -// public void run(int index, int value) { -// try { -// ids.write(value); -// } catch (IOException e) { -// throw new RuntimeException(e); -// } -// } -// }); -// addReader("Schematic.Data", NBTStreamer.ReadType.ELEM, new ByteReader() { -// @Override -// public void run(int index, int value) { -// try { -// datas.write(value); -// } catch (IOException e) { -// throw new RuntimeException(e); -// } -// } -// }); -// addReader("Schematic.AddBlocks", NBTStreamer.ReadType.ELEM, new ByteReader() { -// @Override -// public void run(int index, int value) { -// if (value != 0) { -// int first = value & 0x0F; -// int second = (value & 0xF0) >> 4; -// try { -// if (first != 0) adds.write(first); -// if (second != 0) adds.write(second); -// } catch (IOException e) { -// throw new RuntimeException(e); -// } -// } -// } -// }); -// ByteReader biomeReader = new ByteReader() { -// @Override -// public void run(int index, int value) { -// BiomeType biome = BiomeTypes.getLegacy(value); -// if (biome != null) { -// fc.setBiome(index, biome); -// } -// } -// }; -// NBTStreamReader initializer23 = new NBTStreamReader() { -// @Override -// public void accept(Integer value1, Integer value2) { -// if (fc == null) setupClipboard(length * width * height); -// } -// }; -// addReader("Schematic.AWEBiomes", NBTStreamer.ReadType.INFO,initializer23); -// addReader("Schematic.Biomes", NBTStreamer.ReadType.INFO,initializer23); -// addReader("Schematic.AWEBiomes", NBTStreamer.ReadType.ELEM,biomeReader); // AWE stores as an int[] -// addReader("Schematic.Biomes", NBTStreamer.ReadType.ELEM,biomeReader); // FAWE stores as a byte[] (4x smaller) -// -// // Tiles -// addReader("Schematic.TileEntities", NBTStreamer.ReadType.ELEM,(BiConsumer) (index, value) -> { -// if (fc == null) { -// setupClipboard(0); -// } -// int x = value.getInt("x"); -// int y = value.getInt("y"); -// int z = value.getInt("z"); -// fc.setTile(x, y, z, value); -// }); -// // Entities -// addReader("Schematic.Entities", NBTStreamer.ReadType.ELEM,(BiConsumer) (index, compound) -> { -// if (fc == null) { -// setupClipboard(0); -// } -// String id = compound.getString("id"); -// if (id.isEmpty()) { -// return; -// } -// EntityType type = EntityTypes.parse(id); -// if (type != null) { -// compound.getValue().put("Id", new StringTag(type.getId())); -// BaseEntity state = new BaseEntity(type, compound); -// -// Location loc = compound.getEntityLocation(fc); -// fc.createEntity(loc, state); -// } else { -// Fawe.debug("Invalid entity: " + id); -// } -// }); - } -// -// @Override -// public void readFully() throws IOException { -// super.readFully(); -// if (ids != null) ids.close(); -// if (datas != null) datas.close(); -// if (adds != null) adds.close(); -// FaweInputStream idIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(idOut.toByteArrays()))); -// FaweInputStream dataIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(dataOut.toByteArrays()))); -// -// LegacyMapper remap = LegacyMapper.getInstance(); -// BlockVector3 dimensions = fc.getDimensions(); -// int length = dimensions.getBlockX() * dimensions.getBlockY() * dimensions.getBlockZ(); -// if (adds == null) { -// for (int i = 0; i < length; i++) { -// fc.setBlock(i, remap.getBlockFromLegacyCombinedId(((idIn.read() & 0xFF) << 4) + (dataIn.read() & 0xF))); -// } -// } else { -// FaweInputStream addIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(dataOut.toByteArrays()))); -// for (int i = 0; i < length; i++) { -// fc.setBlock(i, remap.getBlockFromLegacyCombinedId(((addIn.read() & 0xFF) << 8) + ((idIn.read() & 0xFF) << 4) + (dataIn.read() & 0xF))); -// } -// addIn.close(); -// } -// idIn.close(); -// dataIn.close(); -// } - - private void fixStates() { - for (BlockVector3 pos : fc) { - BlockState block = pos.getBlock(fc); - if (block.getMaterial().isAir()) continue; - - int x = pos.getX(); - int y = pos.getY(); - int z = pos.getZ(); - - BlockType type = block.getBlockType(); - if (BlockCategories.STAIRS.contains(type)) { - Direction facing = (Direction) block.getState(PropertyKey.FACING); - - BlockVector3 forward = facing.toBlockVector(); - Direction left = facing.getLeft(); - Direction right = facing.getRight(); - - BlockStateHolder forwardBlock = fc.getBlock(x + forward.getBlockX(), y + forward.getBlockY(), z + forward.getBlockZ()); - BlockType forwardType = forwardBlock.getBlockType(); - if (forwardType.hasProperty(PropertyKey.SHAPE) && forwardType.hasProperty(PropertyKey.FACING)) { - Direction forwardFacing = (Direction) forwardBlock.getState(PropertyKey.FACING); - if (forwardFacing == left) { - BlockState rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); - BlockType rightType = rightBlock.getBlockType(); - if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { - pos.setBlock(fc, block.with(PropertyKey.SHAPE, "inner_left")); - } - return; - } else if (forwardFacing == right) { - BlockState leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); - BlockType leftType = leftBlock.getBlockType(); - if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { - fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_right")); - } - return; - } - } - - BlockState backwardsBlock = fc.getBlock(x - forward.getBlockX(), y - forward.getBlockY(), z - forward.getBlockZ()); - BlockType backwardsType = backwardsBlock.getBlockType(); - if (backwardsType.hasProperty(PropertyKey.SHAPE) && backwardsType.hasProperty(PropertyKey.FACING)) { - Direction backwardsFacing = (Direction) backwardsBlock.getState(PropertyKey.FACING); - if (backwardsFacing == left) { - BlockState rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); - BlockType rightType = rightBlock.getBlockType(); - if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { - pos.setBlock(fc, block.with(PropertyKey.SHAPE, "outer_left")); - } - return; - } else if (backwardsFacing == right) { - BlockState leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); - BlockType leftType = leftBlock.getBlockType(); - if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { - pos.setBlock(fc, block.with(PropertyKey.SHAPE, "outer_right")); - } - return; - } - } - } else { - int group = group(type); - if (group == -1) return; - BlockState set = block; - - if (set.getState(PropertyKey.NORTH) == Boolean.FALSE && merge(group, x, y, z - 1)) set = set.with(PropertyKey.NORTH, true); - if (set.getState(PropertyKey.EAST) == Boolean.FALSE && merge(group, x + 1, y, z)) set = set.with(PropertyKey.EAST, true); - if (set.getState(PropertyKey.SOUTH) == Boolean.FALSE && merge(group, x, y, z + 1)) set = set.with(PropertyKey.SOUTH, true); - if (set.getState(PropertyKey.WEST) == Boolean.FALSE && merge(group, x - 1, y, z)) set = set.with(PropertyKey.WEST, true); - - if (group == 2) { - int ns = ((Boolean) set.getState(PropertyKey.NORTH) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.SOUTH) ? 1 : 0); - int ew = ((Boolean) set.getState(PropertyKey.EAST) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.WEST) ? 1 : 0); - if (Math.abs(ns - ew) != 2 || fc.getBlock(x, y + 1, z).getBlockType().getMaterial().isSolid()) { - set = set.with(PropertyKey.UP, true); - } - } - - if (set != block) pos.setBlock(fc, set); - } - } - } - - private BlockTypeSwitch fullCube = new BlockTypeSwitchBuilder<>(false).add(type -> { - BlockMaterial mat = type.getMaterial(); - return (mat.isFullCube() && !mat.isFragileWhenPushed() && mat.getLightValue() == 0 && mat.isOpaque() && mat.isSolid() && !mat.isTranslucent()); - }, true).build(); - - private boolean merge(int group, int x, int y, int z) { - BlockState block = fc.getBlock(x, y, z); - BlockType type = block.getBlockType(); - return group(type) == group || fullCube.apply(type); - } - - private int group(BlockType type) { - switch (type.getInternalId()) { - case BlockID.ACACIA_FENCE: - case BlockID.BIRCH_FENCE: - case BlockID.DARK_OAK_FENCE: - case BlockID.JUNGLE_FENCE: - case BlockID.OAK_FENCE: - case BlockID.SPRUCE_FENCE: - return 0; - case BlockID.NETHER_BRICK_FENCE: - return 1; - case BlockID.COBBLESTONE_WALL: - case BlockID.MOSSY_COBBLESTONE_WALL: - return 2; - case BlockID.IRON_BARS: - case BlockID.BLACK_STAINED_GLASS_PANE: - case BlockID.BLUE_STAINED_GLASS_PANE: - case BlockID.BROWN_MUSHROOM_BLOCK: - case BlockID.BROWN_STAINED_GLASS_PANE: - case BlockID.CYAN_STAINED_GLASS_PANE: - case BlockID.GLASS_PANE: - case BlockID.GRAY_STAINED_GLASS_PANE: - case BlockID.GREEN_STAINED_GLASS_PANE: - case BlockID.LIGHT_BLUE_STAINED_GLASS_PANE: - case BlockID.LIGHT_GRAY_STAINED_GLASS_PANE: - case BlockID.LIME_STAINED_GLASS_PANE: - case BlockID.MAGENTA_STAINED_GLASS_PANE: - case BlockID.ORANGE_STAINED_GLASS_PANE: - case BlockID.PINK_STAINED_GLASS_PANE: - case BlockID.PURPLE_STAINED_GLASS_PANE: - case BlockID.RED_STAINED_GLASS_PANE: - case BlockID.WHITE_STAINED_GLASS_PANE: - case BlockID.YELLOW_STAINED_GLASS_PANE: - return 3; - default: - return -1; - } - } - - public void addDimensionReaders() { -// addReader("Schematic.Height", -// (BiConsumer) (index, value) -> height = (value)); -// addReader("Schematic.Width", (BiConsumer) (index, value) -> width = (value)); -// addReader("Schematic.Length", -// (BiConsumer) (index, value) -> length = (value)); -// addReader("Schematic.WEOriginX", -// (BiConsumer) (index, value) -> originX = (value)); -// addReader("Schematic.WEOriginY", -// (BiConsumer) (index, value) -> originY = (value)); -// addReader("Schematic.WEOriginZ", -// (BiConsumer) (index, value) -> originZ = (value)); -// addReader("Schematic.WEOffsetX", -// (BiConsumer) (index, value) -> offsetX = (value)); -// addReader("Schematic.WEOffsetY", -// (BiConsumer) (index, value) -> offsetY = (value)); -// addReader("Schematic.WEOffsetZ", -// (BiConsumer) (index, value) -> offsetZ = (value)); - } - - private int height; - private int width; - private int length; - - private int originX; - private int originY; - private int originZ; - - private int offsetX; - private int offsetY; - private int offsetZ; - - private BlockArrayClipboard clipboard; - private LinearClipboard fc; - - private LinearClipboard setupClipboard(int size) { - if (fc != null) { - if (fc.getDimensions().getX() == 0) { -// fc.setDimensions(BlockVector3.at(size, 1, 1)); - } - return fc; - } - if (Settings.IMP.CLIPBOARD.USE_DISK) { - return fc = new DiskOptimizedClipboard(BlockVector3.at(size, 1, 1), uuid); - } else if (Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL == 0) { - return fc = new CPUOptimizedClipboard(BlockVector3.at(size, 1, 1)); - } else { - return fc = new MemoryOptimizedClipboard(BlockVector3.at(size, 1, 1)); - } - } - - public BlockVector3 getOrigin() { - return BlockVector3.at(originX, originY, originZ); - } - - public BlockVector3 getOffset() { - return BlockVector3.at(offsetX, offsetY, offsetZ); - } - - public BlockVector3 getDimensions() { - return BlockVector3.at(width, height, length); - } - - public void setClipboard(LinearClipboard clipboard) { - this.fc = clipboard; - } - - public Clipboard getClipboard() throws IOException { - try { - setupClipboard(0); - addDimensionReaders(); - addBlockReaders(); -// readFully(); - BlockVector3 min = BlockVector3.at(originX, originY, originZ); - BlockVector3 offset = BlockVector3.at(offsetX, offsetY, offsetZ); - BlockVector3 origin = min.subtract(offset); - BlockVector3 dimensions = BlockVector3.at(width, height, length); -// fc.setDimensions(dimensions); - fixStates(); - CuboidRegion region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); -// clipboard.init(region, fc); - clipboard.setOrigin(origin); - return clipboard; - } catch (Throwable e) { - if (fc != null) { - fc.close(); - } - throw e; - } - } - - @Override - public void close() throws IOException { - this.input.close(); - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/InfoReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/InfoReader.java index 9b5dfdea0..1f8091686 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/InfoReader.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/InfoReader.java @@ -1,10 +1,12 @@ package com.boydti.fawe.jnbt.streamer; +import java.io.IOException; + public interface InfoReader extends StreamReader { - void apply(int length, int type); + void apply(int length, int type) throws IOException; @Override - default void apply(int i, Integer value) { + default void apply(int i, Integer value) throws IOException { apply(i, value.intValue()); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/IntValueReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/IntValueReader.java index 635d065ad..fad915aad 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/IntValueReader.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/IntValueReader.java @@ -1,10 +1,12 @@ package com.boydti.fawe.jnbt.streamer; +import java.io.IOException; + public interface IntValueReader extends ValueReader { - void applyInt(int index, int value); + void applyInt(int index, int value) throws IOException; @Override - default void apply(int index, Integer value) { + default void apply(int index, Integer value) throws IOException { applyInt(index, value); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LazyReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LazyReader.java index ffe71ead2..963daab54 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LazyReader.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LazyReader.java @@ -3,7 +3,8 @@ package com.boydti.fawe.jnbt.streamer; import com.sk89q.jnbt.NBTInputStream; import java.io.DataInputStream; +import java.io.IOException; public interface LazyReader extends StreamReader { - void apply(int index, NBTInputStream stream); + void apply(int index, NBTInputStream stream) throws IOException; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LongValueReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LongValueReader.java index 82e1ef8ef..944317a7a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LongValueReader.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LongValueReader.java @@ -1,10 +1,12 @@ package com.boydti.fawe.jnbt.streamer; +import java.io.IOException; + public interface LongValueReader extends ValueReader { - void applyLong(int index, long value); + void applyLong(int index, long value) throws IOException; @Override - default void apply(int index, Long value) { + default void apply(int index, Long value) throws IOException { applyLong(index, value); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ReaderType.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ReaderType.java deleted file mode 100644 index 28a91c27b..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ReaderType.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.boydti.fawe.jnbt.streamer; - -public enum ReaderType { - VALUE, - INFO, - ELEM, -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamDelegate.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamDelegate.java index db889de85..1b0cc5b9a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamDelegate.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamDelegate.java @@ -208,13 +208,13 @@ public class StreamDelegate { return elemReader; } - public void acceptInfo(int length, int type) { + public void acceptInfo(int length, int type) throws IOException { if (infoReader != null) { infoReader.apply(length, type); } } - public boolean acceptLazy(int length, NBTInputStream is) { + public boolean acceptLazy(int length, NBTInputStream is) throws IOException { if (lazyReader != null) { lazyReader.apply(length, is); return true; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamReader.java index 79b17c92e..dadcf005e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamReader.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamReader.java @@ -1,5 +1,7 @@ package com.boydti.fawe.jnbt.streamer; +import java.io.IOException; + public interface StreamReader { - void apply(int i, T value); + void apply(int i, T value) throws IOException; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ValueReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ValueReader.java index 6ff8750ee..63107c0f4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ValueReader.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ValueReader.java @@ -1,21 +1,23 @@ package com.boydti.fawe.jnbt.streamer; -public interface ValueReader extends StreamReader { - void apply(int index, T value); +import java.io.IOException; - default void applyInt(int index, int value) { +public interface ValueReader extends StreamReader { + void apply(int index, T value) throws IOException; + + default void applyInt(int index, int value) throws IOException { apply(index, (T) (Integer) value); } - default void applyLong(int index, long value) { + default void applyLong(int index, long value) throws IOException { apply(index, (T) (Long) value); } - default void applyFloat(int index, float value) { + default void applyFloat(int index, float value) throws IOException { apply(index, (T) (Float) value); } - default void applyDouble(int index, double value) { + default void applyDouble(int index, double value) throws IOException { apply(index, (T) (Double) value); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java index 3effee5b4..0f40e10b7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java @@ -201,11 +201,16 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable if (!hasBiomes()) return; int index = 0; int mbbIndex = HEADER_SIZE + (getVolume() << 1); - for (int z = 0; z < getLength(); z++) { - for (int x = 0; x < getWidth(); x++, index++, mbbIndex++) { - int biome = byteBuffer.get(mbbIndex) & 0xFF; - task.applyInt(index, biome); + try { + for (int z = 0; z < getLength(); z++) { + for (int x = 0; x < getWidth(); x++, index++, mbbIndex++) { + int biome = byteBuffer.get(mbbIndex) & 0xFF; + task.applyInt(index, biome); + } } + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java index 1e6d6a739..a06a8ace6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java @@ -198,10 +198,14 @@ public class Schematic { public void paste(Extent extent, BlockVector3 to, boolean pasteAir) { Region region = clipboard.getRegion().clone(); - final BlockVector3 bot = clipboard.getMinimumPoint(); final BlockVector3 origin = clipboard.getOrigin(); final boolean copyBiomes = clipboard.hasBiomes(); + // To must be relative to the clipboard origin ( player location - clipboard origin ) (as the locations supplied are relative to the world origin) + final int relx = to.getBlockX() - origin.getBlockX(); + final int rely = to.getBlockY() - origin.getBlockY(); + final int relz = to.getBlockZ() - origin.getBlockZ(); + clipboard.apply(clipboard, new Filter() { @Override public void applyBlock(FilterBlock block) { @@ -209,12 +213,10 @@ public class Schematic { } }); + System.out.println("Rel " + relx + "," + rely + "," + relz + " | " + to + " | " + origin); + System.out.println("TODO optimize paste using above apply"); - // Optimize for BlockArrayClipboard - // To must be relative to the clipboard origin ( player location - clipboard origin ) (as the locations supplied are relative to the world origin) - final int relx = to.getBlockX() - origin.getBlockX(); - final int rely = to.getBlockY() - origin.getBlockY(); - final int relz = to.getBlockZ() - origin.getBlockZ(); + Operation visitor = new RegionVisitor(region, new RegionFunction() { // MutableBlockVector2 mpos2d_2 = new MutableBlockVector2(); MutableBlockVector2 mpos2d = new MutableBlockVector2(); @@ -226,6 +228,7 @@ public class Schematic { @Override public boolean apply(BlockVector3 mutable) throws WorldEditException { BlockState block = clipboard.getBlock(mutable); + System.out.println("Pos " + mutable); int xx = mutable.getBlockX() + relx; int zz = mutable.getBlockZ() + relz; if (copyBiomes && xx != mpos2d.getBlockX() && zz != mpos2d.getBlockZ()) { @@ -261,3 +264,4 @@ public class Schematic { } } } + diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java index a95844c29..6cdd56acb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java @@ -20,25 +20,104 @@ package com.sk89q.worldedit.extent.clipboard.io; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.jnbt.CorruptSchematicStreamer; import com.boydti.fawe.jnbt.SchematicStreamer; import static com.google.common.base.Preconditions.checkNotNull; + +import com.boydti.fawe.jnbt.streamer.InfoReader; +import com.boydti.fawe.jnbt.streamer.IntValueReader; +import com.boydti.fawe.jnbt.streamer.StreamDelegate; +import com.boydti.fawe.jnbt.streamer.ValueReader; +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.clipboard.LinearClipboard; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; +import com.boydti.fawe.object.io.FastByteArraysInputStream; +import com.google.common.collect.ImmutableList; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.EntityNBTCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.FlowerPotCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NoteBlockCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.Pre13HangingCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SignCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SkullBlockCompatibilityHandler; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.registry.state.PropertyKey; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.DataFixer; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockCategories; +import com.sk89q.worldedit.world.block.BlockID; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypeSwitch; +import com.sk89q.worldedit.world.block.BlockTypeSwitchBuilder; +import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.entity.EntityTypes; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.world.registry.LegacyMapper; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.function.Function; /** * Reads schematic files based that are compatible with MCEdit and other editors. */ public class SchematicReader implements ClipboardReader { + private static final NBTCompatibilityHandler[] COMPATIBILITY_HANDLERS = { + new SignCompatibilityHandler(), + new FlowerPotCompatibilityHandler(), + new NoteBlockCompatibilityHandler(), + new SkullBlockCompatibilityHandler() + }; + private static final EntityNBTCompatibilityHandler[] ENTITY_COMPATIBILITY_HANDLERS = { + new Pre13HangingCompatibilityHandler() + }; + private NBTInputStream inputStream; private InputStream rootStream; +// private final DataFixer fixer; TODO + + private FastByteArrayOutputStream idOut = new FastByteArrayOutputStream(); + private FastByteArrayOutputStream dataOut = new FastByteArrayOutputStream(); + private FastByteArrayOutputStream addOut; + private FastByteArrayOutputStream biomesOut; + + private FaweOutputStream ids; + private FaweOutputStream datas; + private FaweOutputStream adds; + private FaweOutputStream biomes; + + private List> tiles; + private List> entities; + + private int width, height, length; + private int offsetX, offsetY, offsetZ; + private int originX, originY, originZ; + /** * Create a new instance. * @@ -53,22 +132,394 @@ public class SchematicReader implements ClipboardReader { this.rootStream = in; } - @Override - public Clipboard read() throws IOException { - return read(UUID.randomUUID()); + public StreamDelegate createDelegate() { + StreamDelegate root = new StreamDelegate(); + StreamDelegate schematic = root.add("Schematic"); + schematic.add("Width").withInt((i, v) -> width = v); + schematic.add("Height").withInt((i, v) -> height = v); + schematic.add("Length").withInt((i, v) -> length = v); + + schematic.add("WEOriginX").withInt((i, v) -> originX = v); + schematic.add("WEOriginY").withInt((i, v) -> originY = v); + schematic.add("WEOriginZ").withInt((i, v) -> originZ = v); + + StreamDelegate metadata = schematic.add("Metadata"); + metadata.add("WEOffsetX").withInt((i, v) -> offsetX = v); + metadata.add("WEOffsetY").withInt((i, v) -> offsetY = v); + metadata.add("WEOffsetZ").withInt((i, v) -> offsetZ = v); + + StreamDelegate blocksDelegate = schematic.add("Blocks"); + blocksDelegate.withInfo((length, type) -> ids = new FaweOutputStream(new LZ4BlockOutputStream(idOut))); + blocksDelegate.withInt((index, value) -> ids.write(value)); + + StreamDelegate dataDelegate = schematic.add("Data"); + dataDelegate.withInfo((length, type) -> datas = new FaweOutputStream(new LZ4BlockOutputStream(dataOut))); + dataDelegate.withInt((index, value) -> datas.write(value)); + + StreamDelegate addDelegate = schematic.add("AddBlocks"); + addDelegate.withInfo((length, type) -> { + addOut = new FastByteArrayOutputStream(); + adds = new FaweOutputStream(new LZ4BlockOutputStream(addOut)); + }); + addDelegate.withInt((index, value) -> { + if (value != 0) { + int first = value & 0x0F; + int second = (value & 0xF0) >> 4; + adds.write(first); + adds.write(second); + } else { + adds.write(0); + adds.write(0); + } + }); + + StreamDelegate biomesDelegate = schematic.add("Biomes"); + StreamDelegate aweBiomesDelegate = schematic.add("AWEBiomes"); + + InfoReader biomesInfo = (l, t) -> { + biomesOut = new FastByteArrayOutputStream(); + biomes = new FaweOutputStream(new LZ4BlockOutputStream(biomesOut)); + }; + biomesDelegate.withInfo(biomesInfo); + aweBiomesDelegate.withInfo(biomesInfo); + + IntValueReader biomeReader = (index, value) -> biomes.write(value); + biomesDelegate.withInt(biomeReader); + + + StreamDelegate tilesDelegate = schematic.add("TileEntities"); + tilesDelegate.withInfo((length, type) -> tiles = new ArrayList<>(length)); + tilesDelegate.withElem(new ValueReader>() { + @Override + public void apply(int index, Map tile) { + tiles.add(tile); + } + }); + + StreamDelegate entitiesDelegate = schematic.add("Entities"); + entitiesDelegate.withInfo((length, type) -> entities = new ArrayList<>(length)); + entitiesDelegate.withElem(new ValueReader>() { + @Override + public void apply(int index, Map entity) { + entities.add(entity); + } + }); + return root; } - public Clipboard read(final UUID clipboardId) throws IOException { - try { - return new SchematicStreamer(inputStream, clipboardId).getClipboard(); - } catch (Exception e) { - Fawe.debug("Input is corrupt!"); - e.printStackTrace(); - return new CorruptSchematicStreamer(rootStream, clipboardId).recover(); + private int readCombined(InputStream idIn, InputStream dataIn) throws IOException { + return ((idIn.read() & 0xFF) << 4) + (dataIn.read() & 0xF); + } + + private int readCombined(InputStream idIn, InputStream dataIn, InputStream addIn) throws IOException { + return ((addIn.read() & 0xFF) << 8) + readCombined(idIn, dataIn); + } + + private BlockState getBlock(int combined) { + BlockState state = LegacyMapper.getInstance().getBlockFromLegacyCombinedId(combined); + return state; + } + + private void write(int index, BlockState block, LinearClipboard clipboard) { + clipboard.setBlock(index, block); + } + + private void write(int x, int y, int z, BlockState block, Clipboard clipboard) { + clipboard.setBlock(x, y, z, block); + } + + private void readwrite(int index, InputStream idIn, InputStream dataIn, LinearClipboard out) throws IOException { + readwrite(index, readCombined(idIn, dataIn), out); + } + + private void readwrite(int x, int y, int z, InputStream idIn, InputStream dataIn, Clipboard out) throws IOException { + readwrite(x, y, z, readCombined(idIn, dataIn), out); + } + + private void readwrite(int index, InputStream idIn, InputStream dataIn, InputStream addIn, LinearClipboard out) throws IOException { + readwrite(index, readCombined(idIn, dataIn, addIn), out); + } + + private void readwrite(int x, int y, int z, InputStream idIn, InputStream dataIn, InputStream addIn, Clipboard out) throws IOException { + readwrite(x, y, z, readCombined(idIn, dataIn, addIn), out); + } + + private void readwrite(int index, int combined, LinearClipboard out) throws IOException { + write(index, getBlock(combined), out); + } + + private void readwrite(int x, int y, int z, int combined, Clipboard out) throws IOException { + write(x, y, z, getBlock(combined), out); + } + + @Override + public Clipboard read(UUID uuid, Function createOutput) throws IOException { + StreamDelegate root = createDelegate(); + inputStream.readNamedTagLazy(root); + + if (ids != null) ids.close(); + if (datas != null) datas.close(); + if (adds != null) adds.close(); + if (biomes != null) biomes.close(); + ids = null; + datas = null; + adds = null; + biomes = null; + + BlockVector3 dimensions = BlockVector3.at(width, height, length); + BlockVector3 origin = BlockVector3.at(originX, originY, originZ); + if (offsetX != Integer.MIN_VALUE && offsetY != Integer.MIN_VALUE && offsetZ != Integer.MIN_VALUE) { + origin = origin.subtract(BlockVector3.at(offsetX, offsetY, offsetZ)); + } + + Clipboard clipboard = createOutput.apply(dimensions); + try (InputStream dataIn = new LZ4BlockInputStream(new FastByteArraysInputStream(dataOut.toByteArrays()));InputStream idIn = new LZ4BlockInputStream(new FastByteArraysInputStream(idOut.toByteArrays()))) { + if (addOut != null) { + try (FaweInputStream addIn = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(addOut.toByteArrays())))) { + if (clipboard instanceof LinearClipboard) { + LinearClipboard linear = (LinearClipboard) clipboard; + for (int y = 0, index = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++, index++) { + readwrite(index, idIn, dataIn, addIn, linear); + } + } + } + } else { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + readwrite(x, y, z, idIn, dataIn, addIn, clipboard); + } + } + } + } + } + } else { + if (clipboard instanceof LinearClipboard) { + LinearClipboard linear = (LinearClipboard) clipboard; + for (int y = 0, index = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++, index++) { + readwrite(index, idIn, dataIn, linear); + } + } + } + } else { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + readwrite(x, y, z, idIn, dataIn, clipboard); + } + } + } + } + } + } + + if (biomes != null) { + try (InputStream biomesIn = new LZ4BlockInputStream(new FastByteArraysInputStream(biomesOut.toByteArrays()))) { + if (clipboard instanceof LinearClipboard) { + LinearClipboard linear = (LinearClipboard) clipboard; + int volume = width * length; + for (int index = 0; index < volume; index++) { + BiomeType biome = BiomeTypes.getLegacy(biomesIn.read()); + if (biome != null) linear.setBiome(index, biome); + } + } else { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + BiomeType biome = BiomeTypes.getLegacy(biomesIn.read()); + if (biome != null) clipboard.setBiome(x, 0, z, biome); + } + } + } + } + } + + // tiles + if (tiles != null && !tiles.isEmpty()) { + outer: + for (Map tileRaw : tiles) { + CompoundTag tile = FaweCache.IMP.asTag(tileRaw); + int x = (int) tileRaw.get("x"); + int y = (int) tileRaw.get("y"); + int z = (int) tileRaw.get("z"); + + BlockState block = clipboard.getBlock(x, y, z); + for (NBTCompatibilityHandler compat : COMPATIBILITY_HANDLERS) { + if (compat.isAffectedBlock(block)) { + block = compat.updateNBT(block, tile.getValue()); + BaseBlock baseBlock = block.toBaseBlock(tile); + clipboard.setBlock(x, y, z, baseBlock); + continue outer; + } + } + clipboard.setTile(x, y, z, tile); + } + } + + // entities + if (entities != null && !entities.isEmpty()) { + for (Map entRaw : entities) { + String id = (String) entRaw.get("id"); + if (id == null) { + continue; + } + entRaw.put("Id", id); + EntityType type = EntityTypes.parse(id); + if (type != null) { + CompoundTag ent = FaweCache.IMP.asTag(entRaw); + for (EntityNBTCompatibilityHandler compat : ENTITY_COMPATIBILITY_HANDLERS) { + if (compat.isAffectedEntity(type, ent)) { + ent = compat.updateNBT(type, ent); + } + } + BaseEntity state = new BaseEntity(type, ent); + Location loc = ent.getEntityLocation(clipboard); + clipboard.createEntity(loc, state); + } else { + Fawe.debug("Invalid entity: " + id); + } + } + } + fixStates(clipboard); + clipboard.setOrigin(origin); + return clipboard; + } + + private void fixStates(Clipboard fc) { + for (BlockVector3 pos : fc) { + BlockState block = pos.getBlock(fc); + if (block.getMaterial().isAir()) continue; + + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); + + BlockType type = block.getBlockType(); + if (BlockCategories.STAIRS.contains(type)) { + Direction facing = block.getState(PropertyKey.FACING); + + BlockVector3 forward = facing.toBlockVector(); + Direction left = facing.getLeft(); + Direction right = facing.getRight(); + + BlockStateHolder forwardBlock = fc.getBlock(x + forward.getBlockX(), y + forward.getBlockY(), z + forward.getBlockZ()); + BlockType forwardType = forwardBlock.getBlockType(); + if (forwardType.hasProperty(PropertyKey.SHAPE) && forwardType.hasProperty(PropertyKey.FACING)) { + Direction forwardFacing = (Direction) forwardBlock.getState(PropertyKey.FACING); + if (forwardFacing == left) { + BlockState rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); + BlockType rightType = rightBlock.getBlockType(); + if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { + pos.setBlock(fc, block.with(PropertyKey.SHAPE, "inner_left")); + } + return; + } else if (forwardFacing == right) { + BlockState leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); + BlockType leftType = leftBlock.getBlockType(); + if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { + fc.setBlock(x, y, z, block.with(PropertyKey.SHAPE, "inner_right")); + } + return; + } + } + + BlockState backwardsBlock = fc.getBlock(x - forward.getBlockX(), y - forward.getBlockY(), z - forward.getBlockZ()); + BlockType backwardsType = backwardsBlock.getBlockType(); + if (backwardsType.hasProperty(PropertyKey.SHAPE) && backwardsType.hasProperty(PropertyKey.FACING)) { + Direction backwardsFacing = (Direction) backwardsBlock.getState(PropertyKey.FACING); + if (backwardsFacing == left) { + BlockState rightBlock = fc.getBlock(x + right.toBlockVector().getBlockX(), y + right.toBlockVector().getBlockY(), z + right.toBlockVector().getBlockZ()); + BlockType rightType = rightBlock.getBlockType(); + if (!rightType.hasProperty(PropertyKey.SHAPE) || rightBlock.getState(PropertyKey.FACING) != facing) { + pos.setBlock(fc, block.with(PropertyKey.SHAPE, "outer_left")); + } + return; + } else if (backwardsFacing == right) { + BlockState leftBlock = fc.getBlock(x + left.toBlockVector().getBlockX(), y + left.toBlockVector().getBlockY(), z + left.toBlockVector().getBlockZ()); + BlockType leftType = leftBlock.getBlockType(); + if (!leftType.hasProperty(PropertyKey.SHAPE) || leftBlock.getState(PropertyKey.FACING) != facing) { + pos.setBlock(fc, block.with(PropertyKey.SHAPE, "outer_right")); + } + return; + } + } + } else { + int group = group(type); + if (group == -1) return; + BlockState set = block; + + if (set.getState(PropertyKey.NORTH) == Boolean.FALSE && merge(fc, group, x, y, z - 1)) set = set.with(PropertyKey.NORTH, true); + if (set.getState(PropertyKey.EAST) == Boolean.FALSE && merge(fc, group, x + 1, y, z)) set = set.with(PropertyKey.EAST, true); + if (set.getState(PropertyKey.SOUTH) == Boolean.FALSE && merge(fc, group, x, y, z + 1)) set = set.with(PropertyKey.SOUTH, true); + if (set.getState(PropertyKey.WEST) == Boolean.FALSE && merge(fc, group, x - 1, y, z)) set = set.with(PropertyKey.WEST, true); + + if (group == 2) { + int ns = ((Boolean) set.getState(PropertyKey.NORTH) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.SOUTH) ? 1 : 0); + int ew = ((Boolean) set.getState(PropertyKey.EAST) ? 1 : 0) + ((Boolean) set.getState(PropertyKey.WEST) ? 1 : 0); + if (Math.abs(ns - ew) != 2 || fc.getBlock(x, y + 1, z).getBlockType().getMaterial().isSolid()) { + set = set.with(PropertyKey.UP, true); + } + } + + if (set != block) pos.setBlock(fc, set); + } } } + private BlockTypeSwitch fullCube = new BlockTypeSwitchBuilder<>(false).add(type -> { + BlockMaterial mat = type.getMaterial(); + return (mat.isFullCube() && !mat.isFragileWhenPushed() && mat.getLightValue() == 0 && mat.isOpaque() && mat.isSolid() && !mat.isTranslucent()); + }, true).build(); + + private boolean merge(Clipboard fc, int group, int x, int y, int z) { + BlockState block = fc.getBlock(x, y, z); + BlockType type = block.getBlockType(); + return group(type) == group || fullCube.apply(type); + } + + private int group(BlockType type) { + switch (type.getInternalId()) { + case BlockID.ACACIA_FENCE: + case BlockID.BIRCH_FENCE: + case BlockID.DARK_OAK_FENCE: + case BlockID.JUNGLE_FENCE: + case BlockID.OAK_FENCE: + case BlockID.SPRUCE_FENCE: + return 0; + case BlockID.NETHER_BRICK_FENCE: + return 1; + case BlockID.COBBLESTONE_WALL: + case BlockID.MOSSY_COBBLESTONE_WALL: + return 2; + case BlockID.IRON_BARS: + case BlockID.BLACK_STAINED_GLASS_PANE: + case BlockID.BLUE_STAINED_GLASS_PANE: + case BlockID.BROWN_MUSHROOM_BLOCK: + case BlockID.BROWN_STAINED_GLASS_PANE: + case BlockID.CYAN_STAINED_GLASS_PANE: + case BlockID.GLASS_PANE: + case BlockID.GRAY_STAINED_GLASS_PANE: + case BlockID.GREEN_STAINED_GLASS_PANE: + case BlockID.LIGHT_BLUE_STAINED_GLASS_PANE: + case BlockID.LIGHT_GRAY_STAINED_GLASS_PANE: + case BlockID.LIME_STAINED_GLASS_PANE: + case BlockID.MAGENTA_STAINED_GLASS_PANE: + case BlockID.ORANGE_STAINED_GLASS_PANE: + case BlockID.PINK_STAINED_GLASS_PANE: + case BlockID.PURPLE_STAINED_GLASS_PANE: + case BlockID.RED_STAINED_GLASS_PANE: + case BlockID.WHITE_STAINED_GLASS_PANE: + case BlockID.YELLOW_STAINED_GLASS_PANE: + return 3; + default: + return -1; + } + } + @Override public void close() throws IOException { inputStream.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java index abc2a70a2..cc666ae93 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java @@ -82,6 +82,21 @@ public class SpongeSchematicReader extends NBTSchematicReader { private DataFixer fixer = null; private int dataVersion = -1; + private FastByteArrayOutputStream blocksOut; + private FaweOutputStream blocks; + + private FastByteArrayOutputStream biomesOut; + private FaweOutputStream biomes; + + private List> tiles; + private List> entities; + + private int width, height, length; + private int offsetX, offsetY, offsetZ; + private char[] palette, biomePalette; + private BlockVector3 min; + + /** * Create a new instance. * @@ -92,11 +107,6 @@ public class SpongeSchematicReader extends NBTSchematicReader { this.inputStream = inputStream; } - private int width, height, length; - private int offsetX, offsetY, offsetZ; - private char[] palette, biomePalette; - private BlockVector3 min; - private String fix(String palettePart) { if (fixer == null || dataVersion == -1) return palettePart; return fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart, dataVersion); @@ -112,15 +122,6 @@ public class SpongeSchematicReader extends NBTSchematicReader { return fixer.fixUp(DataFixer.FixTypes.ENTITY, tag, dataVersion); } - private FastByteArrayOutputStream blocksOut; - private FaweOutputStream blocks; - - private FastByteArrayOutputStream biomesOut; - private FaweOutputStream biomes; - - private List> tiles; - private List> entities; - public StreamDelegate createDelegate() { StreamDelegate root = new StreamDelegate(); StreamDelegate schematic = root.add("Schematic"); @@ -136,22 +137,18 @@ public class SpongeSchematicReader extends NBTSchematicReader { metadata.add("WEOffsetZ").withInt((i, v) -> offsetZ = v); StreamDelegate paletteDelegate = schematic.add("Palette"); - paletteDelegate.withValue(new ValueReader>() { - @Override - public void apply(int ignore, Map v) { - palette = new char[v.size()]; - for (Map.Entry entry : v.entrySet()) { - BlockState state = null; - try { - String palettePart = entry.getKey(); - palettePart = fix(entry.getKey()); - state = BlockState.get(palettePart); - } catch (InputParseException e) { - e.printStackTrace(); - } - int index = (int) entry.getValue(); - palette[index] = (char) state.getOrdinal(); + paletteDelegate.withValue((ValueReader>) (ignore, v) -> { + palette = new char[v.size()]; + for (Entry entry : v.entrySet()) { + BlockState state = null; + try { + String palettePart = fix(entry.getKey()); + state = BlockState.get(palettePart); + } catch (InputParseException e) { + e.printStackTrace(); } + int index = (int) entry.getValue(); + palette[index] = (char) state.getOrdinal(); } }); StreamDelegate blockData = schematic.add("BlockData"); @@ -159,58 +156,28 @@ public class SpongeSchematicReader extends NBTSchematicReader { blocksOut = new FastByteArrayOutputStream(); blocks = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut)); }); - blockData.withInt(new IntValueReader() { - @Override - public void applyInt(int index, int value) { - try { - blocks.writeVarInt(value); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); + blockData.withInt((index, value) -> blocks.writeVarInt(value)); StreamDelegate tilesDelegate = schematic.add("TileEntities"); tilesDelegate.withInfo((length, type) -> tiles = new ArrayList<>(length)); - tilesDelegate.withElem(new ValueReader>() { - @Override - public void apply(int index, Map tile) { - tiles.add(tile); - } - }); + tilesDelegate.withElem((ValueReader>) (index, tile) -> tiles.add(tile)); StreamDelegate entitiesDelegate = schematic.add("Entities"); entitiesDelegate.withInfo((length, type) -> entities = new ArrayList<>(length)); - entitiesDelegate.withElem(new ValueReader>() { - @Override - public void apply(int index, Map entity) { - entities.add(entity); - } - }); + entitiesDelegate.withElem((ValueReader>) (index, entity) -> entities.add(entity)); StreamDelegate biomeData = schematic.add("BiomeData"); - biomeData.withInfo(new InfoReader() { - @Override - public void apply(int length, int type) { - biomesOut = new FastByteArrayOutputStream(); - biomes = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut)); - } + biomeData.withInfo((length, type) -> { + biomesOut = new FastByteArrayOutputStream(); + biomes = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut)); }); - biomeData.withElem(new IntValueReader() { - @Override - public void applyInt(int index, int value) { - try { - biomes.write(value); // byte of varInt - } catch (IOException e) { - e.printStackTrace(); - } + biomeData.withElem((IntValueReader) (index, value) -> { + try { + biomes.write(value); // byte of varInt + } catch (IOException e) { + e.printStackTrace(); } }); StreamDelegate biomePaletteDelegate = schematic.add("BiomePalette"); - biomePaletteDelegate.withInfo(new InfoReader() { - @Override - public void apply(int length, int type) { - biomePalette = new char[length]; - } - }); + biomePaletteDelegate.withInfo((length, type) -> biomePalette = new char[length]); biomePaletteDelegate.withElem(new ValueReader>() { @Override public void apply(int index, Map.Entry palettePart) { @@ -220,7 +187,7 @@ public class SpongeSchematicReader extends NBTSchematicReader { } BiomeType biome = BiomeTypes.get(key); if (biome == null) { - System.out.println("Unknown biome " + key); + System. out.println("Unknown biome " + key); biome = BiomeTypes.FOREST; } int paletteIndex = palettePart.getValue().intValue(); @@ -244,13 +211,19 @@ public class SpongeSchematicReader extends NBTSchematicReader { public Clipboard read(UUID uuid, Function createOutput) throws IOException { StreamDelegate root = createDelegate(); inputStream.readNamedTagLazy(root); - BlockVector3 dimensions = BlockVector3.at(width, height, length); - Clipboard clipboard = createOutput.apply(dimensions); + if (blocks != null) blocks.close(); + if (biomes != null) biomes.close(); + blocks = null; + biomes = null; + BlockVector3 dimensions = BlockVector3.at(width, height, length); BlockVector3 origin = min; if (offsetX != Integer.MIN_VALUE && offsetY != Integer.MIN_VALUE && offsetZ != Integer.MIN_VALUE) { origin = origin.subtract(BlockVector3.at(offsetX, offsetY, offsetZ)); } + + Clipboard clipboard = createOutput.apply(dimensions); + if (blocksOut != null && blocksOut.getSize() != 0) { try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(blocksOut.toByteArrays())))) { if (clipboard instanceof LinearClipboard) { @@ -311,7 +284,6 @@ public class SpongeSchematicReader extends NBTSchematicReader { int[] pos = tile.getIntArray("Pos"); int x,y,z; if (pos.length != 3) { - System.out.println("Invalid tile " + tile); if (!tile.containsKey("x") || !tile.containsKey("y") || !tile.containsKey("z")) { return null; } @@ -371,109 +343,6 @@ public class SpongeSchematicReader extends NBTSchematicReader { return clipboard; } - /* - private Clipboard readVersion2(BlockArrayClipboard version1, CompoundTag schematicTag) throws IOException { - Map schematic = schematicTag.getValue(); - if (schematic.containsKey("BiomeData")) { - readBiomes(version1, schematic); - } - if (schematic.containsKey("Entities")) { - readEntities(version1, schematic); - } - return version1; - } - */ - - private void readBiomes(BlockArrayClipboard clipboard, Map schematic) throws IOException { - ByteArrayTag dataTag = requireTag(schematic, "BiomeData", ByteArrayTag.class); - IntTag maxTag = requireTag(schematic, "BiomePaletteMax", IntTag.class); - CompoundTag paletteTag = requireTag(schematic, "BiomePalette", CompoundTag.class); - - Map palette = new HashMap<>(); - if (maxTag.getValue() != paletteTag.getValue().size()) { - throw new IOException("Biome palette size does not match expected size."); - } - - for (Entry palettePart : paletteTag.getValue().entrySet()) { - String key = palettePart.getKey(); - if (fixer != null) { - key = fixer.fixUp(DataFixer.FixTypes.BIOME, key, dataVersion); - } - BiomeType biome = BiomeTypes.get(key); - if (biome == null) { - log.warn("Unknown biome type :" + key + - " in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?"); - } - Tag idTag = palettePart.getValue(); - if (!(idTag instanceof IntTag)) { - throw new IOException("Biome mapped to non-Int tag."); - } - palette.put(((IntTag) idTag).getValue(), biome); - } - - int width = clipboard.getDimensions().getX(); - - byte[] biomes = dataTag.getValue(); - int biomeIndex = 0; - int biomeJ = 0; - int bVal; - int varIntLength; - BlockVector2 min = clipboard.getMinimumPoint().toBlockVector2(); - while (biomeJ < biomes.length) { - bVal = 0; - varIntLength = 0; - - while (true) { - bVal |= (biomes[biomeJ] & 127) << (varIntLength++ * 7); - if (varIntLength > 5) { - throw new IOException("VarInt too big (probably corrupted data)"); - } - if (((biomes[biomeJ] & 128) != 128)) { - biomeJ++; - break; - } - biomeJ++; - } - int z = biomeIndex / width; - int x = biomeIndex % width; - BiomeType type = palette.get(bVal); - clipboard.setBiome(min.add(x, z), type); - biomeIndex++; - } - } - - /* - private void readEntities(BlockArrayClipboard clipboard, Map schematic) throws IOException { - List entList = requireTag(schematic, "Entities", ListTag.class).getValue(); - if (entList.isEmpty()) { - return; - } - for (Tag et : entList) { - if (!(et instanceof CompoundTag)) { - continue; - } - CompoundTag entityTag = (CompoundTag) et; - Map tags = entityTag.getValue(); - String id = requireTag(tags, "Id", StringTag.class).getValue(); - entityTag = entityTag.createBuilder().putString("id", id).remove("Id").build(); - - if (fixer != null) { - entityTag = fixer.fixUp(DataFixer.FixTypes.ENTITY, entityTag, dataVersion); - } - - EntityType entityType = EntityTypes.get(id); - if (entityType != null) { - Location location = NBTConversions.toLocation(clipboard, - requireTag(tags, "Pos", ListTag.class), - requireTag(tags, "Rotation", ListTag.class)); - BaseEntity state = new BaseEntity(entityType, entityTag); - clipboard.createEntity(location, state); - } else { - log.warn("Unknown entity when pasting schematic: " + id); - } - } - } - */ @Override public void close() throws IOException { inputStream.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java index 0c4feae79..f86b04d9b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java @@ -57,7 +57,7 @@ import java.util.Map; public final class LegacyMapper { private static final Logger log = LoggerFactory.getLogger(LegacyMapper.class); - private static LegacyMapper INSTANCE; + private static LegacyMapper INSTANCE = new LegacyMapper(); private final Int2ObjectArrayMap blockStateToLegacyId4Data = new Int2ObjectArrayMap<>(); private final Int2ObjectArrayMap extraId4DataToStateId = new Int2ObjectArrayMap<>(); @@ -267,10 +267,7 @@ public final class LegacyMapper { } } - public static LegacyMapper getInstance() { - if (INSTANCE == null) { - INSTANCE = new LegacyMapper(); - } + public final static LegacyMapper getInstance() { return INSTANCE; }