Skip to main content

godot_core/init/
mod.rs

1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8use std::sync::atomic::{AtomicBool, Ordering};
9
10use godot_ffi as sys;
11use sys::GodotFfi;
12
13use crate::builtin::{GString, StringName};
14use crate::obj::Singleton;
15use crate::out;
16
17mod reexport_pub {
18    // `Engine::singleton()` is not available before `InitLevel::Scenes` for Godot before 4.4.
19    #[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
20    pub use super::sys::is_editor_hint;
21    #[cfg(not(wasm_nothreads))] #[cfg_attr(published_docs, doc(cfg(not(wasm_nothreads))))]
22    pub use super::sys::main_thread_id;
23    pub use super::sys::{GdextBuild, InitStage, is_main_thread};
24}
25pub use reexport_pub::*;
26
27use crate::obj::signal::prune_stored_signal_connections;
28
29#[repr(C)]
30struct InitUserData {
31    library: sys::GDExtensionClassLibraryPtr,
32    #[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
33    main_loop_callbacks: sys::GDExtensionMainLoopCallbacks,
34}
35
36#[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
37unsafe extern "C" fn startup_func<E: ExtensionLibrary>() {
38    let ctx = || "ExtensionLibrary::on_stage_init(MainLoop)".to_string();
39
40    swallow_panics(ctx, || {
41        E::on_stage_init(InitStage::MainLoop);
42    });
43
44    // Now that editor UI is ready, display all warnings/error collected so far.
45    sys::print_deferred_startup_messages();
46}
47
48#[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
49unsafe extern "C" fn frame_func<E: ExtensionLibrary>() {
50    let ctx = || "ExtensionLibrary::on_main_loop_frame()".to_string();
51
52    swallow_panics(ctx, || {
53        E::on_main_loop_frame();
54    });
55}
56
57#[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
58unsafe extern "C" fn shutdown_func<E: ExtensionLibrary>() {
59    let ctx = || "ExtensionLibrary::on_stage_deinit(MainLoop)".to_string();
60
61    swallow_panics(ctx, || {
62        E::on_stage_deinit(InitStage::MainLoop);
63    });
64}
65
66#[doc(hidden)]
67pub unsafe fn __gdext_load_library<E: ExtensionLibrary>(
68    get_proc_address: sys::GDExtensionInterfaceGetProcAddress,
69    library: sys::GDExtensionClassLibraryPtr,
70    init: *mut sys::GDExtensionInitialization,
71) -> sys::GDExtensionBool {
72    let init_code = || {
73        // Make sure the first thing we do is check whether hot reloading should be enabled or not. This is to ensure that if we do anything to
74        // cause TLS-destructors to run then we have a setting already for how to deal with them. Otherwise, this could cause the default
75        // behavior to kick in and disable hot reloading.
76        #[cfg(target_os = "linux")] #[cfg_attr(published_docs, doc(cfg(target_os = "linux")))]
77        sys::linux_reload_workaround::default_set_hot_reload();
78
79        let tool_only_in_editor = match E::editor_run_behavior() {
80            EditorRunBehavior::ToolClassesOnly => true,
81            EditorRunBehavior::AllClasses => false,
82        };
83
84        let config = sys::GdextConfig::new(tool_only_in_editor);
85
86        // SAFETY: no custom code has run yet + no other thread is accessing global handle.
87        unsafe {
88            sys::initialize(get_proc_address, library, config);
89        }
90
91        // With experimental-features enabled, we can always print panics to godot_print!
92        #[cfg(feature = "experimental-threads")] #[cfg_attr(published_docs, doc(cfg(feature = "experimental-threads")))]
93        crate::private::set_gdext_hook(|| true);
94
95        // Without experimental-features enabled, we can only print panics with godot_print! if the panic occurs on the main (Godot) thread.
96        #[cfg(not(feature = "experimental-threads"))]
97        {
98            let main_thread = std::thread::current().id();
99            crate::private::set_gdext_hook(move || std::thread::current().id() == main_thread);
100        }
101
102        // Currently no way to express failure; could be exposed to E if necessary.
103        // No early exit, unclear if Godot still requires output parameters to be set.
104        let success = true;
105        // Leak the userdata. It will be dropped in core level deinitialization.
106        let userdata = Box::into_raw(Box::new(InitUserData {
107            library,
108            #[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
109            main_loop_callbacks: sys::GDExtensionMainLoopCallbacks {
110                startup_func: Some(startup_func::<E>),
111                frame_func: Some(frame_func::<E>),
112                shutdown_func: Some(shutdown_func::<E>),
113            },
114        }));
115
116        let godot_init_params = sys::GDExtensionInitialization {
117            minimum_initialization_level: E::min_level().to_sys(),
118            userdata: userdata.cast::<std::ffi::c_void>(),
119            initialize: Some(ffi_initialize_layer::<E>),
120            deinitialize: Some(ffi_deinitialize_layer::<E>),
121        };
122
123        // SAFETY: Godot is responsible for passing us a valid pointer.
124        unsafe {
125            *init = godot_init_params;
126        }
127
128        success as u8
129    };
130
131    // Use std::panic::catch_unwind instead of handle_panic: handle_panic uses TLS, which
132    // calls `thread_atexit` on linux, which sets the hot reloading flag on linux.
133    // Using std::panic::catch_unwind avoids this, although we lose out on context information
134    // for debugging.
135    let is_success = std::panic::catch_unwind(init_code);
136
137    is_success.unwrap_or(0)
138}
139
140static LEVEL_SERVERS_CORE_LOADED: AtomicBool = AtomicBool::new(false);
141
142unsafe extern "C" fn ffi_initialize_layer<E: ExtensionLibrary>(
143    userdata: *mut std::ffi::c_void,
144    init_level: sys::GDExtensionInitializationLevel,
145) {
146    unsafe {
147        let userdata = userdata.cast::<InitUserData>().as_ref().unwrap();
148        let level = InitLevel::from_sys(init_level);
149        let ctx = || format!("ExtensionLibrary::on_stage_init({level:?})");
150
151        fn try_load<E: ExtensionLibrary>(level: InitLevel, userdata: &InitUserData) {
152            // Workaround for https://github.com/godot-rust/gdext/issues/629:
153            // When using editor plugins, Godot may unload all levels but only reload from Scene upward.
154            // Manually run initialization of lower levels.
155
156            // TODO: Remove this workaround once after the upstream issue is resolved.
157            if level == InitLevel::Scene {
158                if !LEVEL_SERVERS_CORE_LOADED.load(Ordering::Relaxed) {
159                    try_load::<E>(InitLevel::Core, userdata);
160                    try_load::<E>(InitLevel::Servers, userdata);
161                }
162            } else if level == InitLevel::Core {
163                // When it's normal initialization, the `Servers` level is normally initialized.
164                LEVEL_SERVERS_CORE_LOADED.store(true, Ordering::Relaxed);
165            }
166
167            // SAFETY: Godot will call this from the main thread, after `__gdext_load_library` where the library is initialized,
168            // and only once per level.
169            unsafe { gdext_on_level_init(level, userdata) };
170            E::on_stage_init(level.to_stage());
171        }
172
173        // TODO consider crashing if gdext init fails.
174        swallow_panics(ctx, || {
175            try_load::<E>(level, userdata);
176        });
177    }
178}
179
180unsafe extern "C" fn ffi_deinitialize_layer<E: ExtensionLibrary>(
181    userdata: *mut std::ffi::c_void,
182    init_level: sys::GDExtensionInitializationLevel,
183) {
184    unsafe {
185        let level = InitLevel::from_sys(init_level);
186        let ctx = || format!("ExtensionLibrary::on_stage_deinit({level:?})");
187
188        swallow_panics(ctx, || {
189            if level == InitLevel::Core {
190                // Once the CORE api is unloaded, reset the flag to initial state.
191                LEVEL_SERVERS_CORE_LOADED.store(false, Ordering::Relaxed);
192
193                // Drop the userdata.
194                drop(Box::from_raw(userdata.cast::<InitUserData>()));
195            }
196
197            E::on_stage_deinit(level.to_stage());
198            gdext_on_level_deinit(level);
199        });
200    }
201}
202
203/// Tasks needed to be done by gdext internally upon loading an initialization level. Called before user code.
204///
205/// # Safety
206///
207/// - Must be called from the main thread.
208/// - The interface must have been initialized.
209/// - Must only be called once per level.
210unsafe fn gdext_on_level_init(level: InitLevel, _userdata: &InitUserData) {
211    // TODO: in theory, a user could start a thread in one of the early levels, and run concurrent code that messes with the global state
212    // (e.g. class registration). This would break the assumption that the load_class_method_table() calls are exclusive.
213    // We could maybe protect globals with a mutex until initialization is complete, and then move it to a directly-accessible, read-only static.
214
215    // SAFETY: we are in the main thread, initialize has been called, has never been called with this level before.
216    unsafe { sys::load_class_method_table(level) };
217
218    match level {
219        InitLevel::Core => {
220            #[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
221            unsafe {
222                sys::interface_fn!(register_main_loop_callbacks)(
223                    _userdata.library,
224                    &raw const _userdata.main_loop_callbacks,
225                )
226            };
227
228            #[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
229            sys::set_editor_hint(crate::classes::Engine::singleton().is_editor_hint());
230        }
231        InitLevel::Servers => {
232            // SAFETY: called from the main thread, sys::initialized has already been called.
233            unsafe { sys::discover_main_thread() };
234        }
235        InitLevel::Scene => {
236            // SAFETY: On the main thread, api initialized, `Scene` was initialized above.
237            unsafe { ensure_godot_features_compatible() };
238        }
239        InitLevel::Editor => {
240            #[cfg(all(since_api = "4.3", feature = "register-docs"))]
241            // SAFETY: Godot binding is initialized, and this is called from the main thread.
242            unsafe {
243                crate::docs::register();
244            }
245        }
246    }
247
248    crate::registry::class::auto_register_classes(level);
249}
250
251/// Tasks needed to be done by gdext internally upon unloading an initialization level. Called after user code.
252fn gdext_on_level_deinit(level: InitLevel) {
253    if level == InitLevel::Editor {
254        prune_stored_signal_connections();
255    }
256
257    crate::registry::class::unregister_classes(level);
258
259    if level == InitLevel::Core {
260        // If lowest level is unloaded, call global deinitialization.
261        // No business logic by itself, but ensures consistency if re-initialization (hot-reload on Linux) occurs.
262
263        crate::task::cleanup();
264        crate::tools::cleanup();
265
266        // Garbage-collect various statics.
267        // SAFETY: this is the last time meta APIs are used.
268        unsafe {
269            crate::meta::cleanup();
270        }
271
272        // SAFETY: called after all other logic, so no concurrent access.
273        // TODO: multithreading must make sure other threads are joined/stopped here.
274        unsafe {
275            sys::deinitialize();
276        }
277    }
278}
279
280/// Catches panics without propagating them further. Prints error messages.
281fn swallow_panics<E, F>(error_context: E, code: F)
282where
283    E: Fn() -> String,
284    F: FnOnce() + std::panic::UnwindSafe,
285{
286    let _ = crate::private::handle_panic(error_context, code);
287}
288
289// ----------------------------------------------------------------------------------------------------------------------------------------------
290
291/// Defines the entry point for a GDExtension Rust library.
292///
293/// Every library should have exactly one implementation of this trait. It is always used in combination with the
294/// [`#[gdextension]`][gdextension] proc-macro attribute.
295///
296/// # Example
297/// The simplest usage is as follows. This will automatically perform the necessary init and cleanup routines, and register
298/// all classes marked with `#[derive(GodotClass)]`, without needing to mention them in a central list. The order in which
299/// classes are registered is not specified.
300///
301/// ```
302/// use godot::init::*;
303///
304/// struct MyExtension;
305///
306/// #[gdextension]
307/// unsafe impl ExtensionLibrary for MyExtension {}
308/// ```
309///
310/// # Custom entry symbol
311/// 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
312/// that you specify in the `.gdextension` file. Let's say your `.gdextension` file has such a section:
313/// ```toml
314/// [configuration]
315/// entry_symbol = "custom_name"
316/// ```
317/// then you can implement the trait like this:
318/// ```no_run
319/// # use godot::init::*;
320/// struct MyExtension;
321///
322/// #[gdextension(entry_symbol = custom_name)]
323/// unsafe impl ExtensionLibrary for MyExtension {}
324/// ```
325/// Note that this only changes the name. You cannot provide your own function -- use the [`on_stage_init()`][ExtensionLibrary::on_stage_init]
326/// hook for custom startup logic.
327///
328/// # Availability of Godot APIs during init and deinit
329// Init order: see also special_cases.rs > classify_codegen_level().
330/// Godot loads functionality gradually during its startup routines, and unloads it during shutdown. As a result, Godot classes are only
331/// 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).
332///
333/// A few singletons (`Engine`, `Os`, `Time`, `ProjectSettings`) are available from the `Core` level onward and can be used inside
334/// this method. Most other singletons are **not available during init** at all, and will only become accessible once the first frame has
335/// run.
336///
337/// The exact time a class is available depends on the Godot initialization logic, which is quite complex and may change between versions.
338/// To get an up-to-date view, inspect the Godot source code of [main.cpp], particularly `Main::setup()`, `Main::setup2()` and
339/// `Main::cleanup()` methods. Make sure to look at the correct version of the file.
340///
341/// In case of doubt, do not rely on classes being available during init/deinit.
342///
343/// [main.cpp]: https://github.com/godotengine/godot/blob/master/main/main.cpp
344///
345/// # Safety
346/// The library cannot enforce any safety guarantees outside Rust code, which means that **you as a user** are
347/// responsible to uphold them: namely in GDScript code or other GDExtension bindings loaded by the engine.
348/// Violating this may cause undefined behavior, even when invoking _safe_ functions.
349///
350/// If you use the `disengaged` [safeguard level], you accept that UB becomes possible even **in safe Rust APIs**, if you use them wrong
351/// (e.g. accessing a destroyed object).
352///
353/// # Using other GDExtension libraries as dependencies
354///
355/// When using any other GDExtension library as a dependency, the implementor of the user-forwarding `ExtensionLibrary` must be specified
356/// via the `GDRUST_MAIN_EXTENSION` environment variable. That _main_ crate will be responsible
357/// for loading all classes, as well as managing the `ExtensionLibrary` callbacks.
358///
359/// For example, you have a workspace with a crate `my-extension`, that uses libraries `lib-a` and `lib-b` as dependencies.
360/// If the `impl ExtensionLibrary` is called `MyExtension`, then you can build all crates in the workspace with:
361///
362/// ```bash
363/// GDRUST_MAIN_EXTENSION="MyExtension" cargo build
364/// ```
365///
366/// ```ignore
367/// // lib.rs of my-extension crate.
368/// // Usage of other dependencies must be explicitly declared; otherwise, they won't be registered.
369/// extern crate lib_a;
370/// extern crate lib_b;
371///
372/// struct MyExtension;
373///
374/// #[gdextension]
375/// unsafe impl ExtensionLibrary for MyExtension {}
376/// ```
377///
378/// The name of the `ExtensionLibrary` implementor must be unique and different from those used by its dependencies.
379///
380/// Given dependencies must be compilable as `rlib`. That is, they either specify `rlib` as a possible crate-type in their `Cargo.toml`:
381///
382/// ```ignore
383/// # my_dependency/Cargo.toml
384///
385/// [lib]
386/// crate-type = ["cdylib", "rlib"]
387/// ```
388///
389/// Or do not specify `[lib]` at all (and are compiled with `cargo rustc --features ... --crate-type cdylib` instead).
390///
391/// Note that it is the user's responsibility to ensure that the same classes are not loaded twice –
392/// which might be a result of loading another extension in the project which already defines said classes.
393/// Do not use this feature to bundle dependencies to end-users unless it is necessary to do so.
394///
395/// [gdextension]: attr.gdextension.html
396/// [safety]: https://godot-rust.github.io/book/gdext/advanced/safety.html
397/// [safeguard level]: ../index.html#safeguard-levels
398// FIXME intra-doc link
399#[doc(alias = "entry_symbol", alias = "entry_point")]
400pub unsafe trait ExtensionLibrary {
401    /// Determines if and how an extension's code is run in the editor.
402    fn editor_run_behavior() -> EditorRunBehavior {
403        EditorRunBehavior::ToolClassesOnly
404    }
405
406    /// Determines the initialization level at which the extension is loaded (`Scene` by default).
407    ///
408    /// If the level is lower than [`InitLevel::Scene`], the engine needs to be restarted to take effect.
409    fn min_level() -> InitLevel {
410        InitLevel::Scene
411    }
412
413    /// Custom logic when a certain initialization stage is loaded.
414    ///
415    /// This will be invoked for stages >= [`Self::min_level()`], in ascending order. Use `if` or `match` to hook to specific stages.
416    ///
417    /// The stages are loaded in order: `Core` → `Servers` → `Scene` → `Editor` (if in editor) → `MainLoop` (4.5+).  \
418    /// The `MainLoop` stage represents the fully initialized state of Godot, after all initialization levels and classes have been loaded.
419    ///
420    /// See also [`on_main_loop_frame()`][Self::on_main_loop_frame] for per-frame processing.
421    ///
422    /// # Panics
423    /// If the overridden method panics, an error will be printed, but GDExtension loading is **not** aborted.
424    #[allow(unused_variables)]
425    fn on_stage_init(stage: InitStage) {}
426
427    /// Custom logic when a certain initialization stage is unloaded.
428    ///
429    /// This will be invoked for stages >= [`Self::min_level()`], in descending order. Use `if` or `match` to hook to specific stages.
430    ///
431    /// The stages are unloaded in reverse order: `MainLoop` (4.5+) → `Editor` (if in editor) → `Scene` → `Servers` → `Core`.  \
432    /// At the time `MainLoop` is deinitialized, all classes are still available.
433    ///
434    /// # Panics
435    /// If the overridden method panics, an error will be printed, but GDExtension unloading is **not** aborted.
436    #[allow(unused_variables)]
437    fn on_stage_deinit(stage: InitStage) {}
438
439    /// Callback invoked for every process frame.
440    ///
441    /// This is called during the main loop, after Godot is fully initialized. It runs after all
442    /// [`process()`][crate::classes::INode::process] methods on Node, and before the Godot-internal `ScriptServer::frame()`.
443    /// This is intended to be the equivalent of [`IScriptLanguageExtension::frame()`][`crate::classes::IScriptLanguageExtension::frame()`]
444    /// for GDExtension language bindings that don't use the script API.
445    ///
446    /// # Example
447    /// To hook into startup/shutdown of the main loop, use [`on_stage_init()`][Self::on_stage_init] and
448    /// [`on_stage_deinit()`][Self::on_stage_deinit] and watch for [`InitStage::MainLoop`].
449    ///
450    /// ```no_run
451    /// # use godot::init::*;
452    /// # struct MyExtension;
453    /// #[gdextension]
454    /// unsafe impl ExtensionLibrary for MyExtension {
455    ///     fn on_stage_init(stage: InitStage) {
456    ///         if stage == InitStage::MainLoop {
457    ///             // Startup code after fully initialized.
458    ///         }
459    ///     }
460    ///
461    ///     fn on_main_loop_frame() {
462    ///         // Per-frame logic.
463    ///     }
464    ///
465    ///     fn on_stage_deinit(stage: InitStage) {
466    ///         if stage == InitStage::MainLoop {
467    ///             // Cleanup code before shutdown.
468    ///         }
469    ///     }
470    /// }
471    /// ```
472    ///
473    /// # Panics
474    /// If the overridden method panics, an error will be printed, but execution continues.
475    #[cfg(since_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.5")))]
476    fn on_main_loop_frame() {}
477}
478
479/// Determines if and how an extension's code is run in the editor.
480///
481/// By default, Godot 4 runs all virtual lifecycle callbacks (`_ready`, `_process`, `_physics_process`, ...)
482/// for extensions. This behavior is different from Godot 3, where extension classes needed to be explicitly
483/// marked as "tool".
484///
485/// In many cases, users write extension code with the intention to run in games, not inside the editor.
486/// This is why the default behavior in gdext deviates from Godot: lifecycle callbacks are disabled inside the
487/// editor (see [`ToolClassesOnly`][Self::ToolClassesOnly]). It is possible to configure this.
488///
489/// See also [`ExtensionLibrary::editor_run_behavior()`].
490#[derive(Copy, Clone, Debug)]
491#[non_exhaustive]
492pub enum EditorRunBehavior {
493    /// Only runs `#[class(tool)]` classes in the editor.
494    ///
495    /// All classes are registered, and calls from GDScript to Rust are possible. However, virtual lifecycle callbacks
496    /// (`_ready`, `_process`, `_physics_process`, ...) are not run unless the class is annotated with `#[class(tool)]`.
497    ToolClassesOnly,
498
499    /// Runs the extension with full functionality in editor.
500    ///
501    /// Ignores any `#[class(tool)]` annotations.
502    AllClasses,
503}
504
505// ----------------------------------------------------------------------------------------------------------------------------------------------
506
507pub use sys::InitLevel;
508
509// ----------------------------------------------------------------------------------------------------------------------------------------------
510
511/// # Safety
512///
513/// - Must be called from the main thread.
514/// - The interface must be initialized.
515/// - The `Scene` api level must have been initialized.
516unsafe fn ensure_godot_features_compatible() {
517    // The reason why we don't simply call Os::has_feature() here is that we might move the high-level engine classes out of godot-core
518    // later, and godot-core would only depend on godot-sys. This makes future migrations easier. We still have access to builtins though.
519
520    out!("Check Godot precision setting...");
521
522    #[cfg(feature = "debug-log")] // Display safeguards level in debug log.
523    let safeguards_level = if cfg!(safeguards_strict) {
524        "strict"
525    } else if cfg!(safeguards_balanced) {
526        "balanced"
527    } else {
528        "disengaged"
529    };
530    out!("Safeguards: {safeguards_level}");
531
532    let os_class = StringName::from("OS");
533    let single = GString::from("single");
534    let double = GString::from("double");
535
536    let gdext_is_double = cfg!(feature = "double-precision");
537
538    // SAFETY: main thread, after initialize(), valid string pointers, `Scene` initialized.
539    let godot_is_double = unsafe {
540        let is_single = sys::godot_has_feature(os_class.string_sys(), single.sys());
541        let is_double = sys::godot_has_feature(os_class.string_sys(), double.sys());
542
543        assert_ne!(
544            is_single, is_double,
545            "Godot has invalid configuration: single={is_single}, double={is_double}"
546        );
547
548        is_double
549    };
550
551    let s = |is_double: bool| -> &'static str { if is_double { "double" } else { "single" } };
552
553    out!(
554        "Is double precision: Godot={}, gdext={}",
555        s(godot_is_double),
556        s(gdext_is_double)
557    );
558
559    if godot_is_double != gdext_is_double {
560        panic!(
561            "Godot runs with {} precision, but gdext was compiled with {} precision.\n\
562            Cargo feature `double-precision` must be used if and only if Godot is compiled with `precision=double`.\n",
563            s(godot_is_double),
564            s(gdext_is_double),
565        );
566    }
567}