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