GDDI
A lightweight stupidly simple dependency injection and service locator plugin for Godot, built around a simple API for registering and retrieving game services.
Why GDDI?
Stop cluttering your ProjectSettings with dozens of Node-only autoloads. GDDI gives you a single, clean API to register, lazy-load, and swap lightweight RefCounted services, factory instances, or nodes on demand.
Autoload vs GDDI
| Capability | Godot autoloads | GDDI |
|---|---|---|
| Register services | Configure each autoload in project settings | Register multiple providers by key, at runtime or with GDDInit |
| Supported values | Node only |
Object, RefCounted, and Node instances |
| Create on demand | Not built in | Factories create new instances; lazy factories cache one |
| Remove on demand | Cannot unregister services and it not advised to try to free them | Remove a provider or clear all providers at runtime |
| Flexible node placement | Default behaviour cannot be altered | Assign your registered nodes wherever you want |
Key Features
- Register and retrieve dependencies with
provide()andinject(). - Register
Nodeservices and optionally set their name and scene-tree parent. - Keep
RefCountedservices alive while registered. - Create a fresh object on every injection with a factory, or cache the first result with a lazy factory.
- List and remove registered providers, or clear the container.
- Register startup services with
GDDInitinitializer scripts. - Configure timestamped GDDI logs by severity, with caller details for debugging.
Prerequisites
- Godot Engine 4.7 or later.
- A Godot project where you can install and enable editor plugins.
Installation
Follow these steps to set up the project locally:
- Copy the
addons/gddidirectory into your Godot project'saddonsdirectory. - Open the project in Godot and go to Project > Project Settings > Plugins.
- Enable the GDDI plugin. It adds the
GDDIautoload and the initializer setting. - Use
GDDIfrom your scripts.
Autoload warning: GDDI is an autoload, so startup order matters. Avoid using GDDI from other autoloads, or configure GDDI to load before any autoload that depends on it.
Usage / Examples
Register an ordinary object and retrieve it by key:
# Define your provider(s)
var settings := GameSettings.new()
GDDI.provide(&"settings", settings)
# Then access it any where inside your project
var shared_settings: GameSettings = GDDI.inject(&"settings")
Register a node as a service. By default, an unparented node is added under the GDDI autoload. You can choose a parent and name with the options dictionary:
var audio_service := AudioService.new()
GDDI.provide_node(&"audio", audio_service, {
"parent": get_tree().root,
"name": "AudioService",
})
var audio: AudioService = GDDI.inject(&"audio")
Use a factory when each injection should create a new instance, or a lazy factory when the first instance should be reused:
GDDI.provide_factory(&"enemy", func() -> Enemy:
return Enemy.new()
)
GDDI.provide_lazy(&"profile", func() -> PlayerProfile:
return PlayerProfile.load_or_create()
)
var new_enemy: Enemy = GDDI.inject(&"enemy")
var profile: PlayerProfile = GDDI.inject(&"profile")
A factory can inject another provider while it runs. For example, register shared settings first, then inject them when creating each game session:
GDDI.provide(&"settings", GameSettings.new())
GDDI.provide_factory(&"game_session", func() -> GameSession:
var settings: GameSettings = GDDI.inject(&"settings")
return GameSession.new(settings)
)
var session: GameSession = GDDI.inject(&"game_session")
Avoid self-recursion: A factory or lazy factory can inject other providers while it runs, but it must not inject itself using its own key. Doing so calls the same factory again recursively.
Factory arguments are passed to the callable through inject() or injectv():
GDDI.provide_factory(&"weapon", func(weapon_id: String) -> Weapon:
return Weapon.create(weapon_id)
)
var sword: Weapon = GDDI.inject(&"weapon", "sword")
An initializer script can register providers during autoload startup. Add its res:// path under Project Settings > Application > GDDI > Initializers:
extends GDDInit
func _registering_dependencies(di: DI) -> void:
di.provide_factory(&"settings", func():
return GameSettings.new()
)
Logging
GDDI logs provider registration, resolution, removal, initializer loading, and related warnings or errors.
Enable Verbose under Project Settings > Application > GDDI to turn on logging. In debug builds this starts at DEBUG; in non-debug builds it starts at WARNING. When Verbose is disabled, logging is off. You can change the threshold while the game is running:
GDDI.set_log_level(GDDILogger.LogLevel.INFO)
Available levels are DEBUG, INFO, WARNING, ERROR, and NONE. The threshold includes messages at that severity and above; for example, INFO emits info, warning, and error messages. NONE disables all messages.
Changelog for version v1.0.1-stable
2026-10-08 12:13:00
No changelog provided for this version.