From d64c3322668470feebe271f701a69cefe4ee54b3 Mon Sep 17 00:00:00 2001 From: LexManos Date: Fri, 9 Dec 2011 07:58:32 +0000 Subject: [PATCH] Added my EnumHelper classes, this allows for dynamic creation of Enum values at runtime. See My post for a example: http://www.minecraftforum.net/topic/514000-api-minecraft-forge/page__view__findpost__p__10948540 --- .../minecraft/src/forge/EnumHelperClient.java | 99 +++++ .../net/minecraft/src/forge/EnumHelper.java | 342 ++++++++++++++++++ 2 files changed, 441 insertions(+) create mode 100644 forge/forge_client/src/net/minecraft/src/forge/EnumHelperClient.java create mode 100644 forge/forge_common/net/minecraft/src/forge/EnumHelper.java diff --git a/forge/forge_client/src/net/minecraft/src/forge/EnumHelperClient.java b/forge/forge_client/src/net/minecraft/src/forge/EnumHelperClient.java new file mode 100644 index 0000000000..9f9d41947c --- /dev/null +++ b/forge/forge_client/src/net/minecraft/src/forge/EnumHelperClient.java @@ -0,0 +1,99 @@ +package net.minecraft.src.forge; + +import net.minecraft.src.EnumJsonNodeType; +import net.minecraft.src.EnumOS2; +import net.minecraft.src.EnumOptions; +import net.minecraft.src.EnumRarity; + +public class EnumHelperClient extends EnumHelper { + + private static Class[][] ctrs = { + {EnumJsonNodeType.class}, + {EnumOptions.class, String.class, boolean.class, boolean.class}, + {EnumOS2.class}, + {EnumRarity.class, int.class, String.class} + }; + + private static boolean[] decompiled = new boolean[ctrs.length]; + private static boolean isSetup = false; + + public static EnumJsonNodeType addJsonNodeType(String name) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[0], EnumJsonNodeType.class, name, + new Class[] {}, + new Object[]{}); + } + public static EnumOptions addOptions(String name, String langName, boolean isSlider, boolean isToggle) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[1], EnumOptions.class, name, + new Class[] {String.class, boolean.class, boolean.class}, + new Object[]{langName, isSlider, isToggle }); + } + public static EnumOS2 addOS2(String name) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[2], EnumOS2.class, name, + new Class[] {}, + new Object[]{}); + } + public static EnumRarity addRarity(String name, int color, String displayName) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[3], EnumRarity.class, name, + new Class[] {int.class, String.class}, + new Object[]{color, displayName }); + } + + private static void setup() + { + //System.out.println("Enum Helper Initalizing: "); + for(int x = 0; x < ctrs.length; x++) + { + try { + Class[] enumHeaders = new Class[ctrs[x].length + 3]; + enumHeaders[0] = String.class; + enumHeaders[1] = int.class; + enumHeaders[2] = String.class; + enumHeaders[3] = int.class; + + for (int y = 1; y < ctrs[x].length; y++) + enumHeaders[3 + y] = ctrs[x][y]; + + ctrs[x][0].getDeclaredConstructor(enumHeaders); + decompiled[x] = true; + + } catch(Exception e) { + //Nom Nom Nom + } + //System.out.format("\t%-25s %s\r\n", ctrs[x][0].getName().replace("net.minecraft.src.", ""), decompiled[x]); + } + isSetup = true; + } + + static + { + if (!isSetup) + { + setup(); + } + } + +} diff --git a/forge/forge_common/net/minecraft/src/forge/EnumHelper.java b/forge/forge_common/net/minecraft/src/forge/EnumHelper.java new file mode 100644 index 0000000000..94e8242c53 --- /dev/null +++ b/forge/forge_common/net/minecraft/src/forge/EnumHelper.java @@ -0,0 +1,342 @@ +package net.minecraft.src.forge; + +import net.minecraft.src.*; +import java.lang.reflect.*; +import java.util.*; + +public class EnumHelper { + private static Object reflectionFactory = null; + private static Method newConstructorAccessor = null; + private static Method newInstance = null; + private static Method newFieldAccessor = null; + private static Method fieldAccessorSet = null; + private static boolean isSetup = false; + + //Some enums are decompiled with extra arguments, so lets check for that + private static Class[][] ctrs = { + {EnumAction.class}, + {EnumArmorMaterial.class, int.class, int[].class, int.class}, + {EnumArt.class, String.class, int.class, int.class, int.class, int.class}, + {EnumCreatureAttribute.class}, + {EnumCreatureType.class, Class.class, int.class, Material.class, boolean.class}, + {EnumDoor.class}, + {EnumEnchantmentType.class}, + {EnumMobType.class}, + {EnumMovingObjectType.class}, + {EnumSkyBlock.class, int.class}, + {EnumStatus.class}, + {EnumToolMaterial.class, int.class, int.class, float.class, int.class, int.class} + }; + + private static boolean[] decompiled = new boolean[ctrs.length]; + + public static EnumAction addAction(String name) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[0], EnumAction.class, name, + new Class[] {}, + new Object[]{}); + } + public static EnumArmorMaterial addArmorMaterial(String name, int durability, int[] reductionAmounts, int enchantability) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[1], EnumArmorMaterial.class, name, + new Class[] { int.class, int[].class, int.class }, + new Object[]{ durability, reductionAmounts, enchantability }); + } + public static EnumArt addArt(String name, String tile, int sizeX, int sizeY, int offsetX, int offsetY) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[2], EnumArt.class, name, + new Class[] {String.class, int.class, int.class, int.class, int.class}, + new Object[]{tile, sizeX, sizeY, offsetX, offsetY}); + } + public static EnumCreatureAttribute addCreatureAttribute(String name) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[3], EnumCreatureAttribute.class, name, + new Class[] {}, + new Object[]{}); + } + public static EnumCreatureType addCreatureType(String name, Class typeClass, int maxNumber, Material material, boolean peaceful) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[4], EnumCreatureType.class, name, + new Class[] {Class.class, int.class, Material.class, boolean.class}, + new Object[]{typeClass, maxNumber, material, peaceful}); + } + public static EnumDoor addDoor(String name) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[5], EnumDoor.class, name, + new Class[] {}, + new Object[]{}); + } + public static EnumEnchantmentType addEnchantmentType(String name) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[6], EnumEnchantmentType.class, name, + new Class[] {}, + new Object[]{}); + } + public static EnumMobType addMobType(String name) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[7], EnumMobType.class, name, + new Class[] {}, + new Object[]{}); + } + public static EnumMovingObjectType addMovingObjectType(String name) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[8], EnumMovingObjectType.class, name, + new Class[] {}, + new Object[]{}); + } + public static EnumSkyBlock addSkyBlock(String name, int lightValue) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[9], EnumSkyBlock.class, name, + new Class[] {int.class }, + new Object[]{lightValue}); + } + public static EnumStatus addStatus(String name) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[10], EnumStatus.class, name, + new Class[] {}, + new Object[]{}); + } + public static EnumToolMaterial addToolMaterial(String name, int harvestLevel, int maxUses, float efficiency, int damage, int enchantability) + { + if (!isSetup) + { + setup(); + } + + return addEnum(decompiled[11], EnumToolMaterial.class, name, + new Class[] { int.class, int.class, float.class, int.class, int.class }, + new Object[]{ harvestLevel, maxUses, efficiency, damage, enchantability }); + } + + private static void setup() + { + if (isSetup) + { + return; + } + + //System.out.println("Enum Helper Initalizing: "); + for(int x = 0; x < ctrs.length; x++) + { + try { + Class[] enumHeaders = new Class[ctrs[x].length + 3]; + enumHeaders[0] = String.class; + enumHeaders[1] = int.class; + enumHeaders[2] = String.class; + enumHeaders[3] = int.class; + + for (int y = 1; y < ctrs[x].length; y++) + enumHeaders[3 + y] = ctrs[x][y]; + + ctrs[x][0].getDeclaredConstructor(enumHeaders); + decompiled[x] = true; + + } catch(Exception e) { + //Nom Nom Nom + } + //System.out.format("\t%-25s %s\r\n", ctrs[x][0].getName().replace("net.minecraft.src.", ""), decompiled[x]); + } + + + + try { + Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory"); + reflectionFactory = getReflectionFactory.invoke(null); + newConstructorAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newConstructorAccessor", Constructor.class); + newInstance = Class.forName("sun.reflect.ConstructorAccessor").getDeclaredMethod("newInstance", Object[].class); + newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class); + fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class); + } catch (Exception e) { + e.printStackTrace(); + } + + isSetup = true; + } + + + /* + * Everything below this is found at the site below, and updated to be able to compile in Eclipse/Java 1.6+ + * Also modified for use in decompiled code. + * Found at: http://niceideas.ch/roller2/badtrash/entry/java_create_enum_instances_dynamically + */ + + private static Object getConstructorAccessor(boolean decompiled, Class enumClass, Class[] additionalParameterTypes) throws Exception { + Class[] parameterTypes = null; + if (decompiled) + { + parameterTypes = new Class[additionalParameterTypes.length + 4]; + parameterTypes[0] = String.class; + parameterTypes[1] = int.class; + parameterTypes[2] = String.class; + parameterTypes[3] = int.class; + System.arraycopy(additionalParameterTypes, 0, parameterTypes, 4, additionalParameterTypes.length); + } + else + { + parameterTypes = new Class[additionalParameterTypes.length + 2]; + parameterTypes[0] = String.class; + parameterTypes[1] = int.class; + System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length); + } + return newConstructorAccessor.invoke(reflectionFactory, enumClass.getDeclaredConstructor(parameterTypes)); + } + + private static > T makeEnum(boolean decompiled, Class enumClass, String value, int ordinal, Class[] additionalTypes, Object[] additionalValues) throws Exception { + Object[] parms = null; + if (decompiled) + { + parms = new Object[additionalValues.length + 4]; + parms[0] = value; + parms[1] = Integer.valueOf(ordinal); + parms[2] = value; + parms[3] = Integer.valueOf(ordinal); + System.arraycopy(additionalValues, 0, parms, 4, additionalValues.length); + } + else + { + parms = new Object[additionalValues.length + 2]; + parms[0] = value; + parms[1] = Integer.valueOf(ordinal); + System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length); + } + return enumClass.cast(newInstance.invoke(getConstructorAccessor(decompiled, enumClass, additionalTypes), new Object[]{parms})); + } + + private static void setFailsafeFieldValue(Field field, Object target, Object value) throws Exception { + field.setAccessible(true); + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false); + fieldAccessorSet.invoke(fieldAccessor, target, value); + } + + private static void blankField(Class enumClass, String fieldName) throws Exception { + for (Field field : Class.class.getDeclaredFields()) { + if (field.getName().contains(fieldName)) { + field.setAccessible(true); + setFailsafeFieldValue(field, enumClass, null); + break; + } + } + } + + private static void cleanEnumCache(Class enumClass) throws Exception { + blankField(enumClass, "enumConstantDirectory"); + blankField(enumClass, "enumConstants"); + } + + public static > T addEnum(Class enumType, String enumName, boolean decompiled) { + return addEnum(decompiled, enumType, enumName, new Class[]{}, new Object[]{}); + } + + @SuppressWarnings("unchecked") + public static > T addEnum(boolean decompiled, Class enumType, String enumName, Class[] paramTypes, Object[] paramValues) { + if (!isSetup) + { + setup(); + } + + Field valuesField = null; + Field[] fields = enumType.getDeclaredFields(); + int flags = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL | 0x1000 /*SYNTHETIC*/; + String valueType = String.format("[L%s;", enumType.getName().replace('.', '/')); + + for (Field field : fields) { + if (decompiled) + { + if (field.getName().contains("$VALUES")) + { + valuesField = field; + break; + } + } + else + { + if ((field.getModifiers() & flags) == flags && + field.getType().getName().equals(valueType)) + { + valuesField = field; + break; + } + } + } + valuesField.setAccessible(true); + + try { + T[] previousValues = (T[])valuesField.get(enumType); + List values = new ArrayList(Arrays.asList(previousValues)); + T newValue = (T)makeEnum(decompiled, enumType, enumName, values.size(), paramTypes, paramValues); + values.add(newValue); + setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0))); + cleanEnumCache(enumType); + + return newValue; + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e.getMessage(), e); + } + } + + static { + if (!isSetup) + { + setup(); + } + } +} \ No newline at end of file