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:
+ * + * @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 modifier = new StructureModifier(generic.getClass(), null, false) + .withTarget(generic); + + StructureModifier ints = modifier.withType(int.class); + int ping = ints.read(0); + + StructureModifier gameModes = modifier.withType( + EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter()); + NativeGameMode gameMode = gameModes.read(0); + + StructureModifier gameProfiles = modifier.withType( + MinecraftReflection.getGameProfileClass(), BukkitConverters.getWrappedGameProfileConverter()); + WrappedGameProfile gameProfile = gameProfiles.read(0); + + StructureModifier displayNames = modifier.withType( + MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter()); + WrappedChatComponent displayName = displayNames.read(0); + + return new PlayerInfoData(ping, gameMode, gameProfile, displayName); + } + + // Otherwise, return NULL + return null; + } + + // Thanks Java Generics! + @Override + public Class getSpecificType() { + return PlayerInfoData.class; + } + }; + } + + @Override + public boolean equals(Object obj) { + // Fast checks + if (this == obj) return true; + if (obj == null) return false; + + // Only compare objects of similar type + if (obj instanceof PlayerInfoData) { + PlayerInfoData other = (PlayerInfoData) obj; + return ping == other.ping && gameMode == other.gameMode + && profile.equals(other.profile) && displayName.equals(other.displayName); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(ping, gameMode, profile, displayName); + } + + @Override + public String toString() { + return String.format("PlayerInfoData[ping=%s,gameMode=%s,profile=%s,displayName=%s]", ping, gameMode, profile, displayName); + } +} \ No newline at end of file