| 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 |