Godot Module System Architecture: How C++ Modules Are Registered and Initialized

Godot's module system uses a lightweight, layered architecture where each module in modules/<name>/ exposes initialize_<module>_module() and uninitialize_<module>_module() functions that the engine calls at four distinct initialization levels during startup.

The godotengine/godot repository implements a modular architecture that allows developers to extend the engine's core functionality through self-contained C++ modules. Understanding the Godot module system architecture is essential for engine contributors and developers creating custom modules, as it determines how classes, resource loaders, and editor plugins are registered during the engine bootstrap process.

Architecture Overview

The Godot module system operates through four distinct layers that separate module definition from engine integration:

  • Module Definition: Each module resides in modules/<module_name>/ and provides register_types.h and register_types.cpp files implementing the initialization and termination entry points.

  • Module List Generation: During the SCons build process, modules/modules_builders.py reads the enabled module list and generates register_module_types.cpp, which conditionally includes each module based on the MODULE_<NAME>_ENABLED macro.

  • Engine Bootstrap: The engine entry point in main/main.cpp invokes initialize_modules() and uninitialize_modules() at four specific initialization levels to control when modules register their functionality.

  • Conditional Compilation: Modules compile only when their corresponding MODULE_<NAME>_ENABLED macro is defined in the generated modules/modules_enabled.gen.h file.

The Module Registration Pipeline

Step 1: Module Implementation

Each module implements standardized initialization functions in register_types.cpp. The functions receive a ModuleInitializationLevel parameter to determine which engine systems are available during registration.

// modules/gdscript/register_types.cpp
void initialize_gdscript_module(ModuleInitializationLevel p_level) {
    if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
        GDREGISTER_CLASS(GDScript);
        // Register language, loaders, and resource formats
    }
}

void uninitialize_gdscript_module(ModuleInitializationLevel p_level) {
    if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
        // Clean up resources and unregister classes
    }
}

The header file register_types.h declares these functions for the build system:

// modules/gdscript/register_types.h
#pragma once
void initialize_gdscript_module(ModuleInitializationLevel p_level);
void uninitialize_gdscript_module(ModuleInitializationLevel p_level);

Step 2: Build System Code Generation

The SCons build system processes module registration through modules/modules_builders.py. This script generates register_module_types.cpp, which serves as the central dispatcher for all enabled modules.

The generated file includes conditional compilation directives and wraps each module's initialization calls:

// Generated file: register_module_types.cpp
#include "register_module_types.h"
#include "modules/modules_enabled.gen.h"

#ifdef MODULE_GDSCRIPT_ENABLED
    #include "gdscript/register_types.h"
#endif
// ... additional modules

void initialize_modules(ModuleInitializationLevel p_level) {
#ifdef MODULE_GDSCRIPT_ENABLED
    initialize_gdscript_module(p_level);
#endif
    // ... additional module calls
}

void uninitialize_modules(ModuleInitializationLevel p_level) {
#ifdef MODULE_GDSCRIPT_ENABLED
    uninitialize_gdscript_module(p_level);
#endif
    // ... additional module calls
}

Step 3: Engine Bootstrap Invocation

The engine bootstrap sequence in main/main.cpp invokes the generated registration functions at four distinct points:

// main/main.cpp
initialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
// Core singletons initialized

initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
// Server systems initialized

initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
// Scene system initialized

#ifdef TOOLS_ENABLED
initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
// Editor systems initialized
#endif

During shutdown, uninitialize_modules() executes in reverse order (EDITOR → SCENE → SERVERS → CORE) to ensure proper resource cleanup.

Understanding Initialization Levels

The ModuleInitializationLevel enum defines four distinct phases of engine startup. Modules select the appropriate level based on their dependencies:

  • MODULE_INITIALIZATION_LEVEL_CORE: Registers fundamental types and core data structures. Use this for classes that do not depend on server or scene systems.

  • MODULE_INITIALIZATION_LEVEL_SERVERS: Registers server-side functionality, script languages, and resource loaders. Most modules register here because the ResourceLoader and ScriptServer are available at this stage.

  • MODULE_INITIALIZATION_LEVEL_SCENE: Registers scene-related classes, nodes, and resources that depend on the scene tree system.

  • MODULE_INITIALIZATION_LEVEL_EDITOR: Registers editor plugins, tools, and UI extensions. This level compiles only when TOOLS_ENABLED is defined.

Creating a Custom Module

To implement a new module, create a directory under modules/ with the standard registration interface:

File structure:


modules/my_module/
 ├─ register_types.h
 └─ register_types.cpp

register_types.h:

#pragma once
void initialize_my_module(ModuleInitializationLevel p_level);
void uninitialize_my_module(ModuleInitializationLevel p_level);

register_types.cpp:

#include "register_types.h"
#include "core/object/class_db.h"
#include "my_node.h"

void initialize_my_module(ModuleInitializationLevel p_level) {
    if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
        GDREGISTER_CLASS(MyNode);
        
        // Optional: Register resource loader
        Ref<ResourceFormatLoaderMyRes> loader;
        loader.instantiate();
        ResourceLoader::add_resource_format_loader(loader);
    }
}

void uninitialize_my_module(ModuleInitializationLevel p_level) {
    // Cleanup logic
}

Enable the module via SCons:

scons platform=linuxbsd target=release_debug modules_enabled="my_module"

The build system generates modules_enabled.gen.h containing #define MODULE_MY_MODULE_ENABLED, ensuring your module compiles into the final binary.

Summary

  • Modules are self-contained directories under modules/ that implement initialize_<module>_module() and uninitialize_<module>_module() in register_types.cpp.
  • The SCons build system generates register_module_types.cpp via modules/modules_builders.py to conditionally compile and dispatch calls to enabled modules based on MODULE_<NAME>_ENABLED macros.
  • Four initialization levels (CORE, SERVERS, SCENE, EDITOR) control when modules register classes, with most modules initializing at the SERVERS level where ResourceLoader is available.
  • Engine bootstrap in main/main.cpp invokes initialize_modules() at each level during startup and uninitialize_modules() in reverse order during shutdown.
  • Conditional compilation allows modules to be included or excluded from builds without modifying engine source code.

Frequently Asked Questions

What files are required to create a new Godot module?

Every module requires register_types.h and register_types.cpp in the module root directory. The header declares initialize_<module>_module() and uninitialize_<module>_module(), while the implementation file defines these functions and uses macros like GDREGISTER_CLASS() to expose classes to the engine.

How does Godot determine which modules to compile?

The SCons build system generates modules/modules_enabled.gen.h containing #define MODULE_<NAME>_ENABLED macros for each enabled module. The generated register_module_types.cpp uses these macros to conditionally include headers and initialization calls, ensuring only enabled modules compile into the binary.

Which initialization level should my module use for registration?

Use MODULE_INITIALIZATION_LEVEL_SERVERS for most modules, as this is when ResourceLoader, ScriptServer, and core server singletons are available. Use CORE for fundamental data structures without dependencies, SCENE for node types requiring the scene tree, and EDITOR exclusively for editor tooling that must compile only in development builds.

Can Godot modules be loaded dynamically at runtime?

No, Godot modules are statically linked C++ code compiled directly into the engine binary. The registration system in main/main.cpp invokes module initializers during static initialization phases. While the engine supports GDExtension for runtime-loaded plugins, the internal module system requires compile-time inclusion via SCons configuration.

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 →