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.

This commit is contained in:
sk89q 2012-08-23 16:52:37 -07:00
parent a2aae2c4da
commit 48af65cac3
21 changed files with 1037 additions and 515 deletions

View File

@ -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<? extends Tag> 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);
}
}
}

View File

@ -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<BlockVector, BaseBlock> 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<BlockVector, BaseBlock> 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<BlockVector2D> dirtyChunks = new HashSet<BlockVector2D>();
for (Map.Entry<BlockVector, BaseBlock> 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<BlockVector, BaseBlock> 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) {

View File

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

View File

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

View File

@ -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<Integer, Integer> enchantments = new HashMap<Integer, Integer>();
private short data;
private final Map<Integer, Integer> enchantments = new HashMap<Integer, Integer>();
/**
* 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<Integer, Integer> getEnchantments() {
return enchantments;
}

View File

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

View File

@ -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<String, Tag> toTileEntityNBT()
throws DataException {
@Override
public CompoundTag getNbtData() {
Map<String, Tag> values = new HashMap<String, Tag>();
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<String, Tag> values)
throws DataException {
if (values == null) {
@Override
public void setNbtData(CompoundTag rootTag) throws DataException {
if (rootTag == null) {
return;
}
Map<String, Tag> 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<CompoundTag> items = new ArrayList<CompoundTag>();
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");

View File

@ -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<String, Tag> serializeItem(BaseItemStack item) {
Map<String, Tag> data = new HashMap<String, Tag>();

View File

@ -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<String, Tag> toTileEntityNBT()
throws DataException {
@Override
public CompoundTag getNbtData() {
Map<String, Tag> values = new HashMap<String, Tag>();
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<String, Tag> values)
throws DataException {
if (values == null) {
@Override
public void setNbtData(CompoundTag rootTag) throws DataException {
if (rootTag == null) {
return;
}
Map<String, Tag> values = rootTag.getValue();
Tag t = values.get("id");
if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("Trap")) {

View File

@ -15,60 +15,58 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
*/
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<String, Tag> toTileEntityNBT()
throws DataException {
@Override
public CompoundTag getNbtData() {
Map<String, Tag> values = new HashMap<String, Tag>();
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<String, Tag> values)
throws DataException {
if (values == null) {
@Override
public void setNbtData(CompoundTag rootTag) throws DataException {
if (rootTag == null) {
return;
}
Map<String, Tag> 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);
}

View File

@ -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<String, Tag> toTileEntityNBT()
throws DataException {
@Override
public CompoundTag getNbtData() {
Map<String, Tag> values = new HashMap<String, Tag>();
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<String, Tag> values)
throws DataException {
if (values == null) {
@Override
public void setNbtData(CompoundTag rootTag) throws DataException {
if (rootTag == null) {
return;
}
Map<String, Tag> values = rootTag.getValue();
Tag t = values.get("id");
if (!(t instanceof StringTag) || !((StringTag) t).getValue().equals("MobSpawner")) {
throw new DataException("'MobSpawner' tile entity expected");

View File

@ -15,27 +15,30 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
*/
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<String, Tag> toTileEntityNBT()
throws DataException {
@Override
public CompoundTag getNbtData() {
Map<String, Tag> values = new HashMap<String, Tag>();
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<String, Tag> values)
throws DataException {
if (values == null) {
@Override
public void setNbtData(CompoundTag rootTag) throws DataException {
if (rootTag == null) {
return;
}
Map<String, Tag> 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");
}

View File

@ -15,30 +15,32 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
*/
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<String, Tag> toTileEntityNBT()
throws DataException {
@Override
public CompoundTag getNbtData() {
Map<String, Tag> values = new HashMap<String, Tag>();
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<String, Tag> values)
throws DataException {
if (values == null) {
@Override
public void setNbtData(CompoundTag rootTag) throws DataException {
if (rootTag == null) {
return;
}
Map<String, Tag> 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");
}

View File

@ -1,7 +1,7 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010 sk89q <http://www.sk89q.com> and contributors
* Copyright (C) sk89q <http://www.sk89q.com> 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<String, Tag> 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<String, Tag> values)
throws DataException;
String getNbtId();
}

View File

@ -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<BukkitEntity> entities = new ArrayList<BukkitEntity>();
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<UUID> toKill = new HashSet<UUID>();
@ -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);
}
}

View File

@ -0,0 +1,360 @@
// $Id$
/*
* This file is a part of WorldEdit.
* Copyright (c) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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.
* </p>
* 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<String, Tag>());
}
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<String, Tag> values = new HashMap<String, Tag>();
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<Tag> values = new ArrayList<Tag>();
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<? extends Tag> 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<String, Tag> 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());
}
}
}

View File

@ -182,12 +182,12 @@ public class AnvilChunk implements Chunk {
* @return
* @throws DataException
*/
private Map<String, Tag> 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<String, Tag> tileEntity = getBlockTileEntity(pos);
((TileEntityBlock) block).fromTileEntityNBT(tileEntity);
CompoundTag tileEntity = getBlockTileEntity(pos);
((TileEntityBlock) block).setNbtData(tileEntity);
}
return block;

View File

@ -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<String, Tag> 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<String, Tag> tileEntity = getBlockTileEntity(pos);
((TileEntityBlock) block).fromTileEntityNBT(tileEntity);
CompoundTag tileEntity = getBlockTileEntity(pos);
((TileEntityBlock) block).setNbtData(tileEntity);
}
return block;

View File

@ -16,7 +16,6 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
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();

View File

@ -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.
* </p>
* {@link #hasNbtData()} must return true if and only if method does not return null.
*
* @return compound tag, or null
*/

View File

@ -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<String, Tag> values = tileEntityBlock.toTileEntityNBT();
if (values != null) {
values.put("id", new StringTag("id",
tileEntityBlock.getTileEntityID()));
CompoundTag rawTag = tileEntityBlock.getNbtData();
if (rawTag != null) {
Map<String, Tag> values = new HashMap<String, Tag>();
for (Entry<String, Tag> 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);
}
}