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 providesregister_types.handregister_types.cppfiles implementing the initialization and termination entry points. -
Module List Generation: During the SCons build process,
modules/modules_builders.pyreads the enabled module list and generatesregister_module_types.cpp, which conditionally includes each module based on theMODULE_<NAME>_ENABLEDmacro. -
Engine Bootstrap: The engine entry point in
main/main.cppinvokesinitialize_modules()anduninitialize_modules()at four specific initialization levels to control when modules register their functionality. -
Conditional Compilation: Modules compile only when their corresponding
MODULE_<NAME>_ENABLEDmacro is defined in the generatedmodules/modules_enabled.gen.hfile.
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
ResourceLoaderandScriptServerare 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_ENABLEDis 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 implementinitialize_<module>_module()anduninitialize_<module>_module()inregister_types.cpp. - The SCons build system generates
register_module_types.cppviamodules/modules_builders.pyto conditionally compile and dispatch calls to enabled modules based onMODULE_<NAME>_ENABLEDmacros. - Four initialization levels (CORE, SERVERS, SCENE, EDITOR) control when modules register classes, with most modules initializing at the SERVERS level where
ResourceLoaderis available. - Engine bootstrap in
main/main.cppinvokesinitialize_modules()at each level during startup anduninitialize_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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →