godot_ffi/
lib.rs

1#![cfg_attr(published_docs, feature(doc_cfg))]
2/*
3 * Copyright (c) godot-rust; Bromeon and contributors.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 */
8
9//! # Internal crate of [**godot-rust**](https://godot-rust.github.io)
10//!
11//! Do not depend on this crate directly, instead use the `godot` crate.
12//! No SemVer or other guarantees are provided.
13//!
14//! # Contributor docs
15//!
16//! Low level bindings to the provided C core API.
17
18#![cfg_attr(test, allow(unused))]
19
20// ----------------------------------------------------------------------------------------------------------------------------------------------
21// Validations
22
23// More validations in godot crate. #[cfg]s are checked in godot-core.
24
25#[cfg(all(feature = "codegen-lazy-fptrs", feature = "experimental-threads"))] #[cfg_attr(published_docs, doc(cfg(all(feature = "codegen-lazy-fptrs", feature = "experimental-threads"))))]
26compile_error!(
27    "Cannot combine `lazy-function-tables` and `experimental-threads` features;\n\
28    thread safety for lazy-loaded function pointers is not yet implemented."
29);
30
31#[cfg(all(
32    feature = "experimental-wasm-nothreads",
33    feature = "experimental-threads"
34))]
35compile_error!("Cannot use 'experimental-threads' with a nothreads Wasm build yet.");
36
37// ----------------------------------------------------------------------------------------------------------------------------------------------
38
39// Output of generated code. Mimics the file structure, symbols are re-exported.
40#[rustfmt::skip]
41#[allow(
42    non_camel_case_types,
43    non_upper_case_globals,
44    non_snake_case,
45    deref_nullptr,
46    clippy::redundant_static_lifetimes,
47)]
48pub(crate) mod gen {
49    include!(concat!(env!("OUT_DIR"), "/mod.rs"));
50}
51
52pub mod conv;
53
54mod extras;
55mod global;
56mod godot_ffi;
57mod interface_init;
58#[cfg(target_os = "linux")] #[cfg_attr(published_docs, doc(cfg(target_os = "linux")))]
59pub mod linux_reload_workaround;
60mod opaque;
61mod plugins;
62mod string_cache;
63mod toolbox;
64
65#[doc(hidden)]
66#[cfg(target_family = "wasm")] #[cfg_attr(published_docs, doc(cfg(target_family = "wasm")))]
67pub use godot_macros::wasm_declare_init_fn;
68
69// No-op otherwise.
70#[doc(hidden)]
71#[cfg(not(target_family = "wasm"))] #[cfg_attr(published_docs, doc(cfg(not(target_family = "wasm"))))]
72#[macro_export]
73macro_rules! wasm_declare_init_fn {
74    () => {};
75}
76
77// Other
78pub use extras::*;
79pub use gen::central::*;
80pub use gen::gdextension_interface::*;
81pub use gen::interface::*;
82// Method tables
83pub use gen::table_builtins::*;
84pub use gen::table_builtins_lifecycle::*;
85pub use gen::table_core_classes::*;
86pub use gen::table_editor_classes::*;
87pub use gen::table_scene_classes::*;
88pub use gen::table_servers_classes::*;
89pub use gen::table_utilities::*;
90pub use global::*;
91pub use init_level::*;
92pub use string_cache::StringCache;
93pub use toolbox::*;
94
95pub use crate::godot_ffi::{
96    ExtVariantType, GodotFfi, GodotNullableFfi, PrimitiveConversionError, PtrcallType,
97};
98
99// ----------------------------------------------------------------------------------------------------------------------------------------------
100// API to access Godot via FFI
101
102mod binding;
103mod init_level;
104
105pub use binding::*;
106use binding::{
107    initialize_binding, initialize_builtin_method_table, initialize_class_core_method_table,
108    initialize_class_editor_method_table, initialize_class_scene_method_table,
109    initialize_class_server_method_table, runtime_metadata,
110};
111
112#[cfg(not(wasm_nothreads))] #[cfg_attr(published_docs, doc(cfg(not(wasm_nothreads))))]
113static MAIN_THREAD_ID: ManualInitCell<std::thread::ThreadId> = ManualInitCell::new();
114
115// ----------------------------------------------------------------------------------------------------------------------------------------------
116
117pub struct GdextRuntimeMetadata {
118    godot_version: GDExtensionGodotVersion,
119}
120
121impl GdextRuntimeMetadata {
122    /// # Safety
123    ///
124    /// - The `string` field of `godot_version` must not be written to while this struct exists.
125    /// - The `string` field of `godot_version` must be safe to read from while this struct exists.
126    pub unsafe fn new(godot_version: GDExtensionGodotVersion) -> Self {
127        Self { godot_version }
128    }
129}
130
131// SAFETY: The `string` pointer in `godot_version` is only ever read from while the struct exists, so we cannot have any race conditions.
132unsafe impl Sync for GdextRuntimeMetadata {}
133// SAFETY: See `Sync` impl safety doc.
134unsafe impl Send for GdextRuntimeMetadata {}
135
136/// Initializes the library.
137///
138/// # Safety
139///
140/// - The `get_proc_address` pointer must be a function pointer of type [`GDExtensionInterfaceGetProcAddress`] (valid for Godot 4.1+).
141/// - The `library` pointer must be the pointer given by Godot at initialisation.
142/// - This function must not be called from multiple threads.
143/// - This function must be called before any use of [`get_library`].
144pub unsafe fn initialize(
145    get_proc_address: GDExtensionInterfaceGetProcAddress,
146    library: GDExtensionClassLibraryPtr,
147    config: GdextConfig,
148) {
149    out!("Initialize gdext...");
150
151    out!(
152        "Godot version against which gdext was compiled: {}",
153        GdextBuild::godot_static_version_string()
154    );
155
156    // We want to initialize the main thread ID as early as possible.
157    //
158    // SAFETY: We set the main thread ID exactly once here and never again.
159    #[cfg(not(wasm_nothreads))] #[cfg_attr(published_docs, doc(cfg(not(wasm_nothreads))))]
160    unsafe {
161        MAIN_THREAD_ID.set(std::thread::current().id())
162    };
163
164    // Before anything else: if we run into a Godot binary that's compiled differently from gdext, proceeding would be UB -> panic.
165    interface_init::ensure_static_runtime_compatibility(get_proc_address);
166
167    // SAFETY: `ensure_static_runtime_compatibility` succeeded.
168    let version = unsafe { interface_init::runtime_version(get_proc_address) };
169    out!("Godot version of GDExtension API at runtime: {version:?}");
170
171    // SAFETY: `ensure_static_runtime_compatibility` succeeded.
172    let interface = unsafe { interface_init::load_interface(get_proc_address) };
173    out!("Loaded interface.");
174
175    // SAFETY: The interface was successfully loaded from Godot, so we should be able to load the builtin lifecycle table.
176    let global_method_table = unsafe { BuiltinLifecycleTable::load(&interface) };
177    out!("Loaded global method table.");
178
179    let mut string_names = StringCache::new(&interface, &global_method_table);
180
181    // SAFETY: The interface was successfully loaded from Godot, so we should be able to load the utility function table.
182    let utility_function_table =
183        unsafe { UtilityFunctionTable::load(&interface, &mut string_names) };
184    out!("Loaded utility function table.");
185
186    // SAFETY: We do not touch `version` again after passing it to `new` here.
187    let runtime_metadata = unsafe { GdextRuntimeMetadata::new(version) };
188
189    let builtin_method_table = {
190        #[cfg(feature = "codegen-lazy-fptrs")]
191        {
192            None // loaded later
193        }
194        #[cfg(not(feature = "codegen-lazy-fptrs"))]
195        {
196            // SAFETY: The interface was successfully loaded from Godot, so we should be able to load the builtin function table.
197            let table = unsafe { BuiltinMethodTable::load(&interface, &mut string_names) };
198            out!("Loaded builtin method table.");
199            Some(table)
200        }
201    };
202
203    drop(string_names);
204
205    // SAFETY: This function is only called at initialization and not from multiple threads.
206    unsafe {
207        initialize_binding(GodotBinding::new(
208            interface,
209            library,
210            global_method_table,
211            utility_function_table,
212            runtime_metadata,
213            config,
214        ))
215    }
216
217    if let Some(table) = builtin_method_table {
218        // SAFETY: We initialized the bindings above and haven't called this function before.
219        unsafe { initialize_builtin_method_table(table) }
220    }
221
222    out!("Assigned binding.");
223
224    // Lazy case: load afterward because table's internal StringCache stores &'static references to the interface.
225    #[cfg(feature = "codegen-lazy-fptrs")]
226    {
227        // SAFETY: The interface was successfully loaded from Godot, so we should be able to load the builtin function table.
228        let table = unsafe { BuiltinMethodTable::load() };
229
230        unsafe { initialize_builtin_method_table(table) }
231
232        out!("Loaded builtin method table (lazily).");
233    }
234
235    print_preamble(version);
236}
237
238/// Deinitializes the library.
239///
240/// Does not perform much logic, mostly used for consistency:
241/// - Ensure that the binding is not accessed after it has been deinitialized.
242/// - Allow re-initialization for hot-reloading on Linux.
243///
244/// # Safety
245/// See [`initialize`].
246pub unsafe fn deinitialize() {
247    deinitialize_binding();
248
249    // MACOS-PARTIAL-RELOAD: Clear the main thread ID to allow re-initialization during hot reload.
250    #[cfg(not(wasm_nothreads))]
251    {
252        if MAIN_THREAD_ID.is_initialized() {
253            MAIN_THREAD_ID.clear();
254        }
255    }
256}
257
258fn print_preamble(version: GDExtensionGodotVersion) {
259    let api_version: &'static str = GdextBuild::godot_static_version_string();
260    let runtime_version = read_version_string(&version);
261
262    println!("Initialize godot-rust (API {api_version}, runtime {runtime_version})");
263}
264
265/// # Safety
266///
267/// - Must be called from the main thread.
268/// - The interface must have been initialized with [`initialize`] before calling this function.
269/// - Must only be called once for each `api_level`.
270#[inline]
271pub unsafe fn load_class_method_table(api_level: InitLevel) {
272    out!("Load class method table for level '{:?}'...", api_level);
273    let begin = std::time::Instant::now();
274
275    #[cfg(not(feature = "codegen-lazy-fptrs"))]
276    // SAFETY: The interface has been initialized.
277    let interface = unsafe { get_interface() };
278
279    #[cfg(not(feature = "codegen-lazy-fptrs"))]
280    // SAFETY: The interface has been initialized.
281    let mut string_names = StringCache::new(interface, unsafe { builtin_lifecycle_api() });
282
283    let (class_count, method_count);
284    match api_level {
285        InitLevel::Core => {
286            // SAFETY: The interface has been initialized and this function hasn't been called before.
287            unsafe {
288                #[cfg(feature = "codegen-lazy-fptrs")] #[cfg_attr(published_docs, doc(cfg(feature = "codegen-lazy-fptrs")))]
289                initialize_class_core_method_table(ClassCoreMethodTable::load());
290                #[cfg(not(feature = "codegen-lazy-fptrs"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "codegen-lazy-fptrs"))))]
291                initialize_class_core_method_table(ClassCoreMethodTable::load(
292                    interface,
293                    &mut string_names,
294                ));
295            }
296            class_count = ClassCoreMethodTable::CLASS_COUNT;
297            method_count = ClassCoreMethodTable::METHOD_COUNT;
298        }
299        InitLevel::Servers => {
300            // SAFETY: The interface has been initialized and this function hasn't been called before.
301            unsafe {
302                #[cfg(feature = "codegen-lazy-fptrs")] #[cfg_attr(published_docs, doc(cfg(feature = "codegen-lazy-fptrs")))]
303                initialize_class_server_method_table(ClassServersMethodTable::load());
304                #[cfg(not(feature = "codegen-lazy-fptrs"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "codegen-lazy-fptrs"))))]
305                initialize_class_server_method_table(ClassServersMethodTable::load(
306                    interface,
307                    &mut string_names,
308                ));
309            }
310            class_count = ClassServersMethodTable::CLASS_COUNT;
311            method_count = ClassServersMethodTable::METHOD_COUNT;
312        }
313        InitLevel::Scene => {
314            // SAFETY: The interface has been initialized and this function hasn't been called before.
315            unsafe {
316                #[cfg(feature = "codegen-lazy-fptrs")] #[cfg_attr(published_docs, doc(cfg(feature = "codegen-lazy-fptrs")))]
317                initialize_class_scene_method_table(ClassSceneMethodTable::load());
318                #[cfg(not(feature = "codegen-lazy-fptrs"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "codegen-lazy-fptrs"))))]
319                initialize_class_scene_method_table(ClassSceneMethodTable::load(
320                    interface,
321                    &mut string_names,
322                ));
323            }
324            class_count = ClassSceneMethodTable::CLASS_COUNT;
325            method_count = ClassSceneMethodTable::METHOD_COUNT;
326        }
327        InitLevel::Editor => {
328            // SAFETY: The interface has been initialized and this function hasn't been called before.
329            unsafe {
330                #[cfg(feature = "codegen-lazy-fptrs")] #[cfg_attr(published_docs, doc(cfg(feature = "codegen-lazy-fptrs")))]
331                initialize_class_editor_method_table(ClassEditorMethodTable::load());
332                #[cfg(not(feature = "codegen-lazy-fptrs"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "codegen-lazy-fptrs"))))]
333                initialize_class_editor_method_table(ClassEditorMethodTable::load(
334                    interface,
335                    &mut string_names,
336                ));
337            }
338            class_count = ClassEditorMethodTable::CLASS_COUNT;
339            method_count = ClassEditorMethodTable::METHOD_COUNT;
340        }
341    }
342
343    let _elapsed = std::time::Instant::now() - begin;
344    out!(
345        "{:?} level: loaded {} classes and {} methods in {}s.",
346        api_level,
347        class_count,
348        method_count,
349        _elapsed.as_secs_f64()
350    );
351}
352
353/// # Safety
354///
355/// - Must be accessed from the main thread.
356/// - The interface must have been initialized.
357/// - The `Scene` api level must have been initialized.
358/// - `os_class_sname` must be a valid `StringName` pointer.
359/// - `tag_string` must be a valid type pointer of a `String` instance.
360#[inline]
361pub unsafe fn godot_has_feature(
362    os_class_sname: GDExtensionConstStringNamePtr,
363    tag_string: GDExtensionConstTypePtr,
364) -> bool {
365    // Issue a raw C call to OS.has_feature(tag_string).
366
367    // SAFETY: Called from main thread, interface has been initialized, and the scene api has been initialized.
368    let method_bind = unsafe { class_core_api() }.os__has_feature();
369
370    // SAFETY: Called from main thread, and interface has been initialized.
371    let interface = unsafe { get_interface() };
372    let get_singleton = interface.global_get_singleton.unwrap();
373    let class_ptrcall = interface.object_method_bind_ptrcall.unwrap();
374
375    // SAFETY: Interface has been initialized, and `Scene` has been initialized, so `get_singleton` can be called. `os_class_sname` is a valid
376    // `StringName` pointer.
377    let object_ptr = unsafe { get_singleton(os_class_sname) };
378    let mut return_ptr = false;
379    let type_ptrs = [tag_string];
380
381    // SAFETY: We are properly passing arguments to make a ptrcall.
382    unsafe {
383        class_ptrcall(
384            method_bind.0,
385            object_ptr,
386            type_ptrs.as_ptr(),
387            return_ptr.sys_mut(),
388        )
389    }
390
391    return_ptr
392}
393
394/// Get the [`ThreadId`](std::thread::ThreadId) of the main thread.
395///
396/// # Panics
397/// - If it is called before the engine bindings have been initialized.
398#[cfg(not(wasm_nothreads))] #[cfg_attr(published_docs, doc(cfg(not(wasm_nothreads))))]
399pub fn main_thread_id() -> std::thread::ThreadId {
400    assert!(
401        MAIN_THREAD_ID.is_initialized(),
402        "Godot engine not available; make sure you are not calling it from unit/doc tests"
403    );
404
405    // SAFETY: We initialized the cell during library initialization, before any other code is executed.
406    let thread_id = unsafe { MAIN_THREAD_ID.get_unchecked() };
407
408    *thread_id
409}
410
411/// Check if the current thread is the main thread.
412///
413/// # Panics
414/// - If it is called before the engine bindings have been initialized.
415pub fn is_main_thread() -> bool {
416    #[cfg(not(wasm_nothreads))]
417    {
418        std::thread::current().id() == main_thread_id()
419    }
420
421    #[cfg(wasm_nothreads)]
422    {
423        true
424    }
425}
426
427/// Assign the current thread id to be the main thread.
428///
429/// This is required for platforms on which Godot runs the main loop on a different thread than the thread the library was loaded on.
430/// Android is one such platform.
431///
432/// # Safety
433///
434/// - must only be called after [`initialize`] has been called.
435pub unsafe fn discover_main_thread() {
436    #[cfg(not(wasm_nothreads))]
437    {
438        if is_main_thread() {
439            // we don't have to do anything if the current thread is already the main thread.
440            return;
441        }
442
443        let thread_id = std::thread::current().id();
444
445        // SAFETY: initialize must have already been called before this function is called. By clearing and setting the cell again we can reinitialize it.
446        unsafe {
447            MAIN_THREAD_ID.clear();
448            MAIN_THREAD_ID.set(thread_id);
449        }
450    }
451}
452
453/// Construct Godot object.
454///
455/// "NOTIFICATION_POSTINITIALIZE" must be sent after construction since 4.4.
456///
457/// # Safety
458/// `class_name` is assumed to be valid.
459pub unsafe fn classdb_construct_object(
460    class_name: GDExtensionConstStringNamePtr,
461) -> GDExtensionObjectPtr {
462    #[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
463    return interface_fn!(classdb_construct_object)(class_name);
464    #[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
465    return interface_fn!(classdb_construct_object2)(class_name);
466}
467
468// ----------------------------------------------------------------------------------------------------------------------------------------------
469// Macros to access low-level function bindings
470
471#[macro_export]
472#[doc(hidden)]
473macro_rules! builtin_fn {
474    ($name:ident $(@1)?) => {
475        $crate::builtin_lifecycle_api().$name
476    };
477}
478
479#[macro_export]
480#[doc(hidden)]
481macro_rules! builtin_call {
482        ($name:ident ( $($args:expr),* $(,)? )) => {
483            ($crate::builtin_lifecycle_api().$name)( $($args),* )
484        };
485    }
486
487#[macro_export]
488#[doc(hidden)]
489macro_rules! interface_fn {
490    ($name:ident) => {{
491        unsafe { $crate::get_interface().$name.unwrap_unchecked() }
492    }};
493}