Skip to content

Metadata Layer

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

Metadata Layer

Overview

The Metadata Layer is the phase where annotations are interpreted and transformed into executable behavior.

If previous layers answer:

  • Initialization Layer - what exists
  • Registry Layer - what is registered

then the Metadata Layer answers:

“What does this metadata mean, and what should the engine do with it?”

This layer is entirely about annotation handling.


Core Responsibility

The Metadata Layer is responsible for:

  • Scanning all mod classes for annotations
  • Routing annotations to strategies
  • Subscribing strategies to processors
  • Executing processors in a controlled order

No game objects are created here. No registries are touched. Only metadata interpretation happens.


MetadataLayer

Definition

public class MetadataLayer implements EngineLayer

Default Strategy Consumers

public static final AnnotationStrategyConsumer SIMPLE_STRATEGY_CONSUMER
public static final AnnotationStrategyConsumer ASYNC_STRATEGY_CONSUMER

These are predefined execution models for strategies.

Interpretation: They define how a strategy is applied (synchronous vs asynchronous), not what the strategy does.


processAllTasks()

@Override
public void processAllTasks()

Step-by-step behavior

  1. Retrieve all mod classes
Set<Class<?>> classes = ModContext.NEO_MOD.getClasses();
  1. Execute default annotation directors
annotationDirectors.forEach(director -> director.directAll(classes));
  1. Execute dynamically injected annotation directors
InjectionPool.getInstance().getAll(AnnotationDirector.class)
  1. Execute all registered processors
ProcessorPool processorPool = SimpleProcessorPool.getInstance();
processorPool.processAll();

Why two director passes?

  • Default directors - framework-provided behavior
  • Dynamic directors - user extensions, plugins, integrations

This allows third-party metadata processing without modifying core layers.


MetadataLayerBuilder

Default configuration

private static final List<AnnotationDirector> DEFAULT_ANNOTATION_DIRECTORS =
    List.of(
        new ClassAnnotationDirector(),
        new FieldAnnotationDirector(),
        new MethodAnnotationDirector()
    );

This means:

  • Class-level annotations are handled
  • Field-level annotations are handled
  • Method-level annotations are handled

No annotation is implicitly ignored.


Builder execution

this.engineBuilder.processLayer(this.metadataLayer);

The Metadata Layer executes immediately when added, ensuring:

  • All metadata is resolved
  • All processors are prepared
  • No runtime surprises later

AnnotationDirector

Role

An AnnotationDirector:

  • Scans classes
  • Detects annotations
  • Selects appropriate strategies
  • Subscribes them to processors

It does not execute logic itself.

Interpretation: A director is a router, not a worker.


Strategy System

SimpleStrategyPool

public class SimpleStrategyPool implements StrategyPool

This is the global registry of annotation strategies.

It maps:

  • Annotation type
  • Target type (class / field / method)
  • Strategy scope

Strategy lookup

get(annotationClass, typeClass)

This enforces:

  • Annotation type must match
  • Target type must match

If not found → exception There is no silent fallback.


Strategy scopes

Strategies are grouped by StrategyScope.

Scopes control:

  • Which processors receive which strategies
  • Execution grouping
  • Ordering boundaries

Processor System

SimpleProcessorPool

public class SimpleProcessorPool implements ProcessorPool

This is where strategies become executable work.


ProcessorScope

Each processor is identified by:

ProcessorScope

This allows:

  • Multiple independent processors
  • Clear separation of concerns
  • Explicit override behavior

Subscribing strategies

processor.subscribe(strategySpec);

A strategy is:

  • Bound to a processor
  • Queued for execution
  • Isolated from other scopes

Execution

processAll()

This:

  • Iterates processors in insertion order
  • Calls process() on each processor
  • Executes subscribed strategies

No processor is skipped. No implicit ordering.


Overrides & Extensibility

Strategy overrides

override(scope, from, to)

Allows:

  • Replacing framework strategies
  • Custom behavior without forks
  • Safe, scoped modification

Processor overrides

override(scope, processor)

Allows:

  • Replacing execution logic
  • Changing processing model
  • Integrating external systems

Summary

The Metadata Layer:

  • Handles all annotation processing
  • Converts metadata into executable strategies
  • Separates discovery, decision, and execution
  • Supports safe overrides and extensions
  • Guarantees deterministic processing

It is the semantic core of Temporal API.

Clone this wiki locally