Changes

5,412 bytes removed ,  16:08, 2 July 2023
m
no edit summary
Line 46: Line 46:  
: the interface that defines the contract of the capability, so what operations the capability exposes.
 
: the interface that defines the contract of the capability, so what operations the capability exposes.
 
; Capability Implementation
 
; 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'''.
+
: one of the possibly many implementations of the capability interface, that actually carries out the work.
; 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
 
The wary reader may note that both ''persistent'' and ''agnostic'' providers are represented the same way in code. In
Line 62: Line 60:  
providers.
 
providers.
   −
The default capability providers in a Forge environment are: <code>TileEntity</code>, <code>Entity</code>,
+
The default capability providers in a Forge environment are: <code>BlockEntity</code>, <code>Entity</code>,
<code>ItemStack</code>, <code>World</code>, and <code>Chunk</code>. These are all agnostic providers, since they don't
+
<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
 
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.
 
deal with either volatile or non-volatile capabilities.
    
The default capabilities that forge provides are represented by the interfaces <code>IItemHandler</code>,
 
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>IFluidHandler</code>, <code>IFluidHandlerItem</code>, and <code>IEnergyStorage</code>. Each one of these capabilities will be discussed in the corresponding section.
<code>IAnimationStateMachine</code>. Each one of these capabilities will be discussed in the corresponding section.
      
=== <tt>IItemHandler</tt> ===
 
=== <tt>IItemHandler</tt> ===
Line 78: Line 75:  
its presence (e.g. tools that allow accessing remote inventories).
 
its presence (e.g. tools that allow accessing remote inventories).
   −
This effectively '''replaces''' the vanilla interfaces <code>IInventory</code> and <code>ISidedInventory</code>. These
+
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
 
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>.
+
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>.
 
A default reference implementation for this capability interface is provided in <code>ItemStackHandler</code>.
Line 94: Line 91:  
=== <tt>IFluidHandlerItem</tt> ===
 
=== <tt>IFluidHandlerItem</tt> ===
   −
The <code>IFluidHandlerItem</code> capability referes to the ability for an <code>ItemStack</code> capability provider
+
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
 
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.
 
<code>IFluidHandler</code> capability that allows <code>ItemStack</code>s to define a custom container.
Line 106: Line 103:     
A default reference implementation for this capability interface is provided in <code>EnergyStorage</code>.
 
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 ==
 
== Working with Capabilities ==
    
Both capability providers and users need to be able to provide and access capabilities through a common framework,
 
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
+
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''',
 
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.
 
need to work together and with Forge to ensure that the common interface is used sensibly and correctly by all parties.
Line 121: Line 113:  
=== Obtaining a Capability ===
 
=== Obtaining a Capability ===
   −
Before being able to work with a capability, it is necessary to obtain an instance of the <code>Capability</code> object
+
Before being able to work with a capability, it is necessary to obtain an instance of the <code>Capability</code> object itself.
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
+
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.
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:
+
{{Tabs/Code Snippets
 +
|java=
 +
public static Capability<IItemHandler> ITEM_HANDLER = CapabilityManager.get(new CapabilityToken<>(){});
 +
}}
   −
<syntaxhighlight lang="java">
+
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>.
public static Capability<IEnergyStorage> ENERGY = null;
     −
@CapabilityInject(IEnergyStorage.class)
+
This is, for obvious reasons, redundant, since that capability is also available through
private static void onEnergyStorageInit(Capability<IEnergyStorage> capability) {
+
<code>ForgeCapabilities</code>.
    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 ===
Line 207: Line 151:  
With all of the above in mind, part of a capability provider implementation may be similar to the following snippet:
 
With all of the above in mind, part of a capability provider implementation may be similar to the following snippet:
   −
<syntaxhighlight lang="java">
+
{{Tabs/Code Snippets
 +
|java=
 
// suppose the presence of a field 'inventory' of type 'IItemHandler'
 
// suppose the presence of a field 'inventory' of type 'IItemHandler'
   Line 214: Line 159:  
@Override
 
@Override
 
public <T> LazyOptional<T> getCapability(Capability<T> capability, @Nullable Direction direction) {
 
public <T> LazyOptional<T> getCapability(Capability<T> capability, @Nullable Direction direction) {
     if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY
+
     if (capability == ForgeCapabilities.ITEM_HANDLER
             && (direction == null || direction == Direction.UP || direction == Direction.DOWN)) {
+
             && (direction == null {{!}}{{!}} direction == Direction.UP {{!}}{{!}} direction == Direction.DOWN)) {
 
         return this.inventoryOptional.cast();
 
         return this.inventoryOptional.cast();
 
     }
 
     }
Line 226: Line 171:  
     this.inventoryOptional.invalidate();
 
     this.inventoryOptional.invalidate();
 
}
 
}
</syntaxhighlight>
+
}}
    
This possible implementation of a capability provider exposes an <code>IItemHandler</code> capability and restricts
 
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
 
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
+
<code>BlockEntity</code>, then we may also say that the inventory is only accessible from the top and the bottom of the
 
block.
 
block.
    
Moreover, the capability gets automatically invalidated when the provider gets invalidated. Assuming this is a
 
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.
+
<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
 
The <code>super</code> call at the end of the <code>getCapability</code> method is extremely important, since it's what
Line 248: Line 193:  
listen to the <code>AttachCapabilitiesEvent&lt;T&gt;</code>. The <code>T</code> in this case represents the capability
 
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
 
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>,
+
capability provider, not a subclass. As an example, if you want to attach a capability to a <code>MyBlockEntity</code>,
which extends <code>TileEntity</code>, you'll have to listen to <code>AttachCapabilitiesEvent&lt;TileEntity&gt;</code>,
+
which extends <code>BlockEntity</code>, you'll have to listen to <code>AttachCapabilitiesEvent&lt;BlockEntity&gt;</code>,
'''NOT''' to <code>AttachCapabilitiesEvent&lt;MyTileEntity&gt;</code>, since the latter will never fire.
+
'''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  
 
The attaching agent can use the provided methods <code>getObject</code>, <code>addCapability</code>, and  
Line 268: Line 213:  
With the above in mind, part of an attaching agent may be similar to the following snippet of code:
 
With the above in mind, part of an attaching agent may be similar to the following snippet of code:
   −
<syntaxhighlight lang="java">
+
{{Tabs/Code Snippets
 +
|java=
 
@SubscribeEvent
 
@SubscribeEvent
public void onAttachingCapabilities(final AttachCapabilitiesEvent<TileEntity> event) {
+
public void onAttachingCapabilities(final AttachCapabilitiesEvent<BlockEntity> event) {
     if (!(event.getObject() instanceof EnergyBasedTileEntity)) return;
+
     if (!(event.getObject() instanceof EnergyBasedBlockEntity)) return;
   −
     EnergyStorage backend = new EnergyStorage(((EnergyBasedTileEntity) event.getObject()).capacity);
+
     EnergyStorage backend = new EnergyStorage(((EnergyBasedBlockEntity) event.getObject()).capacity);
 
     LazyOptional<IEnergyStorage> optionalStorage = LazyOptional.of(() -> backend);
 
     LazyOptional<IEnergyStorage> optionalStorage = LazyOptional.of(() -> backend);
   Line 279: Line 225:  
         @Override
 
         @Override
 
         public <T> LazyOptional<T> getCapability(Capability<T> cap, @Nullable Direction direction) {
 
         public <T> LazyOptional<T> getCapability(Capability<T> cap, @Nullable Direction direction) {
             if (cap == CapabilityEnergy.ENERGY) {
+
             if (cap == ForgeCapabilities.ENERGY) {
 
                 return optionalStorage.cast();
 
                 return optionalStorage.cast();
 
             }
 
             }
Line 287: Line 233:     
     event.addCapability(new ResourceLocation("examplemod", "fe_compatibility"), provider);
 
     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
 
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>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.
 
<code>LazyOptional</code> for invalidation if the parent capability provider gets invalidated.
   Line 307: Line 252:  
The previous example reworked to use a Persistent Capability Provider may be similar to the following snippet:
 
The previous example reworked to use a Persistent Capability Provider may be similar to the following snippet:
   −
<syntaxhighlight lang="java">
+
{{Tabs/Code Snippets
 +
|java=
 
@SubscribeEvent
 
@SubscribeEvent
public void onAttachingCapabilities(final AttachCapabilitiesEvent<TileEntity> event) {
+
public void onAttachingCapabilities(final AttachCapabilitiesEvent<BlockEntity> event) {
     if (!(event.getObject() instanceof EnergyBasedTileEntity)) return;
+
     if (!(event.getObject() instanceof EnergyBasedBlockEntity)) return;
   −
     EnergyStorage backend = new EnergyStorage(((EnergyBasedTileEntity) event.getObject()).capacity);
+
     EnergyStorage backend = new EnergyStorage(((EnergyBasedBlockEntity) event.getObject()).capacity);
 
     LazyOptional<IEnergyStorage> optionalStorage = LazyOptional.of(() -> backend);
 
     LazyOptional<IEnergyStorage> optionalStorage = LazyOptional.of(() -> backend);
     Capability<IEnergyStorage> capability = CapabilityEnergy.ENERGY;
+
     Capability<IEnergyStorage> capability = ForgeCapabilities.ENERGY;
   −
     ICapabilityProvider provider = new ICapabilitySerializable<IntNBT>() {
+
     ICapabilityProvider provider = new ICapabilitySerializable<IntTag>() {
 
         @Override
 
         @Override
 
         public <T> LazyOptional<T> getCapability(Capability<T> cap, @Nullable Direction direction) {
 
         public <T> LazyOptional<T> getCapability(Capability<T> cap, @Nullable Direction direction) {
Line 326: Line 272:     
         @Override
 
         @Override
         public IntNBT serializeNBT() {
+
         public IntTag serializeNBT() {
             return capability.getStorage().writeNbt(capability, backend, null);
+
             return backend.serializeNBT();
 
         }
 
         }
    
         @Override
 
         @Override
         public void deserializeNBT(IntNBT nbt) {
+
         public void deserializeNBT(IntTag tag) {
             capability.getStorage().readNBT(capability, backend, null, nbt);
+
             backend.deserializeNBT(tag);
 
         }
 
         }
 
     };
 
     };
    
     event.addCapabilities(new ResourceLocation("examplemod", "fe_compatibility"), provider);
 
     event.addCapabilities(new ResourceLocation("examplemod", "fe_compatibility"), provider);
    event.addListener(optionalStorage::invalidate);
   
}
 
}
</syntaxhighlight>
+
}}
    +
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 ===
Line 365: Line 311:  
With the above in mind, part of an user may be similar to the following snippet of code:
 
With the above in mind, part of an user may be similar to the following snippet of code:
   −
<syntaxhighlight lang="java">
+
{{Tabs/Code Snippets
private final Map<Direction, LazyOptional<IEnergyStorage>> cache = new HashMap<>();
+
|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) {
 
private void sendPowerTo(int power, Direction direction) {
Line 372: Line 320:     
     if (targetCapability == null) {
 
     if (targetCapability == null) {
         ICapabilityProvider provider = world.getTileEntity(pos.offset(direction));
+
         ICapabilityProvider provider = level.getBlockEntity(pos.relative(direction));
         targetCapability = provider.getCapability(CapabilityEnergy.ENERGY, direction.getOpposite());
+
         targetCapability = provider.getCapability(ForgeCapabilities.ENERGY, direction.getOpposite());
 
         cache.put(direction, targetCapability);
 
         cache.put(direction, targetCapability);
 
         targetCapability.addListener(self -> cache.put(direction, null));
 
         targetCapability.addListener(self -> cache.put(direction, null));
Line 380: Line 328:  
     targetCapability.ifPresent(storage -> storage.receiveEnergy(power, false));
 
     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
+
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
 
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
 
targeted capability. If the check succeeds, then no lookup is performed; if the check fails, the targeted Capability
Line 394: Line 342:  
a mod may require a custom solution. For this reason, Forge provides a way to define a custom Capability.
 
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
+
Defining a custom Capability requires the user to provide one main component: the Capability Interface. Optionally, a
Capability Implementation, and the Capability Storage. Optionally, a Capability Provider can also be created. In this
+
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  
 
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.
 
for all these components are described in the respective sections of this article.
Line 413: Line 361:  
The Capability Implementation, on the other hand, is the implementation of the previously defined Capability Interface.
 
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
 
Usual rules for interface implementations follow. There can be more than one Capability Implementation for each
capability, but no less than one.
+
capability.  
    
Note that a '''well-formed''' capability implementation should '''not store''' the Capability Provider inside of it: we
 
Note that a '''well-formed''' capability implementation should '''not store''' the Capability Provider inside of it: we
Line 419: Line 367:  
more as a guideline. There are in fact certain situations where this cannot be avoided (e.g. attaching a client-synced
 
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>).
 
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
 
Given all of the above information, this may be an example implementation of both a Capability Interface and a
 
Capability Implementation:
 
Capability Implementation:
   −
<syntaxhighlight lang="java">
+
{{Tabs/Code Snippets
 +
|java=
 
public interface MyCapability {
 
public interface MyCapability {
 
     String getValue();
 
     String getValue();
Line 447: Line 391:  
     }
 
     }
 
}
 
}
</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
+
Note that in this case, only a single implementation is provided.
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 ===
Line 506: Line 402:  
to those for more information.
 
to those for more information.
   −
=== Tying it All Together ===
+
=== Registering Capabilities ===
    
Once all components of a Capability have been created, they must be registered so that the game is aware of the
 
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
+
capability's presence. The registration only requires the Capability Interface.
factory for the default capability implementation.
+
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.
   −
The registration can be performed by calling the <code>register</code> method on the <code>CapabilityManager</code>.
+
==== AutoRegisterCapability annotation ====
This '''needs''' to happen when the <code>FMLCommonSetupEvent</code> is fired on the <code>MOD</code> event bus. The
+
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:
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:
+
{{Tabs/Code Snippets
 +
|java=
 +
@AutoRegisterCapability
 +
public interface MyCapability {
 +
    ...
 +
}
 +
}}
   −
<syntaxhighlight lang="java">
+
==== RegisterCapabilitiesEvent ====
public void onCommonSetup(FMLCommonSetupEvent event) {
+
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:
     CapabilityManager.INSTANCE.register(MyCapability.class, new MyCapabilityStorage(), MyCapabilityImplementation::new);
+
 
 +
{{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 ==
 
== Custom Capability Providers ==
Line 536: Line 448:  
By definition, a custom Capability Provider is everything that implements the <code>ICapabilityProvider</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
 
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 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
 
The easiest way of doing this is extending the <code>CapabilityProvider</code> class provided by Forge. This will
Line 545: Line 457:  
== Code Examples ==
 
== Code Examples ==
   −
* [https://gist.github.com/TheSilkMiner/5cc92ba573e7bdd871dfdbffdd5c2806 A Gist showing a quick and dirty example on how to implement a Capability effectively]
+
* [https://gist.github.com/TheCurle/6db954d680f6f067dcdc791355c32c89 A Gist showing a quick and dirty example on how to implement a Capability effectively]
 +
 
 +
 
 +
[[Category:Data Storage]]