diff --git a/contrib/nmsblocks/CBXNmsBlock_146.class b/contrib/nmsblocks/CBXNmsBlock_146.class
new file mode 100644
index 000000000..0eca14c79
Binary files /dev/null and b/contrib/nmsblocks/CBXNmsBlock_146.class differ
diff --git a/contrib/nmsblocks/CBXNmsBlock_146.java b/contrib/nmsblocks/CBXNmsBlock_146.java
new file mode 100644
index 000000000..3e1d3693a
--- /dev/null
+++ b/contrib/nmsblocks/CBXNmsBlock_146.java
@@ -0,0 +1,442 @@
+// $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 .
+ */
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import net.minecraft.server.v1_4_6.NBTBase;
+import net.minecraft.server.v1_4_6.NBTTagByte;
+import net.minecraft.server.v1_4_6.NBTTagByteArray;
+import net.minecraft.server.v1_4_6.NBTTagCompound;
+import net.minecraft.server.v1_4_6.NBTTagDouble;
+import net.minecraft.server.v1_4_6.NBTTagEnd;
+import net.minecraft.server.v1_4_6.NBTTagFloat;
+import net.minecraft.server.v1_4_6.NBTTagInt;
+import net.minecraft.server.v1_4_6.NBTTagIntArray;
+import net.minecraft.server.v1_4_6.NBTTagList;
+import net.minecraft.server.v1_4_6.NBTTagLong;
+import net.minecraft.server.v1_4_6.NBTTagShort;
+import net.minecraft.server.v1_4_6.NBTTagString;
+import net.minecraft.server.v1_4_6.TileEntity;
+
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_4_6.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.bukkit.BukkitWorld;
+import com.sk89q.worldedit.bukkit.NmsBlock;
+import com.sk89q.worldedit.data.DataException;
+import com.sk89q.worldedit.foundation.Block;
+
+/**
+ * This version needs to be built on CraftBukkit 1.4.6 R0.1 or higher
+ */
+public class CBXNmsBlock_146 extends NmsBlock {
+
+ private static final Logger logger = Logger.getLogger(CBXNmsBlock_146.class.getCanonicalName());
+ private static Field compoundMapField;
+ private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why.
+ private NBTTagCompound nbtData = null;
+
+ static {
+ Field field;
+ try {
+ field = net.minecraft.server.v1_4_6.Block.class.getDeclaredField("isTileEntity");
+ field.setAccessible(true);
+ } catch (Throwable e) {
+ // logger.severe("Could not find NMS block tile entity field!");
+ field = null;
+ }
+ nmsBlock_isTileEntityField = field;
+ }
+
+ public static boolean verify() {
+ return nmsBlock_isTileEntityField != 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 CBXNmsBlock_146(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 CBXNmsBlock_146(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 CBXNmsBlock_146 get(World world, Vector position, int type, int data) {
+ if (!hasTileEntity(type)) {
+ return null;
+ }
+
+ 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 CBXNmsBlock_146(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 (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) {
+ return false;
+ }
+
+ if (block instanceof CBXNmsBlock_146) {
+ CBXNmsBlock_146 nmsProxyBlock = (CBXNmsBlock_146) block;
+ data = nmsProxyBlock.getNmsData(position);
+ } else if (block instanceof TileEntityBlock) {
+ CBXNmsBlock_146 nmsProxyBlock = new CBXNmsBlock_146(
+ block.getId(), 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 block id or data was changed
+ */
+ 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 changed = craftWorld.getHandle().setRawTypeIdAndData(
+ x, y, z, block.getId(), block.getData());
+
+ if (block instanceof BaseBlock) {
+ world.copyToWorld(position, (BaseBlock) block);
+ }
+
+ if (changed) {
+ if (notifyAdjacent) {
+ craftWorld.getHandle().update(x, y, z, block.getId());
+ } else {
+ craftWorld.getHandle().notify(x, y, z);
+ }
+ }
+
+ return changed;
+ }
+
+ public static boolean hasTileEntity(int type) {
+ net.minecraft.server.v1_4_6.Block nmsBlock = getNmsBlock(type);
+ if (nmsBlock == null) {
+ return false;
+ }
+
+ try {
+ return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast
+ } catch (IllegalAccessException e) {
+ return false;
+ }
+ }
+
+ public static net.minecraft.server.v1_4_6.Block getNmsBlock(int type) {
+ if (type < 0 || type >= net.minecraft.server.v1_4_6.Block.byId.length) {
+ return null;
+ }
+ return net.minecraft.server.v1_4_6.Block.byId[type];
+ }
+
+ /**
+ * 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
+ */
+ @SuppressWarnings("unchecked")
+ private static Tag toNative(NBTBase foreign) {
+ if (foreign == null) {
+ return null;
+ }
+ if (foreign instanceof NBTTagCompound) {
+ Map values = new HashMap();
+ Collection
+ * Usage of this class may break terribly in the future, and therefore usage should
+ * be trapped in a handler for {@link Throwable}.
+ */
+public class DefaultNmsBlock extends NmsBlock {
+
+ private static final Logger logger = WorldEdit.logger;
+ private static Field compoundMapField;
+ private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why.
+ private NBTTagCompound nbtData = null;
+
+ static {
+ Field field;
+ try {
+ field = net.minecraft.server.v1_4_R1.Block.class.getDeclaredField("isTileEntity");
+ field.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ // logger.severe("Could not find NMS block tile entity field!");
+ field = null;
+ }
+ nmsBlock_isTileEntityField = field;
+ }
+
+ public static boolean verify() {
+ return nmsBlock_isTileEntityField != 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 DefaultNmsBlock(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 DefaultNmsBlock(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 DefaultNmsBlock get(World world, Vector position, int type, int data) {
+ if (!hasTileEntity(type)) {
+ return null;
+ }
+
+ 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 DefaultNmsBlock(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 (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) {
+ return false;
+ }
+
+ if (block instanceof DefaultNmsBlock) {
+ DefaultNmsBlock nmsProxyBlock = (DefaultNmsBlock) block;
+ data = nmsProxyBlock.getNmsData(position);
+ } else if (block instanceof TileEntityBlock) {
+ DefaultNmsBlock nmsProxyBlock = new DefaultNmsBlock(
+ block.getId(), 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 block id or data was changed
+ */
+ 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 changed = craftWorld.getHandle().setRawTypeIdAndData(
+ x, y, z, block.getId(), block.getData());
+
+ if (block instanceof BaseBlock) {
+ world.copyToWorld(position, (BaseBlock) block);
+ }
+
+ if (changed) {
+ if (notifyAdjacent) {
+ craftWorld.getHandle().update(x, y, z, block.getId());
+ } else {
+ craftWorld.getHandle().notify(x, y, z);
+ }
+ }
+
+ return changed;
+ }
+
+ public static boolean hasTileEntity(int type) {
+ net.minecraft.server.v1_4_R1.Block nmsBlock = getNmsBlock(type);
+ if (nmsBlock == null) {
+ return false;
+ }
+
+ try {
+ return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast
+ } catch (IllegalAccessException e) {
+ return false;
+ }
+ }
+
+ public static net.minecraft.server.v1_4_R1.Block getNmsBlock(int type) {
+ if (type < 0 || type >= net.minecraft.server.v1_4_R1.Block.byId.length) {
+ return null;
+ }
+ return net.minecraft.server.v1_4_R1.Block.byId[type];
+ }
+
+ /**
+ * 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
+ */
+ @SuppressWarnings("unchecked")
+ private static Tag toNative(NBTBase foreign) {
+ if (foreign == null) {
+ return null;
+ }
+ if (foreign instanceof NBTTagCompound) {
+ Map values = new HashMap();
+ Collection foreignValues = null;
+
+ if (compoundMapField == null) {
+ try {
+ // Method name may change!
+ foreignValues = ((NBTTagCompound) foreign).c();
+ } catch (Throwable t) {
+ try {
+ logger.warning("WorldEdit: Couldn't get NBTTagCompound.c(), " +
+ "so we're going to try to get at the 'map' field directly from now on");
+
+ if (compoundMapField == null) {
+ compoundMapField = NBTTagCompound.class.getDeclaredField("map");
+ compoundMapField.setAccessible(true);
+ }
+ } catch (Throwable e) {
+ // Can't do much beyond this
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ if (compoundMapField != null) {
+ try {
+ foreignValues = ((HashMap) compoundMapField.get(foreign)).values();
+ } catch (Throwable e) {
+ // Can't do much beyond this
+ throw new RuntimeException(e);
+ }
+ }
+
+ for (Object obj : foreignValues) {
+ 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 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 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());
+ }
+ }
+
+ public static boolean isValidBlockType(int type) throws NoClassDefFoundError {
+ return type == 0 || (type >= 1 && type < net.minecraft.server.v1_4_R1.Block.byId.length
+ && net.minecraft.server.v1_4_R1.Block.byId[type] != null);
+ }
+}
diff --git a/src/main/java/com/sk89q/worldedit/bukkit/NmsBlock.java b/src/main/java/com/sk89q/worldedit/bukkit/NmsBlock.java
index b95543182..46bd635b0 100644
--- a/src/main/java/com/sk89q/worldedit/bukkit/NmsBlock.java
+++ b/src/main/java/com/sk89q/worldedit/bukkit/NmsBlock.java
@@ -1,442 +1,42 @@
-// $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.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Logger;
-
-import net.minecraft.server.v1_4_6.NBTBase;
-import net.minecraft.server.v1_4_6.NBTTagByte;
-import net.minecraft.server.v1_4_6.NBTTagByteArray;
-import net.minecraft.server.v1_4_6.NBTTagCompound;
-import net.minecraft.server.v1_4_6.NBTTagDouble;
-import net.minecraft.server.v1_4_6.NBTTagEnd;
-import net.minecraft.server.v1_4_6.NBTTagFloat;
-import net.minecraft.server.v1_4_6.NBTTagInt;
-import net.minecraft.server.v1_4_6.NBTTagIntArray;
-import net.minecraft.server.v1_4_6.NBTTagList;
-import net.minecraft.server.v1_4_6.NBTTagLong;
-import net.minecraft.server.v1_4_6.NBTTagShort;
-import net.minecraft.server.v1_4_6.NBTTagString;
-import net.minecraft.server.v1_4_6.TileEntity;
-
-import org.bukkit.World;
-import org.bukkit.craftbukkit.v1_4_6.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 static final Logger logger = Logger.getLogger(NmsBlock.class.getCanonicalName());
- private static Field compoundMapField;
- private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why.
- private NBTTagCompound nbtData = null;
-
- static {
- Field field;
- try {
- field = net.minecraft.server.v1_4_6.Block.class.getDeclaredField("isTileEntity");
- field.setAccessible(true);
- } catch (NoSuchFieldException e) {
- logger.severe("Could not find NMS block tile entity field!");
- field = null;
- }
- nmsBlock_isTileEntityField = field;
- }
-
- /**
- * 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) {
- if (!hasTileEntity(type)) {
- return null;
- }
-
- 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 (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) {
- return false;
- }
-
- 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 block id or data was changed
- */
- 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 changed = craftWorld.getHandle().setRawTypeIdAndData(
- x, y, z, block.getId(), block.getData());
-
- if (block instanceof BaseBlock) {
- world.copyToWorld(position, (BaseBlock) block);
- }
-
- if (changed) {
- if (notifyAdjacent) {
- craftWorld.getHandle().update(x, y, z, block.getId());
- } else {
- craftWorld.getHandle().notify(x, y, z);
- }
- }
-
- return changed;
- }
-
- public static boolean hasTileEntity(int type) {
- net.minecraft.server.v1_4_6.Block nmsBlock = getNmsBlock(type);
- if (nmsBlock == null) {
- return false;
- }
-
- try {
- return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast
- } catch (IllegalAccessException e) {
- return false;
- }
- }
-
- public static net.minecraft.server.v1_4_6.Block getNmsBlock(int type) {
- if (type < 0 || type >= net.minecraft.server.v1_4_6.Block.byId.length) {
- return null;
- }
- return net.minecraft.server.v1_4_6.Block.byId[type];
- }
-
- /**
- * 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
- */
- @SuppressWarnings("unchecked")
- private static Tag toNative(NBTBase foreign) {
- if (foreign == null) {
- return null;
- }
- if (foreign instanceof NBTTagCompound) {
- Map values = new HashMap();
- Collection foreignValues = null;
-
- if (compoundMapField == null) {
- try {
- // Method name may change!
- foreignValues = ((NBTTagCompound) foreign).c();
- } catch (Throwable t) {
- try {
- logger.warning("WorldEdit: Couldn't get NBTTagCompound.c(), " +
- "so we're going to try to get at the 'map' field directly from now on");
-
- if (compoundMapField == null) {
- compoundMapField = NBTTagCompound.class.getDeclaredField("map");
- compoundMapField.setAccessible(true);
- }
- } catch (Throwable e) {
- // Can't do much beyond this
- throw new RuntimeException(e);
- }
- }
- }
-
- if (compoundMapField != null) {
- try {
- foreignValues = ((HashMap) compoundMapField.get(foreign)).values();
- } catch (Throwable e) {
- // Can't do much beyond this
- throw new RuntimeException(e);
- }
- }
-
- for (Object obj : foreignValues) {
- 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 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 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());
- }
- }
-
- public static boolean isValidBlockType(int type) throws NoClassDefFoundError {
- return type == 0 || (type >= 1 && type < net.minecraft.server.v1_4_6.Block.byId.length
- && net.minecraft.server.v1_4_6.Block.byId[type] != null);
- }
-}
+package com.sk89q.worldedit.bukkit;
+
+import org.bukkit.World;
+
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.foundation.Block;
+
+public abstract class NmsBlock extends BaseBlock {
+
+ protected NmsBlock(int type) {
+ super(type);
+ }
+
+ protected NmsBlock(int type, int data) {
+ super(type, data);
+ }
+
+ public static boolean verify() {
+ return false;
+ }
+
+ public static NmsBlock get(World world, Vector vector, int type, int data) {
+ return null;
+ }
+
+ public static boolean set(World world, Vector vector, Block block) {
+ return false;
+ }
+
+ public static boolean setSafely(World world, Vector vector, Block block, boolean notify) {
+ return false;
+ }
+
+ public static boolean hasTileEntity(int type) {
+ return false;
+ }
+
+ public static boolean isValidBlockType(int type) {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java
index 1d9cdafea..b7b9826e1 100644
--- a/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java
+++ b/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java
@@ -28,6 +28,7 @@ import java.util.jar.JarFile;
import java.util.logging.Handler;
import java.util.zip.ZipEntry;
+import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -95,6 +96,7 @@ public class WorldEditPlugin extends JavaPlugin {
// Make the data folders that WorldEdit uses
getDataFolder().mkdirs();
+ new File(getDataFolder() + File.separator + "nmsblocks").mkdir();
// Create the default configuration file
createDefaultConfiguration("config.yml");
@@ -110,15 +112,16 @@ public class WorldEditPlugin extends JavaPlugin {
// Setup interfaces
server = new BukkitServerInterface(this, getServer());
controller = new WorldEdit(server, config);
+ WorldEdit.getInstance().logger.setParent(Bukkit.getLogger());
api = new WorldEditAPI(this);
getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this));
getServer().getMessenger().registerOutgoingPluginChannel(this, CUI_PLUGIN_CHANNEL);
-
// Now we can register events!
getServer().getPluginManager().registerEvents(new WorldEditListener(this), this);
getServer().getScheduler().scheduleAsyncRepeatingTask(this,
new SessionTimer(controller, getServer()), 120, 120);
+
}
/**