001package net.minecraftforge.common; 002 003import java.lang.reflect.*; 004import java.util.*; 005 006import cpw.mods.fml.common.FMLCommonHandler; 007import cpw.mods.fml.common.FMLLog; 008 009import net.minecraft.block.EnumMobType; 010import net.minecraft.block.material.Material; 011import net.minecraft.enchantment.EnumEnchantmentType; 012import net.minecraft.entity.EnumCreatureAttribute; 013import net.minecraft.entity.EnumCreatureType; 014import net.minecraft.entity.EnumEntitySize; 015import net.minecraft.entity.player.EnumStatus; 016import net.minecraft.item.EnumAction; 017import net.minecraft.item.EnumArmorMaterial; 018import net.minecraft.item.EnumToolMaterial; 019import net.minecraft.util.EnumArt; 020import net.minecraft.util.EnumMovingObjectType; 021import net.minecraft.world.EnumSkyBlock; 022import net.minecraft.world.gen.structure.EnumDoor; 023import net.minecraftforge.classloading.FMLForgePlugin; 024 025public class EnumHelper 026{ 027 private static Object reflectionFactory = null; 028 private static Method newConstructorAccessor = null; 029 private static Method newInstance = null; 030 private static Method newFieldAccessor = null; 031 private static Method fieldAccessorSet = null; 032 private static boolean isSetup = false; 033 034 //Some enums are decompiled with extra arguments, so lets check for that 035 private static Class[][] commonTypes = 036 { 037 {EnumAction.class}, 038 {EnumArmorMaterial.class, int.class, int[].class, int.class}, 039 {EnumArt.class, String.class, int.class, int.class, int.class, int.class}, 040 {EnumCreatureAttribute.class}, 041 {EnumCreatureType.class, Class.class, int.class, Material.class, boolean.class}, 042 {EnumDoor.class}, 043 {EnumEnchantmentType.class}, 044 {EnumEntitySize.class}, 045 {EnumMobType.class}, 046 {EnumMovingObjectType.class}, 047 {EnumSkyBlock.class, int.class}, 048 {EnumStatus.class}, 049 {EnumToolMaterial.class, int.class, int.class, float.class, int.class, int.class} 050 }; 051 052 public static EnumAction addAction(String name) 053 { 054 return addEnum(EnumAction.class, name); 055 } 056 public static EnumArmorMaterial addArmorMaterial(String name, int durability, int[] reductionAmounts, int enchantability) 057 { 058 return addEnum(EnumArmorMaterial.class, name, durability, reductionAmounts, enchantability); 059 } 060 public static EnumArt addArt(String name, String tile, int sizeX, int sizeY, int offsetX, int offsetY) 061 { 062 return addEnum(EnumArt.class, name, tile, sizeX, sizeY, offsetX, offsetY); 063 } 064 public static EnumCreatureAttribute addCreatureAttribute(String name) 065 { 066 return addEnum(EnumCreatureAttribute.class, name); 067 } 068 public static EnumCreatureType addCreatureType(String name, Class typeClass, int maxNumber, Material material, boolean peaceful) 069 { 070 return addEnum(EnumCreatureType.class, name, typeClass, maxNumber, material, peaceful); 071 } 072 public static EnumDoor addDoor(String name) 073 { 074 return addEnum(EnumDoor.class, name); 075 } 076 public static EnumEnchantmentType addEnchantmentType(String name) 077 { 078 return addEnum(EnumEnchantmentType.class, name); 079 } 080 public static EnumEntitySize addEntitySize(String name) 081 { 082 return addEnum(EnumEntitySize.class, name); 083 } 084 public static EnumMobType addMobType(String name) 085 { 086 return addEnum(EnumMobType.class, name); 087 } 088 public static EnumMovingObjectType addMovingObjectType(String name) 089 { 090 if (!isSetup) 091 { 092 setup(); 093 } 094 095 return addEnum(EnumMovingObjectType.class, name); 096 } 097 public static EnumSkyBlock addSkyBlock(String name, int lightValue) 098 { 099 return addEnum(EnumSkyBlock.class, name, lightValue); 100 } 101 public static EnumStatus addStatus(String name) 102 { 103 return addEnum(EnumStatus.class, name); 104 } 105 public static EnumToolMaterial addToolMaterial(String name, int harvestLevel, int maxUses, float efficiency, int damage, int enchantability) 106 { 107 return addEnum(EnumToolMaterial.class, name, harvestLevel, maxUses, efficiency, damage, enchantability); 108 } 109 110 private static void setup() 111 { 112 if (isSetup) 113 { 114 return; 115 } 116 117 try 118 { 119 Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory"); 120 reflectionFactory = getReflectionFactory.invoke(null); 121 newConstructorAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newConstructorAccessor", Constructor.class); 122 newInstance = Class.forName("sun.reflect.ConstructorAccessor").getDeclaredMethod("newInstance", Object[].class); 123 newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class); 124 fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class); 125 } 126 catch (Exception e) 127 { 128 e.printStackTrace(); 129 } 130 131 isSetup = true; 132 } 133 134 /* 135 * Everything below this is found at the site below, and updated to be able to compile in Eclipse/Java 1.6+ 136 * Also modified for use in decompiled code. 137 * Found at: http://niceideas.ch/roller2/badtrash/entry/java_create_enum_instances_dynamically 138 */ 139 private static Object getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes) throws Exception 140 { 141 Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2]; 142 parameterTypes[0] = String.class; 143 parameterTypes[1] = int.class; 144 System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length); 145 return newConstructorAccessor.invoke(reflectionFactory, enumClass.getDeclaredConstructor(parameterTypes)); 146 } 147 148 private static < T extends Enum<? >> T makeEnum(Class<T> enumClass, String value, int ordinal, Class<?>[] additionalTypes, Object[] additionalValues) throws Exception 149 { 150 Object[] parms = new Object[additionalValues.length + 2]; 151 parms[0] = value; 152 parms[1] = Integer.valueOf(ordinal); 153 System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length); 154 return enumClass.cast(newInstance.invoke(getConstructorAccessor(enumClass, additionalTypes), new Object[] {parms})); 155 } 156 157 public static void setFailsafeFieldValue(Field field, Object target, Object value) throws Exception 158 { 159 field.setAccessible(true); 160 Field modifiersField = Field.class.getDeclaredField("modifiers"); 161 modifiersField.setAccessible(true); 162 modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 163 Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false); 164 fieldAccessorSet.invoke(fieldAccessor, target, value); 165 } 166 167 private static void blankField(Class<?> enumClass, String fieldName) throws Exception 168 { 169 for (Field field : Class.class.getDeclaredFields()) 170 { 171 if (field.getName().contains(fieldName)) 172 { 173 field.setAccessible(true); 174 setFailsafeFieldValue(field, enumClass, null); 175 break; 176 } 177 } 178 } 179 180 private static void cleanEnumCache(Class<?> enumClass) throws Exception 181 { 182 blankField(enumClass, "enumConstantDirectory"); 183 blankField(enumClass, "enumConstants"); 184 } 185 186 public static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Object... paramValues) 187 { 188 return addEnum(commonTypes, enumType, enumName, paramValues); 189 } 190 191 public static <T extends Enum<? >> T addEnum(Class[][] map, Class<T> enumType, String enumName, Object... paramValues) 192 { 193 for (Class[] lookup : map) 194 { 195 if (lookup[0] == enumType) 196 { 197 Class<?>[] paramTypes = new Class<?>[lookup.length - 1]; 198 if (paramTypes.length > 0) 199 { 200 System.arraycopy(lookup, 1, paramTypes, 0, paramTypes.length); 201 } 202 return addEnum(enumType, enumName, paramTypes, paramValues); 203 } 204 } 205 return null; 206 } 207 208 @SuppressWarnings("unchecked") 209 public static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Class<?>[] paramTypes, Object[] paramValues) 210 { 211 if (!isSetup) 212 { 213 setup(); 214 } 215 216 Field valuesField = null; 217 Field[] fields = enumType.getDeclaredFields(); 218 219 for (Field field : fields) 220 { 221 String name = field.getName(); 222 if (name.equals("$VALUES") || name.equals("ENUM$VALUES")) //Added 'ENUM$VALUES' because Eclipse's internal compiler doesn't follow standards 223 { 224 valuesField = field; 225 break; 226 } 227 } 228 229 int flags = (FMLForgePlugin.RUNTIME_DEOBF ? Modifier.PUBLIC : Modifier.PRIVATE) | Modifier.STATIC | Modifier.FINAL | 0x1000 /*SYNTHETIC*/; 230 if (valuesField == null) 231 { 232 String valueType = String.format("[L%s;", enumType.getName().replace('.', '/')); 233 234 for (Field field : fields) 235 { 236 if ((field.getModifiers() & flags) == flags && 237 field.getType().getName().replace('.', '/').equals(valueType)) //Apparently some JVMs return .'s and some don't.. 238 { 239 valuesField = field; 240 break; 241 } 242 } 243 } 244 245 if (valuesField == null) 246 { 247 FMLLog.severe("Could not find $VALUES field for enum: %s", enumType.getName()); 248 FMLLog.severe("Runtime Deobf: %s", FMLForgePlugin.RUNTIME_DEOBF); 249 FMLLog.severe("Flags: %s", String.format("%16s", Integer.toBinaryString(flags)).replace(' ', '0')); 250 FMLLog.severe("Fields:"); 251 for (Field field : fields) 252 { 253 String mods = String.format("%16s", Integer.toBinaryString(field.getModifiers())).replace(' ', '0'); 254 FMLLog.severe(" %s %s: %s", mods, field.getName(), field.getType().getName()); 255 } 256 return null; 257 } 258 259 valuesField.setAccessible(true); 260 261 try 262 { 263 T[] previousValues = (T[])valuesField.get(enumType); 264 List<T> values = new ArrayList<T>(Arrays.asList(previousValues)); 265 T newValue = (T)makeEnum(enumType, enumName, values.size(), paramTypes, paramValues); 266 values.add(newValue); 267 setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0))); 268 cleanEnumCache(enumType); 269 270 return newValue; 271 } 272 catch (Exception e) 273 { 274 e.printStackTrace(); 275 throw new RuntimeException(e.getMessage(), e); 276 } 277 } 278 279 static 280 { 281 if (!isSetup) 282 { 283 setup(); 284 } 285 } 286}