Implementing Features
Use the initializer lifecycle and Core API to add features to your mod.
Once your mod is scaffolded, the real work begins: implementing features using the initializer lifecycle and the Core API.
The Initializer Lifecycle
Every mod entry point implements one or more initializer interfaces. Each provides hook methods that Nox calls at specific moments.
Base: IModInitializer
All initializers inherit from this one. Every method has a default empty body — override only what you need.
| Method | Called when |
|---|---|
OnInitialize(IModCoreAPI api) | Mod is loaded (synchronous). |
OnInitializeAsync(IModCoreAPI api) | Mod is loaded (async). Use for I/O. |
OnPostInitialize() | All mods have finished OnInitialize. |
OnPostInitializeAsync() | All mods have finished OnInitializeAsync. |
OnUpdate() | Every frame. |
OnLateUpdate() | Every frame, after all Update. |
OnFixedUpdate() | Physics tick (fixed timestep). |
OnPreDispose() | Before the mod is unloaded (sync). |
OnPreDisposeAsync() | Before the mod is unloaded (async). |
OnDispose() | Cleanup — unregister listeners, null references. |
OnDisposeAsync() | Async cleanup. |
Context-specific initializers
Each context adds its own lifecycle methods on top of the base. They all follow the same pattern — pick the one matching your target:
The base IModInitializer methods are always called, regardless of context.
Context-specific methods are called in addition when running in that context.
The Core API (IModCoreAPI)
Passed to OnInitialize, it's your gateway to the Nox runtime:
public void OnInitialize(IModCoreAPI api) {
// Access other mods
var allMods = api.ModAPI.GetMods();
// Read/write persistent config
var config = api.ConfigAPI.GetConfig("my_key");
// Log messages
api.LoggerAPI.Log("Mod initialized!");
// Subscribe to events
api.EventAPI.Subscribe("user.login", OnUserLogin);
// Access assets from your Assets/ folder
var prefab = api.AssetAPI.Load<GameObject>("my_prefab");
}Available APIs
| Property | Type | Description |
|---|---|---|
ModMetadata | IModMetadata | Your mod's metadata (id, version, authors). |
ModAPI | IModAPI | Query and interact with other loaded mods. |
EventAPI | IEventAPI | Pub/sub event system for cross-mod communication. |
AssetAPI | IAssetAPI | Load assets from your mod's Assets/ folder. |
ConfigAPI | IConfigAPI | Persistent per-mod storage with dot-path access. |
LoggerAPI | ILoggerAPI | Structured logging with levels and Unity context. |
LibAPI | ILibAPI | Load native libraries (.dll, .so, .dylib). |
IMainModCoreAPI, IClientModCoreAPI, etc. are currently empty marker interfaces extending IModCoreAPI.
They exist so context-specific APIs can be added in the future without breaking existing mods.
Context-specific CoreAPIs
Each context receives its own CoreAPI variant. Today they are empty extensions of IModCoreAPI, but they allow future context-specific additions:
Writing a Feature End-to-End
Let's build a simple Greeter mod that says hello.
Define the API (SDK layer)
SDK contains only interfaces and enums — no logic:
namespace My.Super.Mod {
public interface IGreeterAPI {
string SayHello(string name);
}
}Simple implementation (CCK layer)
CCK provides a simple, default implementation of the SDK interfaces:
namespace My.Super.Mod.CCK {
public class Greeter : IGreeterAPI {
public string SayHello(string name) {
return $"Hello {name}, welcome to Nox!";
}
}
}Mod logic (Runtime layer)
Runtime contains the actual logic and the Main entry point. Here we implement the API, log on startup, and clean up:
using Nox.CCK.Mods.Cores;
using Nox.CCK.Mods.Initializers;
namespace My.Super.Mod.Runtime {
public class Main : IMainModInitializer, IGreeterAPI {
private IModCoreAPI coreAPI;
private Greeter greeter;
public void OnInitialize(IModCoreAPI api) {
coreAPI = api;
greeter = new Greeter();
coreAPI.LoggerAPI.Log("Greeter mod initialized!");
}
public void OnDispose() {
greeter = null;
coreAPI = null;
}
public string SayHello(string name) {
return greeter.SayHello(name);
}
}
}Best Practices
- Always clean up in
OnDispose: unsubscribe from events, nullify static references, cancel pending async operations. - SDK = interfaces/enums only: no logic, no implementations.
- Use
[NoxPublic]to mark methods intended for public consumption (helps with documentation generation). - Test in both Editor and build: some APIs behave differently (e.g., file paths,
Application.*).
Continue to Building Your Mod to compile and package it.