Changes

961 bytes added ,  16:08, 2 July 2023
m
no edit summary
Line 107: Line 107:     
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 115: 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
 +
|java=
 
public static Capability<IItemHandler> ITEM_HANDLER = CapabilityManager.get(new CapabilityToken<>(){});
 
public static Capability<IItemHandler> ITEM_HANDLER = CapabilityManager.get(new CapabilityToken<>(){});
</syntaxhighlight>
+
}}
    
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>.
 
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 all capabilities are available through
+
This is, for obvious reasons, redundant, since that capability is also available through
 
<code>ForgeCapabilities</code>.
 
<code>ForgeCapabilities</code>.
   Line 150: 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'
   −
private LazyOptional<IItemhandler> inventoryOptional = LazyOptional.empty();
+
private final LazyOptional<IItemhandler> inventoryOptional = LazyOptional.of(() -> this.inventory);
    
@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 == ForgeCapabilities.ITEM_HANDLER
 
     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();
 
     }
 
     }
 
     return super.getCapability(capability, direction); // See note after snippet
 
     return super.getCapability(capability, direction); // See note after snippet
}
  −
  −
@Override
  −
public void invalidateCaps()
  −
{
  −
    this.inventoryOptional = LazyOptional.of(() -> this.inventory);
   
}
 
}
   Line 175: 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 217: 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 237: Line 234:  
     event.addCapability(new ResourceLocation("examplemod", "fe_compatibility"), provider);
 
     event.addCapability(new ResourceLocation("examplemod", "fe_compatibility"), provider);
 
}
 
}
</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 286: Line 284:  
     event.addCapabilities(new ResourceLocation("examplemod", "fe_compatibility"), provider);
 
     event.addCapabilities(new ResourceLocation("examplemod", "fe_compatibility"), provider);
 
}
 
}
</syntaxhighlight>
+
}}
   −
Note that when using capabilities on entities, you should manually invalidate the capability via <code>invalidateCaps()</code>, via etc. <code>PlayerEvent.Clone</code>. This is due to the fact that players are recreated & copied when moving across dimensions, not simply moved.
+
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 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
@AutoRegisterCapability // This annotation registers the capability for you automatically!
+
|java=
 
public interface MyCapability {
 
public interface MyCapability {
 
     String getValue();
 
     String getValue();
Line 392: 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 401:  
sections [[#Exposing a Capability|Exposing a Capability]] and [[#Attaching a Capability|Attaching a Capability]]: refer
 
sections [[#Exposing a Capability|Exposing a Capability]] and [[#Attaching a Capability|Attaching a Capability]]: refer
 
to those for more information.
 
to those for more information.
 +
 +
=== Registering Capabilities ===
 +
 +
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 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:
 +
 +
{{Tabs/Code Snippets
 +
|java=
 +
@AutoRegisterCapability
 +
public interface MyCapability {
 +
    ...
 +
}
 +
}}
 +
 +
==== 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
 +
public void registerCaps(RegisterCapabilitiesEvent event) {
 +
    event.register(MyCapability.class);
 +
}
 +
}}
    
=== Class Diagram ===
 
=== Class Diagram ===