Capabilities
Overview
Balm exposes Capabilities (Forge/NeoForge) and Block APIs (Fabric) through the BalmCapabilities service.
BalmCapabilities capabilities = Balm.getCapabilities();
Default Capabilities
By default, Balm registers capabilities for Minecraft's Container interface as well as Balm's FluidTank and EnergyStorage interfaces.
These capabilities are automatically mapped to the mod-loader native capabilities:
- The
Containercapability is exposed asIItemHandleron Forge and NeoForge, and asItemStorageon Fabric - The
FluidTankcapability is exposed asIFluidHandleron Forge and NeoFore, and asFluidStorageon Fabric - The
EnergyStoragecapability is exposed asIEnergyStorageon Forge and NeoForge, and as a Reborn Energy (third party mod)EnergyStorageon Fabric
This mapping is unidirectional. That means if you expose a Container capability, it will be available to mods supporting IItemHandler or ItemStorage, but querying the Container capability will not provide you with other mod's IItemHandlers or ItemStorages.
It is recommended to use a Mod Proxy or Platform Proxy if you need to access a mod-loader specific capability.
Exposing Default Capabilities
The easiest way to provide these default capabilities is by implementing a respective provider interface on your block entity.
Container→BalmContainerProviderFluidTank→BalmFluidTankProviderEnergyStorage→BalmEnergyStorageProvider
Block entities implementing these interfaces will automatically have them exposed as a capability.
public class YourStorageBlockEntity extends BalmBlockEntity implements BalmContainerProvider {
// ...
@Override
public Container getContainer() {
return container;
}
// ...
}
Retrieving an Existing Capability Type
Most capability-related methods operate on a CapabilityType.
If you are creating a capability yourself, you can use registerType (more on that below), otherwise you can use getType to lookup an already existing capability.
CapabilityType<Block, YourCapability, Direction> SOME_CAPABILITY = capabilities.getType(id("some_capability"), Block.class, YourCapability.class, Direction.class)
Capabilities for Block Entities are registered under the Block scope. There is no separate BlockEntity scope.
On Forge, there is some additional code required before you can access a capability. See "Registering a Capability on Forge" below.
Querying a Capability
You can use getCapability to query for a capability on a block entity using a CapabilityType obtained through either getType or registerType.
EnergyStorage energyStorage = capabilities.getCapability(blockEntity, direction, CommonCapabilities.ENERGY_STORAGE);
Registering a Capability Provider
Capability providers are responsible for exposing capabilities from your block entities. They are registered to specific block entity types.
If you implement BalmContainerProvider, BalmFluidTankProvider or BalmEnergyStorageProvider in your block entity, you do not need to worry about exposing their respective capabilities manually.
capabilities.registerProvider(id("your_capability_provider"),
SOME_CAPABILITY,
(blockEntity, context) -> blockEntity instanceof YourBlockEntity yourBlockEntity ? yourBlockEntity.getSomeCapabilityImplementation() : null,
() -> List.of(
ModBlockEntities.yourBlockEntity.get()
));
Registering a Fallback Capability Provider
You can also register a fallback capability provider. These providers will apply to all block entities.
For example, Balm uses a fallback provider to automatically expose capabilities from block entities that implement the default interfaces (such as BalmContainerProvider).
capabilities.registerFallbackBlockEntityProvider(id("your_fallback_capability_provider"),
SOME_CAPABILITY,
(blockEntity, context) -> blockEntity instanceof YourBlockEntity yourBlockEntity ? yourBlockEntity.getSomeCapabilityImplementation() : null);
Registering a Capability
If you're creating a capability for yourself or other mods to use, you need to register it.
CapabilityType<Block, YourCapability, Direction> YOUR_CAPABILITY = capabilities.registerType(id("your_capability"), Block.class, YourCapability.class, Direction.class);
CapabilityType<Block, YourCapability, Void> YOUR_CAPABILITY = capabilities.registerType(id("your_capability"), Block.class, YourCapability.class, Void.class);
On Forge, there is some additional code required to register a capability. See below for more.
Caveat: Capabilities on Forge
Due to the way Forge's capabilities work, it is necessary to create a CapabilityToken and pre-register capabilities to Balm before calling BalmCapabilities.registerType or BalmCapabilities.getType.
This step cannot be automated by Balm because Forge uses a transformer on CapabilityToken that needs the generic parameter to be statically defined at compile-time.
final var forgeCapabilities = (ForgeBalmCapabilities) Balm.getCapabilities();
forgeCapabilities.preRegisterType(id("your_capability"), CapabilityManager.get(new CapabilityToken<YourCapability>() {
}));
If you use BalmModule, consider registering an additional module in your Forge entrypoint that runs before your common module.
If you use a static intializer, simply make your preRegisterType calls before you call initializeMod.
Caveat: Context for Fluid Tanks on older versions
For backwards compatibility, Balm's default capability for fluid tanks (balm:fluid_tank) has a context of Void.class.
Starting in Minecraft 1.21.5, the context has been changed to Direction.