From 99e092a69ceb871c8617fabe9989dff20a58dede Mon Sep 17 00:00:00 2001 From: LuoZhihao Date: Sat, 21 Jun 2025 13:12:59 +0800 Subject: [PATCH] Update postinit handling `NOTIFICATION_POSTINITIALIZE` will be recevied correctly in godot 4.4 and later --- godot-core/src/builtin/string/gstring.rs | 4 ++++ godot-core/src/classes/class_runtime.rs | 6 +----- godot-core/src/obj/bounds.rs | 6 +----- godot-core/src/obj/gd.rs | 18 ++++++++++++++++++ godot-core/src/registry/callbacks.rs | 15 +++++++++++++-- godot-ffi/src/lib.rs | 18 ++++++++++++++++++ .../src/object_tests/virtual_methods_test.rs | 2 ++ 7 files changed, 57 insertions(+), 12 deletions(-) diff --git a/godot-core/src/builtin/string/gstring.rs b/godot-core/src/builtin/string/gstring.rs index 5d71a9f88..3ccc287c1 100644 --- a/godot-core/src/builtin/string/gstring.rs +++ b/godot-core/src/builtin/string/gstring.rs @@ -325,7 +325,11 @@ impl From<&str> for GString { unsafe { Self::new_with_string_uninit(|string_ptr| { + #[cfg(before_api = "4.3")] let ctor = interface_fn!(string_new_with_utf8_chars_and_len); + #[cfg(since_api = "4.3")] + let ctor = interface_fn!(string_new_with_utf8_chars_and_len2); + ctor( string_ptr, bytes.as_ptr() as *const std::ffi::c_char, diff --git a/godot-core/src/classes/class_runtime.rs b/godot-core/src/classes/class_runtime.rs index 0f77323f5..82eeee4d6 100644 --- a/godot-core/src/classes/class_runtime.rs +++ b/godot-core/src/classes/class_runtime.rs @@ -60,11 +60,7 @@ pub(crate) fn construct_engine_object() -> Gd where T: GodotClass + Bounds, { - // SAFETY: adhere to Godot API; valid class name and returned pointer is an object. - unsafe { - let object_ptr = sys::interface_fn!(classdb_construct_object)(T::class_name().string_sys()); - Gd::from_obj_sys(object_ptr) - } + Gd::new_alloc_postinited() } pub(crate) fn ensure_object_alive( diff --git a/godot-core/src/obj/bounds.rs b/godot-core/src/obj/bounds.rs index 06bbabc51..98b44cc54 100644 --- a/godot-core/src/obj/bounds.rs +++ b/godot-core/src/obj/bounds.rs @@ -389,11 +389,7 @@ impl Declarer for DeclEngine { where T: GodotDefault + Bounds, { - unsafe { - let object_ptr = - sys::interface_fn!(classdb_construct_object)(T::class_name().string_sys()); - Gd::from_obj_sys(object_ptr) - } + Gd::new_alloc_postinited() } } diff --git a/godot-core/src/obj/gd.rs b/godot-core/src/obj/gd.rs index 95b7d2414..7334ea036 100644 --- a/godot-core/src/obj/gd.rs +++ b/godot-core/src/obj/gd.rs @@ -560,6 +560,24 @@ impl Gd { // Do not increment ref-count; assumed to be return value from FFI. sys::ptr_then(object_ptr, |ptr| Gd::from_obj_sys_weak(ptr)) } + + pub(crate) fn new_alloc_postinited() -> Self { + #[cfg(before_api = "4.4")] + unsafe { + let object_ptr = sys::classdb_construct_object(T::class_name().string_sys()); + Gd::from_obj_sys(object_ptr) + } + #[cfg(since_api = "4.4")] + unsafe { + let object_ptr = + sys::classdb_construct_object_no_postinit(T::class_name().string_sys()); + let obj = Gd::::from_obj_sys(object_ptr); + obj.clone() + .upcast_object() + .notify(crate::classes::notify::ObjectNotification::POSTINITIALIZE); + obj + } + } } /// _The methods in this impl block are only available for objects `T` that are manually managed, diff --git a/godot-core/src/registry/callbacks.rs b/godot-core/src/registry/callbacks.rs index 708ffeaba..867d9ac5f 100644 --- a/godot-core/src/registry/callbacks.rs +++ b/godot-core/src/registry/callbacks.rs @@ -33,7 +33,14 @@ pub unsafe extern "C" fn create( _class_userdata: *mut std::ffi::c_void, _notify_postinitialize: sys::GDExtensionBool, ) -> sys::GDExtensionObjectPtr { - create_custom(T::__godot_user_init).unwrap_or(std::ptr::null_mut()) + if let Ok(object_ptr) = create_custom(T::__godot_user_init) { + let mut obj = Gd::::from_obj_sys_weak(object_ptr).upcast_object(); + obj.notify(crate::classes::notify::ObjectNotification::POSTINITIALIZE); + std::mem::forget(obj); + object_ptr + } else { + std::ptr::null_mut() + } } #[cfg(before_api = "4.4")] @@ -92,7 +99,11 @@ where F: FnOnce(Base) -> T, { let base_class_name = T::Base::class_name(); - let base_ptr = unsafe { interface_fn!(classdb_construct_object)(base_class_name.string_sys()) }; + #[cfg(before_api = "4.4")] + let base_ptr = unsafe { sys::classdb_construct_object(base_class_name.string_sys()) }; + #[cfg(since_api = "4.4")] + let base_ptr = + unsafe { sys::classdb_construct_object_no_postinit(base_class_name.string_sys()) }; match create_rust_part_for_existing_godot_part(make_user_instance, base_ptr) { Ok(_extension_ptr) => Ok(base_ptr), diff --git a/godot-ffi/src/lib.rs b/godot-ffi/src/lib.rs index 858c53371..8f353c244 100644 --- a/godot-ffi/src/lib.rs +++ b/godot-ffi/src/lib.rs @@ -440,6 +440,24 @@ pub fn is_main_thread() -> bool { std::thread::current().id() == main_thread_id() } +/// # Safety +/// `class_name` is assumed to be valid. +#[cfg(before_api = "4.4")] +pub unsafe fn classdb_construct_object( + class_name: GDExtensionConstStringNamePtr, +) -> GDExtensionObjectPtr { + interface_fn!(classdb_construct_object)(class_name) +} + +/// # Safety +/// `class_name` is assumed to be valid. +#[cfg(since_api = "4.4")] +pub unsafe fn classdb_construct_object_no_postinit( + class_name: GDExtensionConstStringNamePtr, +) -> GDExtensionObjectPtr { + interface_fn!(classdb_construct_object2)(class_name) +} + // ---------------------------------------------------------------------------------------------------------------------------------------------- // Macros to access low-level function bindings diff --git a/itest/rust/src/object_tests/virtual_methods_test.rs b/itest/rust/src/object_tests/virtual_methods_test.rs index 6828603ad..64106efa7 100644 --- a/itest/rust/src/object_tests/virtual_methods_test.rs +++ b/itest/rust/src/object_tests/virtual_methods_test.rs @@ -517,6 +517,8 @@ fn test_notifications() { assert_eq!( obj.bind().sequence, vec![ + #[cfg(since_api = "4.4")] + ReceivedEvent::Notification(NodeNotification::POSTINITIALIZE), ReceivedEvent::Notification(NodeNotification::UNPAUSED), ReceivedEvent::Notification(NodeNotification::EDITOR_POST_SAVE), ReceivedEvent::Ready,