New Capability system allowing for more manageable world object features.

When combined with @Optional this should address all issues of soft dependancy on mods/apis.
This also addresses the issue of dynamic functionality in TileEntities/Entities.

Current capability providers: TileEntity, Entity, ItemStack

Also added INBTSerializeable, a generic interface for game objects that can be written to/from NBT tags.

Vanilla capabilities will be coming soon, mostly on request and review.
So start requesting capabiliteis on vanilla/Forge features.
This commit is contained in:
LexManos 2016-01-11 16:04:22 -08:00
parent 4672f7bab0
commit 17db34ae31
16 changed files with 988 additions and 44 deletions

View File

@ -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<NBTTagCompound>
{
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<EntityItem> capturedDrops = new java.util.ArrayList<EntityItem>();
+ private UUID persistentID;
+
+ @Deprecated //ToDo: Move to Capabilities?
+ protected java.util.HashMap<String, net.minecraftforge.common.IExtendedEntityProperties> extendedProperties = new java.util.HashMap<String, net.minecraftforge.common.IExtendedEntityProperties>();
+ 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> T getCapability(net.minecraftforge.common.capabilities.Capability<T> 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 =====================================*/
}

View File

@ -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<ItemStack, String>()
@@ -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;
}

View File

@ -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<NBTTagCompound>
{
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<Item> 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> T getCapability(net.minecraftforge.common.capabilities.Capability<T> 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;
+ }
}

View File

@ -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<NBTTagCompound>
{
private static final Logger field_145852_a = LogManager.getLogger();
private static Map < String, Class <? extends TileEntity >> field_145855_i = Maps. < String, Class <? extends TileEntity >> 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> T getCapability(net.minecraftforge.common.capabilities.Capability<T> 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;
+ }
}

View File

@ -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<T>
{
public static interface IStorage<T>
{
/**
* 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<T> 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<T> 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<T> 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<T> storage;
private final Callable<? extends T> factory;
Capability(String name, IStorage<T> storage, Callable<? extends T> factory)
{
this.name = name;
this.storage = storage;
this.factory = factory;
}
}

View File

@ -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<NBTTagCompound>, ICapabilityProvider
{
private ICapabilityProvider[] caps;
private INBTSerializable<NBTBase>[] writers;
private String[] names;
public CapabilityDispatcher(Map<ResourceLocation, ICapabilityProvider> list)
{
this(list, null);
}
@SuppressWarnings("unchecked")
public CapabilityDispatcher(Map<ResourceLocation, ICapabilityProvider> list, ICapabilityProvider parent)
{
List<ICapabilityProvider> lstCaps = Lists.newArrayList();
List<INBTSerializable<NBTBase>> lstWriters = Lists.newArrayList();
List<String> lstNames = Lists.newArrayList();
if (parent != null) // Parents go first!
{
lstCaps.add(parent);
if (parent instanceof INBTSerializable)
{
lstWriters.add((INBTSerializable<NBTBase>)parent);
lstNames.add("Parent");
}
}
for (Map.Entry<ResourceLocation, ICapabilityProvider> entry : list.entrySet())
{
ICapabilityProvider prov = entry.getValue();
lstCaps.add(prov);
if (prov instanceof INBTSerializable)
{
lstWriters.add((INBTSerializable<NBTBase>)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> T getCapability(Capability<T> 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]));
}
}
}
}

View File

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

View File

@ -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 <T> void register(Class<T> type, Capability.IStorage<T> storage, final Class<? extends T> implementation)
{
Preconditions.checkArgument(implementation != null, "Attempted to register a capability with no default implementation");
register(type, storage, new Callable<T>()
{
@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 <T> void register(Class<T> type, Capability.IStorage<T> storage, Callable<? extends T> 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<T> cap = new Capability<T>(realName, storage, factory);
providers.put(realName, cap);
List<Function<Capability<?>, Object>> list = callbacks.get(realName);
if (list != null)
{
for (Function<Capability<?>, Object> func : list)
{
func.apply(cap);
}
}
}
// INTERNAL
private IdentityHashMap<String, Capability<?>> providers = Maps.newIdentityHashMap();
private IdentityHashMap<String, List<Function<Capability<?>, 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<Function<Capability<?>, Object>> list = callbacks.get(capabilityName);
if (list == null)
{
list = Lists.newArrayList();
callbacks.put(capabilityName, list);
}
if (entry.getObjectName().indexOf('(') > 0)
{
list.add(new Function<Capability<?>, 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<Capability<?>, 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;
}
});
}
}
}
}

View File

@ -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> T getCapability(Capability<T> capability, EnumFacing facing);
}

View File

@ -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<T extends NBTBase> extends ICapabilityProvider, INBTSerializable<T>{}

View File

@ -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 extends NBTBase>
{
T serializeNBT();
void deserializeNBT(T nbt);
}

View File

@ -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<ResourceLocation, ICapabilityProvider> caps = Maps.newLinkedHashMap();
private final Map<ResourceLocation, ICapabilityProvider> 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<ResourceLocation, ICapabilityProvider> 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;
}
}
}

View File

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

View File

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

View File

@ -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.<init>(Lnet/minecraft/item/Item;IILnet/minecraft/nbt/NBTTagCompound;)V=|p_i1881_1_,p_i1881_2_,p_i1881_3_,capNBT

View File

@ -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<IExampleCapability> 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<IExampleCapability> 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> T getCapability(Capability<T> 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<IExampleCapability>
{
@Override
public NBTBase writeNBT(Capability<IExampleCapability> capability, IExampleCapability instance, EnumFacing side) {
return null;
}
@Override
public void readNBT(Capability<IExampleCapability> 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!";
}
}
}