diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java
index 609d00d9..0743c9d2 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java
@@ -88,6 +88,7 @@ import com.comphenix.protocol.wrappers.EnumWrappers.TitleAction;
import com.comphenix.protocol.wrappers.EnumWrappers.WorldBorderAction;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedAttribute;
+import com.comphenix.protocol.wrappers.WrappedBlockData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
@@ -612,6 +613,19 @@ public class PacketContainer implements Serializable {
GameProfile.class, BukkitConverters.getWrappedGameProfileConverter());
}
+ /**
+ * Retrieves a read/write structure for BlockData in Minecraft 1.8.
+ *
+ * This modifier will automatically marshall between WrappedBlockData and the
+ * internal Minecraft BlockData.
+ * @return A modifier for BlockData fields.
+ */
+ public StructureModifier getBlockData() {
+ // Conver to and from our wrapper
+ return structureModifier.withType(
+ MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter());
+ }
+
/**
* Retrieves a read/write structure for chat components in Minecraft 1.7.2.
*
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java
index fb5af3b9..06158db5 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java
@@ -1915,6 +1915,24 @@ public class MinecraftReflection {
return clazz != null && obj.getClass().equals(clazz);
}
+ /**
+ * Retrieve the IBlockData class in 1.8.
+ * @return The IBlockData class
+ */
+ public static Class> getIBlockDataClass() {
+ return getMinecraftClass("IBlockData");
+ }
+
+ /**
+ * Determine if the given object is an IBlockData.
+ * @param obj - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isIBlockData(Object obj) {
+ Class> clazz = getIBlockDataClass();
+ return clazz != null && obj.getClass().equals(clazz);
+ }
+
/**
* Retrieve the given class by name.
* @param className - name of the class.
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java
index 5dd1070d..578f9612 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java
@@ -395,7 +395,7 @@ public class BukkitConverters {
/**
* Retrieve a converter for wrapped chat components.
- * @return Wrapped chat componetns.
+ * @return Wrapped chat component.
*/
public static EquivalentConverter getWrappedChatComponentConverter() {
return new IgnoreNullConverter() {
@@ -416,6 +416,29 @@ public class BukkitConverters {
};
}
+ /**
+ * Retrieve a converter for wrapped block data.
+ * @return Wrapped block data.
+ */
+ public static EquivalentConverter getWrappedBlockDataConverter() {
+ return new IgnoreNullConverter() {
+ @Override
+ protected Object getGenericValue(Class> genericType, WrappedBlockData specific) {
+ return specific.getHandle();
+ }
+
+ @Override
+ protected WrappedBlockData getSpecificValue(Object generic) {
+ return new WrappedBlockData(generic);
+ }
+
+ @Override
+ public Class getSpecificType() {
+ return WrappedBlockData.class;
+ }
+ };
+ }
+
/**
* Retrieve a converter for wrapped attribute snapshots.
* @return Wrapped attribute snapshot converter.
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedBlockData.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedBlockData.java
new file mode 100644
index 00000000..d72f05d7
--- /dev/null
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedBlockData.java
@@ -0,0 +1,95 @@
+/**
+ * (c) 2015 dmulloy2
+ */
+package com.comphenix.protocol.wrappers;
+
+import org.bukkit.Material;
+
+import com.comphenix.protocol.reflect.FuzzyReflection;
+import com.comphenix.protocol.reflect.accessors.Accessors;
+import com.comphenix.protocol.reflect.accessors.MethodAccessor;
+import com.comphenix.protocol.utility.MinecraftReflection;
+
+/**
+ * Represents a wrapper around IBlockData.
+ *
+ * @author dmulloy2
+ */
+
+public class WrappedBlockData extends AbstractWrapper {
+ private static final Class> MAGIC_NUMBERS = MinecraftReflection.getCraftBukkitClass("util.CraftMagicNumbers");
+ private static final Class> IBLOCK_DATA = MinecraftReflection.getIBlockDataClass();
+ private static final Class> BLOCK = MinecraftReflection.getBlockClass();
+
+ private static MethodAccessor FROM_LEGACY_DATA = null;
+ private static MethodAccessor GET_NMS_BLOCK = null;
+ private static MethodAccessor GET_BLOCK = null;
+
+ static {
+ FuzzyReflection fuzzy = FuzzyReflection.fromClass(BLOCK);
+ FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("fromLegacyData", IBLOCK_DATA,
+ new Class>[] { int.class }));
+
+ fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
+ GET_NMS_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
+ new Class>[] { int.class }));
+
+ fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
+ GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
+ new Class>[0]));
+ }
+
+ public WrappedBlockData(Object handle) {
+ super(IBLOCK_DATA);
+ setHandle(handle);
+ }
+
+ /**
+ * Retrieves the type of this BlockData.
+ * @return The type of this BlockData.
+ */
+ public Material getType() {
+ Object block = GET_BLOCK.invoke(handle);
+ return BukkitConverters.getBlockConverter().getSpecific(block);
+ }
+
+ /**
+ * Sets the type of this BlockData.
+ * @param type New type
+ */
+ public void setType(Material type) {
+ setTypeAndData(type, 0);
+ }
+
+ /**
+ * Sets the type and data of this BlockData.
+ * @param type New type
+ * @param data New data
+ */
+ public void setTypeAndData(Material type, int data) {
+ Object nmsBlock = GET_NMS_BLOCK.invoke(null, type.getId());
+ Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
+ setHandle(blockData);
+ }
+
+ /**
+ * Creates a new BlockData instance with the given type and no data.
+ * @param type Block type
+ * @return New BlockData
+ */
+ public static WrappedBlockData createData(Material type) {
+ return createData(type, 0);
+ }
+
+ /**
+ * Creates a new BlockData instance with the given type and data.
+ * @param type Block type
+ * @param data Block data
+ * @return New BlockData
+ */
+ public static WrappedBlockData createData(Material type, int data) {
+ Object nmsBlock = GET_NMS_BLOCK.invoke(null, type.getId());
+ Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
+ return new WrappedBlockData(blockData);
+ }
+}
\ No newline at end of file