# Godot GDExtension Initialization Levels: Controlling Class Registration Order in Nim

> Master Godot GDExtension initialization levels Core Servers Scene Editor. Control class registration order in Nim for precise dependency management and unlock seamless GDExtension development.

- Repository: [godot-nim 4+/gdext-nim](https://github.com/godot-nim/gdext-nim)
- Tags: deep-dive
- Published: 2026-03-02

---

**The gdext-nim bindings use four initialization levels (Core, Servers, Scene, Editor) to sequence when GDExtension classes become visible to Godot, ensuring dependencies are satisfied by registering classes in a precise compile-time defined order.**

The gdext-nim library (godot-nim/gdext-nim) implements Godot 4's GDExtension initialization protocol through a structured enum that maps directly to the engine's startup phases. Understanding these **initialization levels** is essential for controlling **class registration order** and ensuring your Nim types are available only after their dependencies are initialized.

## Understanding the Four Initialization Levels

Godot 4 loads GDExtensions in four distinct phases, each represented in the Nim bindings by the `InitializationLevel` enum defined in `src/gdext/gen/gdextensioninterface.nim`:

```nim
type InitializationLevel* {.importc: "GDExtensionInitializationLevel".} = enum
  INITIALIZATION_CORE = 0, INITIALIZATION_SERVERS = 1,
  INITIALIZATION_SCENE = 2, INITIALIZATION_EDITOR = 3

```

Each level corresponds to a specific stage of engine startup:

- **Core** (`INITIALIZATION_CORE`) — Executes first. Registers fundamental Godot types such as `Object` and `RefCounted` that have no external dependencies.
- **Servers** (`INITIALIZATION_SERVERS`) — Executes second. Initializes engine subsystems including `RenderingServer`, `PhysicsServer3D`, and `AudioServer`.
- **Scene** (`INITIALIZATION_SCENE`) — Executes third. Registers classes that depend on the scene tree, such as `Node`, `Resource`, and most custom gameplay classes. This is the **default level** when no pragma is specified.
- **Editor** (`INITIALIZATION_EDITOR`) — Executes fourth and only when the editor is present. Registers editor-only tools and classes that should not exist in exported runtime builds.

## How Initialization Levels Control Class Registration

The `initLevel` pragma in gdext-nim does not affect runtime behavior directly; instead, it controls compile-time symbol storage and runtime registration sequencing through three coordinated mechanisms.

### Compile-Time Registration Tables

When you define a type with the `gdsync` macro and an `initLevel` pragma, the macro stores the type's symbolic name in a compile-time array indexed by the chosen level. In `src/gdext/private/internalbridge.nim`, this array is declared as:

```nim
var implicitRegistrations* {.compileTime.}: array[InitializationLevel, seq[NimNode]]

```

This array acts as a staging area, collecting all classes that must be registered at each specific initialization phase before any runtime code executes.

### The initLevel Pragma and Level Mapping

The `gdsync` macro interprets the pragma's token through the `toLevel` procedure in `src/gdext/bridge.nim`. This mapper converts the pragma's identifier into the corresponding enum value:

```nim
proc toLevel(node: NimNode): InitializationLevel =
  if node.isNil: Initialization_Default
  elif node.eqIdent "Initialization_Core":   Initialization_Core
  elif node.eqIdent "Initialization_Servers": Initialization_Servers
  elif node.eqIdent "Initialization_Scene":   Initialization_Scene
  elif node.eqIdent "Initialization_Editor":  Initialization_Editor
  else: Initialization_Default

```

If no `initLevel` pragma is provided, the macro defaults to `Initialization_Scene`, which explains why most node classes automatically register during the Scene phase.

### Runtime Initialization Sequence

The entry point in `src/gdext.nim` orchestrates the actual registration by iterating through the levels in strict order. During extension startup, the `initializer` procedure executes level-specific setup functions, then invokes `registerImplicitly` to register all classes stored for that level:

```nim
case p_level
of Initialization_Core:
  exec_initialize_core()
  registerImplicitly(Initialization_Core)
of Initialization_Servers:
  exec_initialize_servers()
  registerImplicitly(Initialization_Servers)
of Initialization_Scene:
  initializeExtensionMain()
  exec_initialize_scene()
  registerImplicitly(Initialization_Scene)
of Initialization_Editor:
  exec_initialize_editor()
  registerImplicitly(Initialization_Editor)

```

Consequently, a class declared with `initLevel: Initialization_Servers` is guaranteed to register after the core types but before any scene-dependent classes, ensuring that server APIs like `RenderingServer` are fully initialized when your class becomes visible to the engine.

## Practical Code Examples

### Scene Level Nodes (Default)

Most gameplay classes should use the default Scene level, which provides access to the fully initialized engine including the scene tree and resource loaders:

```nim
type MyNode* {.gdsync.} = ptr object of Node
proc _ready(self: MyNode) {.gdsync.} =
  print "Ready! Scene tree is available."

```

Since no `initLevel` pragma is specified, `gdsync` assigns this to `Initialization_Scene`, storing it in `implicitRegistrations[Initialization_Scene]` for registration during the third initialization phase.

### Server Subsystem Extensions

For extensions that interact with Godot's servers directly—such as custom rendering or physics utilities—explicitly declare the Servers level to ensure dependencies are ready:

```nim
type MyServer* {.gdsync, initLevel: Initialization_Servers.} = ptr object of Object
proc doSomething(self: MyServer) {.gdsync.} =
  # Safe to call: RenderingServer is guaranteed initialized

  RenderingServer.instanceCreate()

```

This class is stored in the Servers slot of the registration array and becomes available during the second initialization phase, allowing subsequent Scene-level classes to reference it.

### Editor-Only Tool Classes

Use the Editor level to prevent classes from being included in runtime exports, keeping editor tooling isolated:

```nim
type MyEditorTool* {.gdsync, tool, initLevel: Initialization_Editor.} = ptr object of Node
proc _process(self: MyEditorTool, delta: float) {.gdsync.} =
  if Engine.isEditorHint:
    print "Running inside the editor"

```

Because this registers only during the Editor phase, the class is absent from exported project binaries, preventing accidental dependencies on editor-only APIs in production builds.

## Summary

- **Four distinct phases** (Core, Servers, Scene, Editor) control when GDExtension classes become visible to Godot, implemented in `src/gdext/gen/gdextensioninterface.nim`.
- **Compile-time arrays** in `src/gdext/private/internalbridge.nim` stage class symbols by level before runtime.
- **The `toLevel` mapper** in `src/gdext/bridge.nim` converts `initLevel` pragma tokens to enum values, defaulting to Scene.
- **Sequential registration** in `src/gdext.nim` ensures classes register only after their dependencies are initialized, using `registerImplicitly` for each phase.
- **Explicit level selection** prevents premature access to uninitialized subsystems and isolates editor-only functionality.

## Frequently Asked Questions

### What happens if I don't specify an initialization level?

If you omit the `initLevel` pragma, the `gdsync` macro defaults to `Initialization_Scene` according to the `toLevel` implementation in `src/gdext/bridge.nim`. Your class will register during the Scene phase, which is appropriate for most Node-derived gameplay classes that need the fully initialized engine.

### Can I register the same class at multiple initialization levels?

No. The `implicitRegistrations` array in `src/gdext/private/internalbridge.nim` stores each class symbol in exactly one sequence corresponding to a single `InitializationLevel` value. If you need functionality across multiple phases, you must design separate classes or use initialization routines that execute at specific levels rather than duplicating class registration.

### Why does my server-level class fail to access RenderingServer?

If you specify `initLevel: Initialization_Core`, your class registers during the first phase—before `RenderingServer` exists. The engine initializes servers during the second phase (`INITIALIZATION_SERVERS`). To access rendering or physics APIs, you must use `initLevel: Initialization_Servers` or later, ensuring the `exec_initialize_servers()` routine has completed before your class is instantiated.

### Are these initialization levels specific to Nim or part of GDExtension?

The levels are defined by Godot's GDExtension C API, not the Nim bindings. The file `src/gdext/gen/gdextensioninterface.nim` imports the enum directly from `GDExtensionInitializationLevel` using `{.importc.}`. All GDExtension libraries (C++, Rust, etc.) use these same four levels; gdext-nim simply provides the `initLevel` pragma as idiomatic syntax for accessing the standard protocol.