Making Custom Component Providers

Making Custom Component Providers  

Barebones Implementation

public class MyCustomProvider implements ComponentProvider {
    private static final ComponentContainer.Factory<MyCustomProvider> CONTAINER_FACTORY =
            .component(MyComponents.KEY1, MyComponent1::new)
            .component(MyComponents.KEY2, MyComponent2::new)

    private final ComponentContainer components = CONTAINER_FACTORY.createContainer(this);

    public ComponentContainer getComponentContainer() {
        return this.components;

The argument class passed to ComponentContainer.Factory can be anything; in most modules it is the provider itself.

Using entrypoints

    private static final ComponentContainer.Factory<MyCustomProvider> CONTAINER_FACTORY;

    static {
        ComponentContainer.Factory.Builder<MyCustomProvider> builder = ComponentContainer.Factory.builder(MyCustomProvider.class);
        for (CustomProviderComponentInitializer entrypoint : FabricLoader.getInstance().getEntrypoints("mymod:custom-provider-components", CustomProviderComponentInitializer.class)) {

    public interface CustomProviderComponentInitializer {
        void registerComponents(ComponentContainer.Factory.Builder<MyCustomProvider> builder);

Adding Synchronization Capabilities

To support AutoSyncedComponent, you must implement 2 other methods (and a temporary kludge):

    public Iterator<ServerPlayerEntity> getRecipientsForComponentSync() {
        // TODO return an iterator over the list of players watching this provider, eg. world.getPlayers().iterator()

    public <C extends AutoSyncedComponent> CustomPayloadS2CPacket toComponentPacket(PacketByteBuf buf, ComponentKey<? super C> key, ComponentPacketWriter writer, ServerPlayerEntity recipient) {
        // TODO write here the information needed to uniquely identify this provider on the client
        // Write the component data
        writer.writeSyncPacket(buf, recipient);
        // Finally, construct the packet with your own channel ID
        return new CustomPayloadS2CPacket(MY_PACKET_ID, buf);

    public boolean supportsCustomComponentPacketWriters() {
        return true;  // temporary required kludge, will be gone in 1.17

Then you must register the corresponding packet listener during the clientโ€™s initialization (relevant wiki page):

            ClientSidePacketRegistry.INSTANCE.register(PACKET_ID, (context, buffer) -> {
                try {
                    // TODO read here the information you wrote in toComponentPacket
                    int myId = buffer.readInt();
                    Identifier componentKeyId = buffer.readIdentifier();
                    ComponentKey<?> componentKey = ComponentRegistry.get(componentKeyId);
                    if (componentKey == null) {
                    PacketByteBuf copy = new PacketByteBuf(buffer.copy());
                    context.getTaskQueue().execute(() -> {
                        try {
                            componentKey.maybeGet(/* TODO get your clientside provider here */)
                                .ifPresent(c -> {
                                    if (c instanceof AutoSyncedComponent) {
                                        ((AutoSyncedComponent) c).applySyncPacket(copy);
                        } finally {
                } catch (Exception e) {
                    LOGGER.error("Error while reading components from network", e);
                    throw e;

CCA Dev Wiki