Changes

870 bytes added ,  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 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
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 321: 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 328: 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 371: Line 371:  
Capability Implementation:
 
Capability Implementation:
   −
<syntaxhighlight lang="java">
+
{{Tabs/Code Snippets
 +
|java=
 
public interface MyCapability {
 
public interface MyCapability {
 
     String getValue();
 
     String getValue();
Line 390: 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 401: 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 439: 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]]