3
0
KCauldronX/patches/org/bukkit/Material.java.patch
2015-03-22 20:38:04 +03:00

273 lines
10 KiB
Diff

--- ../src-base/minecraft/org/bukkit/Material.java
+++ ../src-work/minecraft/org/bukkit/Material.java
@@ -3,6 +3,22 @@
import java.lang.reflect.Constructor;
import java.util.Map;
+// Cauldron start
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.bukkit.inventory.ItemStack;
+
+import net.minecraftforge.common.util.EnumHelper;
+import net.minecraftforge.cauldron.api.inventory.BukkitOreDictionary;
+import net.minecraftforge.cauldron.api.inventory.OreDictionaryEntry;
+// Cauldron end
+
import org.apache.commons.lang.Validate;
import org.bukkit.map.MapView;
import org.bukkit.material.Bed;
@@ -418,14 +434,30 @@
private final int id;
private final Constructor<? extends MaterialData> ctor;
private static Material[] byId = new Material[383];
- private final static Map<String, Material> BY_NAME = Maps.newHashMap();
+ private static Map<String, Material> BY_NAME = Maps.newHashMap(); // Cauldron - remove final
private final int maxStack;
private final short durability;
+ // Cauldron start
+ private static Object reflectionFactory;
+ private static Method newConstructorAccessor;
+ private static Method newInstance;
+ private static Method newFieldAccessor;
+ private static Method fieldAccessorSet;
+ private static boolean isSetup;
+ private boolean isForgeBlock = false;
+ // Cauldron end
private Material(final int id) {
this(id, 64);
}
+ // Cauldron start - constructor used to set if the Material is a block or not
+ private Material(final int id, boolean flag) {
+ this(id, 64);
+ this.isForgeBlock = flag;
+ }
+ // Cauldron end
+
private Material(final int id, final int stack) {
this(id, stack, MaterialData.class);
}
@@ -526,7 +558,7 @@
* @return true if this material is a block
*/
public boolean isBlock() {
- return id < 256;
+ return id < 256 || isForgeBlock; // Cauldron
}
/**
@@ -615,16 +647,202 @@
} catch (NumberFormatException ex) {}
if (result == null) {
- String filtered = name.toUpperCase();
-
- filtered = filtered.replaceAll("\\s+", "_").replaceAll("\\W", "");
+ // Cauldron start - extract to normalizeName()
+ String filtered = normalizeName(name);
result = BY_NAME.get(filtered);
+ // Cauldron end
}
+ // Cauldron start - Try the ore dictionary
+ if (result == null) {
+ BukkitOreDictionary dict = net.minecraftforge.cauldron.api.Cauldron.getOreDictionary();
+ OreDictionaryEntry entry = dict.getOreEntry(name);
+ if (entry != null) {
+ List<ItemStack> items = dict.getDefinitions(entry);
+ if (items.size() > 0) {
+ // TODO check sanity on multiple item results
+ ItemStack item = items.get(0);
+ if (item.getDurability() == 0 || item.getDurability() == Short.MAX_VALUE) {
+ result = item.getType();
+ } else {
+ // bad! we have an item with data!
+ }
+ }
+ }
+ }
+ // Cauldron end
+
return result;
}
+ /* =============================== Cauldron START ============================= */
+
+ // use a normalize() function to ensure it is accessible after a round-trip
+ public static String normalizeName(String name) {
+ return name.toUpperCase().replaceAll("(:|\\s)", "_").replaceAll("\\W", "");
+ }
+
+ public static Material addMaterial(int id, boolean isBlock)
+ {
+ return addMaterial(id, "X" + String.valueOf(id), isBlock);
+ }
+
+ public static Material addMaterial(int id, String name, boolean isBlock) {
+ if (byId[id] == null) {
+ String materialName = normalizeName(name);
+ Material material = (Material) EnumHelper.addEnum(Material.class, materialName, new Class[]{Integer.TYPE, Boolean.TYPE}, new Object[]{Integer.valueOf(id), isBlock});
+ byId[id] = material;
+ BY_NAME.put(materialName, material);
+ BY_NAME.put("X" + String.valueOf(id), material);
+ return material;
+ }
+ return null;
+ }
+
+ public static void setMaterialName(int id, String name, boolean flag) {
+ String materialName = normalizeName(name);
+
+ if (byId[id] == null)
+ {
+ addMaterial(id, materialName, flag);
+ }
+ else // replace existing enum
+ {
+ /* TODO: find out how to do this with Forge's EnumHelper (addEnum?) - used for enabling descriptive (vs numeric) Material names
+ Material material = getMaterial(id);
+ BY_NAME.remove(material);
+ Material newMaterial = EnumHelper.replaceEnum(Material.class, material_name, material.ordinal(), new Class[] { Integer.TYPE }, new Object[] { Integer.valueOf(id) });
+ if (newMaterial == null)
+ System.out.println("Error replacing Material " + name + " with id " + id);
+ else {
+ byId[id] = newMaterial;
+ BY_NAME.put(material_name, newMaterial);
+ }
+ */
+ }
+ }
+
+ private static void setup()
+ {
+ if (isSetup)
+ {
+ return;
+ }
+ try {
+ Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory", new Class[0]);
+ reflectionFactory = getReflectionFactory.invoke(null, new Object[0]);
+ newConstructorAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newConstructorAccessor", new Class[] { Constructor.class });
+ newInstance = Class.forName("sun.reflect.ConstructorAccessor").getDeclaredMethod("newInstance", new Class[] { Object[].class });
+ newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", new Class[] { Field.class, Boolean.TYPE });
+ fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", new Class[] { Object.class, Object.class });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ isSetup = true;
+ }
+
+ private static Object getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes) throws Exception {
+ Class[] parameterTypes = null;
+
+ parameterTypes = new Class[additionalParameterTypes.length + 2];
+ parameterTypes[0] = String.class;
+ parameterTypes[1] = Integer.TYPE;
+ System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
+
+ return newConstructorAccessor.invoke(reflectionFactory, new Object[] { enumClass.getDeclaredConstructor(parameterTypes) });
+ }
+
+ private static <T extends Enum<?>> T makeEnum(Class<T> enumClass, String value, int ordinal, Class<?>[] additionalTypes, Object[] additionalValues) throws Exception {
+ Object[] parms = null;
+
+ parms = new Object[additionalValues.length + 2];
+ parms[0] = value;
+ parms[1] = Integer.valueOf(ordinal);
+ System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
+
+ return (T)enumClass.cast(newInstance.invoke(getConstructorAccessor(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() & 0xFFFFFFEF);
+ Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, new Object[] { field, Boolean.valueOf(false) });
+ fieldAccessorSet.invoke(fieldAccessor, new Object[] { 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 extends Enum<?>> T replaceEnum(Class<T> enumType, String enumName, int ordinal, Class<?>[] paramTypes, Object[] paramValues)
+ {
+ if (!isSetup) setup();
+ Field valuesField = null;
+ Field[] fields = enumType.getDeclaredFields();
+ int flags = 4122;
+ String valueType = String.format("[L%s;", new Object[] { enumType.getName() });
+
+ for (Field field : fields) {
+ if (((field.getModifiers() & flags) != flags) || (!field.getType().getName().equals(valueType))) {
+ continue;
+ }
+ valuesField = field;
+ break;
+ }
+
+ valuesField.setAccessible(true);
+ try
+ {
+ Enum[] previousValues = (Enum[])(Enum[])valuesField.get(enumType);
+ Enum[] newValues = new Enum[previousValues.length];
+ Enum newValue = null;
+ for (Enum enumValue : previousValues)
+ {
+ if (enumValue.ordinal() == ordinal)
+ {
+ newValue = makeEnum(enumType, enumName, ordinal, paramTypes, paramValues);
+ newValues[enumValue.ordinal()] = newValue;
+ }
+ else newValues[enumValue.ordinal()] = enumValue;
+ }
+ List values = new ArrayList(Arrays.asList(newValues));
+
+ setFailsafeFieldValue(valuesField, null, values.toArray((Enum[])(Enum[])Array.newInstance(enumType, 0)));
+ cleanEnumCache(enumType);
+ return (T) newValue;
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ /* =============================== Cauldron END============================= */
+
static {
+ // Cauldron start
+ byId = new Material[32000];
+ BY_NAME = Maps.newHashMap();
+
+ reflectionFactory = null;
+ newConstructorAccessor = null;
+ newInstance = null;
+ newFieldAccessor = null;
+ fieldAccessorSet = null;
+ isSetup = false;
+ // Cauldron end
for (Material material : values()) {
if (byId.length > material.id) {
byId[material.id] = material;