ExtensionLibrary

Trait ExtensionLibrary 

pub unsafe trait ExtensionLibrary {
    // Provided methods
    fn editor_run_behavior() -> EditorRunBehavior { ... }
    fn min_level() -> InitLevel { ... }
    fn on_stage_init(stage: InitStage) { ... }
    fn on_stage_deinit(stage: InitStage) { ... }
    fn on_main_loop_frame() { ... }
}
Expand description

Defines the entry point for a GDExtension Rust library.

Every library should have exactly one implementation of this trait. It is always used in combination with the #[gdextension] proc-macro attribute.

§Example

The simplest usage is as follows. This will automatically perform the necessary init and cleanup routines, and register all classes marked with #[derive(GodotClass)], without needing to mention them in a central list. The order in which classes are registered is not specified.

use godot::init::*;

// This is just a type tag without any functionality.
// Its name is irrelevant.
struct MyExtension;

#[gdextension]
unsafe impl ExtensionLibrary for MyExtension {}

§Custom entry symbol

There is usually no reason to, but you can use a different entry point (C function in the dynamic library). This must match the key that you specify in the .gdextension file. Let’s say your .gdextension file has such a section:

[configuration]
entry_symbol = "custom_name"

then you can implement the trait like this:

struct MyExtension;

#[gdextension(entry_symbol = custom_name)]
unsafe impl ExtensionLibrary for MyExtension {}

Note that this only changes the name. You cannot provide your own function – use the on_stage_init() hook for custom startup logic.

§Availability of Godot APIs during init and deinit

Godot loads functionality gradually during its startup routines, and unloads it during shutdown. As a result, Godot classes are only available above a certain level. Trying to access a class API when it’s not available will panic (if not, please report it as a bug).

A few singletons (Engine, Os, Time, ProjectSettings) are available from the Core level onward and can be used inside this method. Most other singletons are not available during init at all, and will only become accessible once the first frame has run.

The exact time a class is available depends on the Godot initialization logic, which is quite complex and may change between versions. To get an up-to-date view, inspect the Godot source code of main.cpp, particularly Main::setup(), Main::setup2() and Main::cleanup() methods. Make sure to look at the correct version of the file.

In case of doubt, do not rely on classes being available during init/deinit.

§Safety

The library cannot enforce any safety guarantees outside Rust code, which means that you as a user are responsible to uphold them: namely in GDScript code or other GDExtension bindings loaded by the engine. Violating this may cause undefined behavior, even when invoking safe functions.

If you use the disengaged safeguard level, you accept that UB becomes possible even in safe Rust APIs, if you use them wrong (e.g. accessing a destroyed object).

Provided Methods§

fn editor_run_behavior() -> EditorRunBehavior

Determines if and how an extension’s code is run in the editor.

fn min_level() -> InitLevel

Determines the initialization level at which the extension is loaded (Scene by default).

If the level is lower than InitLevel::Scene, the engine needs to be restarted to take effect.

fn on_stage_init(stage: InitStage)

Custom logic when a certain initialization stage is loaded.

This will be invoked for stages >= Self::min_level(), in ascending order. Use if or match to hook to specific stages.

The stages are loaded in order: CoreServersSceneEditor (if in editor) → MainLoop (4.5+).
The MainLoop stage represents the fully initialized state of Godot, after all initialization levels and classes have been loaded.

See also on_main_loop_frame() for per-frame processing.

§Panics

If the overridden method panics, an error will be printed, but GDExtension loading is not aborted.

fn on_stage_deinit(stage: InitStage)

Custom logic when a certain initialization stage is unloaded.

This will be invoked for stages >= Self::min_level(), in descending order. Use if or match to hook to specific stages.

The stages are unloaded in reverse order: MainLoop (4.5+) → Editor (if in editor) → SceneServersCore.
At the time MainLoop is deinitialized, all classes are still available.

§Panics

If the overridden method panics, an error will be printed, but GDExtension unloading is not aborted.

fn on_main_loop_frame()

Available on since_api=4.5 only.

Callback invoked for every process frame.

This is called during the main loop, after Godot is fully initialized. It runs after all process() methods on Node, and before the Godot-internal ScriptServer::frame(). This is intended to be the equivalent of IScriptLanguageExtension::frame() for GDExtension language bindings that don’t use the script API.

§Example

To hook into startup/shutdown of the main loop, use on_stage_init() and on_stage_deinit() and watch for InitStage::MainLoop.

#[gdextension]
unsafe impl ExtensionLibrary for MyExtension {
    fn on_stage_init(stage: InitStage) {
        if stage == InitStage::MainLoop {
            // Startup code after fully initialized.
        }
    }

    fn on_main_loop_frame() {
        // Per-frame logic.
    }

    fn on_stage_deinit(stage: InitStage) {
        if stage == InitStage::MainLoop {
            // Cleanup code before shutdown.
        }
    }
}
§Panics

If the overridden method panics, an error will be printed, but execution continues.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§