Skip to content

Initialization Layer

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

Initialization Layer

The Initialization Layer is the first and most critical layer in the Temporal Engine lifecycle; it discovers mod classes, creates the mod context, and builds the dependency injection environment used by all subsequent layers.


Purpose and Aims

The Initialization Layer exists to:

  • identify and describe the running mod
  • discover all relevant classes belonging to the mod and the API
  • create a per-mod dependency injection container
  • populate that container with core services and external objects
  • prepare a fully initialized runtime context for later layers

Without this layer, no other engine layer can function correctly.


What the Initialization Layer Does

At runtime, the Initialization Layer performs the following steps in strict order:

  1. Creates a NeoMod instance
  2. Discovers mod and API classes
  3. Registers the active mod in ModContext
  4. Creates an InjectionPool
  5. Runs object pool initializers (static and dynamic)

All of this happens inside:

InitializationLayer.processAllTasks()

Step-by-Step Execution Flow

Mod Discovery (NeoMod.create)

NeoMod mod = NeoMod.create(this.modClass, this.classScanners);

This step:

  • validates the presence of @Mod
  • extracts the modId
  • runs all configured ModClassScanners
  • collects all discovered classes into a single set

The result is a complete runtime description of the mod.


Mod Context Registration

ModContext.NEO_MOD = mod;

This establishes:

  • the currently active mod
  • the global execution context for Temporal API

All future dependency resolution depends on this value.


Class Set Construction

Set<Class<?>> initializationClasses = Stream.of(classes, apiClasses)
    .flatMap(Set::stream)
    .collect(Collectors.toSet());

The Initialization Layer combines:

  • mod-specific classes
  • Temporal API internal classes

This ensures that:

  • both user code and framework code participate in dependency injection
  • annotations and processors work uniformly

InjectionPool Creation

ObjectPool objectPool = ModContext.getInstance().createPool(modId);

This creates:

  • a single InjectionPool per mod
  • the core container for all injected objects, factories, handlers, and processors

This pool becomes globally accessible via:

InjectionPool.getInstance()

Object Pool Initialization

The Initialization Layer runs two categories of initializers.

Static (Configured) Initializers

objectPoolInitializers.forEach(initializer -> initializer.initialize(...));

Default initializers include:

  • TemporalRegisterPoolInitializer
  • FactoryPoolInitializer
  • EventBusPoolInitializer
  • ModContainerPoolInitializer
  • InjectedObjectPoolInitializer
  • StrategyPoolInitializer
  • HandlerPoolInitializer
  • ProcessorPoolInitializer

These define the baseline engine capabilities.


Dynamic Initializers

objectPool.getAll(ObjectPoolInitializer.class)

This allows:

  • user-defined initializers
  • dynamically injected initializers
  • plugin-like extensibility

Dynamic initializers are resolved from the InjectionPool itself.


Class Scanners

Class scanners define how classes are discovered during initialization.


ClasspathModClassScanner (default)

ClasspathModClassScanner

This scanner:

  • loads all classes found in the mod file
  • does not filter by annotation
  • guarantees full visibility of mod code

This is the safest and most complete scanner.


AnnotatedModClassScanner

AnnotatedModClassScanner

This scanner:

  • discovers only classes annotated with:
    • @Injected
    • @Strategy
    • @Handler
  • relies on NeoForge scan metadata
  • improves performance for annotation-driven systems

InitializationLayerBuilder

The InitializationLayerBuilder configures and executes the Initialization Layer.

Default Configuration

By default, it uses:

  • ClasspathModClassScanner
  • a full set of engine-provided initializers
  • externally supplied objects (EventBus, ModContainer)

This configuration is sufficient for most mods.


Custom Configuration

You can override behavior using the builder:

configureInitializationLayer()
    .modClass(MyMod.class)
    .classScanners(customScanners)
    .initializers(customInitializers)
    .externalSource(List.of(eventBus, modContainer))
    .and();

Calling .and():

  • finalizes the configuration
  • executes the layer immediately
  • returns control to the EngineBuilder

When Should You Customize the Initialization Layer?

Customization is useful when:

  • you want custom class discovery rules
  • you introduce new dependency injection mechanisms
  • you need to inject external systems early
  • you are extending the Temporal API itself

Most mod developers do not need to customize it.


Key Guarantees After Initialization

After the Initialization Layer finishes:

  • ModContext.NEO_MOD is set
  • InjectionPool exists and is populated
  • class metadata is fully available
  • dependency injection is operational

Every other layer depends on these guarantees.

Clone this wiki locally