Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion godot-core/src/obj/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub struct Base<T: GodotClass> {
// 2.
obj: ManuallyDrop<Gd<T>>,

/// Tracks the initialization state of this `Base<T>` in Debug mode.
/// Tracks the initialization state of this `Base<T>` 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<T>`.
#[cfg(safeguards_balanced)]
Expand Down
2 changes: 1 addition & 1 deletion godot-core/src/obj/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ impl Declarer for DeclUser {
where
T: GodotDefault + Bounds<Declarer = Self>,
{
Gd::default_instance()
Gd::default_user_instance()
}
}

Expand Down
56 changes: 36 additions & 20 deletions godot-core/src/obj/gd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<T>
where
T: cap::GodotDefault,
{
let obj = unsafe {
let object_ptr = sys::classdb_construct_object(T::class_id().string_sys());
postinit(object_ptr);
Gd::<T>::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`._ <br><br>
Expand Down Expand Up @@ -524,24 +558,6 @@ impl<T: GodotClass> Gd<T> {
.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::<T>(std::ptr::null_mut(), sys::conv::SYS_TRUE);
#[cfg(before_api = "4.4")]
let object_ptr = callbacks::create::<T>(std::ptr::null_mut());

Gd::from_obj_sys(object_ptr)
}
}

/// Upgrades to a `DynGd<T, D>` pointer, enabling the `D` abstraction.
///
/// The `D` parameter can typically be inferred when there is a single `AsDyn<...>` implementation for `T`. \
Expand Down
16 changes: 2 additions & 14 deletions godot-core/src/obj/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<Self> {
// 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::<<Self as Bounds>::Declarer>(),
std::any::TypeId::of::<bounds::DeclUser>(),
"__godot_default() called on engine class; must be overridden for engine classes"
);

Gd::default_instance()
}
fn __godot_default() -> Gd<Self>;

/// Only provided for user classes.
#[doc(hidden)]
Expand Down
45 changes: 26 additions & 19 deletions godot-core/src/registry/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -75,7 +76,7 @@ pub unsafe extern "C" fn recreate<T: cap::GodotDefault>(
_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())
}

Expand All @@ -90,6 +91,18 @@ pub unsafe extern "C" fn recreate_null<T>(
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::<Object>::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<T, F>(
make_user_instance: F,
notify_postinitialize: bool,
Expand All @@ -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::<Object>::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.
Expand All @@ -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<T, F, P>(
fn create_rust_part_for_existing_godot_part<T, F>(
make_user_instance: F,
base_ptr: sys::GDExtensionObjectPtr,
postinit: P,
notify_postinitialize: bool,
) -> Result<sys::GDExtensionClassInstancePtr, PanicPayload>
where
T: GodotClass,
F: FnOnce(Base<T::Base>) -> T,
P: Fn(sys::GDExtensionObjectPtr),
{
let class_name = T::class_id();
//out!("create callback: {}", class_name.backing);
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions godot-macros/src/class/data_models/interface_trait_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,10 @@ fn handle_init<'a>(

#(#cfg_attrs)*
impl ::godot::obj::cap::GodotDefault for #class_name {
fn __godot_default() -> ::godot::obj::Gd<Self> {
::godot::obj::Gd::default_user_instance()
}

fn __godot_user_init(base: ::godot::obj::Base<Self::Base>) -> Self {
<Self as #trait_path>::init(base)
}
Expand Down
4 changes: 4 additions & 0 deletions godot-macros/src/class/derive_godot_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self> {
::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 )*
Expand Down
Loading