Engine singletons

It is important for you to understand the Singleton pattern to properly utilize this system.

Controversy

The "Singleton pattern" is often referred to as an anti-pattern, because it violates several good practices for clean, modular code. However, it is also a tool that can be used to solve certain design problems. As such, it is used internally by Godot, and is available to godot-rust users as well.

Read more about criticisms here.

An engine singleton is registered through godot::classes::Engine.

Custom engine singletons in Godot:

  • are Object types
  • are always accessible to GDScript and GDExtension languages
  • must be manually registered and unregistered in the InitLevel::Scene step

Godot provides many built-in singletons in its API. You can find a full list here.

Table of contents

Defining a singleton

Defining a singleton is the same as registering a custom class.

#![allow(unused)]
fn main() {
#[derive(GodotClass)]
#[class(init, base=Object)]
struct MyEditorSingleton {
    base: Base<Object>,
}

#[godot_api]
impl MyEditorSingleton {
    #[func]
    fn foo(&mut self) {}
}
}

Registering a singleton

Registering singletons is done during the InitLevel::Scene stage of initialization.

To achieve this, we can customize our init/shutdown routines by overriding ExtensionLibrary trait methods.

#![allow(unused)]
fn main() {
struct MyExtension;

#[gdextension]
unsafe impl ExtensionLibrary for MyExtension {
    fn on_level_init(level: InitLevel) {
        if level == InitLevel::Scene {
            // The `&str` identifies your singleton and can be
            // used later to access it.
            Engine::singleton().register_singleton(
                "MyEngineSingleton",
                &MyEngineSingleton::new_alloc(),
            );
        }
    }

    fn on_level_deinit(level: InitLevel) {
        if level == InitLevel::Scene {
            // Let's keep a variable of our Engine singleton instance,
            // and MyEngineSingleton name.
            let mut engine = Engine::singleton();
            let singleton_name = "MyEngineSingleton";

            // Here, we manually retrieve our singleton(s) that we've registered,
            // so we can unregister them and free them from memory - unregistering
            // singletons isn't handled automatically by the library.
            if let Some(my_singleton) = engine.get_singleton(singleton_name) {
                // Unregistering from Godot, and freeing from memory is required
                // to avoid memory leaks, warnings, and hot reloading problems.
                engine.unregister_singleton(singleton_name);
                my_singleton.free();
            } else {
                // You can either recover, or panic from here.
                godot_error!("Failed to get singleton");
            }
        }
    }
}
}

Singletons inheriting from RefCounted

Use a manually-managed class as a base (often Object will be enough) for custom singletons to avoid prematurely freeing the object. If for any reason you need to have an instance of a reference-counted object registered as a singleton, this issue thread presents some possible workarounds.

Calling from GDScript

Now that your singleton is available (and once you've recompiled and reloaded), you should be able to access it from GDScript like so:

extends Node

func _ready() -> void:
    MyEditorSingleton.foo()

Calling from Rust

You may also want to access your singleton from Rust as well.

#![allow(unused)]
fn main() {
godot::classes::Engine::singleton()
    .get_singleton(StringName::from("MyEditorSingleton"));
}

For more information on this method, refer to the API docs.

Singletons and the SceneTree

Singletons cannot safely access the scene tree. At any given moment, they may exist without a scene tree being active. While it is technically possible to access the tree through hacky methods, it is highly recommended to use a custom EditorPlugin for this purpose. Creating an EditorPlugin allows for registering an "autoload singleton" which is a Node (or derived) type and is automatically loaded into the SceneTree by Godot when the game starts.