Line 66: |
Line 66: |
| | | |
| 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 104: |
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. This api does not work post 1.12 at the moment.
| |
| | | |
| == 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 120: |
Line 115: |
| Before being able to work with a capability, it is necessary to obtain an instance of the <code>Capability</code> object itself. | | Before being able to work with a capability, it is necessary to obtain an instance of the <code>Capability</code> object itself. |
| | | |
− | A <code>Capability</code> can 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. | + | 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. |
| | | |
− | <syntaxhighlight lang="java">
| + | {{Tabs/Code Snippets |
− | public static Capability<IItemHandler> ITEM_HANDLER_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){}); | + | |java= |
− | </syntaxhighlight>
| + | public static Capability<IItemHandler> ITEM_HANDLER = CapabilityManager.get(new CapabilityToken<>(){}); |
| + | }} |
| | | |
− | The above code will let Forge know that the field <code>ITEM_HANDLER_CAPABILITY</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>. | + | 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>. |
| | | |
| This is, for obvious reasons, redundant, since that capability is also available through | | This is, for obvious reasons, redundant, since that capability is also available through |
− | <code>CapabilityItemHandler</code>. | + | <code>ForgeCapabilities</code>. |
| | | |
| === Exposing a Capability === | | === Exposing a Capability === |
Line 155: |
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 162: |
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 174: |
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 |
Line 216: |
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<BlockEntity> event) { | | public void onAttachingCapabilities(final AttachCapabilitiesEvent<BlockEntity> event) { |
Line 227: |
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 235: |
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 |
Line 255: |
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<BlockEntity> event) { | | public void onAttachingCapabilities(final AttachCapabilitiesEvent<BlockEntity> event) { |
Line 262: |
Line 260: |
| EnergyStorage backend = new EnergyStorage(((EnergyBasedBlockEntity) 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<IntTag>() { | | ICapabilityProvider provider = new ICapabilitySerializable<IntTag>() { |
Line 285: |
Line 283: |
| | | |
| 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 313: |
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 |
| + | |java= |
| // note the use of EnumMap, which is much more performant than HashMap for enum keys | | // 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 final Map<Direction, LazyOptional<IEnergyStorage>> cache = new EnumMap<>(Direction.class); |
Line 322: |
Line 321: |
| if (targetCapability == null) { | | if (targetCapability == null) { |
| ICapabilityProvider provider = level.getBlockEntity(pos.relative(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 329: |
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>BlockEntity</code> the neighboring capability provider | | This example implementation of an user is querying via a <code>BlockEntity</code> the neighboring capability provider |
Line 372: |
Line 371: |
| Capability Implementation: | | Capability Implementation: |
| | | |
− | <syntaxhighlight lang="java">
| + | {{Tabs/Code Snippets |
| + | |java= |
| public interface MyCapability { | | public interface MyCapability { |
| String getValue(); | | String getValue(); |
Line 391: |
Line 391: |
| } | | } |
| } | | } |
− | </syntaxhighlight>
| + | }} |
| | | |
| Note that in this case, only a single implementation is provided. | | Note that in this case, only a single implementation is provided. |
Line 402: |
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 only the Capability Interface. | + | capability's presence. The registration only requires the Capability Interface. |
| + | 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 within the <code>RegisterCapabilitiesEvent</code> which is fired on the <code>MOD</code> event bus. The
| + | ==== AutoRegisterCapability annotation ==== |
− | registration will also automatically inject the created Capability into all relevant fields and methods: refer to
| + | 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: |
− | [[#Obtaining a Capability|Obtaining a Capability]] for more information.
| + | |
| + | {{Tabs/Code Snippets |
| + | |java= |
| + | @AutoRegisterCapability |
| + | public interface MyCapability { |
| + | ... |
| + | } |
| + | }} |
| | | |
− | An example of registration can be found in the snippet that follows: | + | ==== RegisterCapabilitiesEvent ==== |
| + | 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: |
| | | |
− | <syntaxhighlight lang="java">
| + | {{Tabs/Code Snippets |
| + | |java= |
| @SubscribeEvent | | @SubscribeEvent |
| public void registerCaps(RegisterCapabilitiesEvent event) { | | public void registerCaps(RegisterCapabilitiesEvent event) { |
| event.register(MyCapability.class); | | event.register(MyCapability.class); |
| } | | } |
− | </syntaxhighlight>
| + | }} |
| | | |
| === Class Diagram === | | === Class Diagram === |
Line 446: |
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]] | | [[Category:Data Storage]] |