Difference between revisions of "Capabilities"

From Forge Community Wiki
m (Fixing capability invalidation on tile entities: PR #7536)
m
 
(23 intermediate revisions by 10 users not shown)
Line 1: Line 1:
Capabilities allow exposing features in a dynamic and flexible way, without having to resort to directly implementing many interfaces.
+
'''Capabilities''' are a Forge system that allows cross-mod interactions by allowing capability ''providers'' to
 +
dynamically respect contracts and provide specialized behavior without requiring the implementation of many interfaces
 +
or hard dependencies on mods.
  
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.
+
== History ==
 +
In an ideal world, all that would be needed for a mod to provide the equivalent of a capability would be implementing an
 +
interface. This is in fact how cross-mod interaction used to work prior to the introduction of capabilities.
  
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.
+
The real world, though, is often much more complicated: users wanted to be free to combine mods the way they wanted and
 +
saw fit, and developers wanted to be able to declare ''soft'' dependencies on other mods, thus reducing the need of
 +
having a huge mod pack just for testing.
  
== Forge-provided Capabilities ==
+
The first approach used by Forge was conditional stripping of interfaces and methods, but this proved to be problematic.
Forge provides three capabilities: <code>IItemHandler</code>, <code>IFluidHandler</code> and <code>IEnergyStorage</code>.
+
While the idea works well in theory, in practice the ASM editing of classes relied on complex mechanics and could lead
 +
to hard to spot bugs.
  
<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.
+
For this reason, the entire system was redesigned and the concept of '''capabilities''' was born.
  
<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.
+
== The Concept ==
 +
A capability allows any capability provider to conditionally expose a certain ability to do something, e.g. accepting
 +
power or handling items. A capability provider, moreover, can decide to expose a capability only on certain sides,
 +
allowing for easy interactions with hoppers, cables, etc.
  
<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.
+
Capabilities may also be added and removed dynamically both from the "owner" of the capability provider and other mods,
 +
allowing even easier cross-mod interaction. For example, a mod that isn't compatible with Forge Energy could be
 +
converted into one by dynamically attaching the Forge Energy capability and handling the conversion to a third-party
 +
energy system without having to alter the original mod.
  
== Using an Existing Capability  ==
+
== Terminology ==
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.
+
The high flexibility of the system comes with a cost, though, which is terminology. The following section wants to be a
 +
dictionary of sorts, defining all the terms that you may come across when dealing with capabilities.
  
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.
+
In the rest of this article, we will refer to these terms frequently, so make sure you are familiar with them.
<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>.
+
; Capability
 +
: the ability to perform something. In-code this is represented by the <code>Capability</code> class.
 +
; Capability Provider
 +
: something that is able to support capabilities and provides a mean of accessing them. In-code they are represented by implementations of <code>ICapabilityProvider</code>. There are multiple kinds of capability providers:
 +
:; Volatile Provider
 +
:: a provider that doesn't persist data to disk; once the provider ceases to exist for any number of reasons, all capability data gets deleted.
 +
:; Persistent Provider
 +
:: a provider that requires all capabilities to serialize data to disk, in order to persist data even across game restarts. They implement the <code>INBTSerializable</code> interface.
 +
:; Agnostic Provider
 +
:: a provider that isn't neither volatile nor persistent, rather delegates the decision either to the capability directly or to sub-implementations. They also implement the <code>INBTSerializable</code> interface.
 +
; Capability Interface
 +
: the interface that defines the contract of the capability, so what operations the capability exposes.
 +
; Capability Implementation
 +
: one of the possibly many implementations of the capability interface, that actually carries out the work.
  
==Exposing a Capability==
+
The wary reader may note that both ''persistent'' and ''agnostic'' providers are represented the same way in code. In
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.
+
fact, the only difference between them comes from pure semantics in how their serialization methods are designed. This
 +
will be further discussed in their respective sections.
  
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.
+
Moreover, it is also common to refer to the capability interface as simply the ''capability''. While not strictly
 +
correct, due to common usage we will also use this convention. So, to refer to the capability interface
 +
<code>MyCapability</code>, we will usually talk about the "<code>MyCapability</code> capability".
  
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).
+
== Forge-provided Capabilities and Providers ==
 +
In order to ensure mods can work together seamlessly, Forge provides a set of default capabilities and capability
 +
providers.
  
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.
+
The default capability providers in a Forge environment are: <code>BlockEntity</code>, <code>Entity</code>,
 +
<code>ItemStack</code>, <code>Level</code>, and <code>LevelChunk</code>. These are all agnostic providers, since they don't
 +
mandate any sort of capability persistency requirements. Rather, it is the job of whoever subclasses these providers to
 +
deal with either volatile or non-volatile capabilities.
  
<syntaxhighlight lang="java">
+
The default capabilities that forge provides are represented by the interfaces <code>IItemHandler</code>,
// Somewhere in your TileEntity subclass
+
<code>IFluidHandler</code>, <code>IFluidHandlerItem</code>, and <code>IEnergyStorage</code>. Each one of these capabilities will be discussed in the corresponding section.
LazyOptional<IItemHandler> inventoryHandlerLazyOptional;
 
  
// After initializing inventoryHandler
+
=== <tt>IItemHandler</tt> ===
inventoryHandlerLazyOptional = LazyOptional.of(() -> inventoryHandler);
 
  
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
+
The <code>IItemHandler</code> capability refers to the ability for any capability provider to have some sort of internal
  if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
+
'''inventory''' with a certain number of slots, from which items can be inserted and extracted. It is also possible,
    return inventoryHandlerLazyOptional.cast();
+
though, to expose this capability even if no such inventory is present as long as the capability provider can emulate
  }
+
its presence (e.g. tools that allow accessing remote inventories).
  return super.getCapability(cap, side);
+
 
 +
This effectively '''replaces''' the vanilla interfaces <code>Container</code> and <code>WorldlyContainer</code>. These
 +
interfaces are in fact retained only to allow vanilla code to compile and should not be used in mod code. This extends
 +
to anything that implements those vanilla interfaces, such as <code>RandomizableContainerBlockEntity</code>.
 +
 
 +
A default reference implementation for this capability interface is provided in <code>ItemStackHandler</code>.
 +
 
 +
=== <tt>IFluidHandler</tt> ===
 +
 
 +
The <code>IFluidHandler</code> capability refers to the ability for any capability provider to handle and store fluids
 +
in one or multiple fluid tanks. It is effectively the equivalent in terms of fluids of the <code>IItemHandler</code>
 +
capability.
 +
 
 +
A default reference implementation for this capability interface is provided in <code>TileFluidHandler</code>.
 +
 
 +
=== <tt>IFluidHandlerItem</tt> ===
 +
 
 +
The <code>IFluidHandlerItem</code> capability refers to the ability for an <code>ItemStack</code> capability provider
 +
to handle and store fluids in one or multiple fluid tanks. It is basically a specialized version of the
 +
<code>IFluidHandler</code> capability that allows <code>ItemStack</code>s to define a custom container.
 +
 
 +
=== <tt>IEnergyStorage</tt> ===
 +
 
 +
The <code>IEnergyStorage</code> capability refers to the ability for any capability provider to store, consume, and
 +
produce energy. This capability is the base capability for what's commonly known in the modded world as Forge Energy (or
 +
FE), i.e. the energy system most mods use. Its internal design is heavily based on the (now defunct) Redstone Flux
 +
Energy API, supporting both a push and pull system.
 +
 
 +
A default reference implementation for this capability interface is provided in <code>EnergyStorage</code>.
 +
 
 +
== Working with Capabilities ==
 +
 
 +
Both capability providers and users need to be able to provide and access capabilities through a common framework,
 +
otherwise the idea of a dynamic and mod-agnostic system would not really exist. For this reason, both capability providers and
 +
capability ''accessors'' (which we define as everything that wants to access a capability), also known as '''clients''' or '''users''',
 +
need to work together and with Forge to ensure that the common interface is used sensibly and correctly by all parties.
 +
 
 +
=== Obtaining a Capability ===
 +
 
 +
Before being able to work with a capability, it is necessary to obtain an instance of the <code>Capability</code> object itself.
 +
 
 +
A <code>Capability</code> can be obtained at any time using <code>CapabilityManager#get</code>. This takes in an anonymous <code>CapabilityToken</code> to still allow for a soft dependency system while also keeping hold of any generic information needed. As such, you can always obtain a non-null capability.
 +
 
 +
{{Tabs/Code Snippets
 +
|java=
 +
public static Capability<IItemHandler> ITEM_HANDLER = CapabilityManager.get(new CapabilityToken<>(){});
 +
}}
 +
 
 +
The above code will let Forge know that the field <code>ITEM_HANDLER</code> should be analogous with the <code>IItemHandler</code> capability. Note that this does not mean the capability is accessible or registered. To check if it is, call <code>Capability#isRegistered</code>.
 +
 
 +
This is, for obvious reasons, redundant, since that capability is also available through
 +
<code>ForgeCapabilities</code>.
 +
 
 +
=== Exposing a Capability ===
 +
 
 +
Exposing a capability is a voluntary act by a capability provider that allows the capability to be discovered and
 +
accessed by users.
 +
 
 +
To do so, a capability provider needs to juggle a couple more moving pieces to ensure that the capability state remains
 +
consistent and that the lookup remains fast. It is in fact possible for a capability provider to be asked to provide
 +
many capabilities many times in the same tick. For this reason, a provider is asked to do the following:
 +
 
 +
* the <code>LazyOptional</code>s that get returned '''must be cached''';
 +
* if a capability changes exposure state (more on this later), all listeners '''must be notified''';
 +
* if a capability gets invalidated (more on this later), all listeners '''must be notified'''
 +
* the lookup inside <code>getCapability</code> must be performed with '''an <code>if</code>-<code>else</code> chain''';
 +
* all unexposed but still present capabilities '''should be available''' if the provider is queried with a <code>null</code> direction (see ''Accessing a Capability'' for more information);
 +
* if no capability of a given type is available or accessible, the provider '''must call <code>super</code> as long as it is possible to do so'''.
 +
 
 +
Capability providers must also reflect changes in the ''exposure state'' of a capability, meaning that if the
 +
accessibility of a capability from a certain <code>Direction</code> changes (refer to
 +
[[#Accessing a Capability|Accessing a Capability]] for more information), it is the provider's responsibility to trigger
 +
a state response by invalidating the returned <code>LazyOptional</code> and caching a new one. This should also be
 +
performed when a capability gets ''invalidated'', such as when a capability provider gets removed.
 +
 
 +
With all of the above in mind, part of a capability provider implementation may be similar to the following snippet:
 +
 
 +
{{Tabs/Code Snippets
 +
|java=
 +
// suppose the presence of a field 'inventory' of type 'IItemHandler'
 +
 
 +
private final LazyOptional<IItemhandler> inventoryOptional = LazyOptional.of(() -> this.inventory);
 +
 
 +
@Override
 +
public <T> LazyOptional<T> getCapability(Capability<T> capability, @Nullable Direction direction) {
 +
    if (capability == ForgeCapabilities.ITEM_HANDLER
 +
            && (direction == null {{!}}{{!}} direction == Direction.UP {{!}}{{!}} direction == Direction.DOWN)) {
 +
        return this.inventoryOptional.cast();
 +
    }
 +
    return super.getCapability(capability, direction); // See note after snippet
 
}
 
}
  
 
@Override
 
@Override
 
protected void invalidateCaps() {
 
protected void invalidateCaps() {
  super.invalidateCaps();
+
    super.invalidateCaps();
  inventoryHandlerLazyOptional.invalidate();
+
    this.inventoryOptional.invalidate();
 +
}
 +
}}
 +
 
 +
This possible implementation of a capability provider exposes an <code>IItemHandler</code> capability and restricts
 +
access only to the <code>UP</code> and <code>DOWN</code> directions. If we assume this capability provider is a
 +
<code>BlockEntity</code>, then we may also say that the inventory is only accessible from the top and the bottom of the
 +
block.
 +
 
 +
Moreover, the capability gets automatically invalidated when the provider gets invalidated. Assuming this is a
 +
<code>BlockEntity</code>, this usually happens when the block gets removed from the level or unloaded due to distance.
 +
 
 +
The <code>super</code> call at the end of the <code>getCapability</code> method is extremely important, since it's what
 +
allows Attaching external Capabilities to capability providers. Nevertheless, it is not always possible to invoke
 +
<code>super</code>: in those cases, an empty <code>LazyOptional</code> should be returned.
 +
 
 +
=== Attaching a Capability ===
 +
 
 +
Attaching a Capability is a process by which external agents "modify" a Capability Provider, making it expose additional
 +
capabilities other than the already available ones.
 +
 
 +
To do so, the '''attaching agent''' (which means the thing that wants to attach a capability to another provider) must
 +
listen to the <code>AttachCapabilitiesEvent&lt;T&gt;</code>. The <code>T</code> in this case represents the capability
 +
provider you want to attach the capability to. Note that the type of <code>T</code> '''must''' be the base type of the
 +
capability provider, not a subclass. As an example, if you want to attach a capability to a <code>MyBlockEntity</code>,
 +
which extends <code>BlockEntity</code>, you'll have to listen to <code>AttachCapabilitiesEvent&lt;BlockEntity&gt;</code>,
 +
'''NOT''' to <code>AttachCapabilitiesEvent&lt;MyBlockEntity&gt;</code>, since the latter will never fire.
 +
 
 +
The attaching agent can use the provided methods <code>getObject</code>, <code>addCapability</code>, and
 +
<code>addListener</code> to check whether the capability should be attached to the current object and perform the
 +
desired action.
 +
 
 +
When attaching a capability, the attaching agent should also provide a name in the form of a
 +
<code>ResourceLocation</code>. The name '''must''' be under the attaching agent's namespace, but no restrictions are
 +
placed on the actual name, as long as it is unique inside the given namespace.
 +
 
 +
Maybe a little counter-intuitively, the process of attaching does not attach a capability nor a capability interface
 +
directly. Rather, the attaching agent should create its own implementation of a '''Capability Provider''' and attach it
 +
via the event. This is done so that the attaching agent can have control over when, how, and where its capabilities are
 +
exposed, instead of relying on the game itself deciding these parameters. For this reason, all considerations given in
 +
the [[#Exposing a Capability|Exposing a Capability]] section on how to correctly create a Capability Provider.
 +
 
 +
With the above in mind, part of an attaching agent may be similar to the following snippet of code:
 +
 
 +
{{Tabs/Code Snippets
 +
|java=
 +
@SubscribeEvent
 +
public void onAttachingCapabilities(final AttachCapabilitiesEvent<BlockEntity> event) {
 +
    if (!(event.getObject() instanceof EnergyBasedBlockEntity)) return;
 +
 
 +
    EnergyStorage backend = new EnergyStorage(((EnergyBasedBlockEntity) event.getObject()).capacity);
 +
    LazyOptional<IEnergyStorage> optionalStorage = LazyOptional.of(() -> backend);
 +
 
 +
    ICapabilityProvider provider = new ICapabilityProvider() {
 +
        @Override
 +
        public <T> LazyOptional<T> getCapability(Capability<T> cap, @Nullable Direction direction) {
 +
            if (cap == ForgeCapabilities.ENERGY) {
 +
                return optionalStorage.cast();
 +
            }
 +
            return LazyOptional.empty();
 +
        }
 +
    };
 +
 
 +
    event.addCapability(new ResourceLocation("examplemod", "fe_compatibility"), provider);
 +
}
 +
}}
 +
 
 +
This example implementation of an attaching agent attaches a <code>IEnergyStorage</code> capability to all
 +
<code>BlockEntity</code> instance that are a subclass of <code>EnergyBasedBlockEntity</code>. It also sets up the
 +
<code>LazyOptional</code> for invalidation if the parent capability provider gets invalidated.
 +
 
 +
Note also the call of <code>LazyOptional.empty()</code> rather than a <code>super</code>. This is needed because when
 +
attaching a capability, the parent capability provider isn't known. For this reason, it is necessary to return an empty
 +
<code>LazyOptional</code>. The game will then handle automatic merging of the various different providers into a single
 +
one.
 +
 
 +
The above example is one of a '''Volatile''' Capability Provider. On the other hand, mods may want to persist their
 +
data across sessions. In this case, they should attach a '''Persistent''' Capability Provider: this can be done either
 +
by implementing the <code>INBTSerializable</code> interface along with <code>ICapabilityProvider</code> or by
 +
implementing the <code>ICapabilitySerializable</code> interface.
 +
 
 +
The previous example reworked to use a Persistent Capability Provider may be similar to the following snippet:
 +
 
 +
{{Tabs/Code Snippets
 +
|java=
 +
@SubscribeEvent
 +
public void onAttachingCapabilities(final AttachCapabilitiesEvent<BlockEntity> event) {
 +
    if (!(event.getObject() instanceof EnergyBasedBlockEntity)) return;
 +
 
 +
    EnergyStorage backend = new EnergyStorage(((EnergyBasedBlockEntity) event.getObject()).capacity);
 +
    LazyOptional<IEnergyStorage> optionalStorage = LazyOptional.of(() -> backend);
 +
    Capability<IEnergyStorage> capability = ForgeCapabilities.ENERGY;
 +
 
 +
    ICapabilityProvider provider = new ICapabilitySerializable<IntTag>() {
 +
        @Override
 +
        public <T> LazyOptional<T> getCapability(Capability<T> cap, @Nullable Direction direction) {
 +
            if (cap == capability) {
 +
                return optionalStorage.cast();
 +
            }
 +
            return LazyOptional.empty();
 +
        }
 +
 
 +
        @Override
 +
        public IntTag serializeNBT() {
 +
            return backend.serializeNBT();
 +
        }
 +
 
 +
        @Override
 +
        public void deserializeNBT(IntTag tag) {
 +
            backend.deserializeNBT(tag);
 +
        }
 +
    };
 +
 
 +
    event.addCapabilities(new ResourceLocation("examplemod", "fe_compatibility"), provider);
 +
}
 +
}}
 +
 
 +
Note that when using capabilities on entities, you should manually invalidate the capability via <code>invalidateCaps()</code>, using e.g. <code>PlayerEvent.Clone</code>. This is due to the fact that players are recreated & copied when moving across dimensions, not simply moved.
 +
 
 +
=== Accessing a Capability ===
 +
 
 +
Accessing a Capability is the process by which a user is able to '''query''' a Capability Provider for a specific
 +
instance of a capability.
 +
 
 +
This is perhaps the second most important part of the entire capability system, since it is what allows cross-mod
 +
interaction. To obtain an instance of a Capability, the user must first get a hold of the Capability Provider that
 +
should be queried. This can be done in a variety of ways and is outside the scope of this article. The user should
 +
then invoke the <code>getCapability</code> method passing the unique instance of the capability that should be queried
 +
(see [[#Obtaining a Capability|Obtaining a Capability]] for more information) and the querying <code>Direction</code>,
 +
if applicable.
 +
 
 +
The returned object is a <code>LazyOptional</code> wrapping the queried Capability, if the capability provider exposes
 +
it, otherwise it will be empty. The <code>LazyOptional</code> can be either unwrapped via an <code>orElse</code> or
 +
used directly via <code>ifPresent</code>.
 +
 
 +
It is '''highly suggested''' to cache the returned <code>LazyOptional</code> to avoid querying the same provider every
 +
time, in order to improve performance. The user should thus register itself to the invalidation listener via the
 +
<code>addListener</code> method. This ensures that the user will be able to react to the invalidation of the
 +
<code>LazyOptional</code> and remove it from the cache.
 +
 
 +
With the above in mind, part of an user may be similar to the following snippet of code:
 +
 
 +
{{Tabs/Code Snippets
 +
|java=
 +
// note the use of EnumMap, which is much more performant than HashMap for enum keys
 +
private final Map<Direction, LazyOptional<IEnergyStorage>> cache = new EnumMap<>(Direction.class);
 +
 
 +
private void sendPowerTo(int power, Direction direction) {
 +
    LazyOptional<IEnergyStorage> targetCapability = cache.get(direction);
 +
 
 +
    if (targetCapability == null) {
 +
        ICapabilityProvider provider = level.getBlockEntity(pos.relative(direction));
 +
        targetCapability = provider.getCapability(ForgeCapabilities.ENERGY, direction.getOpposite());
 +
        cache.put(direction, targetCapability);
 +
        targetCapability.addListener(self -> cache.put(direction, null));
 +
    }
 +
 
 +
    targetCapability.ifPresent(storage -> storage.receiveEnergy(power, false));
 +
}
 +
}}
 +
 
 +
This example implementation of an user is querying via a <code>BlockEntity</code> the neighboring capability provider
 +
for an <code>IEnergyStorage</code> capability. Before obtaining the provider, the code performs a cache lookup for the
 +
targeted capability. If the check succeeds, then no lookup is performed; if the check fails, the targeted Capability
 +
Provider is obtained and queried for the Capability. The obtained <code>LazyOptional</code> is then cached and a
 +
listener is attached to it so that the cache would be emptied on invalidation. The code then continues with the
 +
interaction with the capability, which is outside the scope of this article.
 +
 
 +
== Creating Custom Capabilities ==
 +
 
 +
While the various capabilities provided by Forge may satisfy the most common use cases, there is always the chance that
 +
a mod may require a custom solution. For this reason, Forge provides a way to define a custom Capability.
 +
 
 +
Defining a custom Capability requires the user to provide one main component: the Capability Interface. Optionally, a
 +
Capability Implementation and Capability Provider can also be created. In this
 +
case, the provider will be used as described in [[#Attaching a Capability|Attaching a Capability]]. The various details
 +
for all these components are described in the respective sections of this article.
 +
 
 +
In this section, we will refer to the implementation of a <code>MyCapability</code> capability, that can be used to
 +
store a single mutable <code>String</code>.
 +
 
 +
Refer also to [[#Code Examples|Code Examples]] for an example on how the various components may be implemented in a
 +
real-world scenario.
 +
 
 +
=== The Capability Interface and the Capability Implementation ===
 +
 
 +
The Capability Interface is one of the most important parts of a Capability: without it, the Capability effectively does
 +
not exist. Designing a Capability Interface is exactly like designing any Java interface, so the particular details will
 +
be glossed over in this section.
 +
 
 +
The Capability Implementation, on the other hand, is the implementation of the previously defined Capability Interface.
 +
Usual rules for interface implementations follow. There can be more than one Capability Implementation for each
 +
capability.
 +
 
 +
Note that a '''well-formed''' capability implementation should '''not store''' the Capability Provider inside of it: we
 +
call the capability implementation ''provider-agnostic''. This is not a hard-requirement, though, rather it should act
 +
more as a guideline. There are in fact certain situations where this cannot be avoided (e.g. attaching a client-synced
 +
capability to an <code>ItemStack</code>).
 +
 
 +
Given all of the above information, this may be an example implementation of both a Capability Interface and a
 +
Capability Implementation:
 +
 
 +
{{Tabs/Code Snippets
 +
|java=
 +
public interface MyCapability {
 +
    String getValue();
 +
    void setValue(String value);
 +
}
 +
 
 +
public class MyCapabilityImplementation implements MyCapability {
 +
    private String value;
 +
 
 +
    @Override
 +
    public String getValue() {
 +
        return this.value;
 +
    }
 +
 
 +
    @Override
 +
    public void setValue(String value) {
 +
        this.value = value;
 +
    }
 +
}
 +
}}
 +
 
 +
Note that in this case, only a single implementation is provided.
 +
 
 +
=== The Capability Provider ===
 +
 
 +
The Capability Provider is an optional component of the capability that allows the Capability to be attached to a
 +
component. The details on how a Capability Provider should behave have already been discussed in the two previous
 +
sections [[#Exposing a Capability|Exposing a Capability]] and [[#Attaching a Capability|Attaching a Capability]]: refer
 +
to those for more information.
 +
 
 +
=== Registering Capabilities ===
 +
 
 +
Once all components of a Capability have been created, they must be registered so that the game is aware of the
 +
capability's presence. The registration only requires the Capability Interface.
 +
After registering, the created Capability will be automatically injected into all relevant fields and methods, see [[#Obtaining a Capability|Obtaining a Capability]] for more information.
 +
As of Forge 1.19.2-43.1.1, there are two ways to register capabilities.
 +
 
 +
==== AutoRegisterCapability annotation ====
 +
On Forge 1.19.2-43.1.1 or higher, a quick and easy way to register a capability is by annotating the Capability Interface with <code>@AutoRegisterCapability</code>. This would look like so:
 +
 
 +
{{Tabs/Code Snippets
 +
|java=
 +
@AutoRegisterCapability
 +
public interface MyCapability {
 +
    ...
 +
}
 +
}}
 +
 
 +
==== RegisterCapabilitiesEvent ====
 +
An alternative way to register capabilities is by calling the <code>register</code> method within the <code>RegisterCapabilitiesEvent</code>. This event is fired on the <code>MOD</code> event bus. For example:
 +
 
 +
{{Tabs/Code Snippets
 +
|java=
 +
@SubscribeEvent
 +
public void registerCaps(RegisterCapabilitiesEvent event) {
 +
    event.register(MyCapability.class);
 
}
 
}
</syntaxhighlight>
+
}}
 +
 
 +
=== Class Diagram ===
 +
 
 +
[[File:Custom Capability Class Diagram.svg|800px|thumb|center|Custom Capability Class Diagram]]
 +
 
 +
In the above diagram the green and red marked areas are classes from Minecraft and Forge respectively. The classes inside the purple area are only needed if you want to [[#Attaching a Capability|Attach a Capability]]. Furthermore this diagram models a persistent provider and its most basic form, for more information on how to create a more complex provider see [[#Custom Capability Providers|Custom Capability Providers]]. To create a volatile provider instead just do not implement <code>INBTSerializable</code>.
 +
 
 +
== Custom Capability Providers ==
 +
 
 +
Much like custom Capabilities, Forge also allows the creation of custom Capability Providers. The main advantage of this
 +
is allowing mods to create custom providers for their custom objects, in order to promote not only cross-mod
 +
compatibility, but also uniformity in the way users may interact with different mod APIs.
 +
 
 +
This section will only give the basic outline of what is necessary to implement a custom Capability Provider: for more
 +
in-depth explanation, people are referred to the game code.
 +
 
 +
By definition, a custom Capability Provider is everything that implements the <code>ICapabilityProvider</code>
 +
interface. In this section, though, we will only cover people that may want to replicate the functionality of one of
 +
the default providers, such as <code>BlockEntity</code> or <code>LevelChunk</code>.
 +
 
 +
The easiest way of doing this is extending the <code>CapabilityProvider</code> class provided by Forge. This will
 +
automatically set up an ''agnostic'' Capability Provider. To fully initialize the capability provider, the subclass
 +
should then invoke the <code>gatherCapabilities</code> method as the last instruction in its constructor, to ensure that
 +
the game is able to recollect and attach all capabilities that other mods may want to attach to the capability provider.
 +
 
 +
== Code Examples ==
 +
 
 +
* [https://gist.github.com/TheCurle/6db954d680f6f067dcdc791355c32c89 A Gist showing a quick and dirty example on how to implement a Capability effectively]
  
<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.
+
[[Category:Data Storage]]

Latest revision as of 16:08, 2 July 2023

Capabilities are a Forge system that allows cross-mod interactions by allowing capability providers to dynamically respect contracts and provide specialized behavior without requiring the implementation of many interfaces or hard dependencies on mods.

History

In an ideal world, all that would be needed for a mod to provide the equivalent of a capability would be implementing an interface. This is in fact how cross-mod interaction used to work prior to the introduction of capabilities.

The real world, though, is often much more complicated: users wanted to be free to combine mods the way they wanted and saw fit, and developers wanted to be able to declare soft dependencies on other mods, thus reducing the need of having a huge mod pack just for testing.

The first approach used by Forge was conditional stripping of interfaces and methods, but this proved to be problematic. While the idea works well in theory, in practice the ASM editing of classes relied on complex mechanics and could lead to hard to spot bugs.

For this reason, the entire system was redesigned and the concept of capabilities was born.

The Concept

A capability allows any capability provider to conditionally expose a certain ability to do something, e.g. accepting power or handling items. A capability provider, moreover, can decide to expose a capability only on certain sides, allowing for easy interactions with hoppers, cables, etc.

Capabilities may also be added and removed dynamically both from the "owner" of the capability provider and other mods, allowing even easier cross-mod interaction. For example, a mod that isn't compatible with Forge Energy could be converted into one by dynamically attaching the Forge Energy capability and handling the conversion to a third-party energy system without having to alter the original mod.

Terminology

The high flexibility of the system comes with a cost, though, which is terminology. The following section wants to be a dictionary of sorts, defining all the terms that you may come across when dealing with capabilities.

In the rest of this article, we will refer to these terms frequently, so make sure you are familiar with them.

Capability
the ability to perform something. In-code this is represented by the Capability class.
Capability Provider
something that is able to support capabilities and provides a mean of accessing them. In-code they are represented by implementations of ICapabilityProvider. There are multiple kinds of capability providers:
Volatile Provider
a provider that doesn't persist data to disk; once the provider ceases to exist for any number of reasons, all capability data gets deleted.
Persistent Provider
a provider that requires all capabilities to serialize data to disk, in order to persist data even across game restarts. They implement the INBTSerializable interface.
Agnostic Provider
a provider that isn't neither volatile nor persistent, rather delegates the decision either to the capability directly or to sub-implementations. They also implement the INBTSerializable interface.
Capability Interface
the interface that defines the contract of the capability, so what operations the capability exposes.
Capability Implementation
one of the possibly many implementations of the capability interface, that actually carries out the work.

The wary reader may note that both persistent and agnostic providers are represented the same way in code. In fact, the only difference between them comes from pure semantics in how their serialization methods are designed. This will be further discussed in their respective sections.

Moreover, it is also common to refer to the capability interface as simply the capability. While not strictly correct, due to common usage we will also use this convention. So, to refer to the capability interface MyCapability, we will usually talk about the "MyCapability capability".

Forge-provided Capabilities and Providers

In order to ensure mods can work together seamlessly, Forge provides a set of default capabilities and capability providers.

The default capability providers in a Forge environment are: BlockEntity, Entity, ItemStack, Level, and LevelChunk. These are all agnostic providers, since they don't mandate any sort of capability persistency requirements. Rather, it is the job of whoever subclasses these providers to deal with either volatile or non-volatile capabilities.

The default capabilities that forge provides are represented by the interfaces IItemHandler, IFluidHandler, IFluidHandlerItem, and IEnergyStorage. Each one of these capabilities will be discussed in the corresponding section.

IItemHandler

The IItemHandler capability refers to the ability for any capability provider to have some sort of internal inventory with a certain number of slots, from which items can be inserted and extracted. It is also possible, though, to expose this capability even if no such inventory is present as long as the capability provider can emulate its presence (e.g. tools that allow accessing remote inventories).

This effectively replaces the vanilla interfaces Container and WorldlyContainer. These interfaces are in fact retained only to allow vanilla code to compile and should not be used in mod code. This extends to anything that implements those vanilla interfaces, such as RandomizableContainerBlockEntity.

A default reference implementation for this capability interface is provided in ItemStackHandler.

IFluidHandler

The IFluidHandler capability refers to the ability for any capability provider to handle and store fluids in one or multiple fluid tanks. It is effectively the equivalent in terms of fluids of the IItemHandler capability.

A default reference implementation for this capability interface is provided in TileFluidHandler.

IFluidHandlerItem

The IFluidHandlerItem capability refers to the ability for an ItemStack capability provider to handle and store fluids in one or multiple fluid tanks. It is basically a specialized version of the IFluidHandler capability that allows ItemStacks to define a custom container.

IEnergyStorage

The IEnergyStorage capability refers to the ability for any capability provider to store, consume, and produce energy. This capability is the base capability for what's commonly known in the modded world as Forge Energy (or FE), i.e. the energy system most mods use. Its internal design is heavily based on the (now defunct) Redstone Flux Energy API, supporting both a push and pull system.

A default reference implementation for this capability interface is provided in EnergyStorage.

Working with Capabilities

Both capability providers and users need to be able to provide and access capabilities through a common framework, otherwise the idea of a dynamic and mod-agnostic system would not really exist. For this reason, both capability providers and capability accessors (which we define as everything that wants to access a capability), also known as clients or users, need to work together and with Forge to ensure that the common interface is used sensibly and correctly by all parties.

Obtaining a Capability

Before being able to work with a capability, it is necessary to obtain an instance of the Capability object itself.

A Capability can be obtained at any time using CapabilityManager#get. This takes in an anonymous CapabilityToken to still allow for a soft dependency system while also keeping hold of any generic information needed. As such, you can always obtain a non-null capability.

public static Capability<IItemHandler> ITEM_HANDLER = CapabilityManager.get(new CapabilityToken<>(){});


The above code will let Forge know that the field ITEM_HANDLER should be analogous with the IItemHandler capability. Note that this does not mean the capability is accessible or registered. To check if it is, call Capability#isRegistered.

This is, for obvious reasons, redundant, since that capability is also available through ForgeCapabilities.

Exposing a Capability

Exposing a capability is a voluntary act by a capability provider that allows the capability to be discovered and accessed by users.

To do so, a capability provider needs to juggle a couple more moving pieces to ensure that the capability state remains consistent and that the lookup remains fast. It is in fact possible for a capability provider to be asked to provide many capabilities many times in the same tick. For this reason, a provider is asked to do the following:

  • the LazyOptionals that get returned must be cached;
  • if a capability changes exposure state (more on this later), all listeners must be notified;
  • if a capability gets invalidated (more on this later), all listeners must be notified
  • the lookup inside getCapability must be performed with an if-else chain;
  • all unexposed but still present capabilities should be available if the provider is queried with a null direction (see Accessing a Capability for more information);
  • if no capability of a given type is available or accessible, the provider must call super as long as it is possible to do so.

Capability providers must also reflect changes in the exposure state of a capability, meaning that if the accessibility of a capability from a certain Direction changes (refer to Accessing a Capability for more information), it is the provider's responsibility to trigger a state response by invalidating the returned LazyOptional and caching a new one. This should also be performed when a capability gets invalidated, such as when a capability provider gets removed.

With all of the above in mind, part of a capability provider implementation may be similar to the following snippet:

// suppose the presence of a field 'inventory' of type 'IItemHandler'

private final LazyOptional<IItemhandler> inventoryOptional = LazyOptional.of(() -> this.inventory);

@Override
public <T> LazyOptional<T> getCapability(Capability<T> capability, @Nullable Direction direction) {
    if (capability == ForgeCapabilities.ITEM_HANDLER
            && (direction == null || direction == Direction.UP || direction == Direction.DOWN)) {
        return this.inventoryOptional.cast();
    }
    return super.getCapability(capability, direction); // See note after snippet
}

@Override
protected void invalidateCaps() {
    super.invalidateCaps();
    this.inventoryOptional.invalidate();
}


This possible implementation of a capability provider exposes an IItemHandler capability and restricts access only to the UP and DOWN directions. If we assume this capability provider is a BlockEntity, then we may also say that the inventory is only accessible from the top and the bottom of the block.

Moreover, the capability gets automatically invalidated when the provider gets invalidated. Assuming this is a BlockEntity, this usually happens when the block gets removed from the level or unloaded due to distance.

The super call at the end of the getCapability method is extremely important, since it's what allows Attaching external Capabilities to capability providers. Nevertheless, it is not always possible to invoke super: in those cases, an empty LazyOptional should be returned.

Attaching a Capability

Attaching a Capability is a process by which external agents "modify" a Capability Provider, making it expose additional capabilities other than the already available ones.

To do so, the attaching agent (which means the thing that wants to attach a capability to another provider) must listen to the AttachCapabilitiesEvent<T>. The T in this case represents the capability provider you want to attach the capability to. Note that the type of T must be the base type of the capability provider, not a subclass. As an example, if you want to attach a capability to a MyBlockEntity, which extends BlockEntity, you'll have to listen to AttachCapabilitiesEvent<BlockEntity>, NOT to AttachCapabilitiesEvent<MyBlockEntity>, since the latter will never fire.

The attaching agent can use the provided methods getObject, addCapability, and addListener to check whether the capability should be attached to the current object and perform the desired action.

When attaching a capability, the attaching agent should also provide a name in the form of a ResourceLocation. The name must be under the attaching agent's namespace, but no restrictions are placed on the actual name, as long as it is unique inside the given namespace.

Maybe a little counter-intuitively, the process of attaching does not attach a capability nor a capability interface directly. Rather, the attaching agent should create its own implementation of a Capability Provider and attach it via the event. This is done so that the attaching agent can have control over when, how, and where its capabilities are exposed, instead of relying on the game itself deciding these parameters. For this reason, all considerations given in the Exposing a Capability section on how to correctly create a Capability Provider.

With the above in mind, part of an attaching agent may be similar to the following snippet of code:

@SubscribeEvent
public void onAttachingCapabilities(final AttachCapabilitiesEvent<BlockEntity> event) {
    if (!(event.getObject() instanceof EnergyBasedBlockEntity)) return;

    EnergyStorage backend = new EnergyStorage(((EnergyBasedBlockEntity) event.getObject()).capacity);
    LazyOptional<IEnergyStorage> optionalStorage = LazyOptional.of(() -> backend);

    ICapabilityProvider provider = new ICapabilityProvider() {
        @Override
        public <T> LazyOptional<T> getCapability(Capability<T> cap, @Nullable Direction direction) {
            if (cap == ForgeCapabilities.ENERGY) {
                return optionalStorage.cast();
            }
            return LazyOptional.empty();
        }
    };

    event.addCapability(new ResourceLocation("examplemod", "fe_compatibility"), provider);
}


This example implementation of an attaching agent attaches a IEnergyStorage capability to all BlockEntity instance that are a subclass of EnergyBasedBlockEntity. It also sets up the LazyOptional for invalidation if the parent capability provider gets invalidated.

Note also the call of LazyOptional.empty() rather than a super. This is needed because when attaching a capability, the parent capability provider isn't known. For this reason, it is necessary to return an empty LazyOptional. The game will then handle automatic merging of the various different providers into a single one.

The above example is one of a Volatile Capability Provider. On the other hand, mods may want to persist their data across sessions. In this case, they should attach a Persistent Capability Provider: this can be done either by implementing the INBTSerializable interface along with ICapabilityProvider or by implementing the ICapabilitySerializable interface.

The previous example reworked to use a Persistent Capability Provider may be similar to the following snippet:

@SubscribeEvent
public void onAttachingCapabilities(final AttachCapabilitiesEvent<BlockEntity> event) {
    if (!(event.getObject() instanceof EnergyBasedBlockEntity)) return;

    EnergyStorage backend = new EnergyStorage(((EnergyBasedBlockEntity) event.getObject()).capacity);
    LazyOptional<IEnergyStorage> optionalStorage = LazyOptional.of(() -> backend);
    Capability<IEnergyStorage> capability = ForgeCapabilities.ENERGY;

    ICapabilityProvider provider = new ICapabilitySerializable<IntTag>() {
        @Override
        public <T> LazyOptional<T> getCapability(Capability<T> cap, @Nullable Direction direction) {
            if (cap == capability) {
                return optionalStorage.cast();
            }
            return LazyOptional.empty();
        }

        @Override
        public IntTag serializeNBT() {
            return backend.serializeNBT();
        }

        @Override
        public void deserializeNBT(IntTag tag) {
            backend.deserializeNBT(tag);
        }
    };

    event.addCapabilities(new ResourceLocation("examplemod", "fe_compatibility"), provider);
}


Note that when using capabilities on entities, you should manually invalidate the capability via invalidateCaps(), using e.g. PlayerEvent.Clone. This is due to the fact that players are recreated & copied when moving across dimensions, not simply moved.

Accessing a Capability

Accessing a Capability is the process by which a user is able to query a Capability Provider for a specific instance of a capability.

This is perhaps the second most important part of the entire capability system, since it is what allows cross-mod interaction. To obtain an instance of a Capability, the user must first get a hold of the Capability Provider that should be queried. This can be done in a variety of ways and is outside the scope of this article. The user should then invoke the getCapability method passing the unique instance of the capability that should be queried (see Obtaining a Capability for more information) and the querying Direction, if applicable.

The returned object is a LazyOptional wrapping the queried Capability, if the capability provider exposes it, otherwise it will be empty. The LazyOptional can be either unwrapped via an orElse or used directly via ifPresent.

It is highly suggested to cache the returned LazyOptional to avoid querying the same provider every time, in order to improve performance. The user should thus register itself to the invalidation listener via the addListener method. This ensures that the user will be able to react to the invalidation of the LazyOptional and remove it from the cache.

With the above in mind, part of an user may be similar to the following snippet of code:

// note the use of EnumMap, which is much more performant than HashMap for enum keys
private final Map<Direction, LazyOptional<IEnergyStorage>> cache = new EnumMap<>(Direction.class);

private void sendPowerTo(int power, Direction direction) {
    LazyOptional<IEnergyStorage> targetCapability = cache.get(direction);

    if (targetCapability == null) {
        ICapabilityProvider provider = level.getBlockEntity(pos.relative(direction));
        targetCapability = provider.getCapability(ForgeCapabilities.ENERGY, direction.getOpposite());
        cache.put(direction, targetCapability);
        targetCapability.addListener(self -> cache.put(direction, null));
    }

    targetCapability.ifPresent(storage -> storage.receiveEnergy(power, false));
}


This example implementation of an user is querying via a BlockEntity the neighboring capability provider for an IEnergyStorage capability. Before obtaining the provider, the code performs a cache lookup for the targeted capability. If the check succeeds, then no lookup is performed; if the check fails, the targeted Capability Provider is obtained and queried for the Capability. The obtained LazyOptional is then cached and a listener is attached to it so that the cache would be emptied on invalidation. The code then continues with the interaction with the capability, which is outside the scope of this article.

Creating Custom Capabilities

While the various capabilities provided by Forge may satisfy the most common use cases, there is always the chance that a mod may require a custom solution. For this reason, Forge provides a way to define a custom Capability.

Defining a custom Capability requires the user to provide one main component: the Capability Interface. Optionally, a Capability Implementation and Capability Provider can also be created. In this case, the provider will be used as described in Attaching a Capability. The various details for all these components are described in the respective sections of this article.

In this section, we will refer to the implementation of a MyCapability capability, that can be used to store a single mutable String.

Refer also to Code Examples for an example on how the various components may be implemented in a real-world scenario.

The Capability Interface and the Capability Implementation

The Capability Interface is one of the most important parts of a Capability: without it, the Capability effectively does not exist. Designing a Capability Interface is exactly like designing any Java interface, so the particular details will be glossed over in this section.

The Capability Implementation, on the other hand, is the implementation of the previously defined Capability Interface. Usual rules for interface implementations follow. There can be more than one Capability Implementation for each capability.

Note that a well-formed capability implementation should not store the Capability Provider inside of it: we call the capability implementation provider-agnostic. This is not a hard-requirement, though, rather it should act more as a guideline. There are in fact certain situations where this cannot be avoided (e.g. attaching a client-synced capability to an ItemStack).

Given all of the above information, this may be an example implementation of both a Capability Interface and a Capability Implementation:

public interface MyCapability {
    String getValue();
    void setValue(String value);
}

public class MyCapabilityImplementation implements MyCapability {
    private String value;

    @Override
    public String getValue() {
        return this.value;
    }

    @Override
    public void setValue(String value) {
        this.value = value;
    }
}


Note that in this case, only a single implementation is provided.

The Capability Provider

The Capability Provider is an optional component of the capability that allows the Capability to be attached to a component. The details on how a Capability Provider should behave have already been discussed in the two previous sections Exposing a Capability and Attaching a Capability: refer to those for more information.

Registering Capabilities

Once all components of a Capability have been created, they must be registered so that the game is aware of the capability's presence. The registration only requires the Capability Interface. After registering, the created Capability will be automatically injected into all relevant fields and methods, see Obtaining a Capability for more information. As of Forge 1.19.2-43.1.1, there are two ways to register capabilities.

AutoRegisterCapability annotation

On Forge 1.19.2-43.1.1 or higher, a quick and easy way to register a capability is by annotating the Capability Interface with @AutoRegisterCapability. This would look like so:

@AutoRegisterCapability
public interface MyCapability {
    ...
}


RegisterCapabilitiesEvent

An alternative way to register capabilities is by calling the register method within the RegisterCapabilitiesEvent. This event is fired on the MOD event bus. For example:

@SubscribeEvent
public void registerCaps(RegisterCapabilitiesEvent event) {
    event.register(MyCapability.class);
}


Class Diagram

Custom Capability Class Diagram

In the above diagram the green and red marked areas are classes from Minecraft and Forge respectively. The classes inside the purple area are only needed if you want to Attach a Capability. Furthermore this diagram models a persistent provider and its most basic form, for more information on how to create a more complex provider see Custom Capability Providers. To create a volatile provider instead just do not implement INBTSerializable.

Custom Capability Providers

Much like custom Capabilities, Forge also allows the creation of custom Capability Providers. The main advantage of this is allowing mods to create custom providers for their custom objects, in order to promote not only cross-mod compatibility, but also uniformity in the way users may interact with different mod APIs.

This section will only give the basic outline of what is necessary to implement a custom Capability Provider: for more in-depth explanation, people are referred to the game code.

By definition, a custom Capability Provider is everything that implements the ICapabilityProvider interface. In this section, though, we will only cover people that may want to replicate the functionality of one of the default providers, such as BlockEntity or LevelChunk.

The easiest way of doing this is extending the CapabilityProvider class provided by Forge. This will automatically set up an agnostic Capability Provider. To fully initialize the capability provider, the subclass should then invoke the gatherCapabilities method as the last instruction in its constructor, to ensure that the game is able to recollect and attach all capabilities that other mods may want to attach to the capability provider.

Code Examples