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 118: |
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 204: |
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 211: |
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 223: |
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 265: |
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 276: |
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 284: |
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 304: |
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 311: |
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 334: |
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 362: |
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 370: |
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 377: |
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 420: |
Line 371: |
| Capability Implementation: | | Capability Implementation: |
| | | |
− | <syntaxhighlight lang="java">
| + | {{Tabs/Code Snippets |
| + | |java= |
| public interface MyCapability { | | public interface MyCapability { |
| String getValue(); | | String getValue(); |
Line 439: |
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 450: |
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. |
| + | |
| + | ==== AutoRegisterCapability annotation ==== |
| + | 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: |
| | | |
− | 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
| + | {{Tabs/Code Snippets |
− | registration will also automatically inject the created Capability into all relevant fields and methods: refer to
| + | |java= |
− | [[#Obtaining a Capability|Obtaining a Capability]] for more information.
| + | @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 === |
| + | |
| + | [[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 488: |
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]] |