1#![cfg_attr(published_docs, feature(doc_cfg))]
2#![cfg_attr(test, allow(unused))]
19
20#[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#[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#[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
77pub use extras::*;
79pub use gen::central::*;
80pub use gen::gdextension_interface::*;
81pub use gen::interface::*;
82pub 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
99mod 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
115pub struct GdextRuntimeMetadata {
118 godot_version: GDExtensionGodotVersion,
119}
120
121impl GdextRuntimeMetadata {
122 pub unsafe fn new(godot_version: GDExtensionGodotVersion) -> Self {
127 Self { godot_version }
128 }
129}
130
131unsafe impl Sync for GdextRuntimeMetadata {}
133unsafe impl Send for GdextRuntimeMetadata {}
135
136pub 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 #[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 interface_init::ensure_static_runtime_compatibility(get_proc_address);
166
167 let version = unsafe { interface_init::runtime_version(get_proc_address) };
169 out!("Godot version of GDExtension API at runtime: {version:?}");
170
171 let interface = unsafe { interface_init::load_interface(get_proc_address) };
173 out!("Loaded interface.");
174
175 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 let utility_function_table =
183 unsafe { UtilityFunctionTable::load(&interface, &mut string_names) };
184 out!("Loaded utility function table.");
185
186 let runtime_metadata = unsafe { GdextRuntimeMetadata::new(version) };
188
189 let builtin_method_table = {
190 #[cfg(feature = "codegen-lazy-fptrs")]
191 {
192 None }
194 #[cfg(not(feature = "codegen-lazy-fptrs"))]
195 {
196 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 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 unsafe { initialize_builtin_method_table(table) }
220 }
221
222 out!("Assigned binding.");
223
224 #[cfg(feature = "codegen-lazy-fptrs")]
226 {
227 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
238pub unsafe fn deinitialize() {
247 deinitialize_binding();
248
249 #[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#[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 let interface = unsafe { get_interface() };
278
279 #[cfg(not(feature = "codegen-lazy-fptrs"))]
280 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 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 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 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 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#[inline]
361pub unsafe fn godot_has_feature(
362 os_class_sname: GDExtensionConstStringNamePtr,
363 tag_string: GDExtensionConstTypePtr,
364) -> bool {
365 let method_bind = unsafe { class_core_api() }.os__has_feature();
369
370 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 let object_ptr = unsafe { get_singleton(os_class_sname) };
378 let mut return_ptr = false;
379 let type_ptrs = [tag_string];
380
381 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#[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 let thread_id = unsafe { MAIN_THREAD_ID.get_unchecked() };
407
408 *thread_id
409}
410
411pub 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
427pub unsafe fn discover_main_thread() {
436 #[cfg(not(wasm_nothreads))]
437 {
438 if is_main_thread() {
439 return;
441 }
442
443 let thread_id = std::thread::current().id();
444
445 unsafe {
447 MAIN_THREAD_ID.clear();
448 MAIN_THREAD_ID.set(thread_id);
449 }
450 }
451}
452
453pub 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#[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}