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<T></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<T></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<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 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<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 | + | 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]] |