Changes

Finish Capability Exposure documentation
Line 75: Line 75:  
=== 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 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'''.
+
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 ====
 
==== Injecting into a Field ====
Line 119: Line 119:  
=== Exposing a Capability ===
 
=== Exposing a Capability ===
   −
Exposing a capability is a voluntary act by a capability provider that allows the capability to be discovered and accessed by clients. This is simply done by returning a non-empty <code>LazyOptional</code> when the <code>getCapability</code> method of a capability provider gets invoked.
+
Exposing a capability is a voluntary act by a capability provider that allows the capability to be discovered and accessed by clients.
 +
 
 +
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'' 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 static final LazyOptional<IItemhandler> INVENTORY_OPTIONAL = LazyOptional.of(() -> 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 INVENTORY_OPTIONAL.cast();
 +
    }
 +
    return super.getCapability(capability, direction); // See note after snippet
 +
}
 +
 
 +
@Override
 +
protected void invalidateCaps() {
 +
    super.invalidateCaps();
 +
    INVENTORY_OPTIONAL.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, either because it's too far away or it simply got broken.
 +
 
 +
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. For more information on when and why this is needed refer to '''The Capability Provider''' section.
    
=== Attaching a Capability ===
 
=== Attaching a Capability ===