Difference between revisions of "User:TheSilkMiner/Capabilities"

From Forge Community Wiki
User:TheSilkMiner/Capabilities
m (client -> user)
(Redirect to actual page; we don't want to break links that are already bookmarked or shared around)
Tag: New redirect
 
Line 1: Line 1:
'''Capabilities''' are a Forge system that allows cross-mod interactions by allowing capability ''providers'' to
+
#REDIRECT [[Capabilities]]
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 <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; one of the various implementations may also be considered the '''default capability implementation'''.
 
; Capability Storage
 
: the manager that handles loading and storing persistent capabilities data from and to disk, guaranteeing preservation of information; in-code this is represented by an implementation of the <code>Capability.IStorage</code> interface.
 
 
 
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
 
<code>MyCapability</code>, we will usually talk about the "<code>MyCapability</code> 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: <code>TileEntity</code>, <code>Entity</code>,
 
<code>ItemStack</code>, <code>World</code>, and <code>Chunk</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.
 
 
 
The default capabilities that forge provides are represented by the interfaces <code>IItemHandler</code>,
 
<code>IFluidHandler</code>, <code>IFluidHandlerItem</code>, <code>IEnergyStorage</code>, and
 
<code>IAnimationStateMachine</code>. Each one of these capabilities will be discussed in the corresponding section.
 
 
 
=== <tt>IItemHandler</tt> ===
 
 
 
The <code>IItemHandler</code> 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 <code>IInventory</code> and <code>ISidedInventory</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>LockableLootTileEntity</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 referes 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>.
 
 
 
=== <tt>IAnimationStateMachine</tt> ===
 
 
 
The <code>IAnimationStateMachine</code> capability refers to the ability for any capability provider to leverage the
 
Forge Animation State Machine API for animations.
 
 
 
== Working with Capabilities ==
 
 
 
Both capability providers and users need to be able to provide and access capabilities through a common framework,
 
otherwise the ideal of dynamic and mod-agnostic 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. Since these objects are created by Forge and there is only '''one''' unique instance for each capability that
 
may exist, this instance cannot be obtained by "common" means. Forge provides two different methods of obtaining such
 
instances: '''injecting''' into a field, or a '''callback method'''.
 
 
 
==== Injecting into a Field ====
 
 
 
A <code>Capability</code> can be injected automatically into a field as soon as it gets created by Forge, following the
 
principle commonly known as '''dependency injection'''. This provides less flexibility, since it doesn't notify the user
 
that the capability has been injected nor runs arbitrary code. Nevertheless, it is '''suggested''' to use this method
 
instead of the callback approach.
 
 
 
To inject the <code>Capability</code> into a field, all that's needed is to declare a <code>static</code> field of type
 
<code>Capability&lt;T&gt;</code>, where <code>T</code> represents the capability interface, and annotate it with
 
<code>@CapabilityInject(T.class)</code>.
 
 
 
For a more practical example, consider the following snippet:
 
 
 
<syntaxhighlight lang="java">
 
@CapabilityInject(IItemHandler.class)
 
public static Capability<IItemHandler> ITEM_HANDLER_CAPABILITY = null;
 
</syntaxhighlight>
 
 
 
The above code will let Forge know that the field <code>ITEM_HANDLER_CAPABILITY</code> should be injected with the
 
unique instance of the <code>IItemHandler</code> capability. Assigning the field to <code>null</code> allows us to
 
provide a reasonable fallback in case the capability we want hasn't been registered yet.
 
 
 
This injection is, for obvious reasons, redundant, since that capability is also available through
 
<code>CapabilityItemHandler</code>.
 
 
 
==== Declaring a Callback ====
 
 
 
Another option is to declare a callback method, meaning a method that will be called with the value of the desired
 
<code>Capability</code> once the instance is available. This gives more flexibility since the method may perform a
 
number of arbitrary actions with the received instance prior to storing it in a field, or may even discard the
 
capability entirely if wanted. Nevertheless, the usage of a field instead of a method is encouraged as a matter of
 
style.
 
 
 
To use a method as a callback, the method must be declared as <code>static</code> and accepting a single parameter of
 
type <code>Capability&lt;T&gt;</code>, where <code>T</code> represents the capability interface. The method should also
 
be annotated with <code>@CapabilityInject(T.class)</code>.
 
 
 
For a more practical example, consider the following snippet:
 
 
 
<syntaxhighlight lang="java">
 
public static Capability<IEnergyStorage> ENERGY = null;
 
 
 
@CapabilityInject(IEnergyStorage.class)
 
private static void onEnergyStorageInit(Capability<IEnergyStorage> capability) {
 
    LOGGER.info("Received IEnergyStorage capability '{}': enabling Forge Energy support", capability);
 
    ENERGY = capability;
 
}
 
</syntaxhighlight>
 
 
 
The above code declares a callback method that will be invoked when a <code>Capability</code> instance for
 
<code>IEnergyStorage</code> is available. The callback then prints a log message and stores the capability into a
 
<code>public</code> field for accessibility. The field is initialized to <code>null</code> to provide a reasonable
 
fallback in case the capability does not exist.
 
 
 
This callback is, for obvious reasons, redundant, since that capability is also available through
 
<code>CapabilityEnergy</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:
 
 
 
<syntaxhighlight lang="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 == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY
 
            && (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();
 
}
 
</syntaxhighlight>
 
 
 
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>TileEntity</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>TileEntity</code>, this usually happens when the block gets removed from the world 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>MyTileEntity</code>,
 
which extends <code>TileEntity</code>, you'll have to listen to <code>AttachCapabilitiesEvent&lt;TileEntity&gt;</code>,
 
'''NOT''' to <code>AttachCapabilitiesEvent&lt;MyTileEntity&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:
 
 
 
<syntaxhighlight lang="java">
 
@SubscribeEvent
 
public void onAttachingCapabilities(final AttachCapabilitiesEvent<TileEntity> event) {
 
    if (!(event.getObject() instanceof EnergyBasedTileEntity)) return;
 
 
 
    EnergyStorage backend = new EnergyStorage(((EnergyBasedTileEntity) 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 == CapabilityEnergy.ENERGY) {
 
                return optionalStorage.cast();
 
            }
 
            return LazyOptional.empty();
 
        }
 
    };
 
 
 
    event.addCapability(new ResourceLocation("examplemod", "fe_compatibility"), provider);
 
    event.addListener(optionalStorage::invalidate);
 
}
 
</syntaxhighlight>
 
 
 
This example implementation of an attaching agent attaches a <code>IEnergyStorage</code> capability to all
 
<code>TileEntity</code> instance that are a subclass of <code>EnergyBasedTileEntity</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:
 
 
 
<syntaxhighlight lang="java">
 
@SubscribeEvent
 
public void onAttachingCapabilities(final AttachCapabilitiesEvent<TileEntity> event) {
 
    if (!(event.getObject() instanceof EnergyBasedTileEntity)) return;
 
 
 
    EnergyStorage backend = new EnergyStorage(((EnergyBasedTileEntity) event.getObject()).capacity);
 
    LazyOptional<IEnergyStorage> optionalStorage = LazyOptional.of(() -> backend);
 
    Capability<IEnergyStorage> capability = CapabilityEnergy.ENERGY;
 
 
 
    ICapabilityProvider provider = new ICapabilitySerializable<IntNBT>() {
 
        @Override
 
        public <T> LazyOptional<T> getCapability(Capability<T> cap, @Nullable Direction direction) {
 
            if (cap == capability) {
 
                return optionalStorage.cast();
 
            }
 
            return LazyOptional.empty();
 
        }
 
 
 
        @Override
 
        public IntNBT serializeNBT() {
 
            return capability.getStorage().writeNbt(capability, backend, null);
 
        }
 
 
 
        @Override
 
        public void deserializeNBT(IntNBT nbt) {
 
            capability.getStorage().readNBT(capability, backend, null, nbt);
 
        }
 
    };
 
 
 
    event.addCapabilities(new ResourceLocation("examplemod", "fe_compatibility"), provider);
 
    event.addListener(optionalStorage::invalidate);
 
}
 
</syntaxhighlight>
 
 
 
 
 
=== 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:
 
 
 
<syntaxhighlight lang="java">
 
private final Map<Direction, LazyOptional<IEnergyStorage>> cache = new HashMap<>();
 
 
 
private void sendPowerTo(int power, Direction direction) {
 
    LazyOptional<IEnergyStorage> targetCapability = cache.get(direction);
 
 
 
    if (targetCapability == null) {
 
        ICapabilityProvider provider = world.getTileEntity(pos.offset(direction));
 
        targetCapability = provider.getCapability(CapabilityEnergy.ENERGY, direction.getOpposite());
 
        cache.put(direction, targetCapability);
 
        targetCapability.addListener(self -> cache.put(direction, null));
 
    }
 
 
 
    targetCapability.ifPresent(storage -> storage.receiveEnergy(power, false));
 
}
 
</syntaxhighlight>
 
 
 
This example implementation of an user is querying via a <code>TileEntity</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 three main components: the Capability Interface, at least one
 
Capability Implementation, and the Capability Storage. Optionally, a 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, but no less than one.
 
 
 
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>).
 
 
 
One of the various Capability Implementation should also act as the default implementation. Other mods can ask the
 
capability to create an instance of the default implementation without ever referring to such an implementation
 
themselves. This guarantees separation of API code from implementation code, which is also one of the goals of the
 
capability system.
 
 
 
Given all of the above information, this may be an example implementation of both a Capability Interface and a
 
Capability Implementation:
 
 
 
<syntaxhighlight lang="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;
 
    }
 
}
 
</syntaxhighlight>
 
 
 
Note that in this case, only a single implementation is provided, which will also act as the default implementation for
 
the <code>MyCapability</code> capability.
 
 
 
=== The Capability Storage ===
 
 
 
The Capability Storage is that component of the Capability System that is responsible for serializing and deserializing
 
a capability. All capabilities must provide one, since certain providers may or may not require that their capabilities
 
are serializable.
 
 
 
The Capability Storage implements the <code>Capability.IStorage&lt;T&gt;</code> interface, where <code>T</code> is the
 
generic type of the Capability Interface the storage refers to. Each capability must have '''one and exactly one'''
 
Capability Storage.
 
 
 
The Storage is usually called by the Capability Provider when serialization or deserialization needs to happen. The
 
Storage is then responsible of reading the data from the given capability instance and convert that into an NBT-based
 
structure that can be serialized. A Storage may also return <code>null</code> to indicate that no serialization is
 
necessary, although some providers may require an empty tag to be supplied instead. At the same time, the Storage is
 
also responsible for restoring the original state of the capability when deserialization happens. In this case, the
 
given NBT structure is guaranteed not to be <code>null</code>.
 
 
 
In all cases, a <code>Direction</code> is provided for context, if available.
 
 
 
Although discouraged as a matter of code cleanliness, it is legal for a Capability Storage to require a specific
 
Capability Implementation for the serialization and deserialization to be successful. If this is the case, this
 
requirement '''must''' be documented, though the code should be refactored wherever possible to remove this requirement.
 
 
 
Given the above information, an example of an implementation of a Capability Storage may be the following:
 
 
 
<syntaxhighlight lang="java">
 
public class MyCapabilityStorage implements Capability.IStorage<MyCapability> {
 
    @Override
 
    @Nullable
 
    INBT writeNBT(Capability<MyCapability> capability, MyCapability instance, Direction direction) {
 
        return StringNBT.valueOf(instance.getValue());
 
    }
 
 
 
    @Override
 
    public void readNBT(Capability<MyCapability> capability, MyCapability instance, Direction direction, INBT nbtData) {
 
        if (!(nbtData instanceof StringNBT)) {
 
            throw new IllegalArgumentException("Unable to deserialize 'MyCapability' from a non-String NBT structure");
 
        }
 
        instance.setValue(((StringNBT) nbtData).getString());
 
    }
 
}
 
</syntaxhighlight>
 
 
 
Note the <code>instanceof</code> check needed to ensure that the given <code>INBT</code> instance is valid for
 
deserialization. A Capability Storage should always perform this check prior to the cast in order to provide a
 
meaningful error message, rather than a cryptic <code>ClassCastException</code>.
 
 
 
=== 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.
 
 
 
=== Tying it All Together ===
 
 
 
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 requires specifying the Capability Interface, the Capability Storage, and a
 
factory for the default capability implementation.
 
 
 
The registration can be performed by calling the <code>register</code> method on the <code>CapabilityManager</code>.
 
This '''needs''' to happen when the <code>FMLCommonSetupEvent</code> is fired on the <code>MOD</code> event bus. The
 
registration will also automatically inject the created Capability into all relevant fields and methods: refer to
 
[[#Obtaining a Capability|Obtaining a Capability]] for more information.
 
 
 
An example of registration can be found in the snippet that follows:
 
 
 
<syntaxhighlight lang="java">
 
public void onCommonSetup(FMLCommonSetupEvent event) {
 
    CapabilityManager.INSTANCE.register(MyCapability.class, new MyCapabilityStorage(), MyCapabilityImplementation::new);
 
}
 
</syntaxhighlight>
 
 
 
== 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>TileEntity</code> or <code>Chunk</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/TheSilkMiner/5cc92ba573e7bdd871dfdbffdd5c2806 A Gist showing a quick and dirty example on how to implement a Capability effectively]
 

Latest revision as of 08:28, 6 May 2021

Redirect to: