Add in an ItemStackHolder - a way to inject ItemStacks without having to have complex lookup code everywhere.
Example: https://gist.github.com/cpw/9af398451a20459ac263
This commit is contained in:
parent
06398fa259
commit
bd6630810f
4 changed files with 240 additions and 0 deletions
|
@ -75,6 +75,7 @@ import cpw.mods.fml.common.functions.ArtifactVersionNameFunction;
|
|||
import cpw.mods.fml.common.functions.ModIdFunction;
|
||||
import cpw.mods.fml.common.registry.GameData;
|
||||
import cpw.mods.fml.common.registry.GameRegistry.Type;
|
||||
import cpw.mods.fml.common.registry.ItemStackHolderInjector;
|
||||
import cpw.mods.fml.common.registry.ObjectHolderRegistry;
|
||||
import cpw.mods.fml.common.toposort.ModSorter;
|
||||
import cpw.mods.fml.common.toposort.ModSortingException;
|
||||
|
@ -530,8 +531,10 @@ public class Loader
|
|||
return;
|
||||
}
|
||||
ObjectHolderRegistry.INSTANCE.findObjectHolders(discoverer.getASMTable());
|
||||
ItemStackHolderInjector.INSTANCE.findHolders(discoverer.getASMTable());
|
||||
modController.distributeStateMessage(LoaderState.PREINITIALIZATION, discoverer.getASMTable(), canonicalConfigDir);
|
||||
ObjectHolderRegistry.INSTANCE.applyObjectHolders();
|
||||
ItemStackHolderInjector.INSTANCE.inject();
|
||||
modController.transition(LoaderState.INITIALIZATION, false);
|
||||
progressBar.step("Initializing Minecraft Engine");
|
||||
}
|
||||
|
@ -714,6 +717,7 @@ public class Loader
|
|||
progressBar.step("Initializing mods Phase 3");
|
||||
modController.transition(LoaderState.POSTINITIALIZATION, false);
|
||||
modController.distributeStateMessage(FMLInterModComms.IMCEvent.class);
|
||||
ItemStackHolderInjector.INSTANCE.inject();
|
||||
modController.distributeStateMessage(LoaderState.POSTINITIALIZATION);
|
||||
progressBar.step("Finishing up");
|
||||
modController.transition(LoaderState.AVAILABLE, false);
|
||||
|
|
|
@ -32,6 +32,10 @@ import net.minecraft.item.ItemStack;
|
|||
import net.minecraft.item.crafting.CraftingManager;
|
||||
import net.minecraft.item.crafting.FurnaceRecipes;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.nbt.JsonToNBT;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTException;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.IChunkProvider;
|
||||
|
@ -39,6 +43,8 @@ import net.minecraft.world.chunk.IChunkProvider;
|
|||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
@ -476,4 +482,70 @@ public class GameRegistry
|
|||
*/
|
||||
String value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ItemStackHolder
|
||||
{
|
||||
/**
|
||||
* The registry name of the item being looked up.
|
||||
* @return The registry name
|
||||
*/
|
||||
public String value();
|
||||
|
||||
/**
|
||||
* The metadata or damage value for the itemstack, defaults to 0.
|
||||
* @return the metadata value
|
||||
*/
|
||||
public int meta() default 0;
|
||||
|
||||
/**
|
||||
* The string serialized nbt value for the itemstack. Defaults to empty for no nbt.
|
||||
*
|
||||
* @return a nbt string
|
||||
*/
|
||||
public String nbt() default "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes an {@link ItemStack} based on the itemName reference, with supplied meta, stackSize and nbt, if possible
|
||||
*
|
||||
* Will return null if the item doesn't exist (because it's not from a loaded mod for example)
|
||||
* Will throw a {@link RuntimeException} if the nbtString is invalid for use in an {@link ItemStack}
|
||||
*
|
||||
* @param itemName a registry name reference
|
||||
* @param meta the meta
|
||||
* @param stackSize the stack size
|
||||
* @param nbtString an nbt stack as a string, will be processed by {@link JsonToNBT}
|
||||
* @return a new itemstack
|
||||
*/
|
||||
public static ItemStack makeItemStack(String itemName, int meta, int stackSize, String nbtString)
|
||||
{
|
||||
if (itemName == null) throw new IllegalArgumentException("The itemName cannot be null");
|
||||
Item item = GameData.getItemRegistry().getObject(itemName);
|
||||
if (item == null) {
|
||||
FMLLog.getLogger().log(Level.TRACE, "Unable to find item with name {}", itemName);
|
||||
return null;
|
||||
}
|
||||
ItemStack is = new ItemStack(item,1,meta);
|
||||
if (!Strings.isNullOrEmpty(nbtString)) {
|
||||
NBTBase nbttag = null;
|
||||
try
|
||||
{
|
||||
nbttag = JsonToNBT.func_150315_a(nbtString);
|
||||
} catch (NBTException e)
|
||||
{
|
||||
FMLLog.getLogger().log(Level.WARN, "Encountered an exception parsing ItemStack NBT string {}", nbtString, e);
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
if (!(nbttag instanceof NBTTagCompound)) {
|
||||
FMLLog.getLogger().log(Level.WARN, "Unexpected NBT string - multiple values {}", nbtString);
|
||||
throw new RuntimeException("Invalid NBT JSON");
|
||||
} else {
|
||||
is.setTagCompound((NBTTagCompound) nbttag);
|
||||
}
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package cpw.mods.fml.common.registry;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.common.discovery.ASMDataTable;
|
||||
import cpw.mods.fml.common.discovery.ASMDataTable.ASMData;
|
||||
|
||||
public enum ItemStackHolderInjector
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
private List<ItemStackHolderRef> itemStackHolders = Lists.newArrayList();
|
||||
|
||||
public void inject() {
|
||||
FMLLog.getLogger().log(Level.INFO, "Injecting itemstacks");
|
||||
for (ItemStackHolderRef ishr: itemStackHolders) {
|
||||
ishr.apply();
|
||||
}
|
||||
FMLLog.getLogger().log(Level.INFO, "Itemstack injection complete");
|
||||
}
|
||||
|
||||
public void findHolders(ASMDataTable table) {
|
||||
FMLLog.info("Identifying ItemStackHolder annotations");
|
||||
Set<ASMData> allItemStackHolders = table.getAll(GameRegistry.ItemStackHolder.class.getName());
|
||||
Map<String, Class<?>> classCache = Maps.newHashMap();
|
||||
for (ASMData data : allItemStackHolders)
|
||||
{
|
||||
String className = data.getClassName();
|
||||
String annotationTarget = data.getObjectName();
|
||||
String value = (String) data.getAnnotationInfo().get("value");
|
||||
int meta = data.getAnnotationInfo().containsKey("meta") ? (Integer) data.getAnnotationInfo().get("meta") : 0;
|
||||
String nbt = data.getAnnotationInfo().containsKey("nbt") ? (String) data.getAnnotationInfo().get("nbt") : "";
|
||||
addHolder(classCache, className, annotationTarget, value, meta, nbt);
|
||||
}
|
||||
FMLLog.info("Found %d ItemStackHolder annotations", allItemStackHolders.size());
|
||||
|
||||
}
|
||||
|
||||
private void addHolder(Map<String, Class<?>> classCache, String className, String annotationTarget, String value, Integer meta, String nbt)
|
||||
{
|
||||
Class<?> clazz;
|
||||
if (classCache.containsKey(className))
|
||||
{
|
||||
clazz = classCache.get(className);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
clazz = Class.forName(className, true, getClass().getClassLoader());
|
||||
classCache.put(className, clazz);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// unpossible?
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
Field f = clazz.getField(annotationTarget);
|
||||
itemStackHolders.add(new ItemStackHolderRef(f, value, meta, nbt));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// unpossible?
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package cpw.mods.fml.common.registry;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.common.registry.GameRegistry.ItemStackHolder;
|
||||
|
||||
|
||||
/**
|
||||
* Internal class used in tracking {@link ItemStackHolder} references
|
||||
*
|
||||
* @author cpw
|
||||
*
|
||||
*/
|
||||
class ItemStackHolderRef {
|
||||
private Field field;
|
||||
private String itemName;
|
||||
private int meta;
|
||||
private String serializednbt;
|
||||
|
||||
|
||||
ItemStackHolderRef(Field field, String itemName, int meta, String serializednbt)
|
||||
{
|
||||
this.field = field;
|
||||
this.itemName = itemName;
|
||||
this.meta = meta;
|
||||
this.serializednbt = serializednbt;
|
||||
makeWritable(field);
|
||||
}
|
||||
|
||||
private static Field modifiersField;
|
||||
private static Object reflectionFactory;
|
||||
private static Method newFieldAccessor;
|
||||
private static Method fieldAccessorSet;
|
||||
private static void makeWritable(Field f)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (modifiersField == null)
|
||||
{
|
||||
Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory");
|
||||
reflectionFactory = getReflectionFactory.invoke(null);
|
||||
newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class);
|
||||
fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class);
|
||||
modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
}
|
||||
modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
|
||||
} catch (Exception e)
|
||||
{
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void apply()
|
||||
{
|
||||
ItemStack is;
|
||||
try
|
||||
{
|
||||
is = GameRegistry.makeItemStack(itemName, meta, 1, serializednbt);
|
||||
} catch (RuntimeException e)
|
||||
{
|
||||
FMLLog.getLogger().log(Level.ERROR, "Caught exception processing itemstack {},{},{} in annotation at {}.{}", itemName, meta, serializednbt,field.getClass().getName(),field.getName());
|
||||
throw e;
|
||||
}
|
||||
try
|
||||
{
|
||||
Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false);
|
||||
fieldAccessorSet.invoke(fieldAccessor, null, is);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.getLogger().log(Level.WARN, "Unable to set {} with value {},{},{}", this.field, this.itemName, this.meta, this.serializednbt);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue