Proper NBT streaming + random access optimizations

This commit is contained in:
Jesse Boyd 2016-07-13 19:00:15 +10:00
parent 78bb55380d
commit ef06ca3b19
9 changed files with 765 additions and 48 deletions

View File

@ -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<String, RunnableVal2> readers;
public NBTStreamer(NBTInputStream stream) {
this.is = stream;
readers = new HashMap<>();
}
public void readFully() throws IOException {
is.readNamedTagLazy(new RunnableVal2<String, RunnableVal2>() {
@Override
public void run(String node, RunnableVal2 result) {
this.value2 = readers.get(node);
}
});
}
public <T, V> void addReader(String node, RunnableVal2<T, V> run) {
if (run instanceof NBTStreamReader) {
((NBTStreamReader) run).init(node);
}
readers.put(node, run);
}
public <T, V> void addReader(RunnableVal2<T, V> run, String... nodes) {
for (String node : nodes) {
addReader(node, run);
}
}
public static abstract class NBTStreamReader<T, V> extends RunnableVal2<T, V> {
private String node;
public void init(String node) {
this.node = node;
}
public String getNode() {
return node;
}
}
}

View File

@ -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<Integer, Short>() {
@Override
public void run(Integer index, Short value) {
height = (value);
}
});
addReader("Schematic.Width", new RunnableVal2<Integer, Short>() {
@Override
public void run(Integer index, Short value) {
width = (value);
}
});
addReader("Schematic.Length", new RunnableVal2<Integer, Short>() {
@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<Integer, Integer>() {
@Override
public void run(Integer index, Integer value) {
originX.set(value);
}
});
addReader("Schematic.WEOriginY", new RunnableVal2<Integer, Integer>() {
@Override
public void run(Integer index, Integer value) {
originY.set(value);
}
});
addReader("Schematic.WEOriginZ", new RunnableVal2<Integer, Integer>() {
@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<Integer, Integer>() {
@Override
public void run(Integer index, Integer value) {
offsetX.set(value);
}
});
addReader("Schematic.WEOffsetY", new RunnableVal2<Integer, Integer>() {
@Override
public void run(Integer index, Integer value) {
offsetY.set(value);
}
});
addReader("Schematic.WEOffsetZ", new RunnableVal2<Integer, Integer>() {
@Override
public void run(Integer index, Integer value) {
offsetZ.set(value);
}
});
// Blocks
RunnableVal2<Integer, Integer> initializer = new RunnableVal2<Integer, Integer>() {
@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<Integer, Byte>() {
int i;
@Override
public void run(Integer index, Byte value) {
fc.setId(i++, value);
}
});
addReader("Schematic.Data.#", new RunnableVal2<Integer, Byte>() {
int i;
@Override
public void run(Integer index, Byte value) {
fc.setData(i++, value);
}
});
addReader("Schematic.AddBlocks.#", new RunnableVal2<Integer, Byte>() {
int i;
@Override
public void run(Integer index, Byte value) {
fc.setAdd(i++, value);
}
});
// Tiles
addReader("Schematic.TileEntities.#", new RunnableVal2<Integer, CompoundTag>() {
@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<Integer, CompoundTag>() {
@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;
}
}

View File

@ -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);

View File

@ -17,4 +17,9 @@ public abstract class RunnableVal2<T, U> implements Runnable {
}
public abstract void run(T value1, U value2);
public RunnableVal2<T, U> runAndGet(T value1, U value2) {
run(value1, value2);
return this;
}
}

View File

@ -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);

View File

@ -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
*/

View File

@ -0,0 +1,347 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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 <strong>NBT</strong>, or <strong>Named Binary Tag</strong>
* streams, and produces an object graph of subclasses of the {@code Tag}
* object.
*
* <p>The NBT format was created by Markus Persson, and the specification may be
* found at <a href="http://www.minecraft.net/docs/NBT.txt">
* http://www.minecraft.net/docs/NBT.txt</a>.</p>
*/
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<String, 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<String, 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<Tag> tagList = new ArrayList<Tag>();
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<String, Tag> tagMap = new HashMap<String, Tag>();
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<Tag> tagList = new ArrayList<Tag>();
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<String, Tag> tagMap = new HashMap<String, Tag>();
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();
}
}

View File

@ -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

View File

@ -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")) {