Skip to content

What is ModContext

Maksym Uimanov edited this page Jan 29, 2026 · 1 revision

What is ModContext

ModContext is the central runtime context responsible for managing mod metadata and dependency injection pools within the Temporal API.


Purpose

The main responsibility of ModContext is to:

  • track the currently active mod
  • maintain a per-mod InjectionPool
  • provide a global access point for injected objects during engine execution

Core Components

ModContext

ModContext is a singleton that maps each loaded mod (NeoMod) to its corresponding InjectionPool.

Key responsibilities:

  • creating injection pools
  • resolving pools by modId
  • removing pools during cleanup
  • exposing the current mod context via static access

The active mod is stored in:

public static volatile NeoMod NEO_MOD;

NeoMod

NeoMod is an immutable runtime representation of a NeoForge mod.

It encapsulates:

  • mod name
  • mod main class
  • mod ID
  • all discovered mod classes

A NeoMod instance is created using:

NeoMod.create(Class<?> clazz, List<ModClassScanner> scanners)

During creation:

  • the @Mod annotation is validated
  • the mod ID is extracted
  • all registered ModClassScanners are executed
  • discovered classes are collected and stored

This class acts as the authoritative metadata source for a mod.


InjectionPool

InjectionPool is a per-mod object container for dependency injection, implementing ObjectPool.

It is responsible for:

  • storing instantiated objects
  • resolving objects by name, class, or InjectionKey
  • caching lookups for performance
  • supporting multiple objects of the same type

Each mod has exactly one InjectionPool, managed by ModContext.


InjectionPool Behavior

Object Storage

Objects are stored internally as:

Map<InjectionKey, Object>

Each object is indexed by:

  • class
  • optional string name
  • cached lookup keys

Retrieving Objects

You can retrieve objects using:

InjectionPool.getFromInstance(Class<T> clazz)
InjectionPool.getFromInstance(String name)

Resolution rules:

  • exact key match is preferred
  • if missing, the first matching instance of the requested type is returned
  • null is returned if no suitable object exists

Registering Objects

Objects can be registered using:

pool.put(MyClass.class);
pool.put("customName", instance);
pool.put(instance);

Object instantiation uses reflection:

ReflectionUtils.createObject(clazz);

Removing Objects

Objects can be removed by:

  • class
  • instance
  • clearing the entire pool

All caches are kept in sync automatically.


InjectionKey

InjectionKey uniquely identifies an object inside an InjectionPool.

It consists of:

  • a string name
  • a concrete class

Keys can be created using:

new InjectionKey(Class<?> clazz)
new InjectionKey(String name, Class<?> clazz)

InjectionKeys are used internally and cached for fast resolution.


Pool Lifecycle

Creating a Pool

ModContext.createPool(modId)

Rules:

  • only one pool per mod is allowed
  • creating a duplicate pool throws PoolCreationException

Retrieving a Pool

ModContext.getPool(modId)

If no pool exists, a PoolGettingException is thrown.


Removing a Pool

ModContext.removePool(modId)

If the pool does not exist, a PoolDeletionException is thrown.


Global Access Pattern

The active mod’s injection pool is accessed via:

InjectionPool.getInstance()

This resolves:

  1. the current ModContext
  2. the active NeoMod
  3. the corresponding InjectionPool

This pattern is used throughout Temporal API (e.g. factories, annotations).


Thread Safety Notes

  • ModContext and LayerContainer use double-checked locking
  • InjectionPool uses concurrent caches for key resolution
  • object storage itself is not synchronized and assumes controlled engine execution

When Do You Use ModContext Directly?

Most users do not interact with ModContext directly.

Direct usage is only recommended when:

  • writing custom engine layers
  • implementing advanced injection logic
  • debugging engine internals

Clone this wiki locally