diff --git a/modules/API/src/main/java/com/comphenix/protocol/events/PacketContainer.java b/modules/API/src/main/java/com/comphenix/protocol/events/PacketContainer.java index 72488d5e..bbea4086 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/events/PacketContainer.java +++ b/modules/API/src/main/java/com/comphenix/protocol/events/PacketContainer.java @@ -46,6 +46,7 @@ import org.bukkit.World; import org.bukkit.WorldType; import org.bukkit.entity.Entity; import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; import com.comphenix.protocol.PacketType; @@ -86,6 +87,7 @@ import com.comphenix.protocol.wrappers.EnumWrappers.PlayerDigType; import com.comphenix.protocol.wrappers.EnumWrappers.PlayerInfoAction; import com.comphenix.protocol.wrappers.EnumWrappers.ResourcePackStatus; import com.comphenix.protocol.wrappers.EnumWrappers.ScoreboardAction; +import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory; import com.comphenix.protocol.wrappers.EnumWrappers.TitleAction; import com.comphenix.protocol.wrappers.EnumWrappers.WorldBorderAction; import com.comphenix.protocol.wrappers.MultiBlockChangeInfo; @@ -578,12 +580,11 @@ public class PacketContainer implements Serializable { return structureModifier.withType( Collection.class, BukkitConverters.getListConverter( - MinecraftReflection.getWatchableObjectClass(), + MinecraftReflection.getDataWatcherItemClass(), BukkitConverters.getWatchableObjectConverter()) ); } - - + /** * Retrieves a read/write structure for block fields. *

@@ -845,6 +846,26 @@ public class PacketContainer implements Serializable { EnumWrappers.getParticleClass(), EnumWrappers.getParticleConverter()); } + /** + * Retrieve a read/write structure for the MobEffectList class in 1.9. + * @return A modifier for MobEffectList fields. + */ + public StructureModifier getEffectTypes() { + // Convert to and from Bukkit + return structureModifier.withType( + MinecraftReflection.getMobEffectListClass(), BukkitConverters.getEffectTypeConverter()); + } + + /** + * Retrieve a read/write structure for the SoundCategory enum in 1.9. + * @return A modifier for SoundCategory enum fields. + */ + public StructureModifier getSoundCategories() { + // Convert to and from the enums + return structureModifier.withType( + EnumWrappers.getSoundCategoryClass(), EnumWrappers.getSoundCategoryConverter()); + } + /** * Retrieves the ID of this packet. *

diff --git a/modules/API/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/modules/API/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index e516329f..9ab146c6 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/modules/API/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -1341,6 +1341,11 @@ public class MinecraftReflection { return getMinecraftClass("MinecraftKey"); } + public static Class getMobEffectListClass() { + // TODO Implement a fallback + return getMinecraftClass("MobEffectList"); + } + /** * Retrieve the ServerConnection abstract class. * @return The ServerConnection class. diff --git a/modules/API/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java b/modules/API/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java index dd2baa96..4cddbe19 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java +++ b/modules/API/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java @@ -1105,7 +1105,7 @@ public class BukkitConverters { put(MinecraftReflection.getItemStackClass(), (EquivalentConverter) getItemStackConverter()). put(MinecraftReflection.getNBTBaseClass(), (EquivalentConverter) getNbtConverter()). put(MinecraftReflection.getNBTCompoundClass(), (EquivalentConverter) getNbtConverter()). - put(MinecraftReflection.getWatchableObjectClass(), (EquivalentConverter) getWatchableObjectConverter()). + put(MinecraftReflection.getDataWatcherItemClass(), (EquivalentConverter) getWatchableObjectConverter()). put(MinecraftReflection.getMobEffectClass(), (EquivalentConverter) getPotionEffectConverter()). put(MinecraftReflection.getNmsWorldClass(), (EquivalentConverter) getWorldConverter()); @@ -1146,4 +1146,38 @@ public class BukkitConverters { } return unwrappers; } + + private static MethodAccessor getMobEffectId = null; + private static MethodAccessor getMobEffect = null; + + public static EquivalentConverter getEffectTypeConverter() { + return new IgnoreNullConverter() { + + @Override + public Class getSpecificType() { + return PotionEffectType.class; + } + + @Override + protected Object getGenericValue(Class genericType, PotionEffectType specific) { + if (getMobEffect == null) { + getMobEffect = Accessors.getMethodAccessor(genericType, "fromId", int.class); + } + + int id = specific.getId(); + return getMobEffect.invoke(null, id); + } + + @Override + protected PotionEffectType getSpecificValue(Object generic) { + Class clazz = MinecraftReflection.getMobEffectListClass(); + if (getMobEffectId == null) { + getMobEffectId = Accessors.getMethodAccessor(clazz, "getId", clazz); + } + + int id = (int) getMobEffectId.invoke(null, generic); + return PotionEffectType.getById(id); + } + }; + } } diff --git a/modules/API/src/main/java/com/comphenix/protocol/wrappers/EnumWrappers.java b/modules/API/src/main/java/com/comphenix/protocol/wrappers/EnumWrappers.java index 23923a8b..7689ab9b 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/wrappers/EnumWrappers.java +++ b/modules/API/src/main/java/com/comphenix/protocol/wrappers/EnumWrappers.java @@ -272,6 +272,40 @@ public abstract class EnumWrappers { } } + public enum SoundCategory { + MASTER("master"), + MUSIC("music"), + RECORDS("record"), + WEATHER("weather"), + BLOCKS("block"), + HOSTILE("hostile"), + NEUTRAL("neutral"), + PLAYERS("player"), + AMBIENT("ambient"), + VOICE("voice"); + + private static final Map LOOKUP; + static { + LOOKUP = new HashMap<>(); + for (SoundCategory category : values()) { + LOOKUP.put(category.key, category); + } + } + + private final String key; + private SoundCategory(String key) { + this.key = key; + } + + public String getKey() { + return key; + } + + public static SoundCategory getByKey(String key) { + return LOOKUP.get(key.toLowerCase()); + } + } + private static Class PROTOCOL_CLASS = null; private static Class CLIENT_COMMAND_CLASS = null; private static Class CHAT_VISIBILITY_CLASS = null; @@ -287,6 +321,7 @@ public abstract class EnumWrappers { private static Class PLAYER_ACTION_CLASS = null; private static Class SCOREBOARD_ACTION_CLASS = null; private static Class PARTICLE_CLASS = null; + private static Class SOUND_CATEGORY_CLASS = null; private static boolean INITIALIZED = false; private static Map, EquivalentConverter> FROM_NATIVE = Maps.newHashMap(); @@ -319,6 +354,7 @@ public abstract class EnumWrappers { PLAYER_ACTION_CLASS = getEnum(PacketType.Play.Client.ENTITY_ACTION.getPacketClass(), 0); SCOREBOARD_ACTION_CLASS = getEnum(PacketType.Play.Server.SCOREBOARD_SCORE.getPacketClass(), 0); PARTICLE_CLASS = getEnum(PacketType.Play.Server.WORLD_PARTICLES.getPacketClass(), 0); + SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.CUSTOM_SOUND_EFFECT.getPacketClass(), 0); associate(PROTOCOL_CLASS, Protocol.class, getClientCommandConverter()); associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter()); @@ -335,6 +371,7 @@ public abstract class EnumWrappers { associate(PLAYER_ACTION_CLASS, PlayerAction.class, getEntityActionConverter()); associate(SCOREBOARD_ACTION_CLASS, ScoreboardAction.class, getUpdateScoreActionConverter()); associate(PARTICLE_CLASS, Particle.class, getParticleConverter()); + associate(SOUND_CATEGORY_CLASS, SoundCategory.class, getSoundCategoryConverter()); INITIALIZED = true; } @@ -443,6 +480,11 @@ public abstract class EnumWrappers { return PARTICLE_CLASS; } + public static Class getSoundCategoryClass() { + initialize(); + return SOUND_CATEGORY_CLASS; + } + // Get the converters public static EquivalentConverter getProtocolConverter() { return new EnumConverter(Protocol.class); @@ -504,6 +546,10 @@ public abstract class EnumWrappers { return new EnumConverter(Particle.class); } + public static EquivalentConverter getSoundCategoryConverter() { + return new EnumConverter(SoundCategory.class); + } + /** * Retrieve a generic enum converter for use with StructureModifiers. * @param enumClass - Enum class diff --git a/modules/API/src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java b/modules/API/src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java index a9ba9f39..b82468b8 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java +++ b/modules/API/src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java @@ -25,37 +25,61 @@ import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode; import com.google.common.base.Objects; /** + * Represents an immutable PlayerInfoData in the PLAYER_INFO packet. * @author dmulloy2 */ - public class PlayerInfoData { private static Constructor constructor; - protected final WrappedGameProfile profile; - protected final int ping; - protected final NativeGameMode gameMode; - protected final WrappedChatComponent displayName; + private final int latency; + private final NativeGameMode gameMode; + private final WrappedGameProfile profile; + private final WrappedChatComponent displayName; // This is the same order as the NMS class, minus the packet (which isn't a field) - public PlayerInfoData(WrappedGameProfile profile, int ping, NativeGameMode gameMode, WrappedChatComponent displayName) { - this.ping = ping; - this.gameMode = gameMode; + public PlayerInfoData(WrappedGameProfile profile, int latency, NativeGameMode gameMode, WrappedChatComponent displayName) { this.profile = profile; + this.latency = latency; + this.gameMode = gameMode; this.displayName = displayName; } + /** + * Gets the GameProfile of the player represented by this data. + * @return The GameProfile + */ public WrappedGameProfile getProfile() { return profile; } + /** + * @deprecated Replaced by {@link #getLatency()} + */ + @Deprecated public int getPing() { - return ping; + return latency; } + /** + * Gets the latency between the client and the server. + * @return The latency + */ + public int getLatency() { + return latency; + } + + /** + * Gets the GameMode of the player represented by this data. + * @return The GameMode + */ public NativeGameMode getGameMode() { return gameMode; } + /** + * Gets the display name of the player represented by this data. + * @return The display name + */ public WrappedChatComponent getDisplayName() { return displayName; } @@ -91,7 +115,7 @@ public class PlayerInfoData { Object result = constructor.newInstance( null, specific.profile.handle, - specific.ping, + specific.latency, EnumWrappers.getGameModeConverter().getGeneric(EnumWrappers.getGameModeClass(), specific.gameMode), specific.displayName != null ? specific.displayName.handle : null ); @@ -112,7 +136,7 @@ public class PlayerInfoData { WrappedGameProfile gameProfile = gameProfiles.read(0); StructureModifier ints = modifier.withType(int.class); - int ping = ints.read(0); + int latency = ints.read(0); StructureModifier gameModes = modifier.withType( EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter()); @@ -122,7 +146,7 @@ public class PlayerInfoData { MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter()); WrappedChatComponent displayName = displayNames.read(0); - return new PlayerInfoData(gameProfile, ping, gameMode, displayName); + return new PlayerInfoData(gameProfile, latency, gameMode, displayName); } // Otherwise, return null @@ -146,7 +170,7 @@ public class PlayerInfoData { // Only compare objects of similar type if (obj instanceof PlayerInfoData) { PlayerInfoData other = (PlayerInfoData) obj; - return profile.equals(other.profile) && ping == other.ping && gameMode == other.gameMode + return profile.equals(other.profile) && latency == other.latency && gameMode == other.gameMode && displayName.equals(other.displayName); } return false; @@ -154,12 +178,12 @@ public class PlayerInfoData { @Override public int hashCode() { - return Objects.hashCode(profile, ping, gameMode, displayName); + return Objects.hashCode(latency, gameMode, profile, displayName); } @Override public String toString() { - return String.format("PlayerInfoData { profile=%s, ping=%s, gameMode=%s, displayName=%s }", - profile, ping, gameMode, displayName); + return String.format("PlayerInfoData[latency=%s, gameMode=%s, profile=%s, displayName=%s", + latency, gameMode, profile, displayName); } } diff --git a/modules/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleMinecraftClient.java b/modules/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleMinecraftClient.java index 19116180..394b93ef 100644 --- a/modules/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleMinecraftClient.java +++ b/modules/ProtocolLib/src/test/java/com/comphenix/integration/protocol/SimpleMinecraftClient.java @@ -33,7 +33,7 @@ public class SimpleMinecraftClient { /** * Query the local server for ping information. * @return The server information. - * @throws IOException + * @throws IOException */ public String queryLocalPing() throws IOException { return queryServerPing(new InetSocketAddress("localhost", 25565)); @@ -173,7 +173,7 @@ public class SimpleMinecraftClient { } } - private static class HandshakePacket extends SimplePacket { + private static class HandshakePacket extends SimplePacket { private int protocol; private String host; private int port; @@ -209,6 +209,7 @@ public class SimpleMinecraftClient { serializer.serializeVarInt(output, type.getCurrentId()); } + @SuppressWarnings("unused") public void read(PacketType type, DataInputStream input) throws IOException { // Note - we don't read the packet id if (this.type != type) {