diff --git a/godot-core/src/obj/base.rs b/godot-core/src/obj/base.rs index 676e34302..7d36dcf05 100644 --- a/godot-core/src/obj/base.rs +++ b/godot-core/src/obj/base.rs @@ -80,7 +80,7 @@ pub struct Base { // 2. obj: ManuallyDrop>, - /// Tracks the initialization state of this `Base` in Debug mode. + /// Tracks the initialization state of this `Base` if safeguards are enabled. /// /// Rc allows to "copy-construct" the base from an existing one, while still affecting the user-instance through the original `Base`. #[cfg(safeguards_balanced)] diff --git a/godot-core/src/obj/bounds.rs b/godot-core/src/obj/bounds.rs index 47086f908..f37f6d6d0 100644 --- a/godot-core/src/obj/bounds.rs +++ b/godot-core/src/obj/bounds.rs @@ -420,7 +420,7 @@ impl Declarer for DeclUser { where T: GodotDefault + Bounds, { - Gd::default_instance() + Gd::default_user_instance() } } diff --git a/godot-core/src/obj/gd.rs b/godot-core/src/obj/gd.rs index b0b4691b7..6a0f6dec1 100644 --- a/godot-core/src/obj/gd.rs +++ b/godot-core/src/obj/gd.rs @@ -19,12 +19,14 @@ use crate::meta::{ ToGodot, }; use crate::obj::{ - bounds, cap, Bounds, DynGd, GdDerefTarget, GdMut, GdRef, GodotClass, Inherits, InstanceId, - OnEditor, RawGd, WithBaseField, WithSignals, + bounds, cap, Base, Bounds, DynGd, GdDerefTarget, GdMut, GdRef, GodotClass, Inherits, + InstanceId, OnEditor, RawGd, WithBaseField, WithSignals, }; +use crate::private::callbacks::postinit; use crate::private::{callbacks, PanicPayload}; use crate::registry::class::try_dynify_object; use crate::registry::property::{object_export_element_type_string, Export, Var}; +use crate::storage::Storage; use crate::{classes, meta, out}; /// Smart pointer to objects owned by the Godot engine. @@ -204,6 +206,38 @@ where pub fn bind_mut(&mut self) -> GdMut<'_, T> { self.raw.bind_mut() } + + /// Creates a new initialized instance. + /// + /// Creation goes through ClassDb, which handles creation of placeholders and other specific logic. + /// + /// Since Godot 4.4 the caller (in this case godot-rust) is responsible for emitting the `POSTINITIALIZE` + /// notification, which should be handled by a not fully-initialized object. This is done so to maintain the consistency – + /// the notification must be handled the same way regardless of how and where the instance was created (in other words, + /// `T::new_gd()` and `T::default()` must be equivalent to the GDScript expression `T.new()`). + /// + /// Before Godot 4.4, Godot itself was responsible for emitting the `POSTINITIALIZE` notification, so we mark the instance as initialized + /// during creation. + /// + /// See also: [`Base`]. + #[doc(hidden)] + pub fn default_user_instance() -> Gd + where + T: cap::GodotDefault, + { + let obj = unsafe { + let object_ptr = sys::classdb_construct_object(T::class_id().string_sys()); + postinit(object_ptr); + Gd::::from_obj_sys(object_ptr) + }; + + // Mark base as initialized after postinit. + #[cfg(since_api = "4.4")] + if let Some(storage) = obj.raw.storage() { + unsafe { Base::from_base(storage.base()).mark_initialized() }; + } + obj + } } /// _The methods in this impl block are available for any `T`._

@@ -524,24 +558,6 @@ impl Gd { .map_err(Self::from_ffi) } - /// Create default instance for all types that have `GodotDefault`. - /// - /// Deliberately more loose than `Gd::default()`, does not require ref-counted memory strategy for user types. - pub(crate) fn default_instance() -> Self - where - T: cap::GodotDefault, - { - unsafe { - // Default value (and compat one) for `p_notify_postinitialize` is true in Godot. - #[cfg(since_api = "4.4")] - let object_ptr = callbacks::create::(std::ptr::null_mut(), sys::conv::SYS_TRUE); - #[cfg(before_api = "4.4")] - let object_ptr = callbacks::create::(std::ptr::null_mut()); - - Gd::from_obj_sys(object_ptr) - } - } - /// Upgrades to a `DynGd` pointer, enabling the `D` abstraction. /// /// The `D` parameter can typically be inferred when there is a single `AsDyn<...>` implementation for `T`. \ diff --git a/godot-core/src/obj/traits.rs b/godot-core/src/obj/traits.rs index f8d3e9dc0..9522f9898 100644 --- a/godot-core/src/obj/traits.rs +++ b/godot-core/src/obj/traits.rs @@ -773,7 +773,7 @@ pub mod cap { use super::*; use crate::builtin::{StringName, Variant}; use crate::meta::PropertyInfo; - use crate::obj::{Base, Bounds, Gd}; + use crate::obj::{Base, Gd}; use crate::storage::{IntoVirtualMethodReceiver, VirtualMethodReceiver}; /// Trait for all classes that are default-constructible from the Godot engine. @@ -804,19 +804,7 @@ pub mod cap { /// - For user-defined classes, this calls `T::init()` or the generated init-constructor. /// - For engine classes, this calls `T::new()`. #[doc(hidden)] - fn __godot_default() -> Gd { - // This is a bit hackish, but the alternatives are: - // 1. Separate trait `GodotUserDefault` for user classes, which then proliferates through all APIs and makes abstraction harder. - // 2. Repeatedly implementing __godot_default() that forwards to something like Gd::default_user_instance(). Possible, but this - // will make the step toward builder APIs more difficult, as users would need to re-implement this as well. - sys::strict_assert_eq!( - std::any::TypeId::of::<::Declarer>(), - std::any::TypeId::of::(), - "__godot_default() called on engine class; must be overridden for engine classes" - ); - - Gd::default_instance() - } + fn __godot_default() -> Gd; /// Only provided for user classes. #[doc(hidden)] diff --git a/godot-core/src/registry/callbacks.rs b/godot-core/src/registry/callbacks.rs index accf583d6..0ce74158a 100644 --- a/godot-core/src/registry/callbacks.rs +++ b/godot-core/src/registry/callbacks.rs @@ -13,6 +13,7 @@ use std::any::Any; use godot_ffi as sys; +use godot_ffi::GDExtensionObjectPtr; use sys::conv::u32_to_usize; use sys::interface_fn; @@ -75,7 +76,7 @@ pub unsafe extern "C" fn recreate( _class_userdata: *mut std::ffi::c_void, object: sys::GDExtensionObjectPtr, ) -> sys::GDExtensionClassInstancePtr { - create_rust_part_for_existing_godot_part(T::__godot_user_init, object, |_| {}) + create_rust_part_for_existing_godot_part(T::__godot_user_init, object, true) .unwrap_or(std::ptr::null_mut()) } @@ -90,6 +91,18 @@ pub unsafe extern "C" fn recreate_null( std::ptr::null_mut() } +#[cfg(since_api = "4.4")] +pub(crate) fn postinit(base_ptr: GDExtensionObjectPtr) { + // Should notify it with a weak pointer, during `NOTIFICATION_POSTINITIALIZE`, ref-counted object is not yet fully-initialized. + // SAFETY: `base_ptr` is either null or points to some valid `GDExtensionObjectPtr` instance. + let mut obj = unsafe { Gd::::from_obj_sys_weak(base_ptr) }; + obj.notify(crate::classes::notify::ObjectNotification::POSTINITIALIZE); + obj.drop_weak(); +} + +#[cfg(before_api = "4.4")] +pub(crate) fn postinit(_base_ptr: GDExtensionObjectPtr) {} + pub(crate) fn create_custom( make_user_instance: F, notify_postinitialize: bool, @@ -101,17 +114,11 @@ where let base_class_name = T::Base::class_id(); let base_ptr = unsafe { sys::classdb_construct_object(base_class_name.string_sys()) }; - let postinit = |base_ptr| { - #[cfg(since_api = "4.4")] - if notify_postinitialize { - // Should notify it with a weak pointer, during `NOTIFICATION_POSTINITIALIZE`, ref-counted object is not yet fully-initialized. - let mut obj = unsafe { Gd::::from_obj_sys_weak(base_ptr) }; - obj.notify(crate::classes::notify::ObjectNotification::POSTINITIALIZE); - obj.drop_weak(); - } - }; - - match create_rust_part_for_existing_godot_part(make_user_instance, base_ptr, postinit) { + match create_rust_part_for_existing_godot_part( + make_user_instance, + base_ptr, + notify_postinitialize, + ) { Ok(_extension_ptr) => Ok(base_ptr), Err(payload) => { // Creation of extension object failed; we must now also destroy the base object to avoid leak. @@ -130,15 +137,14 @@ where /// With godot-rust, custom objects consist of two parts: the Godot object and the Rust object. This method takes the Godot part by pointer, /// creates the Rust part with the supplied state, and links them together. This is used for both brand-new object creation and hot reload. /// 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. -fn create_rust_part_for_existing_godot_part( +fn create_rust_part_for_existing_godot_part( make_user_instance: F, base_ptr: sys::GDExtensionObjectPtr, - postinit: P, + notify_postinitialize: bool, ) -> Result where T: GodotClass, F: FnOnce(Base) -> T, - P: Fn(sys::GDExtensionObjectPtr), { let class_name = T::class_id(); //out!("create callback: {}", class_name.backing); @@ -170,10 +176,11 @@ where ); } - postinit(base_ptr); - - // Mark initialization as complete, now that user constructor has finished. - base_copy.mark_initialized(); + if notify_postinitialize { + postinit(base_ptr); + // Mark initialization as complete, now that user constructor has finished. + base_copy.mark_initialized(); + } // No std::mem::forget(base_copy) here, since Base may stores other fields that need deallocation. Ok(instance_ptr) diff --git a/godot-macros/src/class/data_models/interface_trait_impl.rs b/godot-macros/src/class/data_models/interface_trait_impl.rs index ebcd7525d..851773bca 100644 --- a/godot-macros/src/class/data_models/interface_trait_impl.rs +++ b/godot-macros/src/class/data_models/interface_trait_impl.rs @@ -289,6 +289,10 @@ fn handle_init<'a>( #(#cfg_attrs)* impl ::godot::obj::cap::GodotDefault for #class_name { + fn __godot_default() -> ::godot::obj::Gd { + ::godot::obj::Gd::default_user_instance() + } + fn __godot_user_init(base: ::godot::obj::Base) -> Self { ::init(base) } diff --git a/godot-macros/src/class/derive_godot_class.rs b/godot-macros/src/class/derive_godot_class.rs index 4abe6c105..d816c66fb 100644 --- a/godot-macros/src/class/derive_godot_class.rs +++ b/godot-macros/src/class/derive_godot_class.rs @@ -362,6 +362,10 @@ fn make_godot_init_impl(class_name: &Ident, fields: &Fields) -> TokenStream { quote! { impl ::godot::obj::cap::GodotDefault for #class_name { + fn __godot_default() -> ::godot::obj::Gd { + ::godot::obj::Gd::default_user_instance() + } + fn __godot_user_init(base: ::godot::obj::Base<<#class_name as ::godot::obj::GodotClass>::Base>) -> Self { Self { #( #rest_init )*