diff --git a/core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java b/core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java new file mode 100644 index 00000000..59a4c043 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/jnbt/NBTStreamer.java @@ -0,0 +1,49 @@ +package com.boydti.fawe.jnbt; + +import com.boydti.fawe.object.RunnableVal2; +import com.sk89q.jnbt.NBTInputStream; +import java.io.IOException; +import java.util.HashMap; + +public class NBTStreamer { + private final NBTInputStream is; + private final HashMap readers; + + public NBTStreamer(NBTInputStream stream) { + this.is = stream; + readers = new HashMap<>(); + } + + public void readFully() throws IOException { + is.readNamedTagLazy(new RunnableVal2() { + @Override + public void run(String node, RunnableVal2 result) { + this.value2 = readers.get(node); + } + }); + } + + public void addReader(String node, RunnableVal2 run) { + if (run instanceof NBTStreamReader) { + ((NBTStreamReader) run).init(node); + } + readers.put(node, run); + } + + public void addReader(RunnableVal2 run, String... nodes) { + for (String node : nodes) { + addReader(node, run); + } + } + + public static abstract class NBTStreamReader extends RunnableVal2 { + private String node; + public void init(String node) { + this.node = node; + } + + public String getNode() { + return node; + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java b/core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java new file mode 100644 index 00000000..37cce0f8 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/jnbt/SchematicStreamer.java @@ -0,0 +1,175 @@ +package com.boydti.fawe.jnbt; + +import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.regions.CuboidRegion; +import java.io.IOException; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +public class SchematicStreamer extends NBTStreamer { + private final UUID uuid; + + public SchematicStreamer(NBTInputStream stream, UUID uuid) throws IOException { + super(stream); + this.uuid = uuid; + addReader("Schematic.Height", new RunnableVal2() { + @Override + public void run(Integer index, Short value) { + height = (value); + } + }); + addReader("Schematic.Width", new RunnableVal2() { + @Override + public void run(Integer index, Short value) { + width = (value); + } + }); + addReader("Schematic.Length", new RunnableVal2() { + @Override + public void run(Integer index, Short value) { + length = (value); + } + }); + final AtomicInteger originX = new AtomicInteger(); + final AtomicInteger originY = new AtomicInteger(); + final AtomicInteger originZ = new AtomicInteger(); + addReader("Schematic.WEOriginX", new RunnableVal2() { + @Override + public void run(Integer index, Integer value) { + originX.set(value); + } + }); + addReader("Schematic.WEOriginY", new RunnableVal2() { + @Override + public void run(Integer index, Integer value) { + originY.set(value); + } + }); + addReader("Schematic.WEOriginZ", new RunnableVal2() { + @Override + public void run(Integer index, Integer value) { + originZ.set(value); + } + }); + final AtomicInteger offsetX = new AtomicInteger(); + final AtomicInteger offsetY = new AtomicInteger(); + final AtomicInteger offsetZ = new AtomicInteger(); + addReader("Schematic.WEOffsetX", new RunnableVal2() { + @Override + public void run(Integer index, Integer value) { + offsetX.set(value); + } + }); + addReader("Schematic.WEOffsetY", new RunnableVal2() { + @Override + public void run(Integer index, Integer value) { + offsetY.set(value); + } + }); + addReader("Schematic.WEOffsetZ", new RunnableVal2() { + @Override + public void run(Integer index, Integer value) { + offsetZ.set(value); + } + }); + // Blocks + RunnableVal2 initializer = new RunnableVal2() { + @Override + public void run(Integer length, Integer type) { + setupClipboard(length); + } + }; + addReader("Schematic.Blocks.?", initializer); + addReader("Schematic.Data.?", initializer); + addReader("Schematic.AddBlocks.?", initializer); + addReader("Schematic.Blocks.#", new RunnableVal2() { + int i; + @Override + public void run(Integer index, Byte value) { + fc.setId(i++, value); + } + }); + addReader("Schematic.Data.#", new RunnableVal2() { + int i; + @Override + public void run(Integer index, Byte value) { + fc.setData(i++, value); + } + }); + addReader("Schematic.AddBlocks.#", new RunnableVal2() { + int i; + @Override + public void run(Integer index, Byte value) { + fc.setAdd(i++, value); + } + }); + // Tiles + addReader("Schematic.TileEntities.#", new RunnableVal2() { + @Override + public void run(Integer index, CompoundTag 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.#", new RunnableVal2() { + @Override + public void run(Integer index, CompoundTag compound) { + if (fc == null) { + setupClipboard(0); + } + String id = compound.getString("id"); + if (id.isEmpty()) { + return; + } + ListTag positionTag = compound.getListTag("Pos"); + ListTag directionTag = compound.getListTag("Rotation"); + BaseEntity state = new BaseEntity(id, compound); + fc.createEntity(null, positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2), (float) directionTag.asDouble(0), (float) directionTag.asDouble(1), state); + } + }); + readFully(); + Vector min = new Vector(originX.get(), originY.get(), originZ.get()); + Vector offset = new Vector(offsetX.get(), offsetY.get(), offsetZ.get()); + Vector origin = min.subtract(offset); + Vector dimensions = new Vector(width, height, length); + fc.setDimensions(dimensions); + CuboidRegion region = new CuboidRegion(min, min.add(width, height, length).subtract(Vector.ONE)); + clipboard = new BlockArrayClipboard(region, fc); + clipboard.setOrigin(origin); + } + + private int height; + private int width; + private int length; + + private Clipboard clipboard; + private DiskOptimizedClipboard fc; + + private DiskOptimizedClipboard setupClipboard(int size) { + if (fc != null) { + if (fc.getDimensions().getX() == 0) { + fc.setDimensions(new Vector(size, 1, 1)); + } + return fc; + } + return fc = new DiskOptimizedClipboard(size, 1, 1, uuid); + } + + public Clipboard getClipboard() { + return clipboard; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/BufferedRandomAccessFile.java b/core/src/main/java/com/boydti/fawe/object/BufferedRandomAccessFile.java index 97414c5a..b5f290db 100644 --- a/core/src/main/java/com/boydti/fawe/object/BufferedRandomAccessFile.java +++ b/core/src/main/java/com/boydti/fawe/object/BufferedRandomAccessFile.java @@ -244,6 +244,31 @@ public final class BufferedRandomAccessFile extends RandomAccessFile this.curr_ = pos; } + /* + * Seek and do not flush if within the current buffer when going backwards + * - Assumes no writes were made + * @param pos + * @throws IOException + */ + public void seekUnsafe(long pos) throws IOException + { + if (pos >= this.hi_ || pos < this.lo_) + { + // seeking outside of current buffer -- flush and read + this.flushBuffer(); + this.lo_ = pos & BuffMask_; // start at BuffSz boundary + this.maxHi_ = this.lo_ + (long) this.buff_.length; + if (this.diskPos_ != this.lo_) + { + super.seek(this.lo_); + this.diskPos_ = this.lo_; + } + int n = this.fillBuffer(); + this.hi_ = this.lo_ + (long) n; + } + this.curr_ = pos; + } + public long getFilePointer() { return this.curr_; @@ -273,6 +298,23 @@ public final class BufferedRandomAccessFile extends RandomAccessFile return ((int) res) & 0xFF; // convert byte -> int } + public byte read1() throws IOException { + if (this.curr_ >= this.hi_) + { + // test for EOF + // if (this.hi < this.maxHi) return -1; + if (this.hitEOF_) + return -1; + + // slow path -- read another buffer + this.seek(this.curr_); + if (this.curr_ == this.hi_) + return -1; + } + byte res = this.buff_[(int) (this.curr_++ - this.lo_)]; + return res; + } + public int read(byte[] b) throws IOException { return this.read(b, 0, b.length); diff --git a/core/src/main/java/com/boydti/fawe/object/RunnableVal2.java b/core/src/main/java/com/boydti/fawe/object/RunnableVal2.java index 46cc6218..b3329bb4 100644 --- a/core/src/main/java/com/boydti/fawe/object/RunnableVal2.java +++ b/core/src/main/java/com/boydti/fawe/object/RunnableVal2.java @@ -17,4 +17,9 @@ public abstract class RunnableVal2 implements Runnable { } public abstract void run(T value1, U value2); + + public RunnableVal2 runAndGet(T value1, U value2) { + run(value1, value2); + return this; + } } diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java index 5bf02364..41b9b476 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java @@ -69,16 +69,17 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { long size = (raf.length() - HEADER_SIZE) >> 1; raf.seek(2); last = -1; - raf.read(buffer); - width = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF))); - raf.read(buffer); - height = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF))); - raf.read(buffer); - length = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF))); + width = raf.readChar(); + height = raf.readChar(); + length = raf.readChar(); area = width * length; autoCloseTask(); } + public Vector getDimensions() { + return new Vector(width, height, length); + } + public BlockArrayClipboard toClipboard() { try { CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(width - 1, height - 1, length - 1)) { @@ -92,9 +93,9 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { } raf.seek(8); last = -1; - int ox = (((byte) raf.read() << 8) | ((byte) raf.read()) & 0xFF); - int oy = (((byte) raf.read() << 8) | ((byte) raf.read()) & 0xFF); - int oz = (((byte) raf.read() << 8) | ((byte) raf.read()) & 0xFF); + int ox = raf.readShort(); + int oy = raf.readShort(); + int oz = raf.readShort(); BlockArrayClipboard clipboard = new BlockArrayClipboard(region, this); clipboard.setOrigin(new Vector(ox, oy, oz)); return clipboard; @@ -132,14 +133,29 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { } raf.seek(8); last = -1; - raf.write((byte) (offset.getBlockX() >> 8)); - raf.write((byte) (offset.getBlockX())); + raf.writeShort(offset.getBlockX()); + raf.writeShort(offset.getBlockY()); + raf.writeShort(offset.getBlockZ()); + } catch (IOException e) { + MainUtil.handleError(e); + } + } - raf.write((byte) (offset.getBlockY() >> 8)); - raf.write((byte) (offset.getBlockY())); - - raf.write((byte) (offset.getBlockZ() >> 8)); - raf.write((byte) (offset.getBlockZ())); + public void setDimensions(Vector dimensions) { + try { + if (raf == null) { + open(); + } + width = dimensions.getBlockX(); + height = dimensions.getBlockY(); + length = dimensions.getBlockZ(); + long size = width * height * length * 2l + HEADER_SIZE; + raf.setLength(size); + raf.seek(1); + last = -0; + raf.writeChar(width); + raf.writeChar(height); + raf.writeChar(length); } catch (IOException e) { MainUtil.handleError(e); } @@ -183,13 +199,10 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { raf.setLength(size); // write length etc raf.seek(1); - last = 0; - raf.write((width) & 0xff); - raf.write(((width) >> 8) & 0xff); - raf.write((height) & 0xff); - raf.write(((height) >> 8) & 0xff); - raf.write((length) & 0xff); - raf.write(((length) >> 8) & 0xff); + last = -1; + raf.writeChar(width); + raf.writeChar(height); + raf.writeChar(length); } autoCloseTask(); } @@ -238,18 +251,12 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { } } raf.seek(i); - raf.read(buffer); - int id = ((((int) buffer[1] & 0xFF) << 4) + (((int) buffer[0] & 0xFF) >> 4)); - if (id == 0 && !air) { + int combinedId = raf.readChar(); + if (combinedId == 0 && !air) { continue; } - BaseBlock block; - if (!FaweCache.hasData(id)) { - block = FaweCache.CACHE_BLOCK[id << 4]; - } else { - block = FaweCache.CACHE_BLOCK[(id << 4) + (buffer[0] & 0xF)]; - } - if (FaweCache.hasNBT(id)) { + BaseBlock block = FaweCache.CACHE_BLOCK[combinedId]; + if (FaweCache.hasNBT(block.getId())) { CompoundTag nbt = nbtMap.get(new IntegerTrio((int) pos.x, (int) pos.y, (int) pos.z)); if (nbt != null) { block = new BaseBlock(block.getId(), block.getData()); @@ -274,16 +281,10 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { raf.seek((HEADER_SIZE) + (i << 1)); lastAccessed = System.currentTimeMillis(); } - raf.read(buffer); last = i; - int id = ((((int) buffer[1] & 0xFF) << 4) + (((int) buffer[0] & 0xFF) >> 4)); - BaseBlock block; - if (!FaweCache.hasData(id)) { - block = FaweCache.CACHE_BLOCK[id << 4]; - } else { - block = FaweCache.CACHE_BLOCK[(id << 4) + (buffer[0] & 0xF)]; - } - if (FaweCache.hasNBT(id)) { + int combinedId = raf.readChar(); + BaseBlock block = FaweCache.CACHE_BLOCK[combinedId]; + if (FaweCache.hasNBT(block.getId())) { CompoundTag nbt = nbtMap.get(new IntegerTrio(x, y, z)); if (nbt != null) { block = new BaseBlock(block.getId(), block.getData()); @@ -318,9 +319,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { final int id = block.getId(); final int data = block.getData(); int combined = (id << 4) + data; - buffer[0] = (byte) ((combined) & 0xff); - buffer[1] = (byte) (((combined) >> 8) & 0xFF); - raf.write(buffer); + raf.writeChar(combined); if (FaweCache.hasNBT(id)) { nbtMap.put(new IntegerTrio(x, y, z), block.getNbtData()); } @@ -331,6 +330,101 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { return false; } + public boolean setId(int i, int id) { + try { + if (raf == null) { + open(); + } + if (i != last + 1) { + raf.seek((HEADER_SIZE) + (i << 1)); + lastAccessed = System.currentTimeMillis(); + } + last = i; + int combined = FaweCache.getData(raf.readChar()) + (id << 4); + raf.seekUnsafe(raf.getFilePointer() - 2); + raf.writeChar(combined); + return true; + } catch (Exception e) { + MainUtil.handleError(e); + } + return false; + } + + public boolean setCombined(int i, int combined) { + try { + if (raf == null) { + open(); + } + if (i != last + 1) { + raf.seek((HEADER_SIZE) + (i << 1)); + lastAccessed = System.currentTimeMillis(); + } + last = i; + raf.writeChar(combined); + return true; + } catch (Exception e) { + MainUtil.handleError(e); + } + return false; + } + + public boolean setAdd(int i, int add) { + try { + if (raf == null) { + open(); + } + if (i != last + 1) { + raf.seek((HEADER_SIZE) + (i << 1)); + lastAccessed = System.currentTimeMillis(); + } + last = i; + int combined = raf.readChar() + (add << 4); + raf.seekUnsafe(raf.getFilePointer() - 2); + raf.writeChar(combined); + return true; + } catch (Exception e) { + MainUtil.handleError(e); + } + return false; + } + + public int getCombined(int i) { + try { + if (raf == null) { + open(); + } + if (i != last + 1) { + raf.seek((HEADER_SIZE) + (i << 1)); + lastAccessed = System.currentTimeMillis(); + } + last = i; + return raf.readChar(); + } catch (Exception e) { + MainUtil.handleError(e); + } + return 0; + } + + public boolean setData(int i, int data) { + try { + if (raf == null) { + open(); + } + if (i != last + 1) { + raf.seek((HEADER_SIZE) + (i << 1)); + lastAccessed = System.currentTimeMillis(); + } + last = i; + int combined = (FaweCache.getId(raf.readChar()) << 4) + data; + raf.seekUnsafe(raf.getFilePointer() - 2); + raf.writeChar(combined); + return true; + } catch (Exception e) { + MainUtil.handleError(e); + } + return false; + } + @Override public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) { FaweClipboard.ClipboardEntity ret = new ClipboardEntity(world, x, y, z, yaw, pitch, entity); diff --git a/core/src/main/java/com/boydti/fawe/util/SetQueue.java b/core/src/main/java/com/boydti/fawe/util/SetQueue.java index ba8413db..c577313d 100644 --- a/core/src/main/java/com/boydti/fawe/util/SetQueue.java +++ b/core/src/main/java/com/boydti/fawe/util/SetQueue.java @@ -32,7 +32,7 @@ public class SetQueue { private long last; private long secondLast; private long lastSuccess; - + /** * A queue of tasks that will run when the queue is empty */ diff --git a/core/src/main/java/com/sk89q/jnbt/NBTInputStream.java b/core/src/main/java/com/sk89q/jnbt/NBTInputStream.java new file mode 100644 index 00000000..e97e57f2 --- /dev/null +++ b/core/src/main/java/com/sk89q/jnbt/NBTInputStream.java @@ -0,0 +1,347 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.jnbt; + +import com.boydti.fawe.object.RunnableVal2; +import java.io.Closeable; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class reads NBT, or Named Binary Tag + * streams, and produces an object graph of subclasses of the {@code Tag} + * object. + * + *

The NBT format was created by Markus Persson, and the specification may be + * found at + * http://www.minecraft.net/docs/NBT.txt.

+ */ +public final class NBTInputStream implements Closeable { + + private final DataInputStream is; + + /** + * Creates a new {@code NBTInputStream}, which will source its data + * from the specified input stream. + * + * @param is the input stream + * @throws IOException if an I/O error occurs + */ + public NBTInputStream(InputStream is) throws IOException { + this.is = new DataInputStream(is); + } + + /** + * Reads an NBT tag from the stream. + * + * @return The tag that was read. + * @throws IOException if an I/O error occurs. + */ + public NamedTag readNamedTag() throws IOException { + return readNamedTag(0); + } + + /** + * Reads an NBT from the stream. + * + * @param depth the depth of this tag + * @return The tag that was read. + * @throws IOException if an I/O error occurs. + */ + private NamedTag readNamedTag(int depth) throws IOException { + int type = is.readByte(); + return new NamedTag(readNamedTagName(type), readTagPayload(type, depth)); + } + + public void readNamedTagLazy(RunnableVal2 getReader) throws IOException { + int type = is.readByte(); + String name = readNamedTagName(type); + RunnableVal2 reader = getReader.runAndGet(name, null).value2; + if (reader != null) { + reader.run(0, readTagPaylodRaw(type, 0)); + return; + } + readTagPaylodLazy(type, 0, name, getReader); + } + + private String readNamedTagName(int type) throws IOException { + String name; + if (type != NBTConstants.TYPE_END) { + int nameLength = is.readShort() & 0xFFFF; + byte[] nameBytes = new byte[nameLength]; + is.readFully(nameBytes); + return new String(nameBytes, NBTConstants.CHARSET); + } else { + return ""; + } + } + + private void readTagPaylodLazy(int type, int depth, String node, RunnableVal2 getReader) throws IOException { + switch (type) { + case NBTConstants.TYPE_END: + return; + case NBTConstants.TYPE_BYTE: + is.skip(1); + return; + case NBTConstants.TYPE_SHORT: + is.skip(2); + return; + case NBTConstants.TYPE_INT: + is.skip(4); + return; + case NBTConstants.TYPE_LONG: + is.skip(8); + return; + case NBTConstants.TYPE_FLOAT: + is.skip(4); + return; + case NBTConstants.TYPE_DOUBLE: + is.skip(8); + return; + case NBTConstants.TYPE_STRING: + int length = is.readShort(); + is.skip(length); + return; + case NBTConstants.TYPE_BYTE_ARRAY: + RunnableVal2 reader = getReader.runAndGet(node + ".?", null).value2; + length = is.readInt(); + if (reader != null) { + reader.run(length, NBTConstants.TYPE_BYTE); + } + reader = getReader.runAndGet(node + ".#", null).value2; + if (reader == null) { + is.skip(length); + return; + } + for (int i = 0; i < length; i++) { + reader.run(i, is.readByte()); + } + return; + case NBTConstants.TYPE_LIST: + int childType = is.readByte(); + length = is.readInt(); + reader = getReader.runAndGet(node + ".?", null).value2; + if (reader != null) { + reader.run(length, childType); + } + node += ".#"; + reader = getReader.runAndGet(node, null).value2; + depth++; + if (reader == null) { + for (int i = 0; i < length; ++i) { + readTagPaylodLazy(childType, depth, node, getReader); + } + return; + } + for (int i = 0; i < length; ++i) { + reader.run(i, readTagPayload(childType, depth)); + } + return; + case NBTConstants.TYPE_COMPOUND: + depth++; + for (int i = 0;;i++) { + childType = is.readByte(); + if (childType == NBTConstants.TYPE_END) { + return; + } + String name = readNamedTagName(childType); + String childNode = node + "." + name; + reader = getReader.runAndGet(childNode, null).value2; + if (reader == null) { + readTagPaylodLazy(childType, depth, childNode, getReader); + continue; + } + reader.run(i, readTagPaylodRaw(childType, depth)); + } + case NBTConstants.TYPE_INT_ARRAY: + length = is.readInt(); + reader = getReader.runAndGet(node + ".?", null).value2; + if (reader != null) { + reader.run(length, NBTConstants.TYPE_INT); + } + reader = getReader.runAndGet(node + ".#", null).value2; + if (reader == null) { + is.skip(length << 2); + return; + } + for (int i = 0; i < length; i++) { + reader.run(i, is.readInt()); + } + return; + default: + throw new IOException("Invalid tag type: " + type + "."); + } + } + + private Object readTagPaylodRaw(int type, int depth) throws IOException { + switch (type) { + case NBTConstants.TYPE_END: + if (depth == 0) { + throw new IOException( + "TAG_End found without a TAG_Compound/TAG_List tag preceding it."); + } else { + return null; + } + case NBTConstants.TYPE_BYTE: + return (is.readByte()); + case NBTConstants.TYPE_SHORT: + return (is.readShort()); + case NBTConstants.TYPE_INT: + return (is.readInt()); + case NBTConstants.TYPE_LONG: + return (is.readLong()); + case NBTConstants.TYPE_FLOAT: + return (is.readFloat()); + case NBTConstants.TYPE_DOUBLE: + return (is.readDouble()); + case NBTConstants.TYPE_BYTE_ARRAY: + int length = is.readInt(); + byte[] bytes = new byte[length]; + is.readFully(bytes); + return (bytes); + case NBTConstants.TYPE_STRING: + length = is.readShort(); + bytes = new byte[length]; + is.readFully(bytes); + return (new String(bytes, NBTConstants.CHARSET)); + case NBTConstants.TYPE_LIST: + int childType = is.readByte(); + length = is.readInt(); + List tagList = new ArrayList(); + for (int i = 0; i < length; ++i) { + Tag tag = readTagPayload(childType, depth + 1); + if (tag instanceof EndTag) { + throw new IOException("TAG_End not permitted in a list."); + } + tagList.add(tag); + } + return (tagList); + case NBTConstants.TYPE_COMPOUND: + Map tagMap = new HashMap(); + while (true) { + NamedTag namedTag = readNamedTag(depth + 1); + Tag tag = namedTag.getTag(); + if (tag instanceof EndTag) { + break; + } else { + tagMap.put(namedTag.getName(), tag); + } + } + return (tagMap); + case NBTConstants.TYPE_INT_ARRAY: + length = is.readInt(); + int[] data = new int[length]; + for (int i = 0; i < length; i++) { + data[i] = is.readInt(); + } + return (data); + default: + throw new IOException("Invalid tag type: " + type + "."); + } + } + + /** + * Reads the payload of a tag given the type. + * + * @param type the type + * @param depth the depth + * @return the tag + * @throws IOException if an I/O error occurs. + */ + private Tag readTagPayload(int type, int depth) throws IOException { + switch (type) { + case NBTConstants.TYPE_END: + if (depth == 0) { + throw new IOException( + "TAG_End found without a TAG_Compound/TAG_List tag preceding it."); + } else { + return new EndTag(); + } + case NBTConstants.TYPE_BYTE: + return new ByteTag(is.readByte()); + case NBTConstants.TYPE_SHORT: + return new ShortTag(is.readShort()); + case NBTConstants.TYPE_INT: + return new IntTag(is.readInt()); + case NBTConstants.TYPE_LONG: + return new LongTag(is.readLong()); + case NBTConstants.TYPE_FLOAT: + return new FloatTag(is.readFloat()); + case NBTConstants.TYPE_DOUBLE: + return new DoubleTag(is.readDouble()); + case NBTConstants.TYPE_BYTE_ARRAY: + int length = is.readInt(); + byte[] bytes = new byte[length]; + is.readFully(bytes); + return new ByteArrayTag(bytes); + case NBTConstants.TYPE_STRING: + length = is.readShort(); + bytes = new byte[length]; + is.readFully(bytes); + return new StringTag(new String(bytes, NBTConstants.CHARSET)); + case NBTConstants.TYPE_LIST: + int childType = is.readByte(); + length = is.readInt(); + List tagList = new ArrayList(); + for (int i = 0; i < length; ++i) { + Tag tag = readTagPayload(childType, depth + 1); + if (tag instanceof EndTag) { + throw new IOException("TAG_End not permitted in a list."); + } + tagList.add(tag); + } + + return new ListTag(NBTUtils.getTypeClass(childType), tagList); + case NBTConstants.TYPE_COMPOUND: + Map tagMap = new HashMap(); + while (true) { + NamedTag namedTag = readNamedTag(depth + 1); + Tag tag = namedTag.getTag(); + if (tag instanceof EndTag) { + break; + } else { + tagMap.put(namedTag.getName(), tag); + } + } + + return new CompoundTag(tagMap); + case NBTConstants.TYPE_INT_ARRAY: + length = is.readInt(); + int[] data = new int[length]; + for (int i = 0; i < length; i++) { + data[i] = is.readInt(); + } + return new IntArrayTag(data); + default: + throw new IOException("Invalid tag type: " + type + "."); + } + } + + @Override + public void close() throws IOException { + is.close(); + } + +} diff --git a/core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java b/core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java index d4e6f67e..8d6ba9a4 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java @@ -161,8 +161,8 @@ public class NavigationCommands { } @Command( - aliases = { "jumpto [world,x,y,z]", "j" }, - usage = "", + aliases = { "jumpto", "j" }, + usage = "jumpto [world,x,y,z]", desc = "Teleport to a location", min = 0, max = 0 diff --git a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java index 4cb34220..c3d49c33 100644 --- a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java +++ b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java @@ -20,6 +20,8 @@ package com.sk89q.worldedit.extent.clipboard.io; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.jnbt.SchematicStreamer; import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.IntTag; @@ -76,6 +78,9 @@ public class SchematicReader implements ClipboardReader { } public Clipboard read(WorldData data, UUID clipboardId) throws IOException { + if (Settings.CLIPBOARD.USE_DISK) { + return new SchematicStreamer(inputStream, clipboardId).getClipboard(); + } // Schematic tag NamedTag rootTag = inputStream.readNamedTag(); if (!rootTag.getName().equals("Schematic")) {