godot_core/registry/
callbacks.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
8//! Callbacks that are passed as function pointers to Godot upon class registration.
9//!
10//! Re-exported to `crate::private`.
11#![allow(clippy::missing_safety_doc)]
12
13use std::any::Any;
14
15use godot_ffi as sys;
16use sys::conv::u32_to_usize;
17use sys::interface_fn;
18
19use crate::builder::ClassBuilder;
20use crate::builtin::{StringName, Variant};
21use crate::classes::Object;
22use crate::meta::PropertyInfo;
23use crate::obj::{bounds, cap, AsDyn, Base, Bounds, Gd, GodotClass, Inherits, UserClass};
24use crate::private::{handle_panic, IntoVirtualMethodReceiver, PanicPayload};
25use crate::registry::plugin::ErasedDynGd;
26use crate::storage::{as_storage, InstanceStorage, Storage, StorageRefCounted};
27
28/// Godot FFI default constructor.
29///
30/// If the `init()` constructor panics, null is returned.
31///
32/// Creation callback has `p_notify_postinitialize` parameter since 4.4: <https://github.com/godotengine/godot/pull/91018>.
33#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
34pub unsafe extern "C" fn create<T: cap::GodotDefault>(
35    _class_userdata: *mut std::ffi::c_void,
36    _notify_postinitialize: sys::GDExtensionBool,
37) -> sys::GDExtensionObjectPtr {
38    create_custom(
39        T::__godot_user_init,
40        sys::conv::bool_from_sys(_notify_postinitialize),
41    )
42    .unwrap_or(std::ptr::null_mut())
43}
44
45#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
46pub unsafe extern "C" fn create<T: cap::GodotDefault>(
47    _class_userdata: *mut std::ffi::c_void,
48) -> sys::GDExtensionObjectPtr {
49    // `notify_postinitialize` doesn't matter before 4.4, it's sent by Godot when constructing object and we don't send it.
50    create_custom(T::__godot_user_init, true).unwrap_or(std::ptr::null_mut())
51}
52
53/// Workaround for <https://github.com/godot-rust/gdext/issues/874> before Godot 4.5.
54///
55/// Godot expects a creator function, but doesn't require an actual object to be instantiated.
56#[cfg(all(since_api = "4.4", before_api = "4.5"))] #[cfg_attr(published_docs, doc(cfg(all(since_api = "4.4", before_api = "4.5"))))]
57pub unsafe extern "C" fn create_null<T>(
58    _class_userdata: *mut std::ffi::c_void,
59    _notify_postinitialize: sys::GDExtensionBool,
60) -> sys::GDExtensionObjectPtr {
61    std::ptr::null_mut()
62}
63
64#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
65pub unsafe extern "C" fn create_null<T>(
66    _class_userdata: *mut std::ffi::c_void,
67) -> sys::GDExtensionObjectPtr {
68    std::ptr::null_mut()
69}
70
71/// Godot FFI function for recreating a GDExtension instance, e.g. after a hot reload.
72///
73/// If the `init()` constructor panics, null is returned.
74pub unsafe extern "C" fn recreate<T: cap::GodotDefault>(
75    _class_userdata: *mut std::ffi::c_void,
76    object: sys::GDExtensionObjectPtr,
77) -> sys::GDExtensionClassInstancePtr {
78    create_rust_part_for_existing_godot_part(T::__godot_user_init, object, |_| {})
79        .unwrap_or(std::ptr::null_mut())
80}
81
82/// Workaround for <https://github.com/godot-rust/gdext/issues/874> before Godot 4.5.
83///
84/// Godot expects a creator function, but doesn't require an actual object to be instantiated.
85#[cfg(before_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.5")))]
86pub unsafe extern "C" fn recreate_null<T>(
87    _class_userdata: *mut std::ffi::c_void,
88    _object: sys::GDExtensionObjectPtr,
89) -> sys::GDExtensionClassInstancePtr {
90    std::ptr::null_mut()
91}
92
93pub(crate) fn create_custom<T, F>(
94    make_user_instance: F,
95    notify_postinitialize: bool,
96) -> Result<sys::GDExtensionObjectPtr, PanicPayload>
97where
98    T: GodotClass,
99    F: FnOnce(Base<T::Base>) -> T,
100{
101    let base_class_name = T::Base::class_id();
102    let base_ptr = unsafe { sys::classdb_construct_object(base_class_name.string_sys()) };
103
104    let postinit = |base_ptr| {
105        #[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
106        if notify_postinitialize {
107            // Should notify it with a weak pointer, during `NOTIFICATION_POSTINITIALIZE`, ref-counted object is not yet fully-initialized.
108            let mut obj = unsafe { Gd::<Object>::from_obj_sys_weak(base_ptr) };
109            obj.notify(crate::classes::notify::ObjectNotification::POSTINITIALIZE);
110            obj.drop_weak();
111        }
112    };
113
114    match create_rust_part_for_existing_godot_part(make_user_instance, base_ptr, postinit) {
115        Ok(_extension_ptr) => Ok(base_ptr),
116        Err(payload) => {
117            // Creation of extension object failed; we must now also destroy the base object to avoid leak.
118            // SAFETY: `base_ptr` was just created above.
119            unsafe { interface_fn!(object_destroy)(base_ptr) };
120
121            Err(payload)
122        }
123    }
124
125    // std::mem::forget(base_class_name);
126}
127
128/// Add Rust-side state for a GDExtension base object.
129///
130/// With godot-rust, custom objects consist of two parts: the Godot object and the Rust object. This method takes the Godot part by pointer,
131/// creates the Rust part with the supplied state, and links them together. This is used for both brand-new object creation and hot reload.
132/// During hot reload, Rust objects are disposed of and then created again with updated code, so it's necessary to re-link them to Godot objects.
133fn create_rust_part_for_existing_godot_part<T, F, P>(
134    make_user_instance: F,
135    base_ptr: sys::GDExtensionObjectPtr,
136    postinit: P,
137) -> Result<sys::GDExtensionClassInstancePtr, PanicPayload>
138where
139    T: GodotClass,
140    F: FnOnce(Base<T::Base>) -> T,
141    P: Fn(sys::GDExtensionObjectPtr),
142{
143    let class_name = T::class_id();
144    //out!("create callback: {}", class_name.backing);
145
146    let base = unsafe { Base::from_sys(base_ptr) };
147
148    // User constructor init() can panic, which crashes the engine if unhandled.
149    let context = || format!("panic during {class_name}::init() constructor");
150    let code = || make_user_instance(unsafe { Base::from_base(&base) });
151    let user_instance = handle_panic(context, std::panic::AssertUnwindSafe(code))?;
152
153    // Print shouldn't be necessary as panic itself is printed. If this changes, re-enable in error case:
154    // godot_error!("failed to create instance of {class_name}; Rust init() panicked");
155
156    let mut base_copy = unsafe { Base::from_base(&base) };
157
158    let instance = InstanceStorage::<T>::construct(user_instance, base);
159    let instance_rust_ptr = instance.into_raw();
160    let instance_ptr = instance_rust_ptr as sys::GDExtensionClassInstancePtr;
161
162    let binding_data_callbacks = crate::storage::nop_instance_callbacks();
163    unsafe {
164        interface_fn!(object_set_instance)(base_ptr, class_name.string_sys(), instance_ptr);
165        interface_fn!(object_set_instance_binding)(
166            base_ptr,
167            sys::get_library() as *mut std::ffi::c_void,
168            instance_ptr as *mut std::ffi::c_void,
169            &binding_data_callbacks,
170        );
171    }
172
173    postinit(base_ptr);
174
175    // Mark initialization as complete, now that user constructor has finished.
176    base_copy.mark_initialized();
177
178    // No std::mem::forget(base_copy) here, since Base may stores other fields that need deallocation.
179    Ok(instance_ptr)
180}
181
182pub unsafe extern "C" fn free<T: GodotClass>(
183    _class_user_data: *mut std::ffi::c_void,
184    instance: sys::GDExtensionClassInstancePtr,
185) {
186    {
187        let storage = as_storage::<T>(instance);
188        storage.mark_destroyed_by_godot();
189    } // Ref no longer valid once next statement is executed.
190
191    crate::storage::destroy_storage::<T>(instance);
192}
193
194#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
195pub unsafe extern "C" fn get_virtual<T: cap::ImplementsGodotVirtual>(
196    _class_user_data: *mut std::ffi::c_void,
197    name: sys::GDExtensionConstStringNamePtr,
198    hash: u32,
199) -> sys::GDExtensionClassCallVirtual {
200    // This string is not ours, so we cannot call the destructor on it.
201    let borrowed_string = StringName::borrow_string_sys(name);
202    let method_name = borrowed_string.to_string();
203
204    T::__virtual_call(method_name.as_str(), hash)
205}
206
207#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
208pub unsafe extern "C" fn get_virtual<T: cap::ImplementsGodotVirtual>(
209    _class_user_data: *mut std::ffi::c_void,
210    name: sys::GDExtensionConstStringNamePtr,
211) -> sys::GDExtensionClassCallVirtual {
212    // This string is not ours, so we cannot call the destructor on it.
213    let borrowed_string = StringName::borrow_string_sys(name);
214    let method_name = borrowed_string.to_string();
215
216    T::__virtual_call(method_name.as_str())
217}
218
219#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
220pub unsafe extern "C" fn default_get_virtual<T: UserClass>(
221    _class_user_data: *mut std::ffi::c_void,
222    name: sys::GDExtensionConstStringNamePtr,
223    hash: u32,
224) -> sys::GDExtensionClassCallVirtual {
225    // This string is not ours, so we cannot call the destructor on it.
226    let borrowed_string = StringName::borrow_string_sys(name);
227    let method_name = borrowed_string.to_string();
228
229    T::__default_virtual_call(method_name.as_str(), hash)
230}
231
232#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
233pub unsafe extern "C" fn default_get_virtual<T: UserClass>(
234    _class_user_data: *mut std::ffi::c_void,
235    name: sys::GDExtensionConstStringNamePtr,
236) -> sys::GDExtensionClassCallVirtual {
237    // This string is not ours, so we cannot call the destructor on it.
238    let borrowed_string = StringName::borrow_string_sys(name);
239    let method_name = borrowed_string.to_string();
240
241    T::__default_virtual_call(method_name.as_str())
242}
243
244pub unsafe extern "C" fn to_string<T: cap::GodotToString>(
245    instance: sys::GDExtensionClassInstancePtr,
246    is_valid: *mut sys::GDExtensionBool,
247    out_string: sys::GDExtensionStringPtr,
248) {
249    // Note: to_string currently always succeeds, as it is only provided for classes that have a working implementation.
250
251    let storage = as_storage::<T>(instance);
252    let string = T::__godot_to_string(T::Recv::instance(storage));
253
254    // Transfer ownership to Godot
255    string.move_into_string_ptr(out_string);
256
257    // Note: is_valid comes uninitialized and must be set.
258    *is_valid = sys::conv::SYS_TRUE;
259}
260
261pub unsafe extern "C" fn on_notification<T: cap::GodotNotification>(
262    instance: sys::GDExtensionClassInstancePtr,
263    what: i32,
264    _reversed: sys::GDExtensionBool,
265) {
266    let storage = as_storage::<T>(instance);
267    let mut instance = storage.get_mut();
268
269    T::__godot_notification(&mut *instance, what);
270}
271
272pub unsafe extern "C" fn get_property<T: cap::GodotGet>(
273    instance: sys::GDExtensionClassInstancePtr,
274    name: sys::GDExtensionConstStringNamePtr,
275    ret: sys::GDExtensionVariantPtr,
276) -> sys::GDExtensionBool {
277    let storage = as_storage::<T>(instance);
278    let instance = T::Recv::instance(storage);
279    let property = StringName::new_from_string_sys(name);
280
281    match T::__godot_get_property(instance, property) {
282        Some(value) => {
283            value.move_into_var_ptr(ret);
284            sys::conv::SYS_TRUE
285        }
286        None => sys::conv::SYS_FALSE,
287    }
288}
289
290pub unsafe extern "C" fn set_property<T: cap::GodotSet>(
291    instance: sys::GDExtensionClassInstancePtr,
292    name: sys::GDExtensionConstStringNamePtr,
293    value: sys::GDExtensionConstVariantPtr,
294) -> sys::GDExtensionBool {
295    let storage = as_storage::<T>(instance);
296    let instance = T::Recv::instance(storage);
297
298    let property = StringName::new_from_string_sys(name);
299    let value = Variant::new_from_var_sys(value);
300
301    sys::conv::bool_to_sys(T::__godot_set_property(instance, property, value))
302}
303
304pub unsafe extern "C" fn reference<T: GodotClass>(instance: sys::GDExtensionClassInstancePtr) {
305    let storage = as_storage::<T>(instance);
306    storage.on_inc_ref();
307}
308
309pub unsafe extern "C" fn unreference<T: GodotClass>(instance: sys::GDExtensionClassInstancePtr) {
310    let storage = as_storage::<T>(instance);
311    storage.on_dec_ref();
312}
313
314/// # Safety
315///
316/// Must only be called by Godot as a callback for `get_property_list` for a rust-defined class of type `T`.
317#[deny(unsafe_op_in_unsafe_fn)]
318pub unsafe extern "C" fn get_property_list<T: cap::GodotGetPropertyList>(
319    instance: sys::GDExtensionClassInstancePtr,
320    count: *mut u32,
321) -> *const sys::GDExtensionPropertyInfo {
322    // SAFETY: Godot provides us with a valid instance pointer to a `T`. And it will live until the end of this function.
323    let storage = unsafe { as_storage::<T>(instance) };
324    let instance = T::Recv::instance(storage);
325
326    let property_list = T::__godot_get_property_list(instance);
327    let property_list_sys: Box<[sys::GDExtensionPropertyInfo]> = property_list
328        .into_iter()
329        .map(|prop| prop.into_owned_property_sys())
330        .collect();
331
332    // SAFETY: Godot ensures that `count` is initialized and valid to write into.
333    unsafe {
334        *count = property_list_sys
335            .len()
336            .try_into()
337            .expect("property list cannot be longer than `u32::MAX`");
338    }
339
340    Box::leak(property_list_sys).as_mut_ptr()
341}
342
343/// # Safety
344///
345/// - Must only be called by Godot as a callback for `free_property_list` for a rust-defined class of type `T`.
346/// - Must only be passed to Godot as a callback when [`get_property_list`] is the corresponding `get_property_list` callback.
347#[deny(unsafe_op_in_unsafe_fn)]
348pub unsafe extern "C" fn free_property_list<T: cap::GodotGetPropertyList>(
349    _instance: sys::GDExtensionClassInstancePtr,
350    list: *const sys::GDExtensionPropertyInfo,
351    count: u32,
352) {
353    let list = list as *mut sys::GDExtensionPropertyInfo;
354
355    // SAFETY: `list` comes from `get_property_list` above, and `count` also comes from the same function.
356    // This means that `list` is a pointer to a `&[sys::GDExtensionPropertyInfo]` slice of length `count`.
357    // This means all the preconditions of this function are satisfied except uniqueness of this point.
358    // Uniqueness is guaranteed as Godot called this function at a point where the list is no longer accessed
359    // through any other pointer, and we don't access the slice through any other pointer after this call either.
360    let property_list_slice = unsafe { std::slice::from_raw_parts_mut(list, u32_to_usize(count)) };
361
362    // SAFETY: This slice was created by calling `Box::leak` on a `Box<[sys::GDExtensionPropertyInfo]>`, we can thus
363    // call `Box::from_raw` on this slice to get back the original boxed slice.
364    // Note that this relies on coercion of `&mut` -> `*mut`.
365    let property_list_sys = unsafe { Box::from_raw(property_list_slice) };
366
367    for property_info in property_list_sys.iter() {
368        // SAFETY: The structs contained in this list were all returned from `into_owned_property_sys`.
369        // We only call this method once for each struct and for each list.
370        unsafe {
371            crate::meta::PropertyInfo::free_owned_property_sys(*property_info);
372        }
373    }
374}
375
376/// # Safety
377///
378/// * `instance` must be a valid `T` instance pointer for the duration of this function call.
379/// * `property_name` must be a valid `StringName` pointer for the duration of this function call.
380#[deny(unsafe_op_in_unsafe_fn)]
381unsafe fn raw_property_get_revert<T: cap::GodotPropertyGetRevert>(
382    instance: sys::GDExtensionClassInstancePtr,
383    property_name: sys::GDExtensionConstStringNamePtr,
384) -> Option<Variant> {
385    // SAFETY: `instance` is a valid `T` instance pointer for the duration of this function call.
386    let storage = unsafe { as_storage::<T>(instance) };
387    let instance = T::Recv::instance(storage);
388
389    // SAFETY: `property_name` is a valid `StringName` pointer for the duration of this function call.
390    let property = unsafe { StringName::borrow_string_sys(property_name) };
391    T::__godot_property_get_revert(instance, property.clone())
392}
393
394/// # Safety
395///
396/// - Must only be called by Godot as a callback for `property_can_revert` for a rust-defined class of type `T`.
397#[deny(unsafe_op_in_unsafe_fn)]
398pub unsafe extern "C" fn property_can_revert<T: cap::GodotPropertyGetRevert>(
399    instance: sys::GDExtensionClassInstancePtr,
400    property_name: sys::GDExtensionConstStringNamePtr,
401) -> sys::GDExtensionBool {
402    // SAFETY: Godot provides us with a valid `T` instance pointer and `StringName` pointer for the duration of this call.
403    let revert = unsafe { raw_property_get_revert::<T>(instance, property_name) };
404
405    sys::conv::bool_to_sys(revert.is_some())
406}
407
408/// # Safety
409///
410/// - Must only be called by Godot as a callback for `property_get_revert` for a rust-defined class of type `T`.
411#[deny(unsafe_op_in_unsafe_fn)]
412pub unsafe extern "C" fn property_get_revert<T: cap::GodotPropertyGetRevert>(
413    instance: sys::GDExtensionClassInstancePtr,
414    property_name: sys::GDExtensionConstStringNamePtr,
415    ret: sys::GDExtensionVariantPtr,
416) -> sys::GDExtensionBool {
417    // SAFETY: Godot provides us with a valid `T` instance pointer and `StringName` pointer for the duration of this call.
418    let Some(revert) = (unsafe { raw_property_get_revert::<T>(instance, property_name) }) else {
419        return sys::conv::SYS_FALSE;
420    };
421
422    // SAFETY: Godot provides us with a valid `Variant` pointer.
423    unsafe {
424        revert.move_into_var_ptr(ret);
425    }
426
427    sys::conv::SYS_TRUE
428}
429
430/// Callback for `validate_property`.
431///
432/// Exposes `PropertyInfo` created out of `*mut GDExtensionPropertyInfo` ptr to user and moves edited values back to the pointer.
433///
434/// # Safety
435///
436/// - Must only be called by Godot as a callback for `validate_property` for a rust-defined class of type `T`.
437/// - `property_info_ptr` must be valid for the whole duration of this function call (i.e. - can't be freed nor consumed).
438///
439#[deny(unsafe_op_in_unsafe_fn)]
440pub unsafe extern "C" fn validate_property<T: cap::GodotValidateProperty>(
441    instance: sys::GDExtensionClassInstancePtr,
442    property_info_ptr: *mut sys::GDExtensionPropertyInfo,
443) -> sys::GDExtensionBool {
444    // SAFETY: `instance` is a valid `T` instance pointer for the duration of this function call.
445    let storage = unsafe { as_storage::<T>(instance) };
446    let instance = T::Recv::instance(storage);
447
448    // SAFETY: property_info_ptr must be valid.
449    let mut property_info = unsafe { PropertyInfo::new_from_sys(property_info_ptr) };
450    T::__godot_validate_property(instance, &mut property_info);
451
452    // SAFETY: property_info_ptr remains valid & unchanged.
453    unsafe { property_info.move_into_property_info_ptr(property_info_ptr) };
454
455    sys::conv::SYS_TRUE
456}
457
458// ----------------------------------------------------------------------------------------------------------------------------------------------
459// Safe, higher-level methods
460
461pub fn register_class_by_builder<T: cap::GodotRegisterClass>(_class_builder: &mut dyn Any) {
462    // TODO use actual argument, once class builder carries state
463    // let class_builder = class_builder
464    //     .downcast_mut::<ClassBuilder<T>>()
465    //     .expect("bad type erasure");
466
467    let mut class_builder = ClassBuilder::new();
468    T::__godot_register_class(&mut class_builder);
469}
470
471pub fn register_user_properties<T: cap::ImplementsGodotExports>(_class_builder: &mut dyn Any) {
472    T::__register_exports();
473}
474
475pub fn register_user_methods_constants<T: cap::ImplementsGodotApi>(_class_builder: &mut dyn Any) {
476    // let class_builder = class_builder
477    //     .downcast_mut::<ClassBuilder<T>>()
478    //     .expect("bad type erasure");
479
480    //T::register_methods(class_builder);
481    T::__register_methods();
482    T::__register_constants();
483}
484
485pub fn register_user_rpcs<T: cap::ImplementsGodotApi>(object: &mut dyn Any) {
486    T::__register_rpcs(object);
487}
488
489/// # Safety
490///
491/// `obj` must be castable to `T`.
492#[deny(unsafe_op_in_unsafe_fn)]
493pub unsafe fn dynify_fn<T, D>(obj: Gd<Object>) -> ErasedDynGd
494where
495    T: GodotClass + Inherits<Object> + AsDyn<D> + Bounds<Declarer = bounds::DeclUser>,
496    D: ?Sized + 'static,
497{
498    // SAFETY: `obj` is castable to `T`.
499    let obj = unsafe { obj.try_cast::<T>().unwrap_unchecked() };
500    let obj = obj.into_dyn::<D>();
501    let obj = obj.upcast::<Object>();
502
503    ErasedDynGd {
504        boxed: Box::new(obj),
505    }
506}