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::out;
15
16mod reexport_pub {
17    #[cfg(not(wasm_nothreads))] #[cfg_attr(published_docs, doc(cfg(not(wasm_nothreads))))]
18    pub use super::sys::main_thread_id;
19    pub use super::sys::{is_main_thread, GdextBuild};
20}
21pub use reexport_pub::*;
22
23#[doc(hidden)]
24#[deny(unsafe_op_in_unsafe_fn)]
25pub unsafe fn __gdext_load_library<E: ExtensionLibrary>(
26    get_proc_address: sys::GDExtensionInterfaceGetProcAddress,
27    library: sys::GDExtensionClassLibraryPtr,
28    init: *mut sys::GDExtensionInitialization,
29) -> sys::GDExtensionBool {
30    let init_code = || {
31        // 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
32        // cause TLS-destructors to run then we have a setting already for how to deal with them. Otherwise, this could cause the default
33        // behavior to kick in and disable hot reloading.
34        #[cfg(target_os = "linux")] #[cfg_attr(published_docs, doc(cfg(target_os = "linux")))]
35        sys::linux_reload_workaround::default_set_hot_reload();
36
37        let tool_only_in_editor = match E::editor_run_behavior() {
38            EditorRunBehavior::ToolClassesOnly => true,
39            EditorRunBehavior::AllClasses => false,
40        };
41
42        let config = sys::GdextConfig::new(tool_only_in_editor);
43
44        // SAFETY: no custom code has run yet + no other thread is accessing global handle.
45        unsafe {
46            sys::initialize(get_proc_address, library, config);
47        }
48
49        // With experimental-features enabled, we can always print panics to godot_print!
50        #[cfg(feature = "experimental-threads")] #[cfg_attr(published_docs, doc(cfg(feature = "experimental-threads")))]
51        crate::private::set_gdext_hook(|| true);
52
53        // Without experimental-features enabled, we can only print panics with godot_print! if the panic occurs on the main (Godot) thread.
54        #[cfg(not(feature = "experimental-threads"))]
55        {
56            let main_thread = std::thread::current().id();
57            crate::private::set_gdext_hook(move || std::thread::current().id() == main_thread);
58        }
59
60        // Currently no way to express failure; could be exposed to E if necessary.
61        // No early exit, unclear if Godot still requires output parameters to be set.
62        let success = true;
63
64        let godot_init_params = sys::GDExtensionInitialization {
65            minimum_initialization_level: E::min_level().to_sys(),
66            userdata: std::ptr::null_mut(),
67            initialize: Some(ffi_initialize_layer::<E>),
68            deinitialize: Some(ffi_deinitialize_layer::<E>),
69        };
70
71        // SAFETY: Godot is responsible for passing us a valid pointer.
72        unsafe {
73            *init = godot_init_params;
74        }
75
76        success as u8
77    };
78
79    // Use std::panic::catch_unwind instead of handle_panic: handle_panic uses TLS, which
80    // calls `thread_atexit` on linux, which sets the hot reloading flag on linux.
81    // Using std::panic::catch_unwind avoids this, although we lose out on context information
82    // for debugging.
83    let is_success = std::panic::catch_unwind(init_code);
84
85    is_success.unwrap_or(0)
86}
87
88static LEVEL_SERVERS_CORE_LOADED: AtomicBool = AtomicBool::new(false);
89
90unsafe extern "C" fn ffi_initialize_layer<E: ExtensionLibrary>(
91    _userdata: *mut std::ffi::c_void,
92    init_level: sys::GDExtensionInitializationLevel,
93) {
94    let level = InitLevel::from_sys(init_level);
95    let ctx = || format!("failed to initialize GDExtension level `{level:?}`");
96
97    fn try_load<E: ExtensionLibrary>(level: InitLevel) {
98        // Workaround for https://github.com/godot-rust/gdext/issues/629:
99        // When using editor plugins, Godot may unload all levels but only reload from Scene upward.
100        // Manually run initialization of lower levels.
101
102        // TODO: Remove this workaround once after the upstream issue is resolved.
103        if level == InitLevel::Scene {
104            if !LEVEL_SERVERS_CORE_LOADED.load(Ordering::Relaxed) {
105                try_load::<E>(InitLevel::Core);
106                try_load::<E>(InitLevel::Servers);
107            }
108        } else if level == InitLevel::Core {
109            // When it's normal initialization, the `Servers` level is normally initialized.
110            LEVEL_SERVERS_CORE_LOADED.store(true, Ordering::Relaxed);
111        }
112
113        // SAFETY: Godot will call this from the main thread, after `__gdext_load_library` where the library is initialized,
114        // and only once per level.
115        unsafe { gdext_on_level_init(level) };
116        E::on_level_init(level);
117    }
118
119    // Swallow panics. TODO consider crashing if gdext init fails.
120    let _ = crate::private::handle_panic(ctx, || {
121        try_load::<E>(level);
122    });
123}
124
125unsafe extern "C" fn ffi_deinitialize_layer<E: ExtensionLibrary>(
126    _userdata: *mut std::ffi::c_void,
127    init_level: sys::GDExtensionInitializationLevel,
128) {
129    let level = InitLevel::from_sys(init_level);
130    let ctx = || format!("failed to deinitialize GDExtension level `{level:?}`");
131
132    // Swallow panics.
133    let _ = crate::private::handle_panic(ctx, || {
134        if level == InitLevel::Core {
135            // Once the CORE api is unloaded, reset the flag to initial state.
136            LEVEL_SERVERS_CORE_LOADED.store(false, Ordering::Relaxed);
137        }
138
139        E::on_level_deinit(level);
140        gdext_on_level_deinit(level);
141    });
142}
143
144/// Tasks needed to be done by gdext internally upon loading an initialization level. Called before user code.
145///
146/// # Safety
147///
148/// - Must be called from the main thread.
149/// - The interface must have been initialized.
150/// - Must only be called once per level.
151#[deny(unsafe_op_in_unsafe_fn)]
152unsafe fn gdext_on_level_init(level: InitLevel) {
153    // 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
154    // (e.g. class registration). This would break the assumption that the load_class_method_table() calls are exclusive.
155    // We could maybe protect globals with a mutex until initialization is complete, and then move it to a directly-accessible, read-only static.
156
157    // SAFETY: we are in the main thread, initialize has been called, has never been called with this level before.
158    unsafe { sys::load_class_method_table(level) };
159
160    match level {
161        InitLevel::Servers => {
162            // SAFETY: called from the main thread, sys::initialized has already been called.
163            unsafe { sys::discover_main_thread() };
164        }
165        InitLevel::Scene => {
166            // SAFETY: On the main thread, api initialized, `Scene` was initialized above.
167            unsafe { ensure_godot_features_compatible() };
168        }
169        InitLevel::Editor => {
170            #[cfg(all(since_api = "4.3", feature = "register-docs"))]
171            // SAFETY: Godot binding is initialized, and this is called from the main thread.
172            unsafe {
173                crate::docs::register();
174            }
175        }
176        _ => (),
177    }
178
179    crate::registry::class::auto_register_classes(level);
180}
181
182/// Tasks needed to be done by gdext internally upon unloading an initialization level. Called after user code.
183fn gdext_on_level_deinit(level: InitLevel) {
184    crate::registry::class::unregister_classes(level);
185
186    if level == InitLevel::Core {
187        // If lowest level is unloaded, call global deinitialization.
188        // No business logic by itself, but ensures consistency if re-initialization (hot-reload on Linux) occurs.
189
190        #[cfg(since_api = "4.2")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.2")))]
191        crate::task::cleanup();
192
193        // Garbage-collect various statics.
194        // SAFETY: this is the last time meta APIs are used.
195        unsafe {
196            crate::meta::cleanup();
197        }
198
199        // SAFETY: called after all other logic, so no concurrent access.
200        // TODO: multithreading must make sure other threads are joined/stopped here.
201        unsafe {
202            sys::deinitialize();
203        }
204    }
205}
206
207// ----------------------------------------------------------------------------------------------------------------------------------------------
208
209/// Defines the entry point for a GDExtension Rust library.
210///
211/// Every library should have exactly one implementation of this trait. It is always used in combination with the
212/// [`#[gdextension]`][gdextension] proc-macro attribute.
213///
214/// # Example
215/// The simplest usage is as follows. This will automatically perform the necessary init and cleanup routines, and register
216/// all classes marked with `#[derive(GodotClass)]`, without needing to mention them in a central list. The order in which
217/// classes are registered is not specified.
218///
219/// ```
220/// use godot::init::*;
221///
222/// // This is just a type tag without any functionality.
223/// // Its name is irrelevant.
224/// struct MyExtension;
225///
226/// #[gdextension]
227/// unsafe impl ExtensionLibrary for MyExtension {}
228/// ```
229///
230/// # Custom entry symbol
231/// 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
232/// that you specify in the `.gdextension` file. Let's say your `.gdextension` file has such a section:
233/// ```toml
234/// [configuration]
235/// entry_symbol = "custom_name"
236/// ```
237/// then you can implement the trait like this:
238/// ```no_run
239/// # use godot::init::*;
240/// struct MyExtension;
241///
242/// #[gdextension(entry_symbol = custom_name)]
243/// unsafe impl ExtensionLibrary for MyExtension {}
244/// ```
245/// Note that this only changes the name. You cannot provide your own function -- use the [`on_level_init()`][ExtensionLibrary::on_level_init]
246/// hook for custom startup logic.
247///
248/// # Safety
249/// The library cannot enforce any safety guarantees outside Rust code, which means that **you as a user** are
250/// responsible to uphold them: namely in GDScript code or other GDExtension bindings loaded by the engine.
251/// Violating this may cause undefined behavior, even when invoking _safe_ functions.
252///
253/// [gdextension]: attr.gdextension.html
254/// [safety]: https://godot-rust.github.io/book/gdext/advanced/safety.html
255// FIXME intra-doc link
256#[doc(alias = "entry_symbol", alias = "entry_point")]
257pub unsafe trait ExtensionLibrary {
258    /// Determines if and how an extension's code is run in the editor.
259    fn editor_run_behavior() -> EditorRunBehavior {
260        EditorRunBehavior::ToolClassesOnly
261    }
262
263    /// Determines the initialization level at which the extension is loaded (`Scene` by default).
264    ///
265    /// If the level is lower than [`InitLevel::Scene`], the engine needs to be restarted to take effect.
266    fn min_level() -> InitLevel {
267        InitLevel::Scene
268    }
269
270    /// Custom logic when a certain init-level of Godot is loaded.
271    ///
272    /// This will only be invoked for levels >= [`Self::min_level()`], in ascending order. Use `if` or `match` to hook to specific levels.
273    #[allow(unused_variables)]
274    fn on_level_init(level: InitLevel) {
275        // Nothing by default.
276    }
277
278    /// Custom logic when a certain init-level of Godot is unloaded.
279    ///
280    /// This will only be invoked for levels >= [`Self::min_level()`], in descending order. Use `if` or `match` to hook to specific levels.
281    #[allow(unused_variables)]
282    fn on_level_deinit(level: InitLevel) {
283        // Nothing by default.
284    }
285
286    /// Whether to override the Wasm binary filename used by your GDExtension which the library should expect at runtime. Return `None`
287    /// to use the default where gdext expects either `{YourCrate}.wasm` (default binary name emitted by Rust) or
288    /// `{YourCrate}.threads.wasm` (for builds producing separate single-threaded and multi-threaded binaries).
289    ///
290    /// Upon exporting a game to the web, the library has to know at runtime the exact name of the `.wasm` binary file being used to load
291    /// each GDExtension. By default, Rust exports the binary as `cratename.wasm`, so that is the name checked by godot-rust by default.
292    ///
293    /// However, if you need to rename that binary, you can make the library aware of the new binary name by returning
294    /// `Some("newname.wasm")` (don't forget to **include the `.wasm` extension**).
295    ///
296    /// For example, to have two simultaneous versions, one supporting multi-threading and the other not, you could add a suffix to the
297    /// filename of the Wasm binary of the multi-threaded version in your build process. If you choose the suffix `.threads.wasm`,
298    /// you're in luck as godot-rust already accepts this suffix by default, but let's say you want to use a different suffix, such as
299    /// `-with-threads.wasm`. For this, you can have a `"nothreads"` feature which, when absent, should produce a suffixed binary,
300    /// which can be informed to gdext as follows:
301    ///
302    /// ```no_run
303    /// # use godot::init::*;
304    /// struct MyExtension;
305    ///
306    /// #[gdextension]
307    /// unsafe impl ExtensionLibrary for MyExtension {
308    ///     fn override_wasm_binary() -> Option<&'static str> {
309    ///         // Binary name unchanged ("mycrate.wasm") without thread support.
310    ///         #[cfg(feature = "nothreads")]
311    ///         return None;
312    ///
313    ///         // Tell gdext we add a custom suffix to the binary with thread support.
314    ///         // Please note that this is not needed if "mycrate.threads.wasm" is used.
315    ///         // (You could return `None` as well in that particular case.)
316    ///         #[cfg(not(feature = "nothreads"))]
317    ///         Some("mycrate-with-threads.wasm")
318    ///     }
319    /// }
320    /// ```
321    /// Note that simply overriding this method won't change the name of the Wasm binary produced by Rust automatically: you'll still
322    /// have to rename it by yourself in your build process, as well as specify the updated binary name in your `.gdextension` file.
323    /// This is just to ensure gdext is aware of the new name given to the binary, avoiding runtime errors.
324    fn override_wasm_binary() -> Option<&'static str> {
325        None
326    }
327}
328
329/// Determines if and how an extension's code is run in the editor.
330///
331/// By default, Godot 4 runs all virtual lifecycle callbacks (`_ready`, `_process`, `_physics_process`, ...)
332/// for extensions. This behavior is different from Godot 3, where extension classes needed to be explicitly
333/// marked as "tool".
334///
335/// In many cases, users write extension code with the intention to run in games, not inside the editor.
336/// This is why the default behavior in gdext deviates from Godot: lifecycle callbacks are disabled inside the
337/// editor (see [`ToolClassesOnly`][Self::ToolClassesOnly]). It is possible to configure this.
338///
339/// See also [`ExtensionLibrary::editor_run_behavior()`].
340#[derive(Copy, Clone, Debug)]
341#[non_exhaustive]
342pub enum EditorRunBehavior {
343    /// Only runs `#[class(tool)]` classes in the editor.
344    ///
345    /// All classes are registered, and calls from GDScript to Rust are possible. However, virtual lifecycle callbacks
346    /// (`_ready`, `_process`, `_physics_process`, ...) are not run unless the class is annotated with `#[class(tool)]`.
347    ToolClassesOnly,
348
349    /// Runs the extension with full functionality in editor.
350    ///
351    /// Ignores any `#[class(tool)]` annotations.
352    AllClasses,
353}
354
355// ----------------------------------------------------------------------------------------------------------------------------------------------
356
357/// Stage of the Godot initialization process.
358///
359/// Godot's initialization and deinitialization processes are split into multiple stages, like a stack. At each level,
360/// a different amount of engine functionality is available. Deinitialization happens in reverse order.
361///
362/// See also:
363/// - [`ExtensionLibrary::on_level_init()`]
364/// - [`ExtensionLibrary::on_level_deinit()`]
365pub type InitLevel = sys::InitLevel;
366
367// ----------------------------------------------------------------------------------------------------------------------------------------------
368
369/// # Safety
370///
371/// - Must be called from the main thread.
372/// - The interface must be initialized.
373/// - The `Scene` api level must have been initialized.
374#[deny(unsafe_op_in_unsafe_fn)]
375unsafe fn ensure_godot_features_compatible() {
376    // 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
377    // later, and godot-core would only depend on godot-sys. This makes future migrations easier. We still have access to builtins though.
378
379    out!("Check Godot precision setting...");
380
381    let os_class = StringName::from("OS");
382    let single = GString::from("single");
383    let double = GString::from("double");
384
385    let gdext_is_double = cfg!(feature = "double-precision");
386
387    // SAFETY: main thread, after initialize(), valid string pointers, `Scene` initialized.
388    let godot_is_double = unsafe {
389        let is_single = sys::godot_has_feature(os_class.string_sys(), single.sys());
390        let is_double = sys::godot_has_feature(os_class.string_sys(), double.sys());
391
392        assert_ne!(
393            is_single, is_double,
394            "Godot has invalid configuration: single={is_single}, double={is_double}"
395        );
396
397        is_double
398    };
399
400    let s = |is_double: bool| -> &'static str {
401        if is_double {
402            "double"
403        } else {
404            "single"
405        }
406    };
407
408    out!(
409        "Is double precision: Godot={}, gdext={}",
410        s(godot_is_double),
411        s(gdext_is_double)
412    );
413
414    if godot_is_double != gdext_is_double {
415        panic!(
416            "Godot runs with {} precision, but gdext was compiled with {} precision.\n\
417            Cargo feature `double-precision` must be used if and only if Godot is compiled with `precision=double`.\n",
418            s(godot_is_double), s(gdext_is_double),
419        );
420    }
421}