withType(
+ EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter());
+ }
+
/**
* Retrieves the ID of this packet.
*
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 0def87d1..ffb26d27 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java
@@ -25,6 +25,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import org.bukkit.World;
import org.bukkit.WorldType;
@@ -34,6 +35,7 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.injector.PacketConstructor;
@@ -584,6 +586,8 @@ public class BukkitConverters {
};
}
+
+
/**
* Retrieve the converter used to convert between a PotionEffect and the equivalent NMS Mobeffect.
* @return The potion effect converter.
@@ -706,6 +710,11 @@ public class BukkitConverters {
if (MinecraftReflection.isUsingNetty()) {
builder.put(WrappedGameProfile.class, (EquivalentConverter) getWrappedGameProfileConverter());
builder.put(WrappedChatComponent.class, (EquivalentConverter) getWrappedChatComponentConverter());
+ builder.put(WrappedServerPing.class, (EquivalentConverter) getWrappedServerPingConverter());
+
+ for (Entry, EquivalentConverter>> entry : EnumWrappers.getFromWrapperMap().entrySet()) {
+ builder.put((Class) entry.getKey(), (EquivalentConverter) entry.getValue());
+ }
}
if (hasWorldType)
@@ -743,6 +752,11 @@ public class BukkitConverters {
if (MinecraftReflection.isUsingNetty()) {
builder.put(MinecraftReflection.getGameProfileClass(), (EquivalentConverter) getWrappedGameProfileConverter());
builder.put(MinecraftReflection.getIChatBaseComponentClass(), (EquivalentConverter) getWrappedChatComponentConverter());
+ builder.put(MinecraftReflection.getServerPingClass(), (EquivalentConverter) getWrappedServerPingConverter());
+
+ for (Entry, EquivalentConverter>> entry : EnumWrappers.getFromNativeMap().entrySet()) {
+ builder.put((Class) entry.getKey(), (EquivalentConverter) entry.getValue());
+ }
}
genericConverters = builder.build();
}
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/EnumWrappers.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/EnumWrappers.java
new file mode 100644
index 00000000..54c178b4
--- /dev/null
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/EnumWrappers.java
@@ -0,0 +1,183 @@
+package com.comphenix.protocol.wrappers;
+
+import java.util.Map;
+
+import org.bukkit.GameMode;
+
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.PacketType.Protocol;
+import com.comphenix.protocol.reflect.EquivalentConverter;
+import com.comphenix.protocol.reflect.FuzzyReflection;
+import com.comphenix.protocol.utility.MinecraftReflection;
+import com.google.common.collect.Maps;
+
+/**
+ * Represents a generic enum converter.
+ * @author Kristian
+ */
+public abstract class EnumWrappers {
+ public enum ClientCommand {
+ PERFORM_RESPAWN,
+ REQUEST_STATS,
+ OPEN_INVENTORY_ACHIEVEMENT;
+ }
+
+ public enum ChatVisibility {
+ FULL,
+ SYSTEM,
+ HIDDEN;
+ }
+
+ public enum Difficulty {
+ PEACEFUL,
+ EASY,
+ NORMAL,
+ HARD;
+ }
+
+ public enum EntityUseAction {
+ INTERACT,
+ ATTACK;
+ }
+
+ /**
+ * Represents a native game mode in Minecraft.
+ *
+ * Not to be confused with {@link GameMode} in Bukkit.
+ * @author Kristian
+ */
+ public enum NativeGameMode {
+ NONE,
+ SURVIVAL,
+ CREATIVE,
+ ADVENTURE;
+ }
+
+ private static Class> PROTOCOL_CLASS = null;
+ private static Class> CLIENT_COMMAND_CLASS = null;
+ private static Class> CHAT_VISIBILITY_CLASS = null;
+ private static Class> DIFFICULTY_CLASS = null;
+ private static Class> ENTITY_USE_ACTION_CLASS = null;
+ private static Class> GAMEMODE_CLASS = null;
+
+ private static Map, EquivalentConverter>> FROM_NATIVE = Maps.newHashMap();
+ private static Map, EquivalentConverter>> FROM_WRAPPER = Maps.newHashMap();
+
+ /**
+ * Initialize the wrappers, if we haven't already.
+ */
+ private static boolean initialize() {
+ if (MinecraftReflection.isUsingNetty())
+ return false;
+
+ PROTOCOL_CLASS = getEnum(PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass(), 0);
+ CLIENT_COMMAND_CLASS = getEnum(PacketType.Play.Client.CLIENT_COMMAND.getPacketClass(), 0);
+ CHAT_VISIBILITY_CLASS = getEnum(PacketType.Play.Client.SETTINGS.getPacketClass(), 0);
+ DIFFICULTY_CLASS = getEnum(PacketType.Play.Client.SETTINGS.getPacketClass(), 1);
+ ENTITY_USE_ACTION_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 0);
+ GAMEMODE_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 0);
+
+ associate(PROTOCOL_CLASS, Protocol.class, getClientCommandConverter());
+ associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
+ associate(CHAT_VISIBILITY_CLASS, ChatVisibility.class, getChatVisibilityConverter());
+ associate(DIFFICULTY_CLASS, Difficulty.class, getDifficultyConverter());
+ associate(ENTITY_USE_ACTION_CLASS, EntityUseAction.class, getEntityUseActionConverter());
+ associate(GAMEMODE_CLASS, NativeGameMode.class, getGameModeConverter());
+ return true;
+ }
+
+ private static void associate(Class> nativeClass, Class> wrapperClass, EquivalentConverter> converter) {
+ FROM_NATIVE.put(nativeClass, converter);
+ FROM_WRAPPER.put(wrapperClass, converter);
+ }
+
+ /**
+ * Retrieve the enum field with the given declaration index (in relation to the other enums).
+ * @param clazz - the declaration class.
+ * @param index - the enum index.
+ * @return The type of the enum field.
+ */
+ private static Class> getEnum(Class> clazz, int index) {
+ return FuzzyReflection.fromClass(clazz, true).getFieldListByType(Enum.class).get(index).getType();
+ }
+
+ public static Map, EquivalentConverter>> getFromNativeMap() {
+ return FROM_NATIVE;
+ }
+
+ public static Map, EquivalentConverter>> getFromWrapperMap() {
+ return FROM_WRAPPER;
+ }
+
+ // Get the native enum classes
+ public static Class> getProtocolClass() {
+ initialize();
+ return PROTOCOL_CLASS;
+ }
+ public static Class> getClientCommandClass() {
+ initialize();
+ return CLIENT_COMMAND_CLASS;
+ }
+ public static Class> getChatVisibilityClass() {
+ initialize();
+ return CHAT_VISIBILITY_CLASS;
+ }
+ public static Class> getDifficultyClass() {
+ initialize();
+ return DIFFICULTY_CLASS;
+ }
+ public static Class> getEntityUseActionClass() {
+ initialize();
+ return ENTITY_USE_ACTION_CLASS;
+ }
+ public static Class> getGameModeClass() {
+ initialize();
+ return GAMEMODE_CLASS;
+ }
+
+ // Get the converters
+ public static EquivalentConverter getProtocolConverter() {
+ return new EnumConverter(Protocol.class);
+ }
+ public static EquivalentConverter getClientCommandConverter() {
+ return new EnumConverter(ClientCommand.class);
+ }
+ public static EquivalentConverter getChatVisibilityConverter() {
+ return new EnumConverter(ChatVisibility.class);
+ }
+ public static EquivalentConverter getDifficultyConverter() {
+ return new EnumConverter(Difficulty.class);
+ }
+ public static EquivalentConverter getEntityUseActionConverter() {
+ return new EnumConverter(EntityUseAction.class);
+ }
+ public static EquivalentConverter getGameModeConverter() {
+ return new EnumConverter(NativeGameMode.class);
+ }
+
+ // The common enum converter
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private static class EnumConverter> implements EquivalentConverter {
+ private Class specificType;
+
+ public EnumConverter(Class specificType) {
+ this.specificType = specificType;
+ }
+
+ @Override
+ public T getSpecific(Object generic) {
+ // We know its an enum already!
+ return Enum.valueOf(specificType, ((Enum) generic).name());
+ }
+
+ @Override
+ public Object getGeneric(Class> genericType, T specific) {
+ return Enum.valueOf((Class) genericType, specific.name());
+ }
+
+ @Override
+ public Class getSpecificType() {
+ return specificType;
+ }
+ }
+}
diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/EnumWrappersTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/EnumWrappersTest.java
new file mode 100644
index 00000000..3e1aae09
--- /dev/null
+++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/EnumWrappersTest.java
@@ -0,0 +1,62 @@
+package com.comphenix.protocol.wrappers;
+
+import static org.junit.Assert.*;
+
+import net.minecraft.server.v1_7_R1.EnumChatVisibility;
+import net.minecraft.server.v1_7_R1.EnumClientCommand;
+import net.minecraft.server.v1_7_R1.EnumDifficulty;
+import net.minecraft.server.v1_7_R1.EnumEntityUseAction;
+import net.minecraft.server.v1_7_R1.EnumGamemode;
+import net.minecraft.server.v1_7_R1.EnumProtocol;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.comphenix.protocol.BukkitInitialization;
+import com.comphenix.protocol.reflect.EquivalentConverter;
+import com.comphenix.protocol.reflect.accessors.Accessors;
+import com.comphenix.protocol.reflect.accessors.FieldAccessor;
+
+public class EnumWrappersTest {
+ private static class EnumClass {
+ public EnumProtocol protocol;
+ public EnumClientCommand command;
+ public EnumChatVisibility visibility;
+ public EnumDifficulty difficulty;
+ public EnumEntityUseAction action;
+ public EnumGamemode mode;
+ }
+
+ @BeforeClass
+ public static void initializeBukkit() throws IllegalAccessException {
+ BukkitInitialization.initializePackage();
+ }
+
+ @Test
+ public void testEnum() {
+ EnumClass obj = new EnumClass();
+ obj.protocol = EnumProtocol.LOGIN;
+ obj.command = EnumClientCommand.PERFORM_RESPAWN;
+ obj.visibility = EnumChatVisibility.FULL;
+ obj.difficulty = EnumDifficulty.PEACEFUL;
+ obj.action = EnumEntityUseAction.INTERACT;
+ obj.mode = EnumGamemode.CREATIVE;
+
+ assertEquals(obj.protocol, roundtrip(obj, "protocol", EnumWrappers.getProtocolConverter()) );
+ assertEquals(obj.command, roundtrip(obj, "command", EnumWrappers.getClientCommandConverter()) );
+ assertEquals(obj.visibility, roundtrip(obj, "visibility", EnumWrappers.getChatVisibilityConverter()) );
+ assertEquals(obj.difficulty, roundtrip(obj, "difficulty", EnumWrappers.getDifficultyConverter()) );
+ assertEquals(obj.action, roundtrip(obj, "action", EnumWrappers.getEntityUseActionConverter()) );
+ assertEquals(obj.mode, roundtrip(obj, "mode", EnumWrappers.getGameModeConverter()) );
+ }
+
+ @SuppressWarnings("unchecked")
+ public > T roundtrip(Object target, String fieldName, EquivalentConverter converter) {
+ FieldAccessor accessor = Accessors.getFieldAccessor(target.getClass(), fieldName, true);
+
+ return (T) converter.getGeneric(
+ accessor.getField().getType(),
+ converter.getSpecific(accessor.get(target))
+ );
+ }
+}