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 c2d88b5f..6f6f553b 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java
@@ -85,6 +85,7 @@ import com.comphenix.protocol.wrappers.EnumWrappers.ResourcePackStatus;
import com.comphenix.protocol.wrappers.EnumWrappers.ScoreboardAction;
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.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
@@ -650,6 +651,23 @@ public class PacketContainer implements Serializable {
MinecraftReflection.getServerPingClass(), BukkitConverters.getWrappedServerPingConverter());
}
+ /**
+ * Retrieve a read/write structure for the PlayerInfoData list fields in the following packet:
+ *
+ * - {@link PacketType.Play.Server.PLAYER_INFO}
+ *
+ * @return A modifier for PlayerInfoData list fields.
+ */
+ public StructureModifier> getPlayerInfoDataLists() {
+ // Convert to and from the ProtocolLib wrapper
+ return structureModifier.withType(
+ Collection.class,
+ BukkitConverters.getListConverter(
+ MinecraftReflection.getPlayerInfoDataClass(),
+ PlayerInfoData.getConverter())
+ );
+ }
+
/**
* Retrieve a read/write structure for the Protocol enum in 1.7.2.
* @return A modifier for Protocol enum fields.
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 b226a646..a41a51e7 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java
@@ -1896,6 +1896,24 @@ public class MinecraftReflection {
return unwrapper.unwrapItem(stack);
}
+ /**
+ * Retrieve the PlayerInfoData class in 1.8.
+ * @return The PlayerInfoData class
+ */
+ public static Class> getPlayerInfoDataClass() {
+ return getMinecraftClass("PlayerInfoData");
+ }
+
+ /**
+ * Determine if the given object is a PlayerInfoData.
+ * @param obj - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isPlayerInfoData(Object obj) {
+ Class> clazz = getPlayerInfoDataClass();
+ 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/PlayerInfoData.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java
new file mode 100644
index 00000000..faa6add7
--- /dev/null
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java
@@ -0,0 +1,144 @@
+/**
+ * (c) 2014 dmulloy2
+ */
+package com.comphenix.protocol.wrappers;
+
+import java.lang.reflect.Constructor;
+
+import com.comphenix.protocol.reflect.EquivalentConverter;
+import com.comphenix.protocol.reflect.StructureModifier;
+import com.comphenix.protocol.utility.MinecraftReflection;
+import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
+import com.google.common.base.Objects;
+
+/**
+ * @author dmulloy2
+ */
+
+public class PlayerInfoData {
+ private static Constructor> constructor;
+
+ protected final int ping;
+ protected final NativeGameMode gameMode;
+ protected final WrappedGameProfile profile;
+ protected final WrappedChatComponent displayName;
+
+ public PlayerInfoData(int ping, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName) {
+ this.ping = ping;
+ this.gameMode = gameMode;
+ this.profile = profile;
+ this.displayName = displayName;
+ }
+
+ public int getPing() {
+ return ping;
+ }
+
+ public NativeGameMode getGameMode() {
+ return gameMode;
+ }
+
+ public WrappedGameProfile getProfile() {
+ return profile;
+ }
+
+ public WrappedChatComponent getDisplayName() {
+ return displayName;
+ }
+
+ /**
+ * Used to convert between NMS PlayerInfoData and the wrapper instance.
+ * @return A new converter.
+ */
+ public static EquivalentConverter getConverter() {
+ return new EquivalentConverter() {
+ @Override
+ public Object getGeneric(Class> genericType, PlayerInfoData specific) {
+ if (constructor == null) {
+ try {
+ constructor = MinecraftReflection.getPlayerInfoDataClass().getConstructor(
+ int.class,
+ EnumWrappers.getGameModeClass(),
+ MinecraftReflection.getGameProfileClass(),
+ MinecraftReflection.getIChatBaseComponentClass()
+ );
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot find PlayerInfoData constructor.", e);
+ }
+ }
+
+ // Construct the underlying PlayerInfoData
+ try {
+ Object result = constructor.newInstance(
+ specific.ping,
+ EnumWrappers.getGameModeConverter().getGeneric(EnumWrappers.getGameModeClass(), specific.gameMode),
+ specific.profile.handle,
+ specific.displayName.handle
+ );
+ return result;
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot construct BlockPosition.", e);
+ }
+ }
+
+ @Override
+ public PlayerInfoData getSpecific(Object generic) {
+ if (MinecraftReflection.isPlayerInfoData(generic)) {
+ StructureModifier