From 48af65cac372d331590fb2c5feafcbe2aed5ea2c Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 23 Aug 2012 16:52:37 -0700 Subject: [PATCH] Updated foundation classes a bit and added raw access for NBT. This release marks experimental support for custom blocks, and nearly all future Minecraft blocks, and is also the beginning of the gradual transition to the new foundation classes as a replacement of the current BaseBlock, etc. classes. --- .../java/com/sk89q/jnbt/NBTConstants.java | 36 ++ .../java/com/sk89q/worldedit/EditSession.java | 100 ++--- .../java/com/sk89q/worldedit/LocalWorld.java | 91 ++++- .../com/sk89q/worldedit/blocks/BaseBlock.java | 110 +++--- .../com/sk89q/worldedit/blocks/BaseItem.java | 68 +++- .../sk89q/worldedit/blocks/BaseItemStack.java | 22 +- .../sk89q/worldedit/blocks/ChestBlock.java | 62 ++- .../worldedit/blocks/ContainerBlock.java | 17 +- .../worldedit/blocks/DispenserBlock.java | 62 ++- .../sk89q/worldedit/blocks/FurnaceBlock.java | 109 +++--- .../worldedit/blocks/MobSpawnerBlock.java | 84 ++-- .../com/sk89q/worldedit/blocks/NoteBlock.java | 81 ++-- .../com/sk89q/worldedit/blocks/SignBlock.java | 84 ++-- .../worldedit/blocks/TileEntityBlock.java | 36 +- .../sk89q/worldedit/bukkit/BukkitWorld.java | 98 ++++- .../com/sk89q/worldedit/bukkit/NmsBlock.java | 360 ++++++++++++++++++ .../com/sk89q/worldedit/data/AnvilChunk.java | 8 +- .../com/sk89q/worldedit/data/OldChunk.java | 32 +- .../com/sk89q/worldedit/foundation/Block.java | 38 +- .../sk89q/worldedit/foundation/NbtValued.java | 10 + .../schematic/MCEditSchematicFormat.java | 44 ++- 21 files changed, 1037 insertions(+), 515 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/bukkit/NmsBlock.java diff --git a/src/main/java/com/sk89q/jnbt/NBTConstants.java b/src/main/java/com/sk89q/jnbt/NBTConstants.java index b7b3adb19..b66427551 100644 --- a/src/main/java/com/sk89q/jnbt/NBTConstants.java +++ b/src/main/java/com/sk89q/jnbt/NBTConstants.java @@ -62,5 +62,41 @@ public final class NBTConstants { private NBTConstants() { } + + /** + * Convert a type ID to its corresponding {@link Tag} class. + * + * @param id type ID + * @return tag class + * @throws IllegalArgumentException thrown if the tag ID is not valid + */ + public static Class getClassFromType(int id) { + switch (id) { + case TYPE_END: + return EndTag.class; + case TYPE_BYTE: + return ByteTag.class; + case TYPE_SHORT: + return ShortTag.class; + case TYPE_INT: + return IntTag.class; + case TYPE_LONG: + return LongTag.class; + case TYPE_FLOAT: + return FloatTag.class; + case TYPE_BYTE_ARRAY: + return ByteArrayTag.class; + case TYPE_STRING: + return StringTag.class; + case TYPE_LIST: + return ListTag.class; + case TYPE_COMPOUND: + return CompoundTag.class; + case TYPE_INT_ARRAY: + return IntArrayTag.class; + default: + throw new IllegalArgumentException("Unknown tag type ID of " + id); + } + } } diff --git a/src/main/java/com/sk89q/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java index fef9bc23e..48c0181de 100644 --- a/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/src/main/java/com/sk89q/worldedit/EditSession.java @@ -18,27 +18,35 @@ */ package com.sk89q.worldedit; -import java.util.Deque; -import java.util.LinkedList; -import java.util.Map; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Set; -import java.util.HashSet; -import java.util.Stack; -import java.util.List; import java.util.ArrayList; import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Random; -import com.sk89q.worldedit.regions.*; -import com.sk89q.worldedit.util.TreeGenerator; -import com.sk89q.worldedit.bags.*; -import com.sk89q.worldedit.blocks.*; +import java.util.Set; +import java.util.Stack; + +import com.sk89q.worldedit.bags.BlockBag; +import com.sk89q.worldedit.bags.BlockBagException; +import com.sk89q.worldedit.bags.UnplaceableBlockException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.blocks.ContainerBlock; +import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.expression.Expression; import com.sk89q.worldedit.expression.ExpressionException; import com.sk89q.worldedit.expression.runtime.RValue; import com.sk89q.worldedit.masks.Mask; -import com.sk89q.worldedit.patterns.*; +import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.TreeGenerator; /** * This class can wrap all block editing operations into one "edit session" that @@ -408,53 +416,7 @@ public class EditSession { * @return BaseBlock */ public BaseBlock rawGetBlock(Vector pt) { - world.checkLoadedChunk(pt); - - int type = world.getBlockType(pt); - int data = world.getBlockData(pt); - - switch (type) { - case BlockID.WALL_SIGN: - case BlockID.SIGN_POST: { - SignBlock block = new SignBlock(type, data); - world.copyFromWorld(pt, block); - return block; - } - - case BlockID.CHEST: { - ChestBlock block = new ChestBlock(data); - world.copyFromWorld(pt, block); - return block; - } - - case BlockID.FURNACE: - case BlockID.BURNING_FURNACE: { - FurnaceBlock block = new FurnaceBlock(type, data); - world.copyFromWorld(pt, block); - return block; - } - - case BlockID.DISPENSER: { - DispenserBlock block = new DispenserBlock(data); - world.copyFromWorld(pt, block); - return block; - } - - case BlockID.MOB_SPAWNER: { - MobSpawnerBlock block = new MobSpawnerBlock(data); - world.copyFromWorld(pt, block); - return block; - } - - case BlockID.NOTE_BLOCK: { - NoteBlock block = new NoteBlock(data); - world.copyFromWorld(pt, block); - return block; - } - - default: - return new BaseBlock(type, data); - } + return world.getBlock(pt); } /** @@ -464,8 +426,8 @@ public class EditSession { */ public void undo(EditSession sess) { for (Map.Entry entry : original) { - BlockVector pt = (BlockVector) entry.getKey(); - sess.smartSetBlock(pt, (BaseBlock) entry.getValue()); + BlockVector pt = entry.getKey(); + sess.smartSetBlock(pt, entry.getValue()); } sess.flushQueue(); } @@ -477,8 +439,8 @@ public class EditSession { */ public void redo(EditSession sess) { for (Map.Entry entry : current) { - BlockVector pt = (BlockVector) entry.getKey(); - sess.smartSetBlock(pt, (BaseBlock) entry.getValue()); + BlockVector pt = entry.getKey(); + sess.smartSetBlock(pt, entry.getValue()); } sess.flushQueue(); } @@ -727,8 +689,8 @@ public class EditSession { final Set dirtyChunks = new HashSet(); for (Map.Entry entry : queueAfter) { - BlockVector pt = (BlockVector) entry.getKey(); - rawSetBlock(pt, (BaseBlock) entry.getValue()); + BlockVector pt = entry.getKey(); + rawSetBlock(pt, entry.getValue()); // TODO: use ChunkStore.toChunk(pt) after optimizing it. if (fastMode) { @@ -740,8 +702,8 @@ public class EditSession { // because it might cause the items to drop if (blockBag == null || missingBlocks.size() == 0) { for (Map.Entry entry : queueLast) { - BlockVector pt = (BlockVector) entry.getKey(); - rawSetBlock(pt, (BaseBlock) entry.getValue()); + BlockVector pt = entry.getKey(); + rawSetBlock(pt, entry.getValue()); // TODO: use ChunkStore.toChunk(pt) after optimizing it. if (fastMode) { diff --git a/src/main/java/com/sk89q/worldedit/LocalWorld.java b/src/main/java/com/sk89q/worldedit/LocalWorld.java index 11d1565f3..865ab2636 100644 --- a/src/main/java/com/sk89q/worldedit/LocalWorld.java +++ b/src/main/java/com/sk89q/worldedit/LocalWorld.java @@ -21,9 +21,19 @@ package com.sk89q.worldedit; import java.util.PriorityQueue; import java.util.Random; + import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.blocks.ChestBlock; +import com.sk89q.worldedit.blocks.DispenserBlock; +import com.sk89q.worldedit.blocks.FurnaceBlock; +import com.sk89q.worldedit.blocks.MobSpawnerBlock; +import com.sk89q.worldedit.blocks.NoteBlock; +import com.sk89q.worldedit.blocks.SignBlock; +import com.sk89q.worldedit.foundation.Block; +import com.sk89q.worldedit.foundation.World; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.TreeGenerator; @@ -32,7 +42,7 @@ import com.sk89q.worldedit.util.TreeGenerator; * * @author sk89q */ -public abstract class LocalWorld { +public abstract class LocalWorld implements World { /** * Named flags to use as parameters to {@link LocalWorld#killMobs(Vector, double, int)} */ @@ -64,6 +74,7 @@ public abstract class LocalWorld { * @param type * @return */ + @Deprecated public abstract boolean setBlockType(Vector pt, int type); /** @@ -73,6 +84,7 @@ public abstract class LocalWorld { * @param type * @return */ + @Deprecated public boolean setBlockTypeFast(Vector pt, int type) { return setBlockType(pt, type); } @@ -83,6 +95,7 @@ public abstract class LocalWorld { * @param pt * @return */ + @Deprecated public abstract int getBlockType(Vector pt); /** @@ -91,7 +104,7 @@ public abstract class LocalWorld { * @param pt * @param data */ - + @Deprecated public abstract void setBlockData(Vector pt, int data); /** @@ -100,6 +113,7 @@ public abstract class LocalWorld { * @param pt * @param data */ + @Deprecated public abstract void setBlockDataFast(Vector pt, int data); /** @@ -124,6 +138,7 @@ public abstract class LocalWorld { * @param data * @return */ + @Deprecated public boolean setTypeIdAndData(Vector pt, int type, int data) { boolean ret = setBlockType(pt, type); setBlockData(pt, data); @@ -137,6 +152,7 @@ public abstract class LocalWorld { * @param data * @return */ + @Deprecated public boolean setTypeIdAndDataFast(Vector pt, int type, int data) { boolean ret = setBlockTypeFast(pt, type); setBlockDataFast(pt, data); @@ -149,6 +165,7 @@ public abstract class LocalWorld { * @param pt * @return */ + @Deprecated public abstract int getBlockData(Vector pt); /** @@ -482,6 +499,7 @@ public abstract class LocalWorld { public boolean queueBlockBreakEffect(ServerInterface server, Vector position, int blockId, double priority) { if (taskId == -1) { taskId = server.schedule(0, 1, new Runnable() { + @Override public void run() { int max = Math.max(1, Math.min(30, effectQueue.size() / 3)); for (int i = 0; i < max; ++i) { @@ -509,4 +527,73 @@ public abstract class LocalWorld { public int killEntities(LocalEntity... entities) { return 0; } + + @Override + public boolean setBlock(Vector pt, Block block, boolean notifyAdjacent) { + boolean successful; + + // Default implementation will call the old deprecated methods + if (notifyAdjacent) { + successful = setTypeIdAndData(pt, block.getId(), block.getData()); + } else { + successful = setTypeIdAndDataFast(pt, block.getId(), block.getData()); + } + + if (block instanceof BaseBlock) { + copyToWorld(pt, (BaseBlock) block); + } + + return successful; + } + + @Override + public BaseBlock getBlock(Vector pt) { + checkLoadedChunk(pt); + + int type = getBlockType(pt); + int data = getBlockData(pt); + + switch (type) { + case BlockID.WALL_SIGN: + case BlockID.SIGN_POST: { + SignBlock block = new SignBlock(type, data); + copyFromWorld(pt, block); + return block; + } + + case BlockID.CHEST: { + ChestBlock block = new ChestBlock(data); + copyFromWorld(pt, block); + return block; + } + + case BlockID.FURNACE: + case BlockID.BURNING_FURNACE: { + FurnaceBlock block = new FurnaceBlock(type, data); + copyFromWorld(pt, block); + return block; + } + + case BlockID.DISPENSER: { + DispenserBlock block = new DispenserBlock(data); + copyFromWorld(pt, block); + return block; + } + + case BlockID.MOB_SPAWNER: { + MobSpawnerBlock block = new MobSpawnerBlock(data); + copyFromWorld(pt, block); + return block; + } + + case BlockID.NOTE_BLOCK: { + NoteBlock block = new NoteBlock(data); + copyFromWorld(pt, block); + return block; + } + + default: + return new BaseBlock(type, data); + } + } } diff --git a/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java b/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java index 1ed559b60..121048550 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java +++ b/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java @@ -20,26 +20,20 @@ package com.sk89q.worldedit.blocks; import com.sk89q.worldedit.CuboidClipboard.FlipDirection; +import com.sk89q.worldedit.foundation.Block; /** * Represents a block. * + * @see Block new class to replace this one * @author sk89q */ -public class BaseBlock { +public class BaseBlock extends Block { + /** - * BaseBlock type. - */ - private short type = 0; - /** - * BaseBlock data. - */ - private short data = 0; - - /** - * Construct the block with its type. + * Construct the block with its type, with default data value 0. * - * @param type + * @param type type ID of block */ public BaseBlock(int type) { this(type, 0); @@ -48,40 +42,29 @@ public class BaseBlock { /** * Construct the block with its type and data. * - * @param type - * @param data + * @param type type ID of block + * @param data data value */ public BaseBlock(int type, int data) { - this.type = (short) type; - this.data = (short) data; + super(type, data); } /** + * Get the type of block. + * * @return the type */ public int getType() { - return (int) type; + return getId(); } /** + * Set the type of block. + * * @param type the type to set */ public void setType(int type) { - this.type = (short) type; - } - - /** - * @return the data - */ - public int getData() { - return (int) data; - } - - /** - * @param data the data to set - */ - public void setData(int data) { - this.data = (short) data; + setId(type); } /** @@ -90,24 +73,28 @@ public class BaseBlock { * @return if air */ public boolean isAir() { - return type == BlockID.AIR; + return getType() == BlockID.AIR; } /** * Rotate this block 90 degrees. + * + * @return new data value */ public int rotate90() { - int newData = BlockData.rotate90(type, data); - this.data = (short) newData; - return data; + int newData = BlockData.rotate90(getType(), getData()); + setData(newData); + return newData; } /** * Rotate this block -90 degrees. + * + * @return new data value */ public int rotate90Reverse() { - int newData = BlockData.rotate90Reverse(type, data); - this.data = (short) newData; + int newData = BlockData.rotate90Reverse(getType(), getData()); + setData((short) newData); return newData; } @@ -118,56 +105,55 @@ public class BaseBlock { * @return new data value */ public int cycleData(int increment) { - int newData = BlockData.cycle(this.type, this.data, increment); - this.data = (short) newData; + int newData = BlockData.cycle(getType(), getData(), increment); + setData((short) newData); return newData; } /** * Flip this block. + * + * @return this block */ public BaseBlock flip() { - data = (short) BlockData.flip(type, data); + setData((short) BlockData.flip(getType(), getData())); return this; } /** * Flip this block. - * @param direction + * + * @param direction direction to flip in + * @return this block */ public BaseBlock flip(FlipDirection direction) { - data = (short) BlockData.flip(type, data, direction); + setData((short) BlockData.flip(getType(), getData(), direction)); return this; } + /** + * Checks whether the type ID and data value are equal. + */ @Override public boolean equals(Object o) { if (!(o instanceof BaseBlock)) { return false; } - return type == ((BaseBlock) o).type && data == ((BaseBlock) o).data; - } - - public boolean equalsFuzzy(BaseBlock o) { - return (type == o.type && data == o.data) || data == -1 || o.data == -1; - } - - @Override - public int hashCode() { - int ret = type << 3; - if (data != (byte) -1) ret |= data; - return ret; - } - - @Override - public String toString() { - return "BaseBlock id: " + getType() + " with damage: " + getData(); + return getType() == ((BaseBlock) o).getType() && getData() == ((BaseBlock) o).getData(); + } + + /** + * Checks if the type is the same, and if data is the same if only data != -1. + * + * @param o other block + * @return true if equal + */ + public boolean equalsFuzzy(BaseBlock o) { + return (getType() == o.getType() && getData() == o.getData()) || getData() == -1 || o.getData() == -1; } /** - * - * * @param iter * @return * @deprecated This method is silly diff --git a/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java b/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java index 5494ee991..3d06ae3ff 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java +++ b/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java @@ -23,44 +23,41 @@ import java.util.HashMap; import java.util.Map; /** - * Represents an item. + * Represents an item, without an amount value. See {@link BaseItemStack} for an instance + * with stack amount information. * * @author sk89q */ public class BaseItem { - /** - * Item ID. - */ + private int id; - /** - * Item damage. - */ - private short damage; - - private Map enchantments = new HashMap(); + private short data; + private final Map enchantments = new HashMap(); /** * Construct the object. * - * @param id + * @param id ID of the item */ public BaseItem(int id) { this.id = id; - this.damage = 0; + this.data = 0; } /** * Construct the object. * - * @param id - * @param damage + * @param id ID of the item + * @param data data value of the item */ - public BaseItem(int id, short damage) { + public BaseItem(int id, short data) { this.id = id; - this.damage = damage; + this.data = data; } /** + * Get the type of item. + * * @return the id */ public int getType() { @@ -68,6 +65,8 @@ public class BaseItem { } /** + * Get the type of item. + * * @param id the id to set */ public void setType(int id) { @@ -75,19 +74,48 @@ public class BaseItem { } /** + * Get the damage value. + * * @return the damage */ + @Deprecated public short getDamage() { - return damage; + return data; } /** - * @param damage the damage to set + * Get the data value. + * + * @return the data */ - public void setDamage(short damage) { - this.damage = damage; + public short getData() { + return data; } + /** + * Set the data value. + * + * @param data the damage to set + */ + @Deprecated + public void setDamage(short data) { + this.data = data; + } + + /** + * Set the data value. + * + * @param data the damage to set + */ + public void setData(short data) { + this.data = data; + } + + /** + * Get the map of enchantments. + * + * @return map of enchantments + */ public Map getEnchantments() { return enchantments; } diff --git a/src/main/java/com/sk89q/worldedit/blocks/BaseItemStack.java b/src/main/java/com/sk89q/worldedit/blocks/BaseItemStack.java index 5d12a9bc9..d23c268b9 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/BaseItemStack.java +++ b/src/main/java/com/sk89q/worldedit/blocks/BaseItemStack.java @@ -31,9 +31,9 @@ public class BaseItemStack extends BaseItem { private int amount = 1; /** - * Construct the object. + * Construct the object with default stack size of one, with data value of 0. * - * @param id + * @param id with data value of 0. */ public BaseItemStack(int id) { super(id); @@ -42,8 +42,8 @@ public class BaseItemStack extends BaseItem { /** * Construct the object. * - * @param id - * @param amount + * @param id type ID + * @param amount amount in the stack */ public BaseItemStack(int id, int amount) { super(id); @@ -53,16 +53,18 @@ public class BaseItemStack extends BaseItem { /** * Construct the object. * - * @param id - * @param amount - * @param damage + * @param id type ID + * @param amount amount in the stack + * @param data data value */ - public BaseItemStack(int id, int amount, short damage) { - super(id, damage); + public BaseItemStack(int id, int amount, short data) { + super(id, data); this.amount = amount; } /** + * Get the number of items in the stack. + * * @return the amount */ public int getAmount() { @@ -70,6 +72,8 @@ public class BaseItemStack extends BaseItem { } /** + * Set the amount of items in the stack. + * * @param amount the amount to set */ public void setAmount(int amount) { diff --git a/src/main/java/com/sk89q/worldedit/blocks/ChestBlock.java b/src/main/java/com/sk89q/worldedit/blocks/ChestBlock.java index e21bd822a..1b66e4304 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/ChestBlock.java +++ b/src/main/java/com/sk89q/worldedit/blocks/ChestBlock.java @@ -19,80 +19,71 @@ package com.sk89q.worldedit.blocks; -import com.sk89q.jnbt.*; -import com.sk89q.worldedit.data.*; -import java.util.Map; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.ArrayList; +import java.util.Map; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.data.DataException; /** - * Represents chests. + * Represents a chest block. * * @author sk89q */ public class ChestBlock extends ContainerBlock { /** - * Construct the chest block. + * Construct an empty chest block with the default orientation (data value). */ public ChestBlock() { super(BlockID.CHEST, 27); } /** - * Construct the chest block. + * Construct an empty chest block with a custom data value. * - * @param data + * @param data data indicating the position of the chest */ public ChestBlock(int data) { super(BlockID.CHEST, data, 27); } /** - * Construct the chest block. + * Construct the chest block with a custom data value and a list of items. * - * @param data - * @param items + * @param data data indicating the position of the chest + * @param items array of items */ public ChestBlock(int data, BaseItemStack[] items) { super(BlockID.CHEST, data, 27); setItems(items); } - /** - * Get the tile entity ID. - * - * @return - */ - public String getTileEntityID() { + @Override + public String getNbtId() { return "Chest"; } - /** - * Store additional tile entity data. Returns true if the data is used. - * - * @return map of values - * @throws DataException - */ - public Map toTileEntityNBT() - throws DataException { + @Override + public CompoundTag getNbtData() { Map values = new HashMap(); values.put("Items", new ListTag("Items", CompoundTag.class, serializeInventory(getItems()))); - return values; + return new CompoundTag(getNbtId(), values); } - /** - * Get additional information from the title entity data. - * - * @param values - * @throws DataException - */ - public void fromTileEntityNBT(Map values) - throws DataException { - if (values == null) { + @Override + public void setNbtData(CompoundTag rootTag) throws DataException { + if (rootTag == null) { return; } + + Map values = rootTag.getValue(); Tag t = values.get("id"); if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Chest")) { @@ -100,6 +91,7 @@ public class ChestBlock extends ContainerBlock { } List items = new ArrayList(); + for (Tag tag : NBTUtils.getChildTag(values, "Items", ListTag.class).getValue()) { if (!(tag instanceof CompoundTag)) { throw new DataException("CompoundTag expected as child tag of Chest's Items"); diff --git a/src/main/java/com/sk89q/worldedit/blocks/ContainerBlock.java b/src/main/java/com/sk89q/worldedit/blocks/ContainerBlock.java index 394ff27b0..614dc4f59 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/ContainerBlock.java +++ b/src/main/java/com/sk89q/worldedit/blocks/ContainerBlock.java @@ -19,6 +19,11 @@ package com.sk89q.worldedit.blocks; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import com.sk89q.jnbt.ByteTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.ListTag; @@ -27,17 +32,13 @@ import com.sk89q.jnbt.ShortTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.data.DataException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** * Represents a block that stores items. * * @author sk89q */ public abstract class ContainerBlock extends BaseBlock implements TileEntityBlock { + private BaseItemStack[] items; public ContainerBlock(int type, int inventorySize) { @@ -50,7 +51,6 @@ public abstract class ContainerBlock extends BaseBlock implements TileEntityBloc this.items = new BaseItemStack[inventorySize]; } - /** * Get the list of items. * @@ -68,6 +68,11 @@ public abstract class ContainerBlock extends BaseBlock implements TileEntityBloc public void setItems(BaseItemStack[] items) { this.items = items; } + + @Override + public boolean hasNbtData() { + return true; + } public Map serializeItem(BaseItemStack item) { Map data = new HashMap(); diff --git a/src/main/java/com/sk89q/worldedit/blocks/DispenserBlock.java b/src/main/java/com/sk89q/worldedit/blocks/DispenserBlock.java index 0e5367c89..81c8e9c9b 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/DispenserBlock.java +++ b/src/main/java/com/sk89q/worldedit/blocks/DispenserBlock.java @@ -19,12 +19,17 @@ package com.sk89q.worldedit.blocks; -import com.sk89q.jnbt.*; -import com.sk89q.worldedit.data.*; -import java.util.Map; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.ArrayList; +import java.util.Map; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.data.DataException; /** * Represents dispensers. @@ -34,65 +39,52 @@ import java.util.ArrayList; public class DispenserBlock extends ContainerBlock { /** - * Construct the dispenser block. + * Construct an empty dispenser block. */ public DispenserBlock() { super(BlockID.DISPENSER, 9); } /** - * Construct the dispenser block. + * Construct an empty dispenser block. * - * @param data + * @param data data value (orientation) */ public DispenserBlock(int data) { super(BlockID.DISPENSER, data, 9); } /** - * Construct the dispenser block. + * Construct a dispenser block with the given orientation and inventory. * - * @param data - * @param items + * @param data data value (orientation) + * @param items array of items in the inventory */ public DispenserBlock(int data, BaseItemStack[] items) { super(BlockID.DISPENSER, data, 9); this.setItems(items); } - /** - * Get the tile entity ID. - * - * @return - */ - public String getTileEntityID() { + @Override + public String getNbtId() { return "Trap"; } - /** - * Store additional tile entity data. Returns true if the data is used. - * - * @return map of values - * @throws DataException - */ - public Map toTileEntityNBT() - throws DataException { + @Override + public CompoundTag getNbtData() { Map values = new HashMap(); - values.put("Items", new ListTag("Items", CompoundTag.class, serializeInventory(getItems()))); - return values; + values.put("Items", new ListTag("Items", CompoundTag.class, + serializeInventory(getItems()))); + return new CompoundTag(getNbtId(), values); } - /** - * Get additional information from the title entity data. - * - * @param values - * @throws DataException - */ - public void fromTileEntityNBT(Map values) - throws DataException { - if (values == null) { + @Override + public void setNbtData(CompoundTag rootTag) throws DataException { + if (rootTag == null) { return; } + + Map values = rootTag.getValue(); Tag t = values.get("id"); if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Trap")) { diff --git a/src/main/java/com/sk89q/worldedit/blocks/FurnaceBlock.java b/src/main/java/com/sk89q/worldedit/blocks/FurnaceBlock.java index a65c875cb..b0187a262 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/FurnaceBlock.java +++ b/src/main/java/com/sk89q/worldedit/blocks/FurnaceBlock.java @@ -15,60 +15,58 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . -*/ + */ package com.sk89q.worldedit.blocks; -import com.sk89q.jnbt.*; -import com.sk89q.worldedit.data.*; - import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.HashMap; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.data.DataException; /** - * Represents furnaces. - * + * Represents a furnace block. + * * @author sk89q */ public class FurnaceBlock extends ContainerBlock { - /** - * Fuel time. - */ private short burnTime; - - /** - * Cook time. - */ private short cookTime; /** - * Construct the chest block. - * - * @param type + * Construct an empty furnace block with the default orientation. + * + * @param type type ID */ public FurnaceBlock(int type) { super(type, 2); } /** - * Construct the chest block. - * - * @param type - * @param data + * Construct an empty furnace block with a given orientation. + * + * @param type type ID + * @param data orientation */ public FurnaceBlock(int type, int data) { super(type, data, 2); } /** - * Construct the chest block. - * - * @param type - * @param data - * @param items + * Construct an furnace block with a given orientation and inventory. + * + * @param type type ID + * @param data orientation + * @param items inventory items */ public FurnaceBlock(int type, int data, BaseItemStack[] items) { super(type, data, 2); @@ -76,71 +74,67 @@ public class FurnaceBlock extends ContainerBlock { } /** - * @return the burnTime + * Get the burn time. + * + * @return the burn time */ public short getBurnTime() { return burnTime; } /** - * @param burnTime the burnTime to set + * Set the burn time. + * + * @param burnTime the burn time */ public void setBurnTime(short burnTime) { this.burnTime = burnTime; } /** - * @return the cookTime + * Get the cook time. + * + * @return the cook time */ public short getCookTime() { return cookTime; } /** - * @param cookTime the cookTime to set + * Set the cook time. + * + * @param cookTime the cook time to set */ public void setCookTime(short cookTime) { this.cookTime = cookTime; } - /** - * Get the tile entity ID. - * - * @return - */ - public String getTileEntityID() { + @Override + public String getNbtId() { return "Furnace"; } - /** - * Store additional tile entity data. Returns true if the data is used. - * - * @return map of values - * @throws DataException - */ - public Map toTileEntityNBT() - throws DataException { + @Override + public CompoundTag getNbtData() { Map values = new HashMap(); - values.put("Items", new ListTag("Items", CompoundTag.class, serializeInventory(getItems()))); + values.put("Items", new ListTag("Items", CompoundTag.class, + serializeInventory(getItems()))); values.put("BurnTime", new ShortTag("BurnTime", burnTime)); values.put("CookTime", new ShortTag("CookTime", cookTime)); - return values; + return new CompoundTag(getNbtId(), values); } - /** - * Get additional information from the title entity data. - * - * @param values - * @throws DataException - */ - public void fromTileEntityNBT(Map values) - throws DataException { - if (values == null) { + @Override + public void setNbtData(CompoundTag rootTag) throws DataException { + if (rootTag == null) { return; } + + Map values = rootTag.getValue(); Tag t = values.get("id"); - if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Furnace")) { + if (!(t instanceof StringTag) + || !((StringTag) t).getValue().equals("Furnace")) { throw new DataException("'Furnace' tile entity expected"); } @@ -150,7 +144,8 @@ public class FurnaceBlock extends ContainerBlock { for (Tag tag : items.getValue()) { if (!(tag instanceof CompoundTag)) { - throw new DataException("CompoundTag expected as child tag of Furnace Items"); + throw new DataException( + "CompoundTag expected as child tag of Furnace Items"); } compound.add((CompoundTag) tag); } diff --git a/src/main/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java b/src/main/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java index 92b08315e..14896d050 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java +++ b/src/main/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java @@ -19,30 +19,29 @@ package com.sk89q.worldedit.blocks; -import com.sk89q.jnbt.*; -import com.sk89q.worldedit.MobType; -import com.sk89q.worldedit.data.*; -import java.util.Map; import java.util.HashMap; +import java.util.Map; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.MobType; +import com.sk89q.worldedit.data.DataException; /** - * Represents mob spawners. + * A mob spawner block. * * @author sk89q */ public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock { - /** - * Store mob spawn type. - */ + private String mobType; - /** - * Delay until next spawn. - */ private short delay; /** - * Construct the mob spawner block. - * + * Construct the mob spawner block with a pig as the mob type. */ public MobSpawnerBlock() { super(BlockID.MOB_SPAWNER); @@ -50,9 +49,9 @@ public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock { } /** - * Construct the mob spawner block. + * Construct the mob spawner block with a given mob type. * - * @param mobType + * @param mobType mob type */ public MobSpawnerBlock(String mobType) { super(BlockID.MOB_SPAWNER); @@ -60,9 +59,9 @@ public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock { } /** - * Construct the mob spawner block. + * Construct the mob spawner block with a specified data value. * - * @param data + * @param data data value */ public MobSpawnerBlock(int data) { super(BlockID.MOB_SPAWNER, data); @@ -71,8 +70,8 @@ public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock { /** * Construct the mob spawner block. * - * @param data - * @param mobType + * @param data data value + * @param mobType mob type */ public MobSpawnerBlock(int data, String mobType) { super(BlockID.MOB_SPAWNER, data); @@ -82,7 +81,7 @@ public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock { /** * Get the mob type. * - * @return + * @return the mob type */ public String getMobType() { return mobType; @@ -91,13 +90,15 @@ public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock { /** * Set the mob type. * - * @param mobType + * @param mobType the mob type */ public void setMobType(String mobType) { this.mobType = mobType; } /** + * Get the spawn delay. + * * @return the delay */ public short getDelay() { @@ -105,47 +106,40 @@ public class MobSpawnerBlock extends BaseBlock implements TileEntityBlock { } /** + * Set the spawn delay. + * * @param delay the delay to set */ public void setDelay(short delay) { this.delay = delay; } + + @Override + public boolean hasNbtData() { + return true; + } - /** - * Get the tile entity ID. - * - * @return - */ - public String getTileEntityID() { + @Override + public String getNbtId() { return "MobSpawner"; } - /** - * Store additional tile entity data. Returns true if the data is used. - * - * @return map of values - * @throws DataException - */ - public Map toTileEntityNBT() - throws DataException { + @Override + public CompoundTag getNbtData() { Map values = new HashMap(); values.put("EntityId", new StringTag("EntityId", mobType)); values.put("Delay", new ShortTag("Delay", delay)); - return values; + return new CompoundTag(getNbtId(), values); } - /** - * Get additional information from the title entity data. - * - * @param values - * @throws DataException - */ - public void fromTileEntityNBT(Map values) - throws DataException { - if (values == null) { + @Override + public void setNbtData(CompoundTag rootTag) throws DataException { + if (rootTag == null) { return; } + Map values = rootTag.getValue(); + Tag t = values.get("id"); if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("MobSpawner")) { throw new DataException("'MobSpawner' tile entity expected"); diff --git a/src/main/java/com/sk89q/worldedit/blocks/NoteBlock.java b/src/main/java/com/sk89q/worldedit/blocks/NoteBlock.java index 5a382ed2d..283ca4c37 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/NoteBlock.java +++ b/src/main/java/com/sk89q/worldedit/blocks/NoteBlock.java @@ -15,27 +15,30 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . -*/ + */ package com.sk89q.worldedit.blocks; -import com.sk89q.jnbt.*; -import com.sk89q.worldedit.data.*; -import java.util.Map; import java.util.HashMap; +import java.util.Map; + +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.data.DataException; /** - * + * A note block. + * * @author sk89q */ public class NoteBlock extends BaseBlock implements TileEntityBlock { - /** - * Stores the pitch. - */ + private byte note; /** - * Construct the note block. + * Construct the note block with a data value of 0. */ public NoteBlock() { super(BlockID.NOTE_BLOCK); @@ -43,9 +46,9 @@ public class NoteBlock extends BaseBlock implements TileEntityBlock { } /** - * Construct the note block. - * - * @param data + * Construct the note block with a given data value. + * + * @param data data value */ public NoteBlock(int data) { super(BlockID.NOTE_BLOCK, data); @@ -53,10 +56,10 @@ public class NoteBlock extends BaseBlock implements TileEntityBlock { } /** - * Construct the note block. - * - * @param data - * @param note + * Construct the note block with a given data value and note. + * + * @param data data value + * @param note note */ public NoteBlock(int data, byte note) { super(BlockID.NOTE_BLOCK, data); @@ -64,6 +67,8 @@ public class NoteBlock extends BaseBlock implements TileEntityBlock { } /** + * Get the note. + * * @return the note */ public byte getNote() { @@ -71,50 +76,44 @@ public class NoteBlock extends BaseBlock implements TileEntityBlock { } /** + * Set the note. + * * @param note the note to set */ public void setNote(byte note) { this.note = note; } + + @Override + public boolean hasNbtData() { + return true; + } - /** - * Return the name of the title entity ID. - * - * @return title entity ID - */ - public String getTileEntityID() { + @Override + public String getNbtId() { return "Music"; } - /** - * Store additional tile entity data. Returns true if the data is used. - * - * @return map of values - * @throws DataException - */ - public Map toTileEntityNBT() - throws DataException { + @Override + public CompoundTag getNbtData() { Map values = new HashMap(); values.put("note", new ByteTag("note", note)); - return values; + return new CompoundTag(getNbtId(), values); } - /** - * Get additional information from the title entity data. - * - * @param values - * @throws DataException - */ - public void fromTileEntityNBT(Map values) - throws DataException { - if (values == null) { + @Override + public void setNbtData(CompoundTag rootTag) throws DataException { + if (rootTag == null) { return; } + Map values = rootTag.getValue(); + Tag t; t = values.get("id"); - if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Music")) { + if (!(t instanceof StringTag) + || !((StringTag) t).getValue().equals("Music")) { throw new DataException("'Music' tile entity expected"); } diff --git a/src/main/java/com/sk89q/worldedit/blocks/SignBlock.java b/src/main/java/com/sk89q/worldedit/blocks/SignBlock.java index 24b0a2326..f593b0d05 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/SignBlock.java +++ b/src/main/java/com/sk89q/worldedit/blocks/SignBlock.java @@ -15,30 +15,32 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . -*/ + */ package com.sk89q.worldedit.blocks; -import com.sk89q.jnbt.*; -import com.sk89q.worldedit.data.*; -import java.util.Map; import java.util.HashMap; +import java.util.Map; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.data.DataException; /** - * + * Represents a sign block. + * * @author sk89q */ public class SignBlock extends BaseBlock implements TileEntityBlock { - /** - * Stores the sign's text. - */ + private String[] text; /** * Construct the sign without text. - * - * @param type - * @param data + * + * @param type type ID + * @param data data value (orientation) */ public SignBlock(int type, int data) { super(type, data); @@ -47,17 +49,22 @@ public class SignBlock extends BaseBlock implements TileEntityBlock { /** * Construct the sign with text. - * - * @param type - * @param data - * @param text + * + * @param type type ID + * @param data data value (orientation) + * @param text lines of text */ public SignBlock(int type, int data, String[] text) { super(type, data); + if (text == null) { + this.text = new String[] { "", "", "", "" }; + } this.text = text; } /** + * Get the text. + * * @return the text */ public String[] getText() { @@ -65,55 +72,52 @@ public class SignBlock extends BaseBlock implements TileEntityBlock { } /** + * Set the text. + * * @param text the text to set */ public void setText(String[] text) { + if (text == null) { + throw new IllegalArgumentException("Can't set null text for a sign"); + } this.text = text; } + + @Override + public boolean hasNbtData() { + return true; + } - /** - * Return the name of the title entity ID. - * - * @return title entity ID - */ - public String getTileEntityID() { + @Override + public String getNbtId() { return "Sign"; } - /** - * Store additional tile entity data. Returns true if the data is used. - * - * @return map of values - * @throws DataException - */ - public Map toTileEntityNBT() - throws DataException { + @Override + public CompoundTag getNbtData() { Map values = new HashMap(); values.put("Text1", new StringTag("Text1", text[0])); values.put("Text2", new StringTag("Text2", text[1])); values.put("Text3", new StringTag("Text3", text[2])); values.put("Text4", new StringTag("Text4", text[3])); - return values; + return new CompoundTag(getNbtId(), values); } - /** - * Get additional information from the title entity data. - * - * @param values - * @throws DataException - */ - public void fromTileEntityNBT(Map values) - throws DataException { - if (values == null) { + @Override + public void setNbtData(CompoundTag rootTag) throws DataException { + if (rootTag == null) { return; } + Map values = rootTag.getValue(); + Tag t; text = new String[] { "", "", "", "" }; t = values.get("id"); - if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Sign")) { + if (!(t instanceof StringTag) + || !((StringTag) t).getValue().equals("Sign")) { throw new DataException("'Sign' tile entity expected"); } diff --git a/src/main/java/com/sk89q/worldedit/blocks/TileEntityBlock.java b/src/main/java/com/sk89q/worldedit/blocks/TileEntityBlock.java index eae44de23..11d7fcc2f 100644 --- a/src/main/java/com/sk89q/worldedit/blocks/TileEntityBlock.java +++ b/src/main/java/com/sk89q/worldedit/blocks/TileEntityBlock.java @@ -1,7 +1,7 @@ // $Id$ /* * WorldEdit - * Copyright (C) 2010 sk89q and contributors + * Copyright (C) sk89q and contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,42 +19,22 @@ package com.sk89q.worldedit.blocks; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.data.*; - -import java.util.Map; +import com.sk89q.worldedit.foundation.NbtValued; /** - * A class implementing this interface has extra TileEntityBlock data to store. + * Indicates a block that contains extra data identified as an NBT structure. Compared + * to a {@link NbtValued}, tile entity blocks also contain an ID. * + * @see NbtValued * @author sk89q */ -public interface TileEntityBlock { +public interface TileEntityBlock extends NbtValued { /** * Return the name of the title entity ID. * - * @return tile entity ID + * @return tile entity ID, non-null string */ - public abstract String getTileEntityID(); - - /** - * Store additional tile entity data. - * - * @return map of values - * @throws DataException When invalid data is encountered - */ - public abstract Map toTileEntityNBT() - throws DataException; - - /** - * Get additional information from the tile entity data. - * - * @param values map of data - * @throws DataException When invalid data is encountered - */ - public abstract void fromTileEntityNBT(Map values) - throws DataException; - + String getNbtId(); } diff --git a/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 34c160363..9c9c3bd10 100644 --- a/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -27,17 +27,20 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; -import com.sk89q.worldedit.LocalEntity; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.bukkit.entity.BukkitEntity; -import com.sk89q.worldedit.util.TreeGenerator; +import org.bukkit.Effect; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.TreeType; +import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.Chest; -import org.bukkit.block.Furnace; import org.bukkit.block.CreatureSpawner; +import org.bukkit.block.Furnace; import org.bukkit.block.Sign; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Animals; @@ -56,24 +59,34 @@ import org.bukkit.entity.Tameable; import org.bukkit.inventory.DoubleChestInventory; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import org.bukkit.Effect; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.TreeType; -import org.bukkit.World; import com.sk89q.worldedit.BiomeType; import com.sk89q.worldedit.BlockVector2D; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.EntityType; +import com.sk89q.worldedit.LocalEntity; import com.sk89q.worldedit.LocalWorld; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector2D; -import com.sk89q.worldedit.blocks.*; -import com.sk89q.worldedit.EntityType; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.blocks.ContainerBlock; +import com.sk89q.worldedit.blocks.FurnaceBlock; +import com.sk89q.worldedit.blocks.MobSpawnerBlock; +import com.sk89q.worldedit.blocks.NoteBlock; +import com.sk89q.worldedit.blocks.SignBlock; +import com.sk89q.worldedit.bukkit.entity.BukkitEntity; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.TreeGenerator; public class BukkitWorld extends LocalWorld { + + private static final Logger logger = Logger.getLogger(BukkitWorld.class.getCanonicalName()); private World world; + private boolean skipNmsAccess = false; + private boolean skipNmsSafeSet = false; /** * Construct the object. @@ -211,6 +224,7 @@ public class BukkitWorld extends LocalWorld { * @param pt * @return */ + @Override public BiomeType getBiome(Vector2D pt) { Biome bukkitBiome = world.getBiome(pt.getBlockX(), pt.getBlockZ()); try { @@ -340,6 +354,15 @@ public class BukkitWorld extends LocalWorld { return true; } + if (!skipNmsAccess) { + try { + return NmsBlock.set(world, pt, block); + } catch (Throwable t) { + logger.log(Level.WARNING, "WorldEdit: Failed to do NMS access for direct NBT data copy", t); + skipNmsAccess = true; + } + } + return false; } @@ -400,6 +423,7 @@ public class BukkitWorld extends LocalWorld { org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state; NoteBlock we = (NoteBlock) block; we.setNote(bukkit.getRawNote()); + return true; } return false; @@ -902,6 +926,7 @@ public class BukkitWorld extends LocalWorld { world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).breakNaturally(); } + @Override public LocalEntity[] getEntities(Region region) { List entities = new ArrayList(); for (Vector2D pt : region.getChunks()) { @@ -917,6 +942,7 @@ public class BukkitWorld extends LocalWorld { return entities.toArray(new BukkitEntity[entities.size()]); } + @Override public int killEntities(LocalEntity... entities) { int amount = 0; Set toKill = new HashSet(); @@ -931,4 +957,52 @@ public class BukkitWorld extends LocalWorld { } return amount; } + + @Override + public BaseBlock getBlock(Vector pt) { + int type = getBlockType(pt); + int data = getBlockData(pt); + + switch (type) { + case BlockID.WALL_SIGN: + case BlockID.SIGN_POST: + //case BlockID.CHEST: // Prevent data loss for now + //case BlockID.FURNACE: + //case BlockID.BURNING_FURNACE: + //case BlockID.DISPENSER: + //case BlockID.MOB_SPAWNER: + case BlockID.NOTE_BLOCK: + return super.getBlock(pt); + default: + if (!skipNmsAccess) { + try { + NmsBlock block = NmsBlock.get(world, pt, type, data); + if (block != null) { + return block; + } + } catch (Throwable t) { + logger.log(Level.WARNING, + "WorldEdit: Failed to do NMS access for direct NBT data copy", t); + skipNmsAccess = true; + } + } + } + + return super.getBlock(pt); + } + + @Override + public boolean setBlock(Vector pt, com.sk89q.worldedit.foundation.Block block, boolean notifyAdjacent) { + if (!skipNmsSafeSet) { + try { + return NmsBlock.setSafely(this, pt, block, notifyAdjacent); + } catch (Throwable t) { + logger.log(Level.WARNING, + "WorldEdit: Failed to do NMS safe block set", t); + skipNmsSafeSet = true; + } + } + + return super.setBlock(pt, block, notifyAdjacent); + } } diff --git a/src/main/java/com/sk89q/worldedit/bukkit/NmsBlock.java b/src/main/java/com/sk89q/worldedit/bukkit/NmsBlock.java new file mode 100644 index 000000000..63b09aa93 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/bukkit/NmsBlock.java @@ -0,0 +1,360 @@ +// $Id$ +/* + * This file is a part of WorldEdit. + * Copyright (c) sk89q + * Copyright (c) the 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 + * (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 + * GNU 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.worldedit.bukkit; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagByte; +import net.minecraft.server.NBTTagByteArray; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagDouble; +import net.minecraft.server.NBTTagEnd; +import net.minecraft.server.NBTTagFloat; +import net.minecraft.server.NBTTagInt; +import net.minecraft.server.NBTTagIntArray; +import net.minecraft.server.NBTTagList; +import net.minecraft.server.NBTTagLong; +import net.minecraft.server.NBTTagShort; +import net.minecraft.server.NBTTagString; +import net.minecraft.server.TileEntity; + +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; + +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.EndTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntArrayTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.TileEntityBlock; +import com.sk89q.worldedit.data.DataException; +import com.sk89q.worldedit.foundation.Block; + +/** + * A blind handler of blocks with TileEntity data that directly access Minecraft's + * classes through CraftBukkit. + *

+ * Usage of this class may break terribly in the future, and therefore usage should + * be trapped in a handler for {@link Throwable}. + */ +class NmsBlock extends BaseBlock implements TileEntityBlock { + + private NBTTagCompound nbtData = null; + + /** + * Create a new instance with a given type ID, data value, and previous + * {@link TileEntityBlock}-implementing object. + * + * @param type block type ID + * @param data data value + * @param tileEntityBlock tile entity block + */ + public NmsBlock(int type, int data, TileEntityBlock tileEntityBlock) { + super(type, data); + + nbtData = (NBTTagCompound) fromNative(tileEntityBlock.getNbtData()); + } + + /** + * Create a new instance with a given type ID, data value, and raw + * {@link NBTTagCompound} copy. + * + * @param type block type ID + * @param data data value + * @param nbtData raw NBT data + */ + public NmsBlock(int type, int data, NBTTagCompound nbtData) { + super(type, data); + + this.nbtData = nbtData; + } + + /** + * Build a {@link NBTTagCompound} that has valid coordinates. + * + * @param pt coordinates to set + * @return the tag compound + */ + private NBTTagCompound getNmsData(Vector pt) { + if (nbtData == null) { + return null; + } + + nbtData.set("x", new NBTTagInt("x", pt.getBlockX())); + nbtData.set("y", new NBTTagInt("y", pt.getBlockY())); + nbtData.set("z", new NBTTagInt("z", pt.getBlockZ())); + + return nbtData; + } + + @Override + public boolean hasNbtData() { + return nbtData != null; + } + + @Override + public String getNbtId() { + if (nbtData == null) { + return ""; + } + + return nbtData.getString("id"); + } + + @Override + public CompoundTag getNbtData() { + if (nbtData == null) { + return new CompoundTag(getNbtId(), + new HashMap()); + } + return (CompoundTag) toNative(nbtData); + } + + @Override + public void setNbtData(CompoundTag tag) throws DataException { + if (tag == null) { + this.nbtData = null; + } + this.nbtData = (NBTTagCompound) fromNative(tag); + } + + /** + * Build an instance from the given information. + * + * @param world world to get the block from + * @param position position to get the block at + * @param type type ID of block + * @param data data value of block + * @return the block, or null + */ + public static NmsBlock get(World world, Vector position, int type, int data) { + TileEntity te = ((CraftWorld) world).getHandle().getTileEntity( + position.getBlockX(), position.getBlockY(), position.getBlockZ()); + + if (te != null) { + NBTTagCompound tag = new NBTTagCompound(); + te.b(tag); // Load data + return new NmsBlock(type, data, tag); + } + + return null; + } + + /** + * Set an instance or a {@link TileEntityBlock} to the given position. + * + * @param world world to set the block in + * @param position position to set the block at + * @param block the block to set + * @return true if tile entity data was copied to the world + */ + public static boolean set(World world, Vector position, BaseBlock block) { + NBTTagCompound data = null; + + if (block instanceof NmsBlock) { + NmsBlock nmsProxyBlock = (NmsBlock) block; + data = nmsProxyBlock.getNmsData(position); + } else if (block instanceof TileEntityBlock) { + NmsBlock nmsProxyBlock = new NmsBlock( + block.getType(), block.getData(), block); + data = nmsProxyBlock.getNmsData(position); + } + + if (data != null) { + TileEntity te = ((CraftWorld) world).getHandle().getTileEntity( + position.getBlockX(), position.getBlockY(), position.getBlockZ()); + if (te != null) { + te.a(data); // Load data + return true; + } + } + + return false; + } + + /** + * Tries to set a block 'safely', as in setting the block data to the location, and + * then triggering physics only at the end. + * + * @param world world to set the block in + * @param position position to set the block at + * @param block the block to set + * @param notifyAdjacent true to notify physics and what not + * @return true if set + */ + public static boolean setSafely(BukkitWorld world, Vector position, + Block block, boolean notifyAdjacent) { + + int x = position.getBlockX(); + int y = position.getBlockY(); + int z = position.getBlockZ(); + + CraftWorld craftWorld = ((CraftWorld) world.getWorld()); + + boolean successful = craftWorld.getHandle().setRawTypeIdAndData( + x, y, z, block.getId(), block.getData()); + + if (successful) { + if (block instanceof BaseBlock) { + world.copyToWorld(position, (BaseBlock) block); + } + + if (notifyAdjacent) { + craftWorld.getHandle().update(x, y, z, block.getId()); + } else { + craftWorld.getHandle().notify(x, y, z); + } + } + + return successful; + } + + /** + * Converts from a non-native NMS NBT structure to a native WorldEdit NBT + * structure. + * + * @param foreign non-native NMS NBT structure + * @return native WorldEdit NBT structure + */ + private static Tag toNative(NBTBase foreign) { + if (foreign == null) { + return null; + } + if (foreign instanceof NBTTagCompound) { + Map values = new HashMap(); + for (Object obj : ((NBTTagCompound) foreign).d()) { + NBTBase base = (NBTBase) obj; + values.put(base.getName(), toNative(base)); + } + return new CompoundTag(foreign.getName(), values); + } else if (foreign instanceof NBTTagByte) { + return new ByteTag(foreign.getName(), ((NBTTagByte) foreign).data); + } else if (foreign instanceof NBTTagByteArray) { + return new ByteArrayTag(foreign.getName(), + ((NBTTagByteArray) foreign).data); + } else if (foreign instanceof NBTTagDouble) { + return new DoubleTag(foreign.getName(), + ((NBTTagDouble) foreign).data); + } else if (foreign instanceof NBTTagFloat) { + return new FloatTag(foreign.getName(), ((NBTTagFloat) foreign).data); + } else if (foreign instanceof NBTTagInt) { + return new IntTag(foreign.getName(), ((NBTTagInt) foreign).data); + } else if (foreign instanceof NBTTagIntArray) { + return new IntArrayTag(foreign.getName(), + ((NBTTagIntArray) foreign).data); + } else if (foreign instanceof NBTTagList) { + List values = new ArrayList(); + NBTTagList foreignList = (NBTTagList) foreign; + int type = NBTConstants.TYPE_BYTE; + for (int i = 0; i < foreignList.size(); i++) { + NBTBase foreignTag = foreignList.get(i); + values.add(toNative(foreignTag)); + type = foreignTag.getTypeId(); + } + Class cls = NBTConstants.getClassFromType(type); + return new ListTag(foreign.getName(), cls, values); + } else if (foreign instanceof NBTTagLong) { + return new LongTag(foreign.getName(), ((NBTTagLong) foreign).data); + } else if (foreign instanceof NBTTagShort) { + return new ShortTag(foreign.getName(), ((NBTTagShort) foreign).data); + } else if (foreign instanceof NBTTagString) { + return new StringTag(foreign.getName(), + ((NBTTagString) foreign).data); + } else if (foreign instanceof NBTTagEnd) { + return new EndTag(); + } else { + throw new IllegalArgumentException("Don't know how to make native " + + foreign.getClass().getCanonicalName()); + } + } + + /** + * Converts a WorldEdit-native NBT structure to a NMS structure. + * + * @param foreign structure to convert + * @return non-native structure + */ + private static NBTBase fromNative(Tag foreign) { + if (foreign == null) { + return null; + } + if (foreign instanceof CompoundTag) { + NBTTagCompound tag = new NBTTagCompound(foreign.getName()); + for (Map.Entry entry : ((CompoundTag) foreign) + .getValue().entrySet()) { + tag.set(entry.getKey(), fromNative(entry.getValue())); + } + return tag; + } else if (foreign instanceof ByteTag) { + return new NBTTagByte(foreign.getName(), + ((ByteTag) foreign).getValue()); + } else if (foreign instanceof ByteArrayTag) { + return new NBTTagByteArray(foreign.getName(), + ((ByteArrayTag) foreign).getValue()); + } else if (foreign instanceof DoubleTag) { + return new NBTTagDouble(foreign.getName(), + ((DoubleTag) foreign).getValue()); + } else if (foreign instanceof FloatTag) { + return new NBTTagFloat(foreign.getName(), + ((FloatTag) foreign).getValue()); + } else if (foreign instanceof IntTag) { + return new NBTTagInt(foreign.getName(), + ((IntTag) foreign).getValue()); + } else if (foreign instanceof IntArrayTag) { + return new NBTTagIntArray(foreign.getName(), + ((IntArrayTag) foreign).getValue()); + } else if (foreign instanceof ListTag) { + NBTTagList tag = new NBTTagList(foreign.getName()); + ListTag foreignList = (ListTag) foreign; + for (Tag t : foreignList.getValue()) { + tag.add(fromNative(t)); + } + return tag; + } else if (foreign instanceof LongTag) { + return new NBTTagLong(foreign.getName(), + ((LongTag) foreign).getValue()); + } else if (foreign instanceof ShortTag) { + return new NBTTagShort(foreign.getName(), + ((ShortTag) foreign).getValue()); + } else if (foreign instanceof StringTag) { + return new NBTTagString(foreign.getName(), + ((StringTag) foreign).getValue()); + } else if (foreign instanceof EndTag) { + return new NBTTagEnd(); + } else { + throw new IllegalArgumentException("Don't know how to make NMS " + + foreign.getClass().getCanonicalName()); + } + } +} diff --git a/src/main/java/com/sk89q/worldedit/data/AnvilChunk.java b/src/main/java/com/sk89q/worldedit/data/AnvilChunk.java index e92e2d4f0..8605c9be3 100644 --- a/src/main/java/com/sk89q/worldedit/data/AnvilChunk.java +++ b/src/main/java/com/sk89q/worldedit/data/AnvilChunk.java @@ -182,12 +182,12 @@ public class AnvilChunk implements Chunk { * @return * @throws DataException */ - private Map getBlockTileEntity(Vector pos) throws DataException { + private CompoundTag getBlockTileEntity(Vector pos) throws DataException { if (tileEntities == null) { populateTileEntities(); } - return tileEntities.get(new BlockVector(pos)); + return new CompoundTag("", tileEntities.get(new BlockVector(pos))); } @Override @@ -213,8 +213,8 @@ public class AnvilChunk implements Chunk { } if (block instanceof TileEntityBlock) { - Map tileEntity = getBlockTileEntity(pos); - ((TileEntityBlock) block).fromTileEntityNBT(tileEntity); + CompoundTag tileEntity = getBlockTileEntity(pos); + ((TileEntityBlock) block).setNbtData(tileEntity); } return block; diff --git a/src/main/java/com/sk89q/worldedit/data/OldChunk.java b/src/main/java/com/sk89q/worldedit/data/OldChunk.java index 23ece3928..9f7bd3402 100644 --- a/src/main/java/com/sk89q/worldedit/data/OldChunk.java +++ b/src/main/java/com/sk89q/worldedit/data/OldChunk.java @@ -19,12 +19,28 @@ package com.sk89q.worldedit.data; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.HashMap; -import com.sk89q.jnbt.*; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.blocks.*; + +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTUtils; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.blocks.ChestBlock; +import com.sk89q.worldedit.blocks.DispenserBlock; +import com.sk89q.worldedit.blocks.FurnaceBlock; +import com.sk89q.worldedit.blocks.MobSpawnerBlock; +import com.sk89q.worldedit.blocks.NoteBlock; +import com.sk89q.worldedit.blocks.SignBlock; +import com.sk89q.worldedit.blocks.TileEntityBlock; /** * Represents a chunk. @@ -164,12 +180,12 @@ public class OldChunk implements Chunk { * @return * @throws DataException */ - private Map getBlockTileEntity(Vector pos) throws DataException { + private CompoundTag getBlockTileEntity(Vector pos) throws DataException { if (tileEntities == null) { populateTileEntities(); } - return tileEntities.get(new BlockVector(pos)); + return new CompoundTag("", tileEntities.get(new BlockVector(pos))); } @Override @@ -195,8 +211,8 @@ public class OldChunk implements Chunk { } if (block instanceof TileEntityBlock) { - Map tileEntity = getBlockTileEntity(pos); - ((TileEntityBlock) block).fromTileEntityNBT(tileEntity); + CompoundTag tileEntity = getBlockTileEntity(pos); + ((TileEntityBlock) block).setNbtData(tileEntity); } return block; diff --git a/src/main/java/com/sk89q/worldedit/foundation/Block.java b/src/main/java/com/sk89q/worldedit/foundation/Block.java index 3f77873d1..a155f3198 100644 --- a/src/main/java/com/sk89q/worldedit/foundation/Block.java +++ b/src/main/java/com/sk89q/worldedit/foundation/Block.java @@ -16,7 +16,6 @@ * this program. If not, see . */ - package com.sk89q.worldedit.foundation; import com.sk89q.jnbt.CompoundTag; @@ -34,7 +33,7 @@ import com.sk89q.worldedit.data.DataException; * Implementations can and should extend this class to allow native implementations * of NBT data handling, primarily for performance reasons. Subclasses can only convert * from and to WorldEdit-native NBT structures when absolutely necessary (a.k.a. when - * {@link #getNbtData()} and {@link #setNbtData(CompoundTag)} are called. When + * {@link #getNbtData()} and {@link #setNbtData(CompoundTag)} are called). When * overriding the NBT methods, {@link #getNbtId()} should be overridden too, otherwise * the default implementation will invoke {@link #getNbtData()}, a potentially costly * operation when it is not needed. Implementations may want to cache converted NBT data @@ -64,7 +63,8 @@ public class Block implements TileEntityBlock { // Instances of this class should be _as small as possible_ because there will // be millions of instances of this object. - private short rawIdData; + private short id; + private short data; private CompoundTag nbtData; /** @@ -114,7 +114,7 @@ public class Block implements TileEntityBlock { * @return ID (between 0 and {@link #MAX_ID}) */ public int getId() { - return rawIdData >> 4; + return id; } /** @@ -123,15 +123,16 @@ public class Block implements TileEntityBlock { * @param id block id (between 0 and {@link #MAX_ID}). */ public void setId(int id) { - if (id < MAX_ID) { - throw new IllegalArgumentException("Can't have a block ID above " + MAX_ID); + if (id > MAX_ID) { + throw new IllegalArgumentException("Can't have a block ID above " + + MAX_ID + " (" + id + " given)"); } - + if (id < 0) { throw new IllegalArgumentException("Can't have a block ID below 0"); } - rawIdData = (short) ((id << 4) | rawIdData & 0xf); + this.id = (short) id; } /** @@ -140,7 +141,7 @@ public class Block implements TileEntityBlock { * @return data value (0-15) */ public int getData() { - return rawIdData & 0xf; + return data; } /** @@ -149,15 +150,17 @@ public class Block implements TileEntityBlock { * @param data block data value (between 0 and {@link #MAX_DATA}). */ public void setData(int data) { - if (data < MAX_DATA) { - throw new IllegalArgumentException("Can't have a block data value above " + MAX_DATA); + if (data > MAX_DATA) { + throw new IllegalArgumentException( + "Can't have a block data value above " + MAX_DATA + " (" + + data + " given)"); } if (data < 0) { throw new IllegalArgumentException("Can't have a block data value below 0"); } - rawIdData = (short) ((rawIdData ^ 4) | data); + this.data = (short) data; } /** @@ -173,20 +176,11 @@ public class Block implements TileEntityBlock { setData(data); } - /** - * Returns whether the block contains NBT data. - * - * @return NBT data; - */ + @Override public boolean hasNbtData() { return getNbtData() != null; } - /** - * Gets the ID of the NBT data (tile entity data). - * - * @return ID value (may be blank), or an empty string if no NBT data is set - */ @Override public String getNbtId() { CompoundTag nbtData = getNbtData(); diff --git a/src/main/java/com/sk89q/worldedit/foundation/NbtValued.java b/src/main/java/com/sk89q/worldedit/foundation/NbtValued.java index 8236fdc0f..89dcd2069 100644 --- a/src/main/java/com/sk89q/worldedit/foundation/NbtValued.java +++ b/src/main/java/com/sk89q/worldedit/foundation/NbtValued.java @@ -27,12 +27,22 @@ import com.sk89q.worldedit.data.DataException; * be used in other cases. */ public interface NbtValued { + + /** + * Returns whether the block contains NBT data. {@link #getNbtData()} must not return + * null if this method returns true. + * + * @return true if there is NBT data + */ + public boolean hasNbtData(); /** * Get the object's NBT data (tile entity data). The returned tag, if modified * in any way, should be sent to {@link #setNbtData(CompoundTag)} so that * the instance knows of the changes. Making changes without calling * {@link #setNbtData(CompoundTag)} could have unintended consequences. + *

+ * {@link #hasNbtData()} must return true if and only if method does not return null. * * @return compound tag, or null */ diff --git a/src/main/java/com/sk89q/worldedit/schematic/MCEditSchematicFormat.java b/src/main/java/com/sk89q/worldedit/schematic/MCEditSchematicFormat.java index cc932076e..2fb408bd9 100644 --- a/src/main/java/com/sk89q/worldedit/schematic/MCEditSchematicFormat.java +++ b/src/main/java/com/sk89q/worldedit/schematic/MCEditSchematicFormat.java @@ -18,6 +18,18 @@ package com.sk89q.worldedit.schematic; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.zip.GZIPInputStream; + import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.IntTag; @@ -35,17 +47,6 @@ import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.data.DataException; -import java.io.DataInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.zip.GZIPInputStream; - /** * @author zml2008 */ @@ -176,7 +177,7 @@ public class MCEditSchematicFormat extends SchematicFormat { BaseBlock block = getBlockForId(blocks[index], blockData[index]); if (block instanceof TileEntityBlock && tileEntitiesMap.containsKey(pt)) { - ((TileEntityBlock) block).fromTileEntityNBT(tileEntitiesMap.get(pt)); + ((TileEntityBlock) block).setNbtData(new CompoundTag("", tileEntitiesMap.get(pt))); } clipboard.setBlock(pt, block); } @@ -239,19 +240,22 @@ public class MCEditSchematicFormat extends SchematicFormat { // Store TileEntity data if (block instanceof TileEntityBlock) { - TileEntityBlock tileEntityBlock = - (TileEntityBlock) block; + TileEntityBlock tileEntityBlock = block; // Get the list of key/values from the block - Map values = tileEntityBlock.toTileEntityNBT(); - if (values != null) { - values.put("id", new StringTag("id", - tileEntityBlock.getTileEntityID())); + CompoundTag rawTag = tileEntityBlock.getNbtData(); + if (rawTag != null) { + Map values = new HashMap(); + for (Entry entry : rawTag.getValue().entrySet()) { + values.put(entry.getKey(), entry.getValue()); + } + + values.put("id", new StringTag("id", tileEntityBlock.getNbtId())); values.put("x", new IntTag("x", x)); values.put("y", new IntTag("y", y)); values.put("z", new IntTag("z", z)); - CompoundTag tileEntityTag = - new CompoundTag("TileEntity", values); + + CompoundTag tileEntityTag = new CompoundTag("TileEntity", values); tileEntities.add(tileEntityTag); } }