diff --git a/patches/minecraft/net/minecraft/entity/Entity.java.patch b/patches/minecraft/net/minecraft/entity/Entity.java.patch index 957649fc8..a679974d7 100644 --- a/patches/minecraft/net/minecraft/entity/Entity.java.patch +++ b/patches/minecraft/net/minecraft/entity/Entity.java.patch @@ -1,6 +1,15 @@ --- ../src-base/minecraft/net/minecraft/entity/Entity.java +++ ../src-work/minecraft/net/minecraft/entity/Entity.java -@@ -129,6 +129,14 @@ +@@ -49,7 +49,7 @@ + import net.minecraftforge.fml.relauncher.Side; + import net.minecraftforge.fml.relauncher.SideOnly; + +-public abstract class Entity implements ICommandSender ++public abstract class Entity implements ICommandSender, net.minecraftforge.common.capabilities.ICapabilitySerializable + { + private static final AxisAlignedBB field_174836_a = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D); + private static int field_70152_a; +@@ -129,6 +129,15 @@ protected UUID field_96093_i; private final CommandResultStats field_174837_as; @@ -8,23 +17,25 @@ + private NBTTagCompound customEntityData; + public boolean captureDrops = false; + public java.util.ArrayList capturedDrops = new java.util.ArrayList(); -+ private UUID persistentID; + ++ @Deprecated //ToDo: Move to Capabilities? + protected java.util.HashMap extendedProperties = new java.util.HashMap(); ++ private net.minecraftforge.common.capabilities.CapabilityDispatcher capabilities; + public int func_145782_y() { return this.field_145783_c; -@@ -172,6 +180,8 @@ +@@ -172,6 +181,9 @@ this.field_70180_af.func_75682_a(2, ""); this.field_70180_af.func_75682_a(4, Byte.valueOf((byte)0)); this.func_70088_a(); + net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.EntityEvent.EntityConstructing(this)); + for (net.minecraftforge.common.IExtendedEntityProperties props : extendedProperties.values()) props.init(this, p_i1582_1_); ++ capabilities = net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(this); } protected abstract void func_70088_a(); -@@ -998,10 +1008,7 @@ +@@ -998,10 +1010,7 @@ if (block.func_149688_o() == p_70055_1_) { @@ -36,7 +47,7 @@ } else { -@@ -1356,6 +1363,21 @@ +@@ -1356,6 +1365,23 @@ p_70109_1_.func_74757_a("Silent", this.func_174814_R()); } @@ -54,11 +65,13 @@ + t.printStackTrace(); + } + } ++ ++ if (this.capabilities != null) p_70109_1_.func_74782_a("ForgeCaps", this.capabilities.serializeNBT()); + this.func_70014_b(p_70109_1_); if (this.field_70154_o != null) -@@ -1438,6 +1460,28 @@ +@@ -1438,6 +1464,30 @@ this.func_174805_g(p_70020_1_.func_74767_n("CustomNameVisible")); this.field_174837_as.func_179668_a(p_70020_1_); this.func_174810_b(p_70020_1_.func_74767_n("Silent")); @@ -78,16 +91,18 @@ + } + } + -+ //Rawr, legacy code, Vanilla added a UUID, keep this so older maps will convert properly ++ //Rawr, legacy code, Vanilla added a UUID, keep this so older maps will convert properly TODO: Remove in 1.9 + if (p_70020_1_.func_74764_b("PersistentIDMSB") && p_70020_1_.func_74764_b("PersistentIDLSB")) + { + this.field_96093_i = new UUID(p_70020_1_.func_74763_f("PersistentIDMSB"), p_70020_1_.func_74763_f("PersistentIDLSB")); + } ++ ++ if (this.capabilities != null && p_70020_1_.func_74764_b("ForgeCaps")) this.capabilities.deserializeNBT(p_70020_1_.func_74775_l("ForgeCaps")); + this.func_70037_a(p_70020_1_); if (this.func_142008_O()) -@@ -1512,7 +1556,10 @@ +@@ -1512,7 +1562,10 @@ { EntityItem entityitem = new EntityItem(this.field_70170_p, this.field_70165_t, this.field_70163_u + (double)p_70099_2_, this.field_70161_v, p_70099_1_); entityitem.func_174869_p(); @@ -99,7 +114,7 @@ return entityitem; } else -@@ -1655,6 +1702,7 @@ +@@ -1655,6 +1708,7 @@ public void func_70078_a(Entity p_70078_1_) { @@ -107,7 +122,7 @@ this.field_70149_e = 0.0D; this.field_70147_f = 0.0D; -@@ -1789,7 +1837,7 @@ +@@ -1789,7 +1843,7 @@ public boolean func_70115_ae() { @@ -116,7 +131,7 @@ } public boolean func_70093_af() -@@ -2092,7 +2140,7 @@ +@@ -2092,7 +2146,7 @@ public float func_180428_a(Explosion p_180428_1_, World p_180428_2_, BlockPos p_180428_3_, IBlockState p_180428_4_) { @@ -125,7 +140,7 @@ } public boolean func_174816_a(Explosion p_174816_1_, World p_174816_2_, BlockPos p_174816_3_, IBlockState p_174816_4_, float p_174816_5_) -@@ -2353,4 +2401,184 @@ +@@ -2353,4 +2407,209 @@ EnchantmentHelper.func_151385_b(p_174815_1_, p_174815_2_); } @@ -308,5 +323,30 @@ + { + return this instanceof EntityLivingBase; + } ++ ++ public boolean hasCapability(net.minecraftforge.common.capabilities.Capability capability, net.minecraft.util.EnumFacing facing) ++ { ++ if (getCapability(capability, facing) != null) ++ return true; ++ return capabilities == null ? false : capabilities.hasCapability(capability, facing); ++ } ++ ++ public T getCapability(net.minecraftforge.common.capabilities.Capability capability, net.minecraft.util.EnumFacing facing) ++ { ++ return capabilities == null ? null : capabilities.getCapability(capability, facing); ++ } ++ ++ public void deserializeNBT(NBTTagCompound nbt) ++ { ++ this.func_70020_e(nbt); ++ } ++ ++ public NBTTagCompound serializeNBT() ++ { ++ NBTTagCompound ret = new NBTTagCompound(); ++ ret.func_74778_a("id", this.func_70022_Q()); ++ this.func_70109_d(ret); ++ return ret; ++ } + /* ================================== Forge End =====================================*/ } diff --git a/patches/minecraft/net/minecraft/item/Item.java.patch b/patches/minecraft/net/minecraft/item/Item.java.patch index e6490c861..d7b948951 100644 --- a/patches/minecraft/net/minecraft/item/Item.java.patch +++ b/patches/minecraft/net/minecraft/item/Item.java.patch @@ -57,7 +57,7 @@ Vec3 vec31 = vec3.func_72441_c((double)f6 * d3, (double)f5 * d3, (double)f7 * d3); return p_77621_1_.func_147447_a(vec3, vec31, p_77621_3_, !p_77621_3_, false); } -@@ -371,11 +380,641 @@ +@@ -371,11 +380,659 @@ return false; } @@ -694,12 +694,30 @@ + if (delegate.getResourceName() != null) return delegate.getResourceName().toString(); + return registryName != null ? registryName.toString() : null; + } ++ ++ /** ++ * Called from ItemStack.setItem, will hold extra data for the life of this ItemStack. ++ * Can be retrieved from stack.getCapabilities() ++ * The NBT can be null if this is not called from readNBT or if the item the stack is ++ * changing FROM is different then this item, or the previous item had no capabilities. ++ * ++ * This is called BEFORE the stacks item is set so you can use stack.getItem() to see the OLD item. ++ * Remember that getItem CAN return null. ++ * ++ * @param stack The ItemStack ++ * @param nbt NBT of this item serialized, or null. ++ * @return A holder instance associated with this ItemStack where you can hold capabilities for the life of this item. ++ */ ++ public net.minecraftforge.common.capabilities.ICapabilityProvider initCapabilities(ItemStack stack, NBTTagCompound nbt) ++ { ++ return null; ++ } + /* ======================================== FORGE END =====================================*/ + public static void func_150900_l() { func_179214_a(Blocks.field_150348_b, (new ItemMultiTexture(Blocks.field_150348_b, Blocks.field_150348_b, new Function() -@@ -855,6 +1494,10 @@ +@@ -855,6 +1512,10 @@ private final float field_78011_i; private final int field_78008_j; @@ -710,7 +728,7 @@ private ToolMaterial(int p_i1874_3_, int p_i1874_4_, float p_i1874_5_, float p_i1874_6_, int p_i1874_7_) { this.field_78001_f = p_i1874_3_; -@@ -889,9 +1532,36 @@ +@@ -889,9 +1550,36 @@ return this.field_78008_j; } diff --git a/patches/minecraft/net/minecraft/item/ItemStack.java.patch b/patches/minecraft/net/minecraft/item/ItemStack.java.patch index 8dd896471..090c9e874 100644 --- a/patches/minecraft/net/minecraft/item/ItemStack.java.patch +++ b/patches/minecraft/net/minecraft/item/ItemStack.java.patch @@ -1,14 +1,35 @@ --- ../src-base/minecraft/net/minecraft/item/ItemStack.java +++ ../src-work/minecraft/net/minecraft/item/ItemStack.java -@@ -49,6 +49,7 @@ +@@ -35,7 +35,7 @@ + import net.minecraftforge.fml.relauncher.Side; + import net.minecraftforge.fml.relauncher.SideOnly; + +-public final class ItemStack ++public final class ItemStack implements net.minecraftforge.common.capabilities.ICapabilitySerializable + { + public static final DecimalFormat field_111284_a = new DecimalFormat("#.###"); + public int field_77994_a; +@@ -49,6 +49,10 @@ private Block field_179550_j; private boolean field_179551_k; + private net.minecraftforge.fml.common.registry.RegistryDelegate delegate; ++ private net.minecraftforge.common.capabilities.CapabilityDispatcher capabilities; ++ private NBTTagCompound capNBT; ++ public ItemStack(Block p_i1876_1_) { this((Block)p_i1876_1_, 1); -@@ -80,7 +81,7 @@ +@@ -74,13 +78,15 @@ + this((Item)p_i1880_1_, p_i1880_2_, 0); + } + +- public ItemStack(Item p_i1881_1_, int p_i1881_2_, int p_i1881_3_) ++ public ItemStack(Item p_i1881_1_, int p_i1881_2_, int p_i1881_3_) { this (p_i1881_1_, p_i1881_2_, p_i1881_3_, null); } ++ public ItemStack(Item p_i1881_1_, int p_i1881_2_, int p_i1881_3_, NBTTagCompound capNBT) + { ++ this.capNBT = capNBT; + this.field_179552_h = null; this.field_179553_i = false; this.field_179550_j = null; this.field_179551_k = false; @@ -17,7 +38,7 @@ this.field_77994_a = p_i1881_2_; this.field_77991_e = p_i1881_3_; -@@ -120,11 +121,12 @@ +@@ -120,11 +126,12 @@ public Item func_77973_b() { @@ -31,8 +52,18 @@ boolean flag = this.func_77973_b().func_180614_a(this, p_179546_1_, p_179546_2_, p_179546_3_, p_179546_4_, p_179546_5_, p_179546_6_, p_179546_7_); if (flag) -@@ -169,11 +171,11 @@ +@@ -162,19 +169,23 @@ + p_77955_1_.func_74782_a("tag", this.field_77990_d); + } + ++ if (this.capabilities != null) p_77955_1_.func_74782_a("ForgeCaps", this.capabilities.serializeNBT()); ++ + return p_77955_1_; + } + + public void func_77963_c(NBTTagCompound p_77963_1_) { ++ this.capNBT = p_77963_1_.func_74764_b("ForgeCaps") ? p_77963_1_.func_74775_l("ForgeCaps") : null; if (p_77963_1_.func_150297_b("id", 8)) { - this.field_151002_e = Item.func_111206_d(p_77963_1_.func_74779_i("id")); @@ -43,9 +74,11 @@ - this.field_151002_e = Item.func_150899_d(p_77963_1_.func_74765_d("id")); + this.func_150996_a(Item.func_150899_d(p_77963_1_.func_74765_d("id"))); } ++ this.capNBT = null; this.field_77994_a = p_77963_1_.func_74771_c("Count"); -@@ -197,7 +199,7 @@ + this.field_77991_e = p_77963_1_.func_74765_d("Damage"); +@@ -197,7 +208,7 @@ public int func_77976_d() { @@ -54,7 +87,7 @@ } public boolean func_77985_e() -@@ -207,7 +209,7 @@ +@@ -207,7 +218,7 @@ public boolean func_77984_f() { @@ -63,7 +96,7 @@ } public boolean func_77981_g() -@@ -217,32 +219,27 @@ +@@ -217,32 +228,27 @@ public boolean func_77951_h() { @@ -101,7 +134,7 @@ } public boolean func_96631_a(int p_96631_1_, Random p_96631_2_) -@@ -274,8 +271,8 @@ +@@ -274,8 +280,8 @@ } } @@ -112,7 +145,7 @@ } } -@@ -334,7 +331,7 @@ +@@ -334,7 +340,7 @@ public boolean func_150998_b(Block p_150998_1_) { @@ -121,7 +154,16 @@ } public boolean func_111282_a(EntityPlayer p_111282_1_, EntityLivingBase p_111282_2_) -@@ -742,6 +739,7 @@ +@@ -344,7 +350,7 @@ + + public ItemStack func_77946_l() + { +- ItemStack itemstack = new ItemStack(this.field_151002_e, this.field_77994_a, this.field_77991_e); ++ ItemStack itemstack = new ItemStack(this.field_151002_e, this.field_77994_a, this.field_77991_e, this.capabilities != null ? this.capabilities.serializeNBT() : null); + + if (this.field_77990_d != null) + { +@@ -742,6 +748,7 @@ } } @@ -129,7 +171,7 @@ return list; } -@@ -852,7 +850,7 @@ +@@ -852,7 +859,7 @@ } else { @@ -138,11 +180,48 @@ } return multimap; -@@ -860,6 +858,7 @@ +@@ -860,6 +867,17 @@ public void func_150996_a(Item p_150996_1_) { ++ if (p_150996_1_ == this.field_151002_e && field_151002_e != null && this.capabilities != null) //Item Didn't change but refreshed ++ { ++ net.minecraftforge.common.capabilities.ICapabilityProvider parent = field_151002_e.initCapabilities(this, this.capabilities.serializeNBT()); ++ this.capabilities = net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(field_151002_e, this, parent); ++ } ++ else if (p_150996_1_ != this.field_151002_e && p_150996_1_ != null) // Item Changed ++ { ++ net.minecraftforge.common.capabilities.ICapabilityProvider parent = p_150996_1_.initCapabilities(this, this.capNBT); ++ this.capabilities = net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(field_151002_e, this, parent); ++ } + this.delegate = p_150996_1_ != null ? p_150996_1_.delegate : null; this.field_151002_e = p_150996_1_; } +@@ -946,4 +964,26 @@ + return false; + } + } ++ ++ public boolean hasCapability(net.minecraftforge.common.capabilities.Capability capability, net.minecraft.util.EnumFacing facing) ++ { ++ return this.capabilities == null ? false : this.capabilities.hasCapability(capability, facing); ++ } ++ ++ public T getCapability(net.minecraftforge.common.capabilities.Capability capability, net.minecraft.util.EnumFacing facing) ++ { ++ return this.capabilities == null ? null : this.capabilities.getCapability(capability, facing); ++ } ++ ++ public void deserializeNBT(NBTTagCompound nbt) ++ { ++ this.func_77963_c(nbt); ++ } ++ ++ public NBTTagCompound serializeNBT() ++ { ++ NBTTagCompound ret = new NBTTagCompound(); ++ this.func_77955_b(ret); ++ return ret; ++ } + } diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntity.java.patch b/patches/minecraft/net/minecraft/tileentity/TileEntity.java.patch index ac86c28d6..8f49c7b04 100644 --- a/patches/minecraft/net/minecraft/tileentity/TileEntity.java.patch +++ b/patches/minecraft/net/minecraft/tileentity/TileEntity.java.patch @@ -1,22 +1,33 @@ --- ../src-base/minecraft/net/minecraft/tileentity/TileEntity.java +++ ../src-work/minecraft/net/minecraft/tileentity/TileEntity.java -@@ -59,6 +59,7 @@ +@@ -17,7 +17,7 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + +-public abstract class TileEntity ++public abstract class TileEntity implements net.minecraftforge.common.capabilities.ICapabilitySerializable + { + private static final Logger field_145852_a = LogManager.getLogger(); + private static Map < String, Class > field_145855_i = Maps. < String, Class > newHashMap(); +@@ -59,6 +59,8 @@ public void func_145839_a(NBTTagCompound p_145839_1_) { this.field_174879_c = new BlockPos(p_145839_1_.func_74762_e("x"), p_145839_1_.func_74762_e("y"), p_145839_1_.func_74762_e("z")); + if (p_145839_1_.func_74764_b("ForgeData")) this.customTileData = p_145839_1_.func_74775_l("ForgeData"); ++ if (this.capabilities != null && p_145839_1_.func_74764_b("ForgeCaps")) this.capabilities.deserializeNBT(p_145839_1_.func_74775_l("ForgeCaps")); } public void func_145841_b(NBTTagCompound p_145841_1_) -@@ -75,6 +76,7 @@ +@@ -75,6 +77,8 @@ p_145841_1_.func_74768_a("x", this.field_174879_c.func_177958_n()); p_145841_1_.func_74768_a("y", this.field_174879_c.func_177956_o()); p_145841_1_.func_74768_a("z", this.field_174879_c.func_177952_p()); + if (this.customTileData != null) p_145841_1_.func_74782_a("ForgeData", this.customTileData); ++ if (this.capabilities != null) p_145841_1_.func_74782_a("ForgeCaps", this.capabilities.serializeNBT()); } } -@@ -82,9 +84,10 @@ +@@ -82,9 +86,10 @@ { TileEntity tileentity = null; @@ -28,7 +39,7 @@ if (oclass != null) { -@@ -102,7 +105,17 @@ +@@ -102,7 +107,17 @@ } else { @@ -46,7 +57,7 @@ } return tileentity; -@@ -134,7 +147,6 @@ +@@ -134,7 +149,6 @@ } } @@ -54,7 +65,7 @@ public double func_145835_a(double p_145835_1_, double p_145835_3_, double p_145835_5_) { double d0 = (double)this.field_174879_c.func_177958_n() + 0.5D - p_145835_1_; -@@ -279,4 +291,146 @@ +@@ -279,4 +293,176 @@ func_145826_a(TileEntityFlowerPot.class, "FlowerPot"); func_145826_a(TileEntityBanner.class, "Banner"); } @@ -199,5 +210,35 @@ + public void onLoad() + { + // NOOP ++ } ++ ++ private net.minecraftforge.common.capabilities.CapabilityDispatcher capabilities; ++ public TileEntity() ++ { ++ capabilities = net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(this); ++ } ++ ++ public boolean hasCapability(net.minecraftforge.common.capabilities.Capability capability, net.minecraft.util.EnumFacing facing) ++ { ++ if (getCapability(capability, facing) != null) ++ return true; ++ return capabilities == null ? false : capabilities.hasCapability(capability, facing); ++ } ++ ++ public T getCapability(net.minecraftforge.common.capabilities.Capability capability, net.minecraft.util.EnumFacing facing) ++ { ++ return capabilities == null ? null : capabilities.getCapability(capability, facing); ++ } ++ ++ public void deserializeNBT(NBTTagCompound nbt) ++ { ++ this.func_145839_a(nbt); ++ } ++ ++ public NBTTagCompound serializeNBT() ++ { ++ NBTTagCompound ret = new NBTTagCompound(); ++ this.func_145841_b(ret); ++ return ret; + } } diff --git a/src/main/java/net/minecraftforge/common/capabilities/Capability.java b/src/main/java/net/minecraftforge/common/capabilities/Capability.java new file mode 100644 index 000000000..94627012a --- /dev/null +++ b/src/main/java/net/minecraftforge/common/capabilities/Capability.java @@ -0,0 +1,106 @@ +package net.minecraftforge.common.capabilities; + +import java.util.concurrent.Callable; + +import com.google.common.base.Throwables; + +import net.minecraft.nbt.NBTBase; +import net.minecraft.util.EnumFacing; + +/** + * This is the core holder object Capabilities. + * Each capability will have ONE instance of this class, + * and it will the the one passed into the ICapabilityProvider functions. + * + * The CapabilityManager is in charge of creating this class. + */ +public class Capability +{ + public static interface IStorage + { + /** + * Serialize the capability instance to a NBTTag. + * This allows for a central implementation of saving the data. + * + * It is important to note that it is up to the API defining + * the capability what requirements the 'instance' value must have. + * + * Due to the possibility of manipulating internal data, some + * implementations MAY require that the 'instance' be an instance + * of the 'default' implementation. + * + * Review the API docs for more info. + * + * @param capability The Capability being stored. + * @param instance An instance of that capabilities interface. + * @param side The side of the object the instance is associated with. + * @return a NBT holding the data. Null if no data needs to be stored. + */ + NBTBase writeNBT(Capability capability, T instance, EnumFacing side); + + /** + * Read the capability instance from a NBT tag. + * + * This allows for a central implementation of saving the data. + * + * It is important to note that it is up to the API defining + * the capability what requirements the 'instance' value must have. + * + * Due to the possibility of manipulating internal data, some + * implementations MAY require that the 'instance' be an instance + * of the 'default' implementation. + * + * Review the API docs for more info. * + * + * @param capability The Capability being stored. + * @param instance An instance of that capabilities interface. + * @param side The side of the object the instance is associated with. + * @param A NBT holding the data. Must not be null, as doesn't make sense to call this function with nothing to read... + */ + void readNBT(Capability capability, T instance, EnumFacing side, NBTBase nbt); + } + + /** + * @return The unique name of this capability, typically this is + * the fully qualified class name for the target interface. + */ + public String getName() { return name; } + /** + * @return An instance of the default storage handler. You can safely use this store your default implementation in NBT. + */ + public IStorage getStorage() { return storage; } + + /** + * A NEW instance of the default implementation. + * + * If it important to note that if you want to use the default storage + * you may be required to use this exact implementation. + * Refer to the owning API of the Capability in question. + * + * @return A NEW instance of the default implementation. + */ + public T getDefaultInstance() + { + try + { + return this.factory.call(); + } + catch (Exception e) + { + Throwables.propagate(e); + } + return null; + } + + // INTERNAL + private final String name; + private final IStorage storage; + private final Callable factory; + + Capability(String name, IStorage storage, Callable factory) + { + this.name = name; + this.storage = storage; + this.factory = factory; + } +} diff --git a/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java b/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java new file mode 100644 index 000000000..145378db2 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java @@ -0,0 +1,117 @@ +package net.minecraftforge.common.capabilities; + +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; + +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.util.INBTSerializable; + +/** + * A high-speed implementation of a capability delegator. + * This is used to wrap the results of the AttachCapabilitiesEvent. + * It is HIGHLY recommended that you DO NOT use this approach unless + * you MUST delegate to multiple providers instead just implement y + * our handlers using normal if statements. + * + * Internally the handlers are baked into arrays for fast iteration. + * The ResourceLocations will be used for the NBT Key when serializing. + */ +public final class CapabilityDispatcher implements INBTSerializable, ICapabilityProvider +{ + private ICapabilityProvider[] caps; + private INBTSerializable[] writers; + private String[] names; + + public CapabilityDispatcher(Map list) + { + this(list, null); + } + + @SuppressWarnings("unchecked") + public CapabilityDispatcher(Map list, ICapabilityProvider parent) + { + List lstCaps = Lists.newArrayList(); + List> lstWriters = Lists.newArrayList(); + List lstNames = Lists.newArrayList(); + + if (parent != null) // Parents go first! + { + lstCaps.add(parent); + if (parent instanceof INBTSerializable) + { + lstWriters.add((INBTSerializable)parent); + lstNames.add("Parent"); + } + } + + for (Map.Entry entry : list.entrySet()) + { + ICapabilityProvider prov = entry.getValue(); + lstCaps.add(prov); + if (prov instanceof INBTSerializable) + { + lstWriters.add((INBTSerializable)prov); + lstNames.add(entry.getKey().toString()); + } + } + + caps = lstCaps.toArray(new ICapabilityProvider[lstCaps.size()]); + writers = lstWriters.toArray(new INBTSerializable[lstWriters.size()]); + names = lstNames.toArray(new String[lstNames.size()]); + } + + @Override + public boolean hasCapability(Capability capability, EnumFacing facing) + { + for (ICapabilityProvider cap : caps) + { + if (cap.hasCapability(capability, facing)) + { + return true; + } + } + return false; + } + + @Override + public T getCapability(Capability capability, EnumFacing facing) + { + for (ICapabilityProvider cap : caps) + { + T ret = cap.getCapability(capability, facing); + if (ret != null) + { + return ret; + } + } + return null; + } + + @Override + public NBTTagCompound serializeNBT() + { + NBTTagCompound nbt = new NBTTagCompound(); + for (int x = 0; x < writers.length; x++) + { + nbt.setTag(names[x], writers[x].serializeNBT()); + } + return nbt; + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) + { + for (int x = 0; x < writers.length; x++) + { + if (nbt.hasKey(names[x])) + { + writers[x].deserializeNBT(nbt.getTag(names[x])); + } + } + } +} diff --git a/src/main/java/net/minecraftforge/common/capabilities/CapabilityInject.java b/src/main/java/net/minecraftforge/common/capabilities/CapabilityInject.java new file mode 100644 index 000000000..5671d5e91 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/capabilities/CapabilityInject.java @@ -0,0 +1,34 @@ +package net.minecraftforge.common.capabilities; + +import java.lang.annotation.*; + +/** + * When placed on a FIELD, the field will be set to an + * instance of Capability once that capability is registered. + * That field must be static and be able to hold a instance + * of 'Capability' + * + * Example: + * @CapabilityInject(IExampleCapability.class) + * private static final Capability TEST_CAP = null; + * + * When placed on a METHOD, the method will be invoked once the + * capability is registered. This allows you to have a 'enable features' + * callback. It MUST have one parameter of type 'Capability; + * + * Example: + * @CapabilityInject(IExampleCapability.class) + * private static void capRegistered(Capability cap) {} + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +public @interface CapabilityInject +{ + /** + * The capability interface to listen for registration. + * Note: + * When reading annotations, DO NOT call this function as it will cause a hard dependency on the class. + */ + Class value(); +} diff --git a/src/main/java/net/minecraftforge/common/capabilities/CapabilityManager.java b/src/main/java/net/minecraftforge/common/capabilities/CapabilityManager.java new file mode 100644 index 000000000..5e319e173 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/capabilities/CapabilityManager.java @@ -0,0 +1,170 @@ +package net.minecraftforge.common.capabilities; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.concurrent.Callable; + +import org.apache.logging.log4j.Level; +import org.objectweb.asm.Type; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import net.minecraftforge.common.util.EnumHelper; +import net.minecraftforge.fml.common.FMLLog; +import net.minecraftforge.fml.common.discovery.ASMDataTable; + +public enum CapabilityManager +{ + INSTANCE; + + /** + * Registers a capability to be consumed by others. + * APIs who define the capability should call this. + * To retrieve the Capability instance, use the @CapabilityInject annotation. + * + * @param type The Interface to be registered + * @param storage A default implementation of the storage handler. + * @param implementation A default implementation of the interface. + */ + public void register(Class type, Capability.IStorage storage, final Class implementation) + { + Preconditions.checkArgument(implementation != null, "Attempted to register a capability with no default implementation"); + register(type, storage, new Callable() + { + @Override + public T call() throws Exception + { + try { + return (T)implementation.newInstance(); + } catch (InstantiationException e) { + Throwables.propagate(e); + } catch (IllegalAccessException e) { + Throwables.propagate(e); + } + return null; + } + }); + } + + /** + * Registers a capability to be consumed by others. + * APIs who define the capability should call this. + * To retrieve the Capability instance, use the @CapabilityInject annotation. + * + * @param type The Interface to be registered + * @param storage A default implementation of the storage handler. + * @param factor A Factory that will produce new instances of the default implementation. + */ + public void register(Class type, Capability.IStorage storage, Callable factory) + { + Preconditions.checkArgument(type != null, "Attempted to register a capability with invalid type"); + Preconditions.checkArgument(storage != null, "Attempted to register a capability with no storage implementation"); + Preconditions.checkArgument(factory != null, "Attempted to register a capability with no default implementation factory"); + String realName = type.getName().intern(); + Preconditions.checkState(!providers.containsKey(realName), "Can not register a capability implementation multiple times: %s", realName); + + Capability cap = new Capability(realName, storage, factory); + providers.put(realName, cap); + + List, Object>> list = callbacks.get(realName); + if (list != null) + { + for (Function, Object> func : list) + { + func.apply(cap); + } + } + } + + // INTERNAL + private IdentityHashMap> providers = Maps.newIdentityHashMap(); + private IdentityHashMap, Object>>> callbacks = Maps.newIdentityHashMap(); + public void injectCapabilities(ASMDataTable data) + { + for (ASMDataTable.ASMData entry : data.getAll(CapabilityInject.class.getName())) + { + final String targetClass = entry.getClassName(); + final String targetName = entry.getObjectName(); + Type type = (Type)entry.getAnnotationInfo().get("value"); + if (type == null) + { + FMLLog.log(Level.WARN, "Unable to inject capability at %s.%s (Invalid Annotation)", targetClass, targetName); + } + final String capabilityName = type.getInternalName().replace('/', '.').intern(); + + List, Object>> list = callbacks.get(capabilityName); + if (list == null) + { + list = Lists.newArrayList(); + callbacks.put(capabilityName, list); + } + + if (entry.getObjectName().indexOf('(') > 0) + { + list.add(new Function, Object>() + { + @Override + public Object apply(Capability input) + { + try + { + for (Method mtd : Class.forName(targetClass).getDeclaredMethods()) + { + if (targetName.equals(mtd.getName() + Type.getMethodDescriptor(mtd))) + { + if ((mtd.getModifiers() & Modifier.STATIC) != Modifier.STATIC) + { + FMLLog.log(Level.WARN, "Unable to inject capability %s at %s.%s (Non-Static)", capabilityName, targetClass, targetName); + return null; + } + + mtd.setAccessible(true); + mtd.invoke(null, input); + return null; + } + } + FMLLog.log(Level.WARN, "Unable to inject capability %s at %s.%s (Method Not Found)", capabilityName, targetClass, targetName); + } + catch (Exception e) + { + FMLLog.log(Level.WARN, e, "Unable to inject capability %s at %s.%s", capabilityName, targetClass, targetName); + } + return null; + } + }); + } + else + { + list.add(new Function, Object>() + { + @Override + public Object apply(Capability input) + { + try + { + Field field = Class.forName(targetClass).getDeclaredField(targetName); + if ((field.getModifiers() & Modifier.STATIC) != Modifier.STATIC) + { + FMLLog.log(Level.WARN, "Unable to inject capability %s at %s.%s (Non-Static)", capabilityName, targetClass, targetName); + return null; + } + EnumHelper.setFailsafeFieldValue(field, null, input); + } + catch (Exception e) + { + FMLLog.log(Level.WARN, e, "Unable to inject capability %s at %s.%s", capabilityName, targetClass, targetName); + } + return null; + } + }); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java b/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java new file mode 100644 index 000000000..13f79d9a2 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java @@ -0,0 +1,35 @@ +package net.minecraftforge.common.capabilities; + +import net.minecraft.util.EnumFacing; + +public interface ICapabilityProvider +{ + /** + * Determines if this object has support for the capability in question on the specific side. + * The return value of this MIGHT change during runtime if this object gains or looses support + * for a capability. + * + * Example: + * A Pipe getting a cover placed on one side causing it loose the Inventory attachment function for that side. + * + * This is a light weight version of getCapability, intended for metadata uses. + * + * @param capability The capability to check + * @param facing The Side to check from: + * CAN BE NULL. Null is defined to represent 'internal' or 'self' + * @return True if this object supports the capability. + */ + boolean hasCapability(Capability capability, EnumFacing facing); + + /** + * Retrieves the handler for the capability requested on the specific side. + * The return value CAN be null if the object does not support the capability. + * The return value CAN be the same for multiple faces. + * + * @param capability The capability to check + * @param facing The Side to check from: + * CAN BE NULL. Null is defined to represent 'internal' or 'self' + * @return True if this object supports the capability. + */ + T getCapability(Capability capability, EnumFacing facing); +} diff --git a/src/main/java/net/minecraftforge/common/capabilities/ICapabilitySerializable.java b/src/main/java/net/minecraftforge/common/capabilities/ICapabilitySerializable.java new file mode 100644 index 000000000..82169e2c4 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/capabilities/ICapabilitySerializable.java @@ -0,0 +1,7 @@ +package net.minecraftforge.common.capabilities; + +import net.minecraft.nbt.NBTBase; +import net.minecraftforge.common.util.INBTSerializable; + +//Just a mix of the two, useful in patches to lower the size. +public interface ICapabilitySerializable extends ICapabilityProvider, INBTSerializable{} diff --git a/src/main/java/net/minecraftforge/common/util/INBTSerializable.java b/src/main/java/net/minecraftforge/common/util/INBTSerializable.java new file mode 100644 index 000000000..317c20d52 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/util/INBTSerializable.java @@ -0,0 +1,13 @@ +package net.minecraftforge.common.util; + +import net.minecraft.nbt.NBTBase; + +/** + * An interface designed to unify various things in the Minecraft + * code base that can be serialized to and from a NBT tag. + */ +public interface INBTSerializable +{ + T serializeNBT(); + void deserializeNBT(T nbt); +} diff --git a/src/main/java/net/minecraftforge/event/AttachCapabilitiesEvent.java b/src/main/java/net/minecraftforge/event/AttachCapabilitiesEvent.java new file mode 100644 index 000000000..a05b6780c --- /dev/null +++ b/src/main/java/net/minecraftforge/event/AttachCapabilitiesEvent.java @@ -0,0 +1,117 @@ +package net.minecraftforge.event; + +import java.util.Collections; +import java.util.Map; + +import com.google.common.collect.Maps; + +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fml.common.eventhandler.Event; + +/** + * Fired whenever an object with Capabilities support {currently TileEntity/Item/Entity) + * is created. Allowing for the attachment of arbitrary capability providers. + * + * Please note that as this is fired for ALL object creations efficient code is recommended. + * And if possible use one of the sub-classes to filter your intended objects. + */ +public class AttachCapabilitiesEvent extends Event +{ + private final Object obj; + private final Map caps = Maps.newLinkedHashMap(); + private final Map view = Collections.unmodifiableMap(caps); + public AttachCapabilitiesEvent(Object obj) + { + this.obj = obj; + } + + /** + * Retrieves the object that is being created, Not much state is set. + */ + public Object getObject() + { + return this.obj; + } + + /** + * Adds a capability to be attached to this object. + * Keys MUST be unique, it is suggested that you set the domain to your mod ID. + * If the capability is an instance of INBTSerializeable, this key will be used when serializing this capability. + * + * @param key The name of owner of this capability provider. + * @param cap The capability provider + */ + public void addCapability(ResourceLocation key, ICapabilityProvider cap) + { + if (caps.containsKey(key)) + throw new IllegalStateException("Duplicate Capability Key: " + key + " " + cap); + this.caps.put(key, cap); + } + + /** + * A unmodifiable view of the capabilities that will be attached to this object. + */ + public Map getCapabilities() + { + return view; + } + + + /** + * A version of the parent event which is only fired for Tile Entities. + */ + public static class TileEntity extends AttachCapabilitiesEvent + { + private final net.minecraft.tileentity.TileEntity te; + public TileEntity(net.minecraft.tileentity.TileEntity te) + { + super(te); + this.te = te; + } + public net.minecraft.tileentity.TileEntity getTileEntity() + { + return this.te; + } + } + + /** + * A version of the parent event which is only fired for Entities. + */ + public static class Entity extends AttachCapabilitiesEvent + { + private final net.minecraft.entity.Entity entity; + public Entity(net.minecraft.entity.Entity entity) + { + super(entity); + this.entity = entity; + } + public net.minecraft.entity.Entity getEntity() + { + return this.entity; + } + } + + /** + * A version of the parent event which is only fired for ItemStacks. + */ + public static class Item extends AttachCapabilitiesEvent + { + private final net.minecraft.item.ItemStack stack; + private final net.minecraft.item.Item item; + public Item(net.minecraft.item.Item item, net.minecraft.item.ItemStack stack) + { + super(item); + this.item = item; + this.stack = stack; + } + public net.minecraft.item.Item getItem() + { + return this.item; + } + public net.minecraft.item.ItemStack getItemStack() + { + return this.stack; + } + } +} diff --git a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java index b7e53a40f..108408914 100644 --- a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java +++ b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java @@ -16,7 +16,9 @@ import net.minecraft.entity.monster.EntityZombie; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer.EnumStatus; import net.minecraft.init.Blocks; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.BlockPos; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumFacing; @@ -33,6 +35,8 @@ import net.minecraftforge.client.event.ClientChatReceivedEvent; import net.minecraftforge.client.event.RenderBlockOverlayEvent; import net.minecraftforge.client.event.RenderBlockOverlayEvent.OverlayType; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.capabilities.CapabilityDispatcher; +import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.event.brewing.PotionBrewEvent; import net.minecraftforge.event.entity.EntityEvent; @@ -91,7 +95,7 @@ public class ForgeEventFactory MinecraftForge.EVENT_BUS.post(event); return event; } - + public static NeighborNotifyEvent onNeighborNotify(World world, BlockPos pos, IBlockState state, EnumSet notifiedSides) { NeighborNotifyEvent event = new NeighborNotifyEvent(world, pos, state, notifiedSides); @@ -142,7 +146,7 @@ public class ForgeEventFactory MinecraftForge.EVENT_BUS.post(event); return event.getResult(); } - + public static int getExperienceDrop(EntityLivingBase entity, EntityPlayer attackingPlayer, int originalExperience) { LivingExperienceDropEvent event = new LivingExperienceDropEvent(entity, attackingPlayer, originalExperience); @@ -323,7 +327,7 @@ public class ForgeEventFactory MinecraftForge.EVENT_BUS.post(event); return event.name; } - + public static PlaySoundAtEntityEvent onPlaySoundAtEntity(Entity entity, String name, float volume, float pitch) { PlaySoundAtEntityEvent event = new PlaySoundAtEntityEvent(entity, name, volume, pitch); @@ -362,18 +366,18 @@ public class ForgeEventFactory { return !MinecraftForge.EVENT_BUS.post(new EntityInteractEvent(player, entity)); } - + public static boolean canMountEntity(Entity entityMounting, Entity entityBeingMounted, boolean isMounting) { boolean isCanceled = MinecraftForge.EVENT_BUS.post(new EntityMountEvent(entityMounting, entityBeingMounted, entityMounting.worldObj, isMounting)); - + if(isCanceled) { entityMounting.setPositionAndRotation(entityMounting.posX, entityMounting.posY, entityMounting.posZ, entityMounting.prevRotationYaw, entityMounting.prevRotationPitch); return false; } - else - return true; + else + return true; } public static EnumStatus onPlayerSleepInBed(EntityPlayer player, BlockPos pos) @@ -392,7 +396,7 @@ public class ForgeEventFactory { MinecraftForge.EVENT_BUS.post(new PlayerFlyableFallEvent(player, distance, multiplier)); } - + public static boolean onPlayerSpawnSet(EntityPlayer player, BlockPos pos, boolean forced) { return MinecraftForge.EVENT_BUS.post(new PlayerSetSpawnEvent(player, pos, forced)); } @@ -460,20 +464,40 @@ public class ForgeEventFactory { MinecraftForge.EVENT_BUS.post(new PotionBrewEvent.Post(brewingItemStacks)); } - + public static boolean renderFireOverlay(EntityPlayer player, float renderPartialTicks) { return renderBlockOverlay(player, renderPartialTicks, OverlayType.FIRE, Blocks.fire.getDefaultState(), new BlockPos(player)); } - + public static boolean renderWaterOverlay(EntityPlayer player, float renderPartialTicks) { return renderBlockOverlay(player, renderPartialTicks, OverlayType.WATER, Blocks.water.getDefaultState(), new BlockPos(player)); } - + public static boolean renderBlockOverlay(EntityPlayer player, float renderPartialTicks, OverlayType type, IBlockState block, BlockPos pos) { return MinecraftForge.EVENT_BUS.post(new RenderBlockOverlayEvent(player, renderPartialTicks, type, block, pos)); } + public static CapabilityDispatcher gatherCapabilities(TileEntity tileEntity) + { + return gatherCapabilities(new AttachCapabilitiesEvent.TileEntity(tileEntity), null); + } + + public static CapabilityDispatcher gatherCapabilities(Entity entity) + { + return gatherCapabilities(new AttachCapabilitiesEvent.Entity(entity), null); + } + + public static CapabilityDispatcher gatherCapabilities(Item item, ItemStack stack, ICapabilityProvider parent) + { + return gatherCapabilities(new AttachCapabilitiesEvent.Item(item, stack), parent); + } + + private static CapabilityDispatcher gatherCapabilities(AttachCapabilitiesEvent event, ICapabilityProvider parent) + { + MinecraftForge.EVENT_BUS.post(event); + return event.getCapabilities().size() > 0 ? new CapabilityDispatcher(event.getCapabilities(), parent) : null; + } } diff --git a/src/main/java/net/minecraftforge/fml/common/Loader.java b/src/main/java/net/minecraftforge/fml/common/Loader.java index 4c739b42e..51e3e153c 100644 --- a/src/main/java/net/minecraftforge/fml/common/Loader.java +++ b/src/main/java/net/minecraftforge/fml/common/Loader.java @@ -27,6 +27,7 @@ import java.util.Properties; import java.util.Set; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.capabilities.CapabilityManager; import net.minecraftforge.fml.common.LoaderState.ModState; import net.minecraftforge.fml.common.ModContainer.Disableable; import net.minecraftforge.fml.common.ProgressManager.ProgressBar; @@ -551,6 +552,7 @@ public class Loader } ObjectHolderRegistry.INSTANCE.findObjectHolders(discoverer.getASMTable()); ItemStackHolderInjector.INSTANCE.findHolders(discoverer.getASMTable()); + CapabilityManager.INSTANCE.injectCapabilities(discoverer.getASMTable()); modController.distributeStateMessage(LoaderState.PREINITIALIZATION, discoverer.getASMTable(), canonicalConfigDir); ObjectHolderRegistry.INSTANCE.applyObjectHolders(); ItemStackHolderInjector.INSTANCE.inject(); diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc index fa4d271f6..26194f107 100644 --- a/src/main/resources/forge.exc +++ b/src/main/resources/forge.exc @@ -42,3 +42,4 @@ net/minecraft/server/management/ServerConfigurationManager.initializeConnectionT net/minecraft/item/ItemMonsterPlacer.spawnCreature(Lnet/minecraft/world/World;Ljava/lang/String;DDD)Lnet/minecraft/entity/Entity;=|p_77840_0_,name,p_77840_2_,p_77840_4_,p_77840_6_ net/minecraft/stats/StatList.mergeStatBases([Lnet/minecraft/stats/StatBase;Lnet/minecraft/block/Block;Lnet/minecraft/block/Block;Z)V=|p_151180_0_,p_151180_1_,p_151180_2_,useItemIds +net/minecraft/item/ItemStack.(Lnet/minecraft/item/Item;IILnet/minecraft/nbt/NBTTagCompound;)V=|p_i1881_1_,p_i1881_2_,p_i1881_3_,capNBT diff --git a/src/test/java/net/minecraftforge/test/TestCapabilityMod.java b/src/test/java/net/minecraftforge/test/TestCapabilityMod.java new file mode 100644 index 000000000..f0ce8844f --- /dev/null +++ b/src/test/java/net/minecraftforge/test/TestCapabilityMod.java @@ -0,0 +1,140 @@ +package net.minecraftforge.test; + +import net.minecraft.init.Items; +import net.minecraft.nbt.NBTBase; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.Capability.IStorage; +import net.minecraftforge.common.capabilities.CapabilityInject; +import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +@Mod(modid="forge.testcapmod",version="1.0") +public class TestCapabilityMod +{ + // A Holder/Marker for if this capability is installed. + // MUST be Static final doesn't matter but is suggested to prevent + // you from overriding it elsewhere. + // As Annotations and generic's are erased/unload at runtime this + // does NOT create a hard dependency on the class. + @CapabilityInject(IExampleCapability.class) + private static final Capability TEST_CAP = null; + + @Mod.EventHandler + public void preinit(FMLPreInitializationEvent evt) + { + // If you are a API, provide register your capability ASAP. + // You MUST supply a default save handler and a default implementation + // If you are a CONSUMER of the capability DO NOT register it. Only APIs should. + CapabilityManager.INSTANCE.register(IExampleCapability.class, new Storage(), DefaultImpl.class); + MinecraftForge.EVENT_BUS.register(this); + } + + @SubscribeEvent + public void onInteract(PlayerInteractEvent event) + { + if (event.action != PlayerInteractEvent.Action.LEFT_CLICK_BLOCK) return; + if (event.entityPlayer.getHeldItem() == null) return; + if (event.entityPlayer.getHeldItem().getItem() != Items.stick) return; + + // This is just a example of how to interact with the TE, note the strong type binding that getCapability has + TileEntity te = event.world.getTileEntity(event.pos); + if (te != null && te.hasCapability(TEST_CAP, event.face)) + { + event.setCanceled(true); + IExampleCapability inv = te.getCapability(TEST_CAP, event.face); + System.out.println("Hi I'm a " + inv.getOwnerType()); + } + } + + + // Example of having this annotation on a method, this will be called when the capability is present. + // You could do something like register event handlers to attach these capabilities to objects, or + // setup your factory, who knows. Just figured i'd give you the power. + @CapabilityInject(IExampleCapability.class) + private static void capRegistered(Capability cap) + { + System.out.println("IExampleCapability was registered wheeeeee!"); + } + + // An example of how to attach a capability to an arbitrary Tile entity. + // Note: Doing this IS NOT recommended for normal implementations. + // If you control the TE it is HIGHLY recommend that you implement a fast + // version of the has/getCapability functions yourself. So you have control + // over everything yours being called first. + @SubscribeEvent + public void onTELoad(AttachCapabilitiesEvent.TileEntity event) + { + // Having the Provider implement the cap is not recomended as this creates a hard dep on the cap interface. + // And doesnt allow for sidedness. + // But as this is a example and we dont care about that here we go. + class Provider implements ICapabilityProvider, IExampleCapability + { + private TileEntity te; + + Provider(TileEntity te) + { + this.te = te; + } + @Override + public boolean hasCapability(Capability capability, EnumFacing facing) + { + return TEST_CAP != null && capability == TEST_CAP; + } + @SuppressWarnings("unchecked") //There isnt anything sane we can do about this. + @Override + public T getCapability(Capability capability, EnumFacing facing) + { + if (TEST_CAP != null && capability == TEST_CAP) return (T)this; + return null; + } + @Override + public String getOwnerType() { + return te.getClass().getName(); + } + } + + //Attach it! The resource location MUST be unique it's recomneded that you tag it with your modid and what the cap is. + event.addCapability(new ResourceLocation("TestCapabilityMod:DummyCap"), new Provider(event.getTileEntity())); + } + + // Capabilities SHOULD be interfaces, NOT concrete classes, this allows for + // the most flexibility for the implementors. + public static interface IExampleCapability + { + String getOwnerType(); + } + + // Storage implementations are required, tho there is some flexibility here. + // If you are the API provider you can also say that in order to use the default storage + // the consumer MUST use the default impl, to allow you to access innards. + // This is just a contract you will have to stipulate in the documentation of your cap. + public static class Storage implements IStorage + { + @Override + public NBTBase writeNBT(Capability capability, IExampleCapability instance, EnumFacing side) { + return null; + } + + @Override + public void readNBT(Capability capability, IExampleCapability instance, EnumFacing side, NBTBase nbt) { + } + } + + // You MUST also supply a default implementation. + // This is to make life easier on consumers. + public static class DefaultImpl implements IExampleCapability { + @Override + public String getOwnerType(){ + return "Default Implementation!"; + } + } +} \ No newline at end of file