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}