-
Notifications
You must be signed in to change notification settings - Fork 41
Registering and using a component
To get started, you only need a class implementing the Component
interface. It is good practice to make an interface for your component separate from the implementation, so that internals get properly encapsulated and so that the component itself can be used as an API by other mods.
Example:
public interface IntComponent extends Component {
int getValue();
}
class RandomIntComponent implements IntComponent {
private int value = (int) (Math.random() * 20);
@Override public int getValue() { return this.value; }
@Override public void readFromNbt(CompoundTag tag) { this.value = tag.getInt("value"); }
@Override public void writeToNbt(CompoundTag tag) { tag.putInt("value", this.value); }
}
A Component
implementation can also implement some extended interfaces to help with tasks such as server-client synchronization or data transfers. More information on those interfaces is available in the javadoc and in the documentation for each module.
Components are provided by various objects through the ComponentProvider
interface.
To interact with those, you need to obtain a ComponentKey
instance - a unique key made up of an identifier and of the component's type information. Such an instance can be retrieved in a few ways, all using the ComponentRegistry
. Note that the same Component
implementation can be reused between several ComponentKey
s.
Since 2.4, components are declared and attached statically. These components will have runtime-generated dedicated fields in the relevant component containers, guaranteeing a ComponentKey#get
performance comparable to direct field access.
To register a component, you must first declare your component type id in your fabric.mod.json
's custom
properties:
{
"schemaVersion": 1,
"id": "mymod",
"custom": {
"cardinal-components": [
"mymod:magik"
]
}
}
It is safe to declare a component type that belongs to another mod in this array. It is also safe to declare an id that may not be registered at runtime.
Then, to retrieve the ComponentKey
:
// retrieving a type for my component or for a required dependency's
public static final ComponentKey<IntComponent> MAGIK =
ComponentRegistry.getOrCreate(new Identifier("mymod", "magik"), IntComponent.class);
If your mod uses another mod's component, you may not want to (or may not be able to) register it yourself - in which case you can use the get
method (note that it returns null
if the component was not registered).
// retrieving a component type registered by an optional dependency
public static final Lazy<@Nullable ComponentKey<?>> BLUE =
new Lazy<>(() -> ComponentRegistry.get(new Identifier("theirmod:blue")));
Components use component factories that will be used to initialize generated ComponentContainer
fields.
Those factories are registered through dedicated entrypoints, which are typically implemented as follows:
public final class MyComponents implements XComponentInitializer[, YComponentInitializer...] {
public static final ComponentKey<IntComponent> MAGIK = ...;
@Override
public void registerXComponentFactories(XComponentFactoryRegistry registry) {
registry.register(MAGIK, XIntComponent::new);
}
}
Where X
is one of the possible component providers (eg. EntityComponentInitializer
, ItemComponentInitializer
).
The component registrar should then be added as an entrypoint to the fabric.mod.json
file, with the component module's name as a key. For example:
{
"schemaVersion": 1,
"id": "mymod",
"entrypoints": {
"cardinal-components-entity": [
"a.b.c.MyComponents"
]
},
"custom": {
"cardinal-components": [
"mymod:magik"
]
}
}
Components are added to a provider in the order they were registered. This ordering is reflected in (de)serialization, synchronization, and ticking. Currently, components are initialized all at once, which means a component cannot reference another component in its constructor (unless the latter is attached to another provider).