Changes

1,193 bytes removed ,  16:08, 2 July 2023
m
no edit summary
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'''.
     −
==== Getting the 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.
   −
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.
+
{{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<IItemHandler> ITEM_HANDLER_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});
  −
</syntaxhighlight>
  −
 
  −
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>.
      
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>.
 
  −
==== 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
  −
type <code>Capability&lt;T&gt;</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:
  −
 
  −
<syntaxhighlight lang="java">
  −
public static Capability<IEnergyStorage> ENERGY = null;
  −
 
  −
@CapabilityInject(IEnergyStorage.class)
  −
private static void onEnergyStorageInit(Capability<IEnergyStorage> capability) {
  −
    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 192: 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 199: 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 211: 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 253: 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 264: 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 272: 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 292: 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 299: 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 322: 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 350: 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 358: 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 365: 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 408: Line 371:  
Capability Implementation:
 
Capability Implementation:
   −
<syntaxhighlight lang="java">
+
{{Tabs/Code Snippets
 +
|java=
 
public interface MyCapability {
 
public interface MyCapability {
 
     String getValue();
 
     String getValue();
Line 427: 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 438: 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.
     −
An example of registration can be found in the snippet that follows:
+
{{Tabs/Code Snippets
 +
|java=
 +
@AutoRegisterCapability
 +
public interface MyCapability {
 +
    ...
 +
}
 +
}}
   −
<syntaxhighlight lang="java">
+
==== 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:
 +
 
 +
{{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 476: 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]]