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..c45c63a7c 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,18 @@ where pub fn bind_mut(&mut self) -> GdMut<'_, T> { self.raw.bind_mut() } + + #[doc(hidden)] + pub fn default_user_instance() -> Gd + where + T: cap::GodotDefault, + { + let obj = Gd::default_instance(); + 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`._

@@ -532,13 +546,9 @@ impl Gd { 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) + let object_ptr = sys::classdb_construct_object(T::class_id().string_sys()); + postinit(object_ptr); + Gd::::from_obj_sys(object_ptr) } } diff --git a/godot-core/src/registry/callbacks.rs b/godot-core/src/registry/callbacks.rs index accf583d6..ddd318221 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, false, true) .unwrap_or(std::ptr::null_mut()) } @@ -90,6 +91,17 @@ 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. + 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 +113,12 @@ 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, + 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,15 @@ 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, + mark_initialized: 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 +177,14 @@ where ); } - postinit(base_ptr); + if notify_postinitialize { + postinit(base_ptr); + } - // Mark initialization as complete, now that user constructor has finished. - base_copy.mark_initialized(); + if mark_initialized { + // 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 e2e0db80c..b4bfb2c78 100644 --- a/godot-macros/src/class/derive_godot_class.rs +++ b/godot-macros/src/class/derive_godot_class.rs @@ -333,6 +333,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 )*