Changes

3,736 bytes removed ,  20:35, 2 August 2021
Update to 1.17
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.
Line 78: Line 76:  
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 92:  
=== <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 109: Line 107:  
=== <tt>IAnimationStateMachine</tt> ===
 
=== <tt>IAnimationStateMachine</tt> ===
   −
The <code>IAnimationStateMachine</code> capability refers to the ability for any capability provider to leverage the
+
The <code>IAnimationStateMachine</code> capability refers to the ability for any capability provider to leverage the Forge Animation State Machine API for animations. This api does not work post 1.12 at the moment.
Forge Animation State Machine API for animations.
      
== Working with Capabilities ==
 
== Working with Capabilities ==
Line 230: Line 227:  
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 245:  
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 270: Line 267:  
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="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 292: Line 289:     
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 309: Line 306:  
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="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 = CapabilityEnergy.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 323:     
         @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);
 
         }
 
         }
 
     };
 
     };
Line 372: Line 369:     
     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(CapabilityEnergy.ENERGY, direction.getOpposite());
 
         cache.put(direction, targetCapability);
 
         cache.put(direction, targetCapability);
Line 382: Line 379:  
</syntaxhighlight>
 
</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 391:  
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 410:  
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 416:  
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
Line 449: Line 441:  
</syntaxhighlight>
 
</syntaxhighlight>
   −
Note that in this case, only a single implementation is provided, which will also act as the default implementation for
+
Note that in this case, only a single implementation is provided.
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 ===
Line 509: Line 453:     
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 requires specifying only the Capability Interface.
factory for the default capability implementation.
      
The registration can be performed by calling the <code>register</code> method on the <code>CapabilityManager</code>.
 
The registration can be performed by calling the <code>register</code> method on the <code>CapabilityManager</code>.
Line 521: Line 464:  
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="java">
 
public void onCommonSetup(FMLCommonSetupEvent event) {
 
public void onCommonSetup(FMLCommonSetupEvent event) {
     CapabilityManager.INSTANCE.register(MyCapability.class, new MyCapabilityStorage(), MyCapabilityImplementation::new);
+
     CapabilityManager.INSTANCE.register(MyCapability.class);
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 536: Line 479:  
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