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:
cpw 2015-05-30 14:07:54 -04:00
parent 06398fa259
commit bd6630810f
4 changed files with 240 additions and 0 deletions

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}