From f0ab1d5f185270f6dcd9ea2cbf9bac1dd47155f1 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Fri, 1 Nov 2019 18:29:13 +0100 Subject: [PATCH] finish overhaul of NBT stream api --- .../com/boydti/fawe/jnbt/NBTStreamer.java | 90 ----- .../boydti/fawe/jnbt/SchematicStreamer.java | 24 +- .../boydti/fawe/jnbt/streamer/ElemReader.java | 5 + .../boydti/fawe/jnbt/streamer/InfoReader.java | 10 + .../fawe/jnbt/streamer/IntValueReader.java | 10 + .../boydti/fawe/jnbt/streamer/LazyReader.java | 9 + .../fawe/jnbt/streamer/LongValueReader.java | 10 + .../boydti/fawe/jnbt/streamer/ReaderType.java | 7 + .../fawe/jnbt/streamer/StreamDelegate.java | 224 ++++++++++++ .../fawe/jnbt/streamer/StreamReader.java | 5 + .../fawe/jnbt/streamer/ValueReader.java | 21 ++ .../visualization/cfi/WritableMCAChunk.java | 71 +++- .../clipboard/CPUOptimizedClipboard.java | 9 +- .../object/clipboard/DelegateClipboard.java | 20 +- .../clipboard/DiskOptimizedClipboard.java | 6 +- .../object/clipboard/LinearClipboard.java | 13 +- .../clipboard/MemoryOptimizedClipboard.java | 13 +- .../object/clipboard/ReadOnlyClipboard.java | 7 - .../object/clipboard/WorldCopyClipboard.java | 2 +- .../object/clipboard/WorldCutClipboard.java | 2 +- .../java/com/sk89q/jnbt/NBTInputStream.java | 325 +++++++++++------- .../java/com/sk89q/jnbt/anvil/MCAWorld.java | 4 - .../extent/clipboard/BlockArrayClipboard.java | 7 - .../worldedit/extent/clipboard/Clipboard.java | 8 +- .../clipboard/io/SpongeSchematicReader.java | 299 ++++++++-------- .../worldedit/session/ClipboardHolder.java | 6 +- 26 files changed, 755 insertions(+), 452 deletions(-) delete mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ElemReader.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/InfoReader.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/IntValueReader.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LazyReader.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LongValueReader.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ReaderType.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamDelegate.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamReader.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ValueReader.java delete mode 100644 worldedit-core/src/main/java/com/sk89q/jnbt/anvil/MCAWorld.java diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java deleted file mode 100644 index 7c0bd76a4..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.boydti.fawe.jnbt; - -import com.boydti.fawe.FaweCache; -import com.boydti.fawe.config.BBC; -import com.boydti.fawe.object.exception.FaweException; - -import com.sk89q.jnbt.NBTInputStream; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.function.BiConsumer; - -public class NBTStreamer { - private final NBTInputStream is; - private final HashMap readers; - - public NBTStreamer(NBTInputStream stream) { - this.is = stream; - readers = new HashMap<>(); - } - - /** - * Reads the entire stream and runs the applicable readers - * - * @throws IOException - */ - public void readFully() throws IOException { - is.readNamedTagLazy(readers::get); - is.close(); - } - - /** - * Reads the stream until all readers have been used
- * - Use readFully if you expect a reader to appear more than once - * - Can exit early without having reading the entire file - * - * @throws IOException - */ - public void readQuick() throws IOException { - try { - is.readNamedTagLazy(node -> { - if (readers.isEmpty()) { - throw FaweCache.MANUAL; - } - return readers.remove(node); - }); - } catch (FaweException ignore) {} - is.close(); - } - - public void addReader(String node, BiConsumer run) { - if (run instanceof NBTStreamReader) { - ((NBTStreamReader) run).init(node); - } - readers.put(node, run); - } - - public static abstract class NBTStreamReader implements BiConsumer { - private String node; - - public void init(String node) { - this.node = node; - } - - public String getNode() { - return node; - } - } - - public static abstract class ByteReader implements BiConsumer { - @Override - public void accept(Integer index, Integer value) { - run(index, value); - } - - public abstract void run(int index, int byteValue); - } - - public interface LazyReader extends BiConsumer {} - - public static abstract class LongReader implements BiConsumer { - @Override - public void accept(Integer index, Long value) { - run(index, value); - } - - public abstract void run(int index, long byteValue); - } -} 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 index 845b2a7cb..1e6eeedd4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java @@ -81,10 +81,10 @@ public class SchematicStreamer extends NBTStreamer { } }; - addReader("Schematic.Blocks.?", idInit); - addReader("Schematic.Data.?", dataInit); - addReader("Schematic.AddBlocks.?", addInit); - addReader("Schematic.Blocks.#", new ByteReader() { + 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 { @@ -94,7 +94,7 @@ public class SchematicStreamer extends NBTStreamer { } } }); - addReader("Schematic.Data.#", new ByteReader() { + addReader("Schematic.Data", NBTStreamer.ReadType.ELEM, new ByteReader() { @Override public void run(int index, int value) { try { @@ -104,7 +104,7 @@ public class SchematicStreamer extends NBTStreamer { } } }); - addReader("Schematic.AddBlocks.#", new ByteReader() { + addReader("Schematic.AddBlocks", NBTStreamer.ReadType.ELEM, new ByteReader() { @Override public void run(int index, int value) { if (value != 0) { @@ -134,13 +134,13 @@ public class SchematicStreamer extends NBTStreamer { if (fc == null) setupClipboard(length * width * height); } }; - addReader("Schematic.AWEBiomes.?", initializer23); - addReader("Schematic.Biomes.?", initializer23); - addReader("Schematic.AWEBiomes.#", biomeReader); // AWE stores as an int[] - addReader("Schematic.Biomes.#", biomeReader); // FAWE stores as a byte[] (4x smaller) + 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.#", (BiConsumer) (index, value) -> { + addReader("Schematic.TileEntities", NBTStreamer.ReadType.ELEM,(BiConsumer) (index, value) -> { if (fc == null) { setupClipboard(0); } @@ -150,7 +150,7 @@ public class SchematicStreamer extends NBTStreamer { fc.setTile(x, y, z, value); }); // Entities - addReader("Schematic.Entities.#", (BiConsumer) (index, compound) -> { + addReader("Schematic.Entities", NBTStreamer.ReadType.ELEM,(BiConsumer) (index, compound) -> { if (fc == null) { setupClipboard(0); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ElemReader.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ElemReader.java new file mode 100644 index 000000000..7019cef1d --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ElemReader.java @@ -0,0 +1,5 @@ +package com.boydti.fawe.jnbt.streamer; + +public interface ElemReader extends StreamReader { + void apply(int index, T value); +} 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 new file mode 100644 index 000000000..9b5dfdea0 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/InfoReader.java @@ -0,0 +1,10 @@ +package com.boydti.fawe.jnbt.streamer; + +public interface InfoReader extends StreamReader { + void apply(int length, int type); + + @Override + default void apply(int i, Integer value) { + 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 new file mode 100644 index 000000000..635d065ad --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/IntValueReader.java @@ -0,0 +1,10 @@ +package com.boydti.fawe.jnbt.streamer; + +public interface IntValueReader extends ValueReader { + void applyInt(int index, int value); + + @Override + default void apply(int index, Integer value) { + 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 new file mode 100644 index 000000000..ffe71ead2 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LazyReader.java @@ -0,0 +1,9 @@ +package com.boydti.fawe.jnbt.streamer; + +import com.sk89q.jnbt.NBTInputStream; + +import java.io.DataInputStream; + +public interface LazyReader extends StreamReader { + void apply(int index, NBTInputStream stream); +} 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 new file mode 100644 index 000000000..82e1ef8ef --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/LongValueReader.java @@ -0,0 +1,10 @@ +package com.boydti.fawe.jnbt.streamer; + +public interface LongValueReader extends ValueReader { + void applyLong(int index, long value); + + @Override + default void apply(int index, Long value) { + 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 new file mode 100644 index 000000000..28a91c27b --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ReaderType.java @@ -0,0 +1,7 @@ +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 new file mode 100644 index 000000000..7503db628 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamDelegate.java @@ -0,0 +1,224 @@ +package com.boydti.fawe.jnbt.streamer; + +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.NBTInputStream; + +import java.io.DataInputStream; +import java.io.IOException; + +public class StreamDelegate { + private static final byte[][] ZERO_KEYS = new byte[0][]; + private static final StreamDelegate[] ZERO_VALUES = new StreamDelegate[0]; + + private byte[] buffer; + private byte[][] keys; + private StreamDelegate[] values; + + private LazyReader lazyReader; + private ElemReader elemReader; + private InfoReader infoReader; + private ValueReader valueReader; + + public StreamDelegate() { + keys = ZERO_KEYS; + values = ZERO_VALUES; + } + + public StreamDelegate addAndGetParent(String name) { + add(name); + return this; + } + + public StreamDelegate add() { + return add(""); + } + + public StreamDelegate add(String name) { + return add(name, new StreamDelegate()); + } + + private StreamDelegate add(String name, StreamDelegate scope) { + if (valueReader != null) { + System.out.println("Scope " + name + " | " + scope + " may not run, as the stream is only read once, and a value reader is already set"); + } + byte[] bytes = name.getBytes(NBTConstants.CHARSET); + int maxSize = bytes.length; + + byte[][] tmpKeys = new byte[keys.length + 1][]; + StreamDelegate[] tmpValues = new StreamDelegate[keys.length + 1]; + tmpKeys[keys.length] = bytes; + tmpValues[keys.length] = scope; + + int i = 0; + for (; i < keys.length; i++) { + byte[] key = keys[i]; + if (key.length >= bytes.length) { + tmpKeys[i] = bytes; + tmpValues[i] = scope; + break; + } + tmpKeys[i] = key; + tmpValues[i] = values[i]; + maxSize = Math.max(maxSize, key.length); + } + for (; i < keys.length; i++) { + byte[] key = keys[i]; + tmpKeys[i + 1] = key; + tmpValues[i + 1] = values[i]; + maxSize = Math.max(maxSize, key.length); + } + + this.keys = tmpKeys; + this.values = tmpValues; + if (this.buffer == null || buffer.length < maxSize) { + buffer = new byte[maxSize]; + } + return scope; + } + + public StreamDelegate get0() { + if (keys.length > 0 && keys[0].length == 0) { + return values[0]; + } + return null; + } + + public StreamDelegate get(DataInputStream is) throws IOException { + int nameLength = is.readShort() & 0xFFFF; + if (nameLength == 0 && keys.length > 0 && keys[0].length == 0) { + return values[0]; + } + if (nameLength > buffer.length) { + is.skipBytes(nameLength); + return null; + } + int index = 0; + outer: + switch (keys.length) { + case 0: + break; + default: { + for (; index < keys.length; index++) { + byte[] key = keys[index]; + if (key.length < nameLength) continue; + if (key.length == nameLength) { + break; + } else { + break outer; + } + } + if (index != keys.length - 1) { + int max; + for (max = index + 1; max < keys.length;) { + byte[] key = keys[max]; + if (key.length == nameLength) { + max++; + continue; + } + break; + } + if (index != max) { + is.readFully(buffer, 0, nameLength); + middle: + for (int i = index; i < max; i++) { + byte[] key = keys[i]; + for (int j = 0; j < nameLength; j++) { + if (buffer[j] != key[j]) { + continue middle; + } + } + return values[i]; + } + return null; + } + } + } + case 1: { + byte[] key = keys[index]; + if (key.length == nameLength) { + int i = 0; + for (; nameLength > 0; nameLength--, i++) { + byte b = is.readByte(); + buffer[i] = b; + if (b != key[i]) { + nameLength--; + break outer; + } + + } + return values[index]; + } + break; + } + } + is.skipBytes(nameLength); + return null; + } + + public StreamDelegate withLong(LongValueReader valueReader) { + return withValue(valueReader); + } + + public StreamDelegate withInt(IntValueReader valueReader) { + return withValue(valueReader); + } + + public StreamDelegate withValue(ValueReader valueReader) { + if (keys.length != 0) { + System.out.println("Reader " + valueReader + " may not run, as the stream is only read once, and a value reader is already set"); + } + this.valueReader = valueReader; + return this; + } + + public StreamDelegate withStream(LazyReader lazyReader) { + this.lazyReader = lazyReader; + return this; + } + + public StreamDelegate withElem(ElemReader elemReader) { + this.elemReader = elemReader; + return this; + } + + public StreamDelegate withInfo(InfoReader infoReader) { + this.infoReader = infoReader; + return this; + } + + public void acceptRoot(NBTInputStream is, int type, int depth) throws IOException { + if (lazyReader != null) { + lazyReader.apply(0, is); + } else if (elemReader != null) { + Object raw = is.readTagPaylodRaw(type, depth); + elemReader.apply(0, raw); + } else if (valueReader != null) { + Object raw = is.readTagPaylodRaw(type, depth); + valueReader.apply(0, raw); + } else { + is.readTagPaylodLazy(type, depth + 1, this); + } + } + + public ValueReader getValueReader() { + return valueReader; + } + + public ElemReader getElemReader() { + return elemReader; + } + + public void acceptInfo(int length, int type) { + if (infoReader != null) { + infoReader.apply(length, type); + } + } + + public boolean acceptLazy(int length, NBTInputStream is) { + if (lazyReader != null) { + lazyReader.apply(length, is); + return true; + } + return false; + } +} 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 new file mode 100644 index 000000000..79b17c92e --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/StreamReader.java @@ -0,0 +1,5 @@ +package com.boydti.fawe.jnbt.streamer; + +public interface StreamReader { + void apply(int i, T value); +} 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 new file mode 100644 index 000000000..6ff8750ee --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/streamer/ValueReader.java @@ -0,0 +1,21 @@ +package com.boydti.fawe.jnbt.streamer; + +public interface ValueReader extends StreamReader { + void apply(int index, T value); + + default void applyInt(int index, int value) { + apply(index, (T) (Integer) value); + } + + default void applyLong(int index, long value) { + apply(index, (T) (Long) value); + } + + default void applyFloat(int index, float value) { + apply(index, (T) (Float) value); + } + + default void applyDouble(int index, double value) { + apply(index, (T) (Double) value); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java index 67d3f5fcb..41b50a67a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java @@ -13,6 +13,8 @@ import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTConstants; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; @@ -31,6 +33,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -55,7 +58,27 @@ public class WritableMCAChunk implements IChunkSet { public WritableMCAChunk() {} + private boolean readLayer(Section section) { + if (section.palette == null || section.palette[section.palette.length - 1] == null || section.layer == -1 || section.blocks == null) { + // not initialized + return false; + } + // TODO load + section.layer = -1; + section.blocks = null; + section.palette = null; + return true; + } + + private static class Section { + public int layer = -1; + public long[] blocks; + public BlockState[] palette; + } + public WritableMCAChunk(NBTInputStream nis, int chunkX, int chunkZ, boolean readPos) throws IOException { + this.chunkX = chunkX; + this.chunkZ = chunkZ; NBTStreamer streamer = new NBTStreamer(nis); streamer.addReader(".Level.InhabitedTime", new RunnableVal2() { @Override @@ -69,21 +92,49 @@ public class WritableMCAChunk implements IChunkSet { lastUpdate = value; } }); - streamer.addReader(".Level.Sections.#", new RunnableVal2() { + + + Section section = new Section(); + streamer.addReader(".Level.Sections.Y", new RunnableVal2() { @Override - public void run(Integer index, CompoundTag tag) { - int layer = tag.getByte("Y"); - // "Palette" + public void run(Integer index, Byte y) { + section.layer = y; + readLayer(section); } }); - streamer.addReader(".Level.Sections.Palette.#", new RunnableVal2() { + streamer.addReader(".Level.Sections.Palette", NBTStreamer.ReadType.ELEM,new RunnableVal2() { @Override - public void run(Integer index, CompoundTag entry) { - String name = entry.getString("Name"); - entry. + public void run(Integer index, CompoundTag compound) { + String name = compound.getString("Name"); + BlockType type = BlockTypes.get(name); + BlockState state = type.getDefaultState(); + CompoundTag properties = (CompoundTag) compound.getValue().get("Properties"); + if (properties != null) { + for (Map.Entry entry : properties.getValue().entrySet()) { + String key = entry.getKey(); + String value = ((StringTag) entry.getValue()).getValue(); + Property property = type.getProperty(key); + state = state.with(property, property.getValueFor(value)); + } + } + section.palette[index] = state; + readLayer(section); } }); - streamer.addReader(".Level.TileEntities.#", new RunnableVal2() { + streamer.addReader(".Level.Sections", NBTStreamer.ReadType.INFO,new RunnableVal2() { + @Override + public void run(Integer value1, Integer value2) { + section.layer = -1; + } + }); + streamer.addReader(".Level.Sections.BlockStates", new RunnableVal2() { + @Override + public void run(Integer value1, long[] values) { + section.blocks = values; + readLayer(section); + } + }); + streamer.addReader(".Level.TileEntities", NBTStreamer.ReadType.ELEM,new RunnableVal2() { @Override public void run(Integer index, CompoundTag tile) { int x = tile.getInt("x") & 15; @@ -92,7 +143,7 @@ public class WritableMCAChunk implements IChunkSet { tiles.put(x, y, z, tile); } }); - streamer.addReader(".Level.Entities.#", new RunnableVal2() { + streamer.addReader(".Level.Entities", NBTStreamer.ReadType.ELEM,new RunnableVal2() { @Override public void run(Integer index, CompoundTag entityTag) { long least = entityTag.getLong("UUIDLeast"); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java index 8fa1e4b14..aedb8e8aa 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java @@ -1,6 +1,6 @@ package com.boydti.fawe.object.clipboard; -import com.boydti.fawe.jnbt.NBTStreamer; +import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.boydti.fawe.object.IntegerTrio; import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.jnbt.CompoundTag; @@ -8,7 +8,6 @@ import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.Location; @@ -16,8 +15,6 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; 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.BlockTypes; import javax.annotation.Nullable; import java.util.ArrayList; @@ -67,12 +64,12 @@ public class CPUOptimizedClipboard extends LinearClipboard { } @Override - public void streamBiomes(NBTStreamer.ByteReader task) { + public void streamBiomes(IntValueReader task) { if (!hasBiomes()) return; int index = 0; for (int z = 0; z < getLength(); z++) { for (int x = 0; x < getWidth(); x++, index++) { - task.run(index, biomes[index].getInternalId()); + task.applyInt(index, biomes[index].getInternalId()); } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java index 58fae0c0f..1cb9b41d5 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java @@ -1,37 +1,22 @@ package com.boydti.fawe.object.clipboard; -import com.boydti.fawe.beta.IBatchProcessor; -import com.boydti.fawe.jnbt.NBTStreamer; -import com.boydti.fawe.object.changeset.FaweChangeSet; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.function.generator.GenBase; -import com.sk89q.worldedit.function.generator.Resource; -import com.sk89q.worldedit.function.mask.Mask; -import com.sk89q.worldedit.function.operation.Operation; -import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockType; import javax.annotation.Nullable; import java.net.URI; import java.util.List; -import java.util.Set; import java.util.UUID; public class DelegateClipboard implements Clipboard { @@ -172,4 +157,9 @@ public class DelegateClipboard implements Clipboard { public boolean setBiome(int x, int y, int z, BiomeType biome) { return parent.setBiome(x, y, z, biome); } + + @Override + public void close() { + parent.close(); + } } 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 b71749e9d..0624ab354 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 @@ -2,7 +2,7 @@ package com.boydti.fawe.object.clipboard; import com.boydti.fawe.Fawe; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.jnbt.NBTStreamer; +import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.boydti.fawe.object.IntegerTrio; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.ReflectionUtils; @@ -197,14 +197,14 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable } @Override - public void streamBiomes(NBTStreamer.ByteReader task) { + public void streamBiomes(IntValueReader task) { 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.run(index, biome); + task.applyInt(index, biome); } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/LinearClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/LinearClipboard.java index 217353184..a7ffbd996 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/LinearClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/LinearClipboard.java @@ -1,29 +1,20 @@ package com.boydti.fawe.object.clipboard; import com.boydti.fawe.beta.AbstractFilterBlock; -import com.boydti.fawe.beta.SingleFilterBlock; -import com.boydti.fawe.jnbt.NBTStreamer; -import com.boydti.fawe.util.ReflectionUtils; +import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.google.common.collect.ForwardingIterator; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.util.collection.DoubleArrayList; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import java.io.Closeable; -import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; /** * Best used when clipboard selections are small, or using legacy formats @@ -48,7 +39,7 @@ public abstract class LinearClipboard extends SimpleClipboard implements Clipboa * @param task * @param air */ - public abstract void streamBiomes(NBTStreamer.ByteReader task); + public abstract void streamBiomes(IntValueReader task); public abstract Collection getTileEntities(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java index 6cae8c40d..7f668991f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java @@ -1,7 +1,7 @@ package com.boydti.fawe.object.clipboard; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.jnbt.NBTStreamer; +import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.boydti.fawe.object.IntegerTrio; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.ReflectionUtils; @@ -10,7 +10,6 @@ import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.Location; @@ -20,6 +19,8 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; + +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -29,10 +30,6 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import net.jpountz.util.SafeUtils; - -import javax.annotation.Nullable; - public class MemoryOptimizedClipboard extends LinearClipboard { private static final int BLOCK_SIZE = 1048576 * 2; @@ -101,12 +98,12 @@ public class MemoryOptimizedClipboard extends LinearClipboard { } @Override - public void streamBiomes(NBTStreamer.ByteReader task) { + public void streamBiomes(IntValueReader task) { if (!hasBiomes()) return; int index = 0; for (int z = 0; z < getLength(); z++) { for (int x = 0; x < getWidth(); x++, index++) { - task.run(index, biomes[index] & 0xFF); + task.applyInt(index, biomes[index] & 0xFF); } } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java index f901efea7..620c582ba 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java @@ -1,21 +1,14 @@ package com.boydti.fawe.object.clipboard; -import com.boydti.fawe.jnbt.NBTStreamer; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; -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.regions.Region; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; -import javax.annotation.Nullable; import java.util.List; public abstract class ReadOnlyClipboard extends SimpleClipboard { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java index ff3d07741..43a9a45d8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java @@ -72,7 +72,7 @@ public class WorldCopyClipboard extends ReadOnlyClipboard { } @Override - public void close() throws IOException { + public void close() { } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCutClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCutClipboard.java index 2e8a3e797..33d3c28a6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCutClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCutClipboard.java @@ -35,7 +35,7 @@ public class WorldCutClipboard extends WorldCopyClipboard { } @Override - public void close() throws IOException { + public void close() { if (extent instanceof EditSession) { ((EditSession) extent).flushQueue(); } else { diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java index 10c79cda0..b8e76867d 100644 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java +++ b/worldedit-core/src/main/java/com/sk89q/jnbt/NBTInputStream.java @@ -19,7 +19,9 @@ package com.sk89q.jnbt; -import com.boydti.fawe.jnbt.NBTStreamer; +import com.boydti.fawe.jnbt.streamer.ElemReader; +import com.boydti.fawe.jnbt.streamer.StreamDelegate; +import com.boydti.fawe.jnbt.streamer.ValueReader; import java.io.Closeable; import java.io.DataInputStream; @@ -29,8 +31,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Function; /** * This class reads NBT, or Named Binary Tag @@ -86,19 +86,23 @@ public final class NBTInputStream implements Closeable { return readTagPayload(type, 0); } - public void readNamedTagLazy(Function getReader) throws IOException { - int type = is.readByte(); - String name = readNamedTagName(type); - BiConsumer reader = getReader.apply(name); - if (reader != null) { - reader.accept(0, readTagPaylodRaw(type, 0)); - return; + public void readNamedTagLazy(StreamDelegate scope) throws IOException { + try { + int type = is.readByte(); + if (type == NBTConstants.TYPE_END) return; + + StreamDelegate child = scope.get(is); + if (child != null) { + child.acceptRoot(this, type, 0); + } else { + readTagPaylodLazy(type, 0); + } + } catch (Throwable e) { + e.printStackTrace(); } - readTagPaylodLazy(type, 0, name, getReader); } public String readNamedTagName(int type) throws IOException { - String name; if (type != NBTConstants.TYPE_END) { int nameLength = is.readShort() & 0xFFFF; byte[] nameBytes = new byte[nameLength]; @@ -111,7 +115,7 @@ public final class NBTInputStream implements Closeable { private byte[] buf; - public void readTagPaylodLazy(int type, int depth, String node, Function getReader) throws IOException { + public void readTagPaylodLazy(int type, int depth) throws IOException { switch (type) { case NBTConstants.TYPE_END: return; @@ -134,22 +138,121 @@ public final class NBTInputStream implements Closeable { is.skipBytes(8); return; case NBTConstants.TYPE_STRING: - int length = is.readShort(); + int length = is.readShort() & 0xFFFF; is.skipBytes(length); return; case NBTConstants.TYPE_BYTE_ARRAY: - BiConsumer reader = getReader.apply(node + ".?"); + is.skipBytes(is.readInt()); + return; + case NBTConstants.TYPE_LIST: { + int childType = is.readByte(); length = is.readInt(); - if (reader != null) { - reader.accept(length, NBTConstants.TYPE_BYTE); + for (int i = 0; i < length; ++i) { + readTagPaylodLazy(childType, depth + 1); } - reader = getReader.apply(node + ".#"); - if (reader == null) { + return; + } + case NBTConstants.TYPE_COMPOUND: { + // readDataPayload + depth++; + while(true) { + int childType = is.readByte(); + if (childType == NBTConstants.TYPE_END) { + return; + } + is.skipBytes(is.readShort() & 0xFFFF); + readTagPaylodLazy(childType, depth + 1); + } + } + case NBTConstants.TYPE_INT_ARRAY: { + is.skipBytes(is.readInt() << 2); + return; + } + case NBTConstants.TYPE_LONG_ARRAY: { + is.skipBytes(is.readInt() << 3); + return; + } + default: + throw new IOException("Invalid tag type: " + type + "."); + } + } + + public void readTagPaylodLazy(int type, int depth, StreamDelegate scope) throws IOException { + switch (type) { + case NBTConstants.TYPE_END: + return; + case NBTConstants.TYPE_BYTE: { + ValueReader value = scope.getValueReader(); + if (value != null) { + value.applyInt(0, is.readByte()); + } else { + is.skipBytes(1); + } + return; + } + case NBTConstants.TYPE_SHORT: { + ValueReader value = scope.getValueReader(); + if (value != null) { + value.applyInt(0, is.readShort()); + } else { + is.skipBytes(2); + } + return; + } + case NBTConstants.TYPE_INT: { + ValueReader value = scope.getValueReader(); + if (value != null) { + value.applyInt(0, is.readInt()); + } else { + is.skipBytes(4); + } + return; + } + case NBTConstants.TYPE_LONG: { + ValueReader value = scope.getValueReader(); + if (value != null) { + value.applyLong(0, is.readLong()); + } else { + is.skipBytes(8); + } + return; + } + case NBTConstants.TYPE_FLOAT: { + ValueReader value = scope.getValueReader(); + if (value != null) { + value.applyFloat(0, is.readFloat()); + } else { + is.skipBytes(4); + } + return; + } + case NBTConstants.TYPE_DOUBLE: { + ValueReader value = scope.getValueReader(); + if (value != null) { + value.applyDouble(0, is.readDouble()); + } else { + is.skipBytes(8); + } + return; + } + case NBTConstants.TYPE_STRING: { + ValueReader value = scope.getValueReader(); + int length = is.readShort() & 0xFFFF; + if (value != null) { + byte[] bytes = new byte[length]; + is.readFully(bytes); + value.apply(0, new String(bytes, NBTConstants.CHARSET)); + } else { is.skipBytes(length); - return; } - if (reader instanceof NBTStreamer.ByteReader) { - NBTStreamer.ByteReader byteReader = (NBTStreamer.ByteReader) reader; + return; + } + case NBTConstants.TYPE_BYTE_ARRAY: { + int length = is.readInt(); + scope.acceptInfo(length, NBTConstants.TYPE_BYTE); + if (scope.acceptLazy(length, this)) return; + ValueReader valueReader = scope.getValueReader(); + if (valueReader != null) { int i = 0; DataInputStream dis = is; if (length > 1024) { @@ -160,109 +263,102 @@ public final class NBTInputStream implements Closeable { for (; left > 1024; left -= 1024) { dis.readFully(buf); for (byte b : buf) { - byteReader.run(i++, b & 0xFF); + valueReader.applyInt(i++, b & 0xFF); } } } for (; i < length; i++) { - byteReader.run(i, dis.read()); - } - } else if (reader instanceof NBTStreamer.LazyReader) { - reader.accept(length, is); - } else { - for (int i = 0; i < length; i++) { - reader.accept(i, is.readByte()); + valueReader.applyInt(i, dis.read()); } + return; } + is.skipBytes(length); return; - case NBTConstants.TYPE_LIST: + } + case NBTConstants.TYPE_LIST: { int childType = is.readByte(); - if (childType == NBTConstants.TYPE_LIST) { - childType = NBTConstants.TYPE_COMPOUND; - } - length = is.readInt(); - reader = getReader.apply(node + ".?"); - if (reader != null) { - reader.accept(length, childType); - } - node += ".#"; - reader = getReader.apply(node); - depth++; - if (reader == null) { + int length = is.readInt(); + StreamDelegate child; + scope.acceptInfo(length, childType); + ValueReader valueReader = scope.getValueReader(); + if (valueReader != null) { for (int i = 0; i < length; ++i) { - readTagPaylodLazy(childType, depth, node, getReader); + valueReader.apply(i, readTagPaylodRaw(childType, depth + 1)); } return; } - for (int i = 0; i < length; ++i) { - reader.accept(i, readTagPayload(childType, depth)); - } - return; - case NBTConstants.TYPE_COMPOUND: - depth++; - // 3 - for (int i = 0; ; i++) { - childType = is.readByte(); - if (childType == NBTConstants.TYPE_END) { - return; + child = scope.get0(); + if (child == null) { + for (int i = 0; i < length; ++i) { + readTagPaylodLazy(childType, depth + 1); } - String name = readNamedTagName(childType); - String childNode = node + "." + name; - reader = getReader.apply(childNode); - if (reader == null) { - readTagPaylodLazy(childType, depth, childNode, getReader); - continue; - } - reader.accept(i, readTagPaylodRaw(childType, depth)); - } - case NBTConstants.TYPE_INT_ARRAY: { - length = is.readInt(); - reader = getReader.apply(node + ".?"); - if (reader != null) { - reader.accept(length, NBTConstants.TYPE_INT); - } - reader = getReader.apply(node + ".#"); - if (reader == null) { - is.skipBytes(length << 2); - return; - } - if (reader instanceof NBTStreamer.ByteReader) { - NBTStreamer.ByteReader byteReader = (NBTStreamer.ByteReader) reader; - for (int i = 0; i < length; i++) { - byteReader.run(i, is.readInt()); - } - } else if (reader instanceof NBTStreamer.LazyReader) { - reader.accept(length, is); } else { - for (int i = 0; i < length; i++) { - reader.accept(i, is.readInt()); + for (int i = 0; i < length; ++i) { + readTagPaylodLazy(childType, depth + 1, child); } } return; } - case NBTConstants.TYPE_LONG_ARRAY: { - length = is.readInt(); - reader = getReader.apply(node + ".?"); - if (reader != null) { - reader.accept(length, NBTConstants.TYPE_LONG); - } - reader = getReader.apply(node + ".#"); - if (reader == null) { - is.skipBytes(length << 3); + case NBTConstants.TYPE_COMPOUND: { + // readDataPayload + depth++; + scope.acceptInfo(-1, NBTConstants.TYPE_BYTE); + ValueReader valueReader = scope.getValueReader(); + if (valueReader != null) { + valueReader.apply(0, this.readDataPayload(type, depth)); return; } - if (reader instanceof NBTStreamer.LongReader) { - NBTStreamer.LongReader longReader = (NBTStreamer.LongReader) reader; - for (int i = 0; i < length; i++) { - longReader.run(i, is.readLong()); - } - } else if (reader instanceof NBTStreamer.LazyReader) { - reader.accept(length, is); - } else { - for (int i = 0; i < length; i++) { - reader.accept(i, is.readLong()); + ElemReader elem = scope.getElemReader(); + if (elem != null) { + for (int i = 0; ; i++) { + int childType = is.readByte(); + if (childType == NBTConstants.TYPE_END) { + return; + } + is.skipBytes(is.readShort() & 0xFFFF); + Object child = readTagPaylodRaw(childType, depth); + elem.apply(i, child); } } + while(true) { + int childType = is.readByte(); + if (childType == NBTConstants.TYPE_END) { + return; + } + StreamDelegate child = scope.get(is); + if (child == null) { + readTagPaylodLazy(childType, depth + 1); + } else { + readTagPaylodLazy(childType, depth + 1, child); + } + } + } + case NBTConstants.TYPE_INT_ARRAY: { + int length = is.readInt(); + scope.acceptInfo(length, NBTConstants.TYPE_INT); + if (scope.acceptLazy(length, this)) return; + ValueReader valueReader = scope.getValueReader(); + if (valueReader != null) { + for (int i = 0; i < length; i++) { + valueReader.applyInt(i, is.readInt()); + } + return; + } + is.skipBytes(length << 2); + return; + } + case NBTConstants.TYPE_LONG_ARRAY: { + int length = is.readInt(); + scope.acceptInfo(length, NBTConstants.TYPE_LONG); + if (scope.acceptLazy(length, this)) return; + ValueReader valueReader = scope.getValueReader(); + if (valueReader != null) { + for (int i = 0; i < length; i++) { + valueReader.applyLong(i, is.readLong()); + } + return; + } + is.skipBytes(length << 3); return; } @@ -294,7 +390,7 @@ public final class NBTInputStream implements Closeable { } } - private Object readTagPaylodRaw(int type, int depth) throws IOException { + public Object readTagPaylodRaw(int type, int depth) throws IOException { switch (type) { case NBTConstants.TYPE_END: if (depth == 0) { @@ -327,9 +423,6 @@ public final class NBTInputStream implements Closeable { return (new String(bytes, NBTConstants.CHARSET)); case NBTConstants.TYPE_LIST: int childType = is.readByte(); - if (childType == NBTConstants.TYPE_LIST) { - childType = NBTConstants.TYPE_COMPOUND; - } length = is.readInt(); List tagList = new ArrayList<>(); for (int i = 0; i < length; ++i) { @@ -432,9 +525,6 @@ public final class NBTInputStream implements Closeable { return new StringTag(new String(bytes, NBTConstants.CHARSET)); case NBTConstants.TYPE_LIST: int childType = is.readByte(); - if (childType == NBTConstants.TYPE_LIST) { - childType = NBTConstants.TYPE_COMPOUND; - } length = is.readInt(); List tagList = new ArrayList<>(); for (int i = 0; i < length; ++i) { @@ -514,9 +604,6 @@ public final class NBTInputStream implements Closeable { return new String(bytes, NBTConstants.CHARSET); case NBTConstants.TYPE_LIST: int childType = is.readByte(); - if (childType == NBTConstants.TYPE_LIST) { - childType = NBTConstants.TYPE_COMPOUND; - } length = is.readInt(); ArrayList list = new ArrayList<>(); for (int i = 0; i < length; ++i) { @@ -526,22 +613,18 @@ public final class NBTInputStream implements Closeable { } list.add(obj); } - return list; case NBTConstants.TYPE_COMPOUND: Map map = new HashMap<>(); while (true) { int newType = is.readByte(); + if (newType == NBTConstants.TYPE_END) { + return map; + } String name = readNamedTagName(newType); Object data = readDataPayload(newType, depth + 1); - if (data == null) { - break; - } else { - map.put(name, data); - } + map.put(name, data); } - - return map; case NBTConstants.TYPE_INT_ARRAY: { length = is.readInt(); int[] data = new int[length]; diff --git a/worldedit-core/src/main/java/com/sk89q/jnbt/anvil/MCAWorld.java b/worldedit-core/src/main/java/com/sk89q/jnbt/anvil/MCAWorld.java deleted file mode 100644 index 506bda9eb..000000000 --- a/worldedit-core/src/main/java/com/sk89q/jnbt/anvil/MCAWorld.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.sk89q.jnbt.anvil; - -public class MCAWorld { -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java index bf341f006..8c4b6bee3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java @@ -77,13 +77,6 @@ public class BlockArrayClipboard extends DelegateClipboard implements Clipboard, this.origin = region.getMinimumPoint(); } - @Override - public void close() throws IOException { - if (getParent() instanceof Closeable) { - ((Closeable) getParent()).close(); - } - } - @Override public Region getRegion() { return region; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java index 6f8e17521..8d7bb89b4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java @@ -34,6 +34,7 @@ import com.sk89q.worldedit.regions.Regions; import com.sk89q.worldedit.util.Location; import javax.annotation.Nullable; +import java.io.Closeable; import java.net.URI; import java.util.Iterator; import java.util.UUID; @@ -41,7 +42,7 @@ import java.util.UUID; /** * Specifies an object that implements something suitable as a "clipboard." */ -public interface Clipboard extends Extent, Iterable { +public interface Clipboard extends Extent, Iterable, Closeable { static Clipboard create(BlockVector3 size, UUID uuid) { if (Settings.IMP.CLIPBOARD.USE_DISK) { return new DiskOptimizedClipboard(size, uuid); @@ -144,4 +145,9 @@ public interface Clipboard extends Extent, Iterable { return apply((Iterable) region, filter); } } + + @Override + default void 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 f7afdb6e4..2dce70ba4 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 @@ -19,56 +19,38 @@ package com.sk89q.worldedit.extent.clipboard.io; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.boydti.fawe.Fawe; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.jnbt.NBTStreamer; -import com.boydti.fawe.jnbt.NBTStreamer.LazyReader; -import com.boydti.fawe.object.FaweInputStream; -import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.jnbt.streamer.StreamDelegate; +import com.boydti.fawe.jnbt.streamer.ValueReader; 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.boydti.fawe.util.IOUtil; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.ListTag; 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.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; -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.BlockState; -import com.sk89q.worldedit.world.block.BlockTypes; -import com.sk89q.worldedit.world.entity.EntityType; -import com.sk89q.worldedit.world.entity.EntityTypes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; -import java.util.function.BiConsumer; import java.util.function.Function; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static com.google.common.base.Preconditions.checkNotNull; /** * Reads schematic files using the Sponge Schematic Specification. @@ -149,140 +131,151 @@ public class SpongeSchematicReader extends NBTSchematicReader { FastByteArrayOutputStream blocksOut = new FastByteArrayOutputStream(); FastByteArrayOutputStream biomesOut = new FastByteArrayOutputStream(); + StreamDelegate root = new StreamDelegate(); + StreamDelegate schematic = root.add("Schematic"); + schematic.add("DataVersion").withInt((i, v) -> dataVersion = v); + 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("Offset").withValue((ValueReader) (index, v) -> min = BlockVector3.at(v[0], v[1], v[2])); + + 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 paletteDelegate = schematic.add("Palette"); + paletteDelegate.withValue(new ValueReader>() { + @Override + public void apply(int index, Map value) { - NBTStreamer streamer = new NBTStreamer(inputStream); - streamer.addReader("Schematic.DataVersion", (BiConsumer) (i, v) -> dataVersion = v); - streamer.addReader("Schematic.Width", (BiConsumer) (i, v) -> width = v); - streamer.addReader("Schematic.Height", (BiConsumer) (i, v) -> height = v); - streamer.addReader("Schematic.Length", (BiConsumer) (i, v) -> length = v); - streamer.addReader("Schematic.Offset", (BiConsumer) (i, v) -> min = BlockVector3.at(v[0], v[1], v[2])); - streamer.addReader("Schematic.Metadata.WEOffsetX", (BiConsumer) (i, v) -> offsetX = v); - streamer.addReader("Schematic.Metadata.WEOffsetY", (BiConsumer) (i, v) -> offsetY = v); - streamer.addReader("Schematic.Metadata.WEOffsetZ", (BiConsumer) (i, v) -> offsetZ = v); - streamer.addReader("Schematic.Palette", (BiConsumer>) (i, v) -> { - palette = new char[v.size()]; - for (Map.Entry entry : v.entrySet()) { - BlockState state = null; - try { - String palettePart = fix(entry.getKey()); - state = BlockState.get(palettePart); - } catch (InputParseException e) { - e.printStackTrace(); - } - int index = ((IntTag) entry.getValue()).getValue(); - palette[index] = (char) state.getOrdinal(); } }); - /// readBiomes - - streamer.addReader("Schematic.BlockData.#", (LazyReader) (arrayLen, dis) -> { - try (FaweOutputStream blocks = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut))) { - IOUtil.copy(dis, blocks, arrayLen); - } catch (IOException e) { - e.printStackTrace(); - } - }); - streamer.addReader("Schematic.Biomes.#", (LazyReader) (arrayLen, dis) -> { - try (FaweOutputStream biomes = new FaweOutputStream(new LZ4BlockOutputStream(biomesOut))) { - IOUtil.copy(dis, biomes, arrayLen); - } catch (IOException e) { - e.printStackTrace(); - } - }); - streamer.addReader("Schematic.TileEntities.#", (BiConsumer) (index, value) -> { - if (fc == null) { - setupClipboard(0, uuid); - } - int[] pos = value.getIntArray("Pos"); - int x,y,z; - if (pos.length != 3) { - System.out.println("Invalid tile " + value); - if (!value.containsKey("x") || !value.containsKey("y") || !value.containsKey("z")) { - return; - } - x = value.getInt("x"); - y = value.getInt("y"); - z = value.getInt("z"); - } else { - x = pos[0]; - y = pos[1]; - z = pos[2]; - } - Map values = value.getValue(); - Tag id = values.get("Id"); - if (id != null) { - values.put("x", new IntTag(x)); - values.put("y", new IntTag(y)); - values.put("z", new IntTag(z)); - values.put("id", id); - } - values.remove("Id"); - values.remove("Pos"); - value = fixBlockEntity(value); - fc.setTile(x, y, z, value); - }); - streamer.addReader("Schematic.Entities.#", (BiConsumer) (index, compound) -> { - if (fc == null) { - setupClipboard(0, uuid); - } - Map value = compound.getValue(); - StringTag id = (StringTag) value.get("Id"); - if (id == null) { - id = (StringTag) value.get("id"); - if (id == null) { - return; - } - } else { - value.put("id", id); - value.remove("Id"); - } - - EntityType type = EntityTypes.parse(id.getValue()); - if (type != null) { - compound = fixEntity(compound); - BaseEntity state = new BaseEntity(type, compound); - Location loc = compound.getEntityLocation(fc); - fc.createEntity(loc, state); - } else { - Fawe.debug("Invalid entity: " + id); - } - }); - streamer.readFully(); - if (fc == null) setupClipboard(length * width * height, uuid); -// fc.setDimensions(BlockVector3.at(width, height, length)); - BlockVector3 origin = min; - CuboidRegion region; - if (offsetX != Integer.MIN_VALUE && offsetY != Integer.MIN_VALUE && offsetZ != Integer.MIN_VALUE) { - origin = origin.subtract(BlockVector3.at(offsetX, offsetY, offsetZ)); - } - region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); - if (blocksOut.getSize() != 0) { - try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(blocksOut.toByteArrays())))) { - int volume = width * height * length; - if (palette.length < 128) { - for (int index = 0; index < volume; index++) { - BlockState state = BlockTypes.states[palette[fis.read()]]; - fc.setBlock(index, state); - } - } else { - for (int index = 0; index < volume; index++) { - BlockState state = BlockTypes.states[palette[fis.readVarInt()]]; - fc.setBlock(index, state); - } - } - } - } - if (biomesOut.getSize() != 0) { - try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(biomesOut.toByteArrays())))) { - int volume = width * length; - for (int index = 0; index < volume; index++) { - fc.setBiome(index, BiomeTypes.get(fis.read())); - } - } - } -// clipboard.init(region, fc); - clipboard.setOrigin(origin); +// schematic.add("Palette", (BiConsumer>) (i, v) -> { +// palette = new char[v.size()]; +// for (Map.Entry entry : v.entrySet()) { +// BlockState state = null; +// try { +// String palettePart = fix(entry.getKey()); +// state = BlockState.get(palettePart); +// } catch (InputParseException e) { +// e.printStackTrace(); +// } +// int index = ((IntTag) entry.getValue()).getValue(); +// palette[index] = (char) state.getOrdinal(); +// } +// }); +// +// /// readBiomes +// +// streamer.addReader("Schematic.BlockData", NBTStreamer.ReadType.ELEM, (NBTStreamer.LazyReader) (arrayLen, dis) -> { +// try (FaweOutputStream blocks = new FaweOutputStream(new LZ4BlockOutputStream(blocksOut))) { +// IOUtil.copy(dis, blocks, arrayLen); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// }); +// streamer.addReader("Schematic.BlockData", NBTStreamer.ReadType.ELEM, (NBTStreamer.LazyReader) (arrayLen, dis) -> { +// try (FaweOutputStream biomes = new FaweOutputStream(new LZ4BlockOutputStream(biomesOut))) { +// IOUtil.copy(dis, biomes, arrayLen); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// }); +// streamer.addReader("Schematic.TileEntities", NBTStreamer.ReadType.ELEM,(BiConsumer) (index, value) -> { +// if (fc == null) { +// setupClipboard(0, uuid); +// } +// int[] pos = value.getIntArray("Pos"); +// int x,y,z; +// if (pos.length != 3) { +// System.out.println("Invalid tile " + value); +// if (!value.containsKey("x") || !value.containsKey("y") || !value.containsKey("z")) { +// return; +// } +// x = value.getInt("x"); +// y = value.getInt("y"); +// z = value.getInt("z"); +// } else { +// x = pos[0]; +// y = pos[1]; +// z = pos[2]; +// } +// Map values = value.getValue(); +// Tag id = values.get("Id"); +// if (id != null) { +// values.put("x", new IntTag(x)); +// values.put("y", new IntTag(y)); +// values.put("z", new IntTag(z)); +// values.put("id", id); +// } +// values.remove("Id"); +// values.remove("Pos"); +// value = fixBlockEntity(value); +// fc.setTile(x, y, z, value); +// }); +// streamer.addReader("Schematic.Entities", NBTStreamer.ReadType.ELEM,(BiConsumer) (index, compound) -> { +// if (fc == null) { +// setupClipboard(0, uuid); +// } +// Map value = compound.getValue(); +// StringTag id = (StringTag) value.get("Id"); +// if (id == null) { +// id = (StringTag) value.get("id"); +// if (id == null) { +// return; +// } +// } else { +// value.put("id", id); +// value.remove("Id"); +// } +// +// EntityType type = EntityTypes.parse(id.getValue()); +// if (type != null) { +// compound = fixEntity(compound); +// BaseEntity state = new BaseEntity(type, compound); +// Location loc = compound.getEntityLocation(fc); +// fc.createEntity(loc, state); +// } else { +// Fawe.debug("Invalid entity: " + id); +// } +// }); +// streamer.readFully(); +// if (fc == null) setupClipboard(length * width * height, uuid); +//// fc.setDimensions(BlockVector3.at(width, height, length)); +// BlockVector3 origin = min; +// CuboidRegion region; +// if (offsetX != Integer.MIN_VALUE && offsetY != Integer.MIN_VALUE && offsetZ != Integer.MIN_VALUE) { +// origin = origin.subtract(BlockVector3.at(offsetX, offsetY, offsetZ)); +// } +// region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); +// if (blocksOut.getSize() != 0) { +// try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(blocksOut.toByteArrays())))) { +// int volume = width * height * length; +// if (palette.length < 128) { +// for (int index = 0; index < volume; index++) { +// BlockState state = BlockTypes.states[palette[fis.read()]]; +// fc.setBlock(index, state); +// } +// } else { +// for (int index = 0; index < volume; index++) { +// BlockState state = BlockTypes.states[palette[fis.readVarInt()]]; +// fc.setBlock(index, state); +// } +// } +// } +// } +// if (biomesOut.getSize() != 0) { +// try (FaweInputStream fis = new FaweInputStream(new LZ4BlockInputStream(new FastByteArraysInputStream(biomesOut.toByteArrays())))) { +// int volume = width * length; +// for (int index = 0; index < volume; index++) { +// fc.setBiome(index, BiomeTypes.get(fis.read())); +// } +// } +// } +//// clipboard.init(region, fc); +// clipboard.setOrigin(origin); return clipboard; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java index faf7db200..93e6b2b4a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/ClipboardHolder.java @@ -26,6 +26,8 @@ import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.transform.Identity; import com.sk89q.worldedit.math.transform.Transform; + +import java.io.Closeable; import java.util.Collections; import java.util.List; @@ -112,8 +114,8 @@ public class ClipboardHolder { } public void close() { - if (clipboard instanceof BlockArrayClipboard) { - ((BlockArrayClipboard) clipboard).close(); + if (clipboard != null) { + clipboard.close(); } clipboard = null; }