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<T></code>. The <code>T</code> in this case represents the capability | | 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 | | 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<TileEntity></code>, | + | which extends <code>BlockEntity</code>, you'll have to listen to <code>AttachCapabilitiesEvent<BlockEntity></code>, |
− | '''NOT''' to <code>AttachCapabilitiesEvent<MyTileEntity></code>, since the latter will never fire. | + | '''NOT''' to <code>AttachCapabilitiesEvent<MyBlockEntity></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<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 === |
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 |