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<T></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<T></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<T></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<TileEntity></code>,
−
'''NOT''' to <code>AttachCapabilitiesEvent<MyTileEntity></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<T></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]