Skip to content

Registry Layer

Maksym Uimanov edited this page Jan 30, 2026 · 2 revisions

Registry Layer

Overview

The Registry Layer is the phase where Temporal API connects your object model to NeoForge’s registry and event system.

If the Initialization Layer answers the question:

“What exists in the mod?”

then the Registry Layer answers:

“What gets registered into Minecraft and how?”

This layer is responsible for:

  • Binding factories to the NeoForge IEventBus
  • Registering DeferredRegisters
  • Creating and wiring custom registries
  • Ensuring all registrations happen exactly once and at the correct lifecycle phase

Purpose & Motivation

Why this layer exists

NeoForge registration is:

  • Event-driven
  • Order-sensitive
  • Easy to break with static init mistakes

The Registry Layer exists to:

  1. Centralize all registration logic
  2. Decouple mod code from raw NeoForge APIs
  3. Guarantee lifecycle correctness
  4. Allow registration logic to be extended dynamically

This avoids:

  • Static init side effects
  • Double registration
  • Registering too late / too early
  • Tight coupling to Forge internals

RegistryLayer

Responsibility

public class RegistryLayer implements EngineLayer

This layer:

  • Pulls the global ObjectPool
  • Resolves the NeoForge EventBus
  • Executes FactoryRegistrars

Execution Flow

@Override
public void processAllTasks() {
    ObjectPool objectPool = InjectionPool.getInstance();
    IEventBus eventBus = objectPool.get(IEventBus.class);

    factoryRegistrars.forEach(factoryRegistrar -> {
        factoryRegistrar.registerFactories(eventBus);
    });
}

What happens:

  1. Retrieves IEventBus from the injection pool
  2. Iterates over all FactoryRegistrars
  3. Each registrar attaches its factories to the event bus

No registration happens immediately - only event listeners are attached.


RegistryLayerBuilder

Default configuration

private static final List<FactoryRegistrar> DEFAULT_FACTORY_REGISTRARS = List.of(new FieldTypeFactoryRegistrar());

By default, Temporal API:

  • Enables field-based factory discovery
  • Requires zero manual registration for common cases

Builder usage

new RegistryLayerBuilder(engineBuilder)
    .factoryRegistrars(customRegistrars)
    .and();

What .and() does

This:

  • Finalizes configuration
  • Executes the Registry Layer immediately
  • Locks in factory registration before runtime events fire

FactoryRegistrar

What is it

A FactoryRegistrar is a bridge between:

  • Temporal abstractions
  • NeoForge’s IEventBus

Their only responsibility:

void registerFactories(IEventBus eventBus);

They do not register objects directly. They register listeners that will do so later.


TemporalRegister

The framework changes the way objects are subscribed to EventBus by introducing TemporalRegister, a child class of DeferredRegister

Field-based loading

protected void loadFields(Class<?>... containers)

TemporalRegister:

  • Forces class loading
  • Triggers static field initialization
  • Ensures all entries are registered before events fire

Purpose:

  • Lazy access to registries
  • Works for both built-in and custom registries
  • Safe to inject and reuse

These automatically:

  • Use the current mod ID
  • Bind to correct vanilla registries
  • Create correct DeferredHolder types

State Protection

TemporalRegister tracks:

  • seenRegisterEvent
  • seenNewRegistryEvent
  • registeredEventBus

Every mutating operation checks these flags.

If violated exception, not undefined behavior


Factories & SubFactories

Their role:

  • Define default object creation or extend default factory behavior
  • Support new registration patterns
  • Enable framework-level extensibility

They integrate via:

  • FactoryRegistrar
  • ObjectPool discovery
  • EventBus listeners

This allows third-party extensions without modifying core code.


Summary

The Registry Layer is where:

  • Objects become Minecraft content
  • Factories attach to lifecycle events
  • Registries are created safely

Clone this wiki locally