A capability can be thought of much like a trait. It allows exposing features in a dynamic and flexible way, without having to resort to directly implementing many interfaces.
In general terms, a capability is a marker object (generally a singleton) which provides an implementation of the capability, specific to the parent object. For instance, in the case of a chest with an inventory, we have:
-
The parent object - this is the Tile Entity, ItemStack, Entity, or whatever it may be that "owns" the capability. In this case, it is the chest Tile Entity.
-
The capability - the "idea" of an inventory can be considered the capability. A capability is not any one implementation of an inventory, but more of a contract stating whether the parent object has the ability (or capability) to be treated as an inventory.
-
The implementation - which is the actual code that handles the inventory logic. In the past, this would have simply been the Tile Entity which had methods inside it for this purpose. Now, a special purpose object handles this logic.
Capabilities may provide a default implementation, but it is not required. Additionally, capabilities can define 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 TileEntities, Entities, and ItemStacks, 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.
You may be asking yourself "Why is this system better?" While it's true that capabilities are more complex than simple interfaces, they provide a few key benefits that make them an improvement.
Capabilities allow you to separate implementation of a feature from game objects. For instance, inventory code is now separated from tile entities. This allows you to better reuse code across different tile entities, instead of reimplementing inventory logic.
Gone are the days of @Optional
, with capabilities it is easier than ever to support other mods. With the use of @CapabilityInject
, it is easy to tell if a mod is present or not. If not, the capability simply will not exist! This results in a query for a null
capability, which will simply return false
from hasCapability
.
Hopefully it is obvious why capabilities are now the preferred option for many systems in both forge and other mods.
Forge currently provides three capabilities:
-
IItemHandler
- Used for handling inventory slots. This is not only used for TileEntities, but Entities (extra player slots, mob/creature inventories/bags) and ItemStacks (portable backpacks and such) as well. It replaces the old
IInventory
andISidedInventory
with an automation-friendly system.
- Used for handling inventory slots. This is not only used for TileEntities, but Entities (extra player slots, mob/creature inventories/bags) and ItemStacks (portable backpacks and such) as well. It replaces the old
-
IFluidHandler
- Represents a fluid containing object. It replaces the old
IFluidHandler
with a more consistent and automation-friendly system.
- Represents a fluid containing object. It replaces the old
-
IEnergyStorage
- Represents an energy containing object. It is based on the RedstoneFlux API by TeamCoFH, but modified to make sense as a capability.
In order to obtain a capability, you will need to refer it by its unique instance. For instance, the item handler capability is primarily stored in CapabilityItemHandler.ITEM_HANDLER_CAPABILITY
, but it is possible to get another reference by using the @CapabilityInject
annotation.
@CapabilityInject(IItemHandler.class)
static Capability<IItemHandler> ITEM_HANDLER_CAPABILITY = null;
When the annotation is 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 (null
), 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.
Additionally, 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.