Cardinal Components Block
This module has two purposes, allowing mods to attach components to BlockEntity
objects. It was also previously used to expose components as API on any block, but this functionality has been superseded by Fabric API’s API Lookup API.
Usage
Warning: BlockEntity components currently require their holder to call both super.readNbt(nbt)
and super.writeNbt(nbt)
. (Modded) block entities that do not fulfill this criteria will not get their components properly saved.
Registration
Entity components are registered by a BlockComponentInitializer
, exposed as cardinal-components-block
in the mod json (more information on the component registration page).
Component factories are registered per block entity class, thereby attaching components to all instances of that class and of its subclasses. Registering a factory to both a class and its subclass will cause the latter factory to override the former, letting you eg. use a different implementation for trapped chests than for generic containers.
Instead of a specific class, you can also register a component factory with a Predicate<Class<? extends BlockEntity>>
. This lets you use your own criteria like “implements a specific interface”.
Synchronization
BlockEntity components can be automatically synchronized by implementing AutoSyncedComponent
- more information is available on the component synchronization page.
Ticking
BlockEntity components also support both server and client ticking. Components get ticked right after the block entity they are attached to, provided the latter gets ticked through World#tickBlockEntities
. This means that only block entities that implement Tickable
are currently supported.
API Lookup integration
Several helper methods exist in BlockComponents
to help interact with Fabric API’s API Lookup API.
Let us pretend we have the FLUID_CONTAINER
API, as defined in BlockApiLookup
’s usage example.
One could write a compound component that can be attached to any BlockEntity
and give access to a different tank for each face of the block:
public interface FluidContainerCompound extends Component {
ComponentKey<FluidContainerCompound> KEY = ComponentRegistry.register(Identifier.of("mymod:fluid_container_compound"), FluidContainerCompound.class);
FluidContainer get(Direction side);
}
This component could be then used to expose the FLUID_CONTAINER
API on any block, with a single call to BlockComponents#exposeApi
:
@Override
public void onInitialize() {
BlockComponents.exposeApi(
FluidContainerCompound.KEY,
MyApi.FLUID_CONTAINER,
FluidContainerCompound::get
);
}
V1/V2 API (legacy)
Blocks implement the BlockComponentProvider
interface instead of the regular ComponentProvider
. Custom blocks may re-implement that interface themselves to provide components independently of the presence of a BlockEntity. Usually the block simply proxies its Block Entity, however the Block Entity does not need to implement BlockComponentProvider
if the block already has a custom implementation. The module also includes several utility classes to help with providing block components.
Components are entirely compatible with LibBlockAttributes’ attributes.
Since Component
is an interface, any attribute instance can easily implement it. Conversely, making an Attribute
for an existing Component
should be as simple as calling Attributes.create(MyComponent.class)
.
This version of the API does not allow attaching components to existing (eg. vanilla) blocks.