How Hot Reloading Works in gdext-nim: Configuration and Limitations

Hot reloading in gdext-nim is enabled by setting reloadable = true in the .gdextension configuration, allowing Godot’s GDExtensionManager to unload and reload the shared library when the editor regains focus after a rebuild.

gdext-nim is a Nim binding for Godot 4’s GDExtension API that supports hot reloading of native shared libraries during development. By marking an extension as reloadable in the configuration DSL, developers can iterate on Nim code without restarting the Godot editor. This article examines the implementation in the source code, the exact reload mechanism, and the critical limitations you must account for in production workflows.

Enabling Hot Reloading in gdext-nim

The reload capability is controlled through the .gdextension file generated during the build process. According to the godot-nim/gdext-nim source code, two specific files handle this configuration: the DSL definition and the build configuration helper.

The Configuration DSL

In src/gdext/private/configdsl.nim, the configuration DSL exposes the reloadable entry that writes the flag to the final INI file:

config.eval:
  entry_symbol = "init_library"
  compatibility_minimum = 4.6
  reloadable = true          # <─ enables hot-reload

The Build Configuration Step

During the configure build step defined in src/gdext/buildconf.nim, the system automatically injects the reloadable flag into the configuration:

configuration.update(setting.updateMethod, "reloadable", "true")

This generates a .gdextension file containing:

[configuration]
entry_symbol = "init_library"
compatibility_minimum = 4.6
reloadable = true

When this file is present, Godot’s GDExtensionManager recognizes the library as eligible for hot reloading.

The Hot Reloading Workflow

Once enabled, the reload process is triggered by Godot’s editor lifecycle rather than explicit Nim code.

How Godot Triggers the Reload

Godot exposes the reload capability through GDExtensionManager.reload_extension(path), which gdext-nim binds in src/gdext/classes/gdgdextensionmanager.nim:


# From gdgdextensionmanager.nim

proc reloadExtension*(extension: String): Error {.gdcall.}

When the editor window regains focus after a successful build, Godot automatically executes this method for any extension marked reloadable = true. As noted in the tutorial and development guide, you simply rebuild and focus the editor to trigger the update.

What Happens During a Reload

The engine performs four distinct operations:

  1. Unload the previously loaded shared library using the internal unload_extension binding.
  2. Load the new library binary via load_extension.
  3. Re-register all classes, methods, signals, and properties defined in Nim through the init_library entry point.
  4. Preserve existing node instances while re-initializing their fields through the newly registered constructors.

Limitations of gdext Hot Reloading

While hot reloading accelerates development, the implementation has specific constraints regarding state management and structural changes.

Structural Fragility

Adding new classes or methods works reliably, but removing classes, changing inheritance hierarchies, or altering exported property layouts can leave the editor in an inconsistent state. The documentation advises a full editor restart if puzzling errors appear after such changes.

Persistent Runtime State

Because the Nim runtime does not reset process memory during a reload, module-level globals and static data persist across reloads. You must manually re-initialize cached state in methods like _ready to avoid undefined behavior.

Platform and Workflow Constraints

  • Editor focus requirement: Godot checks for changes only when the editor window gains focus. If you remain in an external IDE, the reload waits until you switch back.
  • Windows file locks: On Windows, the build script works around DLL locks by writing to a temporary file and renaming it, but occasional lock contention may still require a manual restart.
  • Shared library only: Hot reloading requires dynamic library builds (.so, .dll, .dylib). Static builds cannot be hot-reloaded.

Error Handling Limitations

If the new library fails to load (e.g., missing symbols), Godot logs an error but continues using the previously loaded version. This silent failure may hide problems until you manually restart the editor.

Practical Implementation Examples

The following patterns demonstrate safe hot reloading practices based on the source code structure.

Basic Configuration

A minimal bootstrap.nim entry point that generates a reloadable extension:

import gdext
import classes/gdMyClass  # Your {.gdsync.} classes

GDExtensionEntryPoint   # Generates the .gdextension file with reloadable = true

Build and reload:

gdextwiz build    # Compiles to libnim.so

# Focus Godot editor to trigger automatic reload

Handling Persistent State

Reset module-level state after reloads by implementing initialization logic in _ready:

type MyClass* {.gdsync.} = ptr object of Node
  counter*: int = 0

method _ready(self: MyClass) {.gdsync.} =
  # Re-initialize after hot reload

  self.counter = 0
  print "State reset after reload"

Forcing a Manual Reload

From GDScript, you can trigger a reload programmatically:

var manager = Engine.get_singleton("GDExtensionManager")
manager.reload_extension("res://my_extension.gdextension")

Summary

  • Enable hot reloading by setting reloadable = true in the configuration DSL (src/gdext/private/configdsl.nim), which src/gdext/buildconf.nim injects during the configure step.
  • Godot’s GDExtensionManager.reload_extension unloads and reloads the shared library when the editor regains focus after a build.
  • New classes and methods register automatically, but structural changes (removed classes, property layout changes) often require a full editor restart.
  • Global variables and cached resources survive reloads; explicitly reset state in _ready methods.
  • The feature works only with dynamic shared libraries and requires the editor window to receive focus to detect changes.

Frequently Asked Questions

How do I enable hot reloading in a gdext-nim project?

Set reloadable = true inside the config.eval block in your Nim source or rely on the default GDExtensionEntryPoint template, which generates a .gdextension file containing reloadable = true. Rebuild with gdextwiz build and focus the Godot editor to trigger the reload.

Why does my gdext extension not reload after building?

Godot checks for file changes only when the editor window gains focus. If you stay in your IDE or terminal, the reload will not trigger until you switch back to the editor. Additionally, ensure your .gdextension file contains the reloadable = true flag and that you are building a dynamic shared library (not a static binary).

Can I hot reload changes to class properties or inheritance in gdext-nim?

Adding properties or methods generally works, but removing classes, changing inheritance hierarchies, or altering the memory layout of exported properties can corrupt the editor state. If you encounter cryptic errors after such changes, save your work and restart the Godot editor completely.

How do I handle global variables that persist across hot reloads?

Module-level globals in Nim survive hot reloads because the process memory is not reset. You must manually re-initialize any cached data or singletons in your class's _ready method or another initialization hook to ensure they match the newly loaded library's state.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →