7,629 bytes added
, 19:36, 4 January 2021
Capabilities allow exposing features in a dynamic and flexible way, without having to resort to directly implementing many interfaces.
In general terms, each capability provides a feature in the form of an interface, alongside with a default implementation which can be requested, and a storage handler for at least this default implementation. The storage handler can support other implementations, but this is up to the capability implementor, so look it up in their documentation before trying to use the default storage with non-default implementations.
Forge adds capability support to <code>TileEntities</code>, <code>Entities</code>, <code>ItemStack</code>s, <code>World</code>s and <code>Chunk</code>s, which can be exposed either by attaching them through an event or by overriding the capability methods in your own implementations of the objects. This will be explained in more detail in the following sections.
== Forge-provided Capabilities ==
Forge provides three capabilities: <code>IItemHandler</code>, <code>IFluidHandler</code> and <code>IEnergyStorage</code>.
<code>IItemHandler</code> exposes an interface for handling inventory slots. It can be applied to <code>TileEntities</code> (chests, machines, etc.), <code>Entities</code> (extra player slots, mob/creature inventories/bags), or <code>ItemStacks</code> (portable backpacks and such). It replaces the old <code>IInventory</code> and <code>ISidedInventory</code> with an automation-friendly system.
<code>IFluidHandler</code> exposes an interface for handling fluid inventories. It can also be applied to <code>TileEntities</code> <code>Entities</code>, or <code>ItemStacks</code>. It replaces the old <code>IFluidHandler</code> with a more consistent and automation-friendly system.
<code>IEnergyStorage</code> exposes an interface for handling energy containers. It can be applied to <code>TileEntities</code>, <code>Entities</code> or <code>ItemStacks</code>. It is based on the RedstoneFlux API by TeamCoFH.
== Using an Existing Capability ==
As mentioned earlier, <code>TileEntities</code>, <code>Entities</code>, and <code>ItemStacks</code> implement the capability provider feature, through the <code>ICapabilityProvider</code> interface. This interface adds the method <code>getCapability</code>, which can be used to query the capabilities present in the objects.
In order to obtain a capability, you will need to refer it by its unique instance. In the case of the <code>IItemHandler</code>, this capability is primarily stored in <code><nowiki>CapabilityItemHandler#ITEM_HANDLER_CAPABILITY</nowiki></code>, but it is possible to get other instance references by using the <code>@CapabilityInject</code> annotation.
<syntaxhighlight lang="java">
@CapabilityInject(IItemHandler.class)
static Capability<IItemHandler> ITEM_HANDLER_CAPABILITY = null;
</syntaxhighlight>
This annotation can be applied to fields and methods. When applied to a field, it will assign the instance of the capability (the same one gets assigned to all fields) upon registration of the capability, and left to the existing value (<code>null</code>), if the capability was never registered. Because local static field accesses are fast, it is a good idea to keep your own local copy of the reference for objects that work with capabilities. This annotation can also be used on a method, in order to get notified when a capability is registered, so that certain features can be enabled conditionally.
Both the <code>getCapability</code> methods have a second parameter, of type <code>Direction</code>, which can be used in the to request the specific instance for that one face. If passed <code>null</code>, it can be assumed that the request comes either from within the block, or from some place where the side has no meaning, such as a different dimension. In this case a general capability instance that does not care about sides will be requested instead. The return type of <code>getCapability</code> will correspond to the type declared in the capability passed to the method. For the item handler capability, this is indeed <code>IItemHandler</code>.
==Exposing a Capability==
In order to expose a capability, you will first need an instance of the underlying capability type. Note that you should assign a separate instance to each object that keeps the capability, since the capability will most probably be tied to the containing object.
There’s two ways to obtain such an instance, through the <code>Capability</code> itself, or by explicitly instantiating an implementation of it. The first method is designed to use a default implementation via <code>Capability#getDefaultInstance</code>, if those default values are useful for you. In the case of the item handler capability, the default implementation will expose a single slot inventory, which is most probably not what you want.
The second method can be used to provide custom implementations. In the case of <code>IItemHandler</code>, the default implementation uses the <code>ItemStackHandler</code> class, which has an optional argument in the constructor, to specify a number of slots. However, relying on the existence of these default implementations should be avoided, as the purpose of the capability system is to prevent loading errors in contexts where the capability is not present, so instantiation should be protected behind a check testing if the capability has been registered (see the remarks about <code>@CapabilityInject</code> in the previous section).
Once you have your own instance of the capability interface, you will want to notify users of the capability system that you expose this capability and provide a holder of the instance. This is done by overriding the <code>getCapability</code> method, and comparing the instance with the capability you are exposing. If your machine has different slots based on which side is being queried, you can test this with the <code>side</code> parameter. For <code>Entities</code> and <code>ItemStack</code>s, this parameter can be ignored, but it is still possible to have side as a context, such as different armor slots on a player (top side => head slot?), or about the surrounding blocks in the inventory (west => slot on the left?). Don’t forget to fall back to <code>super</code>, otherwise the attached capabilities will stop working. Make sure to invalidate the holder of the instance at the end of the provider's lifecycle.
<syntaxhighlight lang="java">
// Somewhere in your TileEntity subclass
LazyOptional<IItemHandler> inventoryHandlerLazyOptional;
// After initializing inventoryHandler
inventoryHandlerLazyOptional = LazyOptional.of(() -> inventoryHandler);
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
return inventoryHandlerLazyOptional.cast();
}
return super.getCapability(cap, side);
}
@Override
protected void invalidateCaps() {
super.invalidateCaps();
inventoryHandlerLazyOptional.invalidate();
}
</syntaxhighlight>
<code>Item</code>s are a special case since their capability providers are stored on an <code>ItemStack</code>. Instead, a provider should be attached through <code>Item#initCapabilities</code> when applicable. This should hold your capabilities for the lifecycle of the stack.
It is strongly suggested that direct checks in code are used to test for capabilities instead of attempting to rely on maps or other data structures, since capability tests can be done by many objects every tick, and they need to be as fast as possible in order to avoid slowing down the game.