From d9b69e909515e8abfcd3f864cf692cd807fca94c Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 8 Dec 2021 20:53:23 +0100 Subject: [PATCH 1/8] Remove deprecated methods Changes: * ClassBuilder * Remove add_method() * Remove add_method_advanced() -- now private * Remove add_method_with_rpc_mode() * Rename add_property() -> build_property() * Remove ScriptMethod, ScriptMethodFn, ScriptMethodAttributes * Color * Remove rgb(), rgba() * PoolArray type aliases * Add name of Godot equivalent + API link --- examples/array_export/src/lib.rs | 11 ++- examples/spinning_cube/src/lib.rs | 4 +- gdnative-core/src/core_types/byte_array.rs | 2 + gdnative-core/src/core_types/color.rs | 12 --- gdnative-core/src/core_types/color_array.rs | 2 + gdnative-core/src/core_types/float32_array.rs | 2 + gdnative-core/src/core_types/int32_array.rs | 2 + gdnative-core/src/core_types/string_array.rs | 2 + gdnative-core/src/core_types/vector2_array.rs | 2 + gdnative-core/src/core_types/vector3_array.rs | 2 + gdnative-core/src/export/class_builder.rs | 91 +++++++------------ gdnative-core/src/export/method.rs | 22 +---- gdnative-derive/src/lib.rs | 2 +- gdnative-derive/src/native_script.rs | 2 +- test/src/test_register.rs | 2 +- 15 files changed, 65 insertions(+), 95 deletions(-) diff --git a/examples/array_export/src/lib.rs b/examples/array_export/src/lib.rs index d7f47cac7..9430aba55 100644 --- a/examples/array_export/src/lib.rs +++ b/examples/array_export/src/lib.rs @@ -14,25 +14,28 @@ impl ExportsArrays { fn register(builder: &ClassBuilder) { builder - .add_property::("single_array") + .build_property::("single_array") .with_setter(ExportsArrays::set_single_array) .done(); + builder - .add_property::("single_array_range") + .build_property::("single_array_range") .with_setter(ExportsArrays::set_single_array_range) .with_hint(ArrayHint::with_element_hint::(IntHint::Range( RangeHint::new(-5, 5), ))) .done(); + builder - .add_property::("double_array") + .build_property::("double_array") .with_setter(ExportsArrays::set_double_array) .with_hint(ArrayHint::with_element_hint::( ArrayHint::new(), )) .done(); + builder - .add_property::("double_array_range") + .build_property::("double_array_range") .with_setter(ExportsArrays::set_double_array_range) .with_hint(ArrayHint::with_element_hint::( ArrayHint::with_element_hint::(IntHint::Range(RangeHint::new(-5, 5))), diff --git a/examples/spinning_cube/src/lib.rs b/examples/spinning_cube/src/lib.rs index a79f29c23..8a961b3b7 100644 --- a/examples/spinning_cube/src/lib.rs +++ b/examples/spinning_cube/src/lib.rs @@ -15,7 +15,7 @@ struct RustTest { fn register_properties(builder: &ClassBuilder) { builder - .add_property::("test/test_enum") + .build_property::("test/test_enum") .with_hint(StringHint::Enum(EnumHint::new(vec![ "Hello".into(), "World".into(), @@ -25,7 +25,7 @@ fn register_properties(builder: &ClassBuilder) { .done(); builder - .add_property("test/test_flags") + .build_property("test/test_flags") .with_hint(IntHint::Flags(EnumHint::new(vec![ "A".into(), "B".into(), diff --git a/gdnative-core/src/core_types/byte_array.rs b/gdnative-core/src/core_types/byte_array.rs index b95bf7447..db37f4d54 100644 --- a/gdnative-core/src/core_types/byte_array.rs +++ b/gdnative-core/src/core_types/byte_array.rs @@ -1,6 +1,8 @@ use crate::core_types::PoolArray; /// A reference-counted vector of `u8` that uses Godot's pool allocator. +/// +/// See [`PoolByteArray`](https://docs.godotengine.org/en/stable/classes/class_poolbytearray.html) in Godot. pub type ByteArray = PoolArray; godot_test!( diff --git a/gdnative-core/src/core_types/color.rs b/gdnative-core/src/core_types/color.rs index 97e8e55c3..dd86c6b8d 100644 --- a/gdnative-core/src/core_types/color.rs +++ b/gdnative-core/src/core_types/color.rs @@ -15,18 +15,6 @@ pub struct Color { } impl Color { - #[deprecated] - #[inline] - pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color { - Color { r, g, b, a } - } - - #[deprecated] - #[inline] - pub fn rgb(r: f32, g: f32, b: f32) -> Color { - Color { r, g, b, a: 1.0 } - } - #[inline] pub fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Color { Color { r, g, b, a } diff --git a/gdnative-core/src/core_types/color_array.rs b/gdnative-core/src/core_types/color_array.rs index 2e067855d..27ee2d54d 100644 --- a/gdnative-core/src/core_types/color_array.rs +++ b/gdnative-core/src/core_types/color_array.rs @@ -2,6 +2,8 @@ use crate::core_types::Color; use crate::core_types::PoolArray; /// A reference-counted vector of `Color` that uses Godot's pool allocator. +/// +/// See [`PoolColorArray`](https://docs.godotengine.org/en/stable/classes/class_poolcolorarray.html) in Godot. pub type ColorArray = PoolArray; godot_test!( diff --git a/gdnative-core/src/core_types/float32_array.rs b/gdnative-core/src/core_types/float32_array.rs index 037806757..9cebcb961 100644 --- a/gdnative-core/src/core_types/float32_array.rs +++ b/gdnative-core/src/core_types/float32_array.rs @@ -1,6 +1,8 @@ use crate::core_types::PoolArray; /// A reference-counted vector of `f32` that uses Godot's pool allocator. +/// +/// See [`PoolRealArray`](https://docs.godotengine.org/en/stable/classes/class_poolrealarray.html) in Godot. pub type Float32Array = PoolArray; godot_test!( diff --git a/gdnative-core/src/core_types/int32_array.rs b/gdnative-core/src/core_types/int32_array.rs index 869af4645..4fde65c00 100644 --- a/gdnative-core/src/core_types/int32_array.rs +++ b/gdnative-core/src/core_types/int32_array.rs @@ -1,6 +1,8 @@ use crate::core_types::PoolArray; /// A reference-counted vector of `i32` that uses Godot's pool allocator. +/// +/// See [`PoolIntArray`](https://docs.godotengine.org/en/stable/classes/class_poolintarray.html) in Godot. pub type Int32Array = PoolArray; godot_test!( diff --git a/gdnative-core/src/core_types/string_array.rs b/gdnative-core/src/core_types/string_array.rs index 0286180db..afe1ed1af 100644 --- a/gdnative-core/src/core_types/string_array.rs +++ b/gdnative-core/src/core_types/string_array.rs @@ -2,6 +2,8 @@ use crate::core_types::GodotString; use crate::core_types::PoolArray; /// A reference-counted vector of `GodotString` that uses Godot's pool allocator. +/// +/// See [`PoolStringArray`](https://docs.godotengine.org/en/stable/classes/class_poolstringarray.html) in Godot. pub type StringArray = PoolArray; godot_test!( diff --git a/gdnative-core/src/core_types/vector2_array.rs b/gdnative-core/src/core_types/vector2_array.rs index ab8cdd5eb..f9e4a5b2b 100644 --- a/gdnative-core/src/core_types/vector2_array.rs +++ b/gdnative-core/src/core_types/vector2_array.rs @@ -2,6 +2,8 @@ use crate::core_types::PoolArray; use crate::core_types::Vector2; /// A reference-counted vector of `Vector2` that uses Godot's pool allocator. +/// +/// See [`PoolVector2Array`](https://docs.godotengine.org/en/stable/classes/class_poolvector2array.html) in Godot. pub type Vector2Array = PoolArray; godot_test!( diff --git a/gdnative-core/src/core_types/vector3_array.rs b/gdnative-core/src/core_types/vector3_array.rs index a8a765181..e18d6778c 100644 --- a/gdnative-core/src/core_types/vector3_array.rs +++ b/gdnative-core/src/core_types/vector3_array.rs @@ -2,6 +2,8 @@ use crate::core_types::PoolArray; use crate::core_types::Vector3; /// A reference-counted vector of `Vector3` that uses Godot's pool allocator. +/// +/// See [`PoolVector3Array`](https://docs.godotengine.org/en/stable/classes/class_poolvector3array.html) in Godot. pub type Vector3Array = PoolArray; godot_test!( diff --git a/gdnative-core/src/export/class_builder.rs b/gdnative-core/src/export/class_builder.rs index 350bdb904..189d5c105 100644 --- a/gdnative-core/src/export/class_builder.rs +++ b/gdnative-core/src/export/class_builder.rs @@ -30,9 +30,6 @@ //! For full examples, see [`examples`](https://github.com/godot-rust/godot-rust/tree/master/examples) //! in the godot-rust repository. -// Temporary for unsafe method registration -#![allow(deprecated)] - use std::ffi::CString; use std::marker::PhantomData; use std::ptr; @@ -58,58 +55,6 @@ impl ClassBuilder { } } - #[inline] - #[deprecated(note = "Unsafe registration is deprecated. Use `build_method` instead.")] - pub fn add_method_advanced(&self, method: ScriptMethod) { - let method_name = CString::new(method.name).unwrap(); - - let rpc = match method.attributes.rpc_mode { - RpcMode::Master => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_MASTER, - RpcMode::Remote => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_REMOTE, - RpcMode::Puppet => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_PUPPET, - RpcMode::RemoteSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_REMOTESYNC, - RpcMode::Disabled => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_DISABLED, - RpcMode::MasterSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_MASTERSYNC, - RpcMode::PuppetSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_PUPPETSYNC, - }; - - let attr = sys::godot_method_attributes { rpc_type: rpc }; - - let method_desc = sys::godot_instance_method { - method: method.method_ptr, - method_data: method.method_data, - free_func: method.free_func, - }; - - unsafe { - (get_api().godot_nativescript_register_method)( - self.init_handle, - self.class_name.as_ptr() as *const _, - method_name.as_ptr() as *const _, - attr, - method_desc, - ); - } - } - - #[inline] - #[deprecated(note = "Unsafe registration is deprecated. Use `build_method` instead.")] - pub fn add_method_with_rpc_mode(&self, name: &str, method: ScriptMethodFn, rpc_mode: RpcMode) { - self.add_method_advanced(ScriptMethod { - name, - method_ptr: Some(method), - attributes: ScriptMethodAttributes { rpc_mode }, - method_data: ptr::null_mut(), - free_func: None, - }); - } - - #[inline] - #[deprecated(note = "Unsafe registration is deprecated. Use `build_method` instead.")] - pub fn add_method(&self, name: &str, method: ScriptMethodFn) { - self.add_method_with_rpc_mode(name, method, RpcMode::Disabled); - } - /// Returns a `MethodBuilder` which can be used to add a method to the class being /// registered. /// @@ -183,7 +128,7 @@ impl ClassBuilder { /// /// fn my_register(builder: &ClassBuilder) { /// builder - /// .add_property("foo") + /// .build_property("foo") /// .with_default(5) /// .with_hint((-10..=30).into()) /// .with_getter(MyType::get_foo) @@ -193,7 +138,7 @@ impl ClassBuilder { /// } /// ``` #[inline] - pub fn add_property<'a, T>(&'a self, name: &'a str) -> PropertyBuilder<'a, C, T> + pub fn build_property<'a, T>(&'a self, name: &'a str) -> PropertyBuilder<'a, C, T> where T: Export, { @@ -237,6 +182,38 @@ impl ClassBuilder { ); } } + + pub(crate) fn add_method(&self, method: ScriptMethod) { + let method_name = CString::new(method.name).unwrap(); + + let rpc = match method.attributes.rpc_mode { + RpcMode::Master => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_MASTER, + RpcMode::Remote => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_REMOTE, + RpcMode::Puppet => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_PUPPET, + RpcMode::RemoteSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_REMOTESYNC, + RpcMode::Disabled => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_DISABLED, + RpcMode::MasterSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_MASTERSYNC, + RpcMode::PuppetSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_PUPPETSYNC, + }; + + let attr = sys::godot_method_attributes { rpc_type: rpc }; + + let method_desc = sys::godot_instance_method { + method: method.method_ptr, + method_data: method.method_data, + free_func: method.free_func, + }; + + unsafe { + (get_api().godot_nativescript_register_method)( + self.init_handle, + self.class_name.as_ptr() as *const _, + method_name.as_ptr() as *const _, + attr, + method_desc, + ); + } + } } pub struct Signal<'l> { diff --git a/gdnative-core/src/export/method.rs b/gdnative-core/src/export/method.rs index 9a5e9516c..e191e18d6 100644 --- a/gdnative-core/src/export/method.rs +++ b/gdnative-core/src/export/method.rs @@ -1,8 +1,5 @@ //! Method registration -// Temporary for unsafe method registration -#![allow(deprecated)] - use std::borrow::Cow; use std::fmt; use std::marker::PhantomData; @@ -59,7 +56,7 @@ where free_func: Some(free_func::), }; - self.class_builder.add_method_advanced(script_method); + self.class_builder.add_method(script_method); } } @@ -86,14 +83,11 @@ where free_func: None, }; - self.class_builder.add_method_advanced(script_method); + self.class_builder.add_method(script_method); } } -#[deprecated( - note = "Unsafe registration is deprecated. Use the safe, higher-level `MethodBuilder` API instead." -)] -pub type ScriptMethodFn = unsafe extern "C" fn( +type ScriptMethodFn = unsafe extern "C" fn( *mut sys::godot_object, *mut libc::c_void, *mut libc::c_void, @@ -118,17 +112,11 @@ impl Default for RpcMode { } } -#[deprecated( - note = "Unsafe registration is deprecated. Use the safe, higher-level `MethodBuilder` API instead." -)] -pub struct ScriptMethodAttributes { +pub(crate) struct ScriptMethodAttributes { pub rpc_mode: RpcMode, } -#[deprecated( - note = "Unsafe registration is deprecated. Use the safe, higher-level `MethodBuilder` API instead." -)] -pub struct ScriptMethod<'l> { +pub(crate) struct ScriptMethod<'l> { pub name: &'l str, pub method_ptr: Option, pub attributes: ScriptMethodAttributes, diff --git a/gdnative-derive/src/lib.rs b/gdnative-derive/src/lib.rs index 7ca6e5e74..8b8938fe7 100644 --- a/gdnative-derive/src/lib.rs +++ b/gdnative-derive/src/lib.rs @@ -177,7 +177,7 @@ pub fn profiled(meta: TokenStream, input: TokenStream) -> TokenStream { /// } /// fn my_register_function(builder: &ClassBuilder) { /// builder.add_signal(Signal { name: "foo", args: &[] }); -/// builder.add_property::("bar") +/// builder.build_property::("bar") /// .with_getter(|_, _| 42.0) /// .with_hint(FloatHint::Range(RangeHint::new(0.0, 100.0))) /// .done(); diff --git a/gdnative-derive/src/native_script.rs b/gdnative-derive/src/native_script.rs index 828d1130d..61214e973 100644 --- a/gdnative-derive/src/native_script.rs +++ b/gdnative-derive/src/native_script.rs @@ -79,7 +79,7 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result) { builder - .add_property("value") + .build_property("value") .with_default(42) .with_setter(RegisterProperty::set_value) .with_getter(RegisterProperty::get_value) From 2be3957a26a9dc0f7cb28b50379c2c970d170b2f Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 13 Dec 2021 12:31:40 +0100 Subject: [PATCH 2/8] Rename Element -> PoolElement Makes the relation to PoolArray clearer. --- gdnative-core/src/core_types/access.rs | 6 +- gdnative-core/src/core_types/mod.rs | 2 +- gdnative-core/src/core_types/pool_array.rs | 66 +++++++++++----------- gdnative-core/src/core_types/variant.rs | 6 +- 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/gdnative-core/src/core_types/access.rs b/gdnative-core/src/core_types/access.rs index 643877ca6..d82f28402 100644 --- a/gdnative-core/src/core_types/access.rs +++ b/gdnative-core/src/core_types/access.rs @@ -50,12 +50,12 @@ pub unsafe trait Guard: private::Sealed { pub unsafe trait WritePtr: Guard + private::Sealed {} pub(crate) mod private { - use crate::core_types::pool_array::Element; + use crate::core_types::PoolElement; pub trait Sealed {} - impl<'a, T: Element> Sealed for crate::core_types::pool_array::ReadGuard<'a, T> {} - impl<'a, T: Element> Sealed for crate::core_types::pool_array::WriteGuard<'a, T> {} + impl<'a, T: PoolElement> Sealed for crate::core_types::pool_array::ReadGuard<'a, T> {} + impl<'a, T: PoolElement> Sealed for crate::core_types::pool_array::WriteGuard<'a, T> {} } impl MaybeUnaligned { diff --git a/gdnative-core/src/core_types/mod.rs b/gdnative-core/src/core_types/mod.rs index f8acbf58c..bfb5319e6 100644 --- a/gdnative-core/src/core_types/mod.rs +++ b/gdnative-core/src/core_types/mod.rs @@ -37,7 +37,7 @@ pub use float32_array::*; pub use geom::*; pub use int32_array::*; pub use node_path::*; -pub use pool_array::*; // TODO rename Element to something more specific +pub use pool_array::*; pub use rid::*; pub use string::*; pub use string_array::*; diff --git a/gdnative-core/src/core_types/pool_array.rs b/gdnative-core/src/core_types/pool_array.rs index b5b69d114..1259d8a87 100644 --- a/gdnative-core/src/core_types/pool_array.rs +++ b/gdnative-core/src/core_types/pool_array.rs @@ -25,7 +25,7 @@ use crate::private::get_api; /// When using this type, it's generally better to perform mutations in batch using `write`, /// or the `append` methods, as opposed to `push` or `set`, because the latter ones trigger /// CoW behavior each time they are called. -pub struct PoolArray { +pub struct PoolArray { inner: T::SysArray, } @@ -36,7 +36,7 @@ pub type Read<'a, T> = Aligned>; /// as opposed to every time with methods like `push`. pub type Write<'a, T> = Aligned>; -impl Drop for PoolArray { +impl Drop for PoolArray { #[inline] fn drop(&mut self) { unsafe { @@ -45,28 +45,28 @@ impl Drop for PoolArray { } } -impl Default for PoolArray { +impl Default for PoolArray { #[inline] fn default() -> Self { PoolArray::new() } } -impl fmt::Debug for PoolArray { +impl fmt::Debug for PoolArray { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.read().iter()).finish() } } -impl Clone for PoolArray { +impl Clone for PoolArray { #[inline] fn clone(&self) -> Self { self.new_ref() } } -impl NewRef for PoolArray { +impl NewRef for PoolArray { /// Creates a new reference to this reference-counted instance. #[inline] fn new_ref(&self) -> Self { @@ -78,7 +78,7 @@ impl NewRef for PoolArray { } } -impl PoolArray { +impl PoolArray { /// Creates an empty array. #[inline] pub fn new() -> Self { @@ -265,7 +265,7 @@ impl PoolArray { } } -impl PoolArray { +impl PoolArray { /// Creates a new `PoolArray` by copying from `src`. /// /// # Panics @@ -297,7 +297,7 @@ impl PoolArray { // `FromIterator` and `Extend` implementations collect into `Vec` first, because Rust `Vec`s // are better at handling unknown lengths than the Godot arrays (`push` CoWs every time!) -impl FromIterator for PoolArray { +impl FromIterator for PoolArray { #[inline] fn from_iter>(iter: I) -> Self { let vec = iter.into_iter().collect::>(); @@ -305,7 +305,7 @@ impl FromIterator for PoolArray { } } -impl Extend for PoolArray { +impl Extend for PoolArray { #[inline] fn extend>(&mut self, iter: I) { let mut vec = iter.into_iter().collect::>(); @@ -313,7 +313,7 @@ impl Extend for PoolArray { } } -impl PartialEq for PoolArray { +impl PartialEq for PoolArray { #[inline] fn eq(&self, other: &Self) -> bool { if self.len() != other.len() { @@ -326,16 +326,16 @@ impl PartialEq for PoolArray { left.as_slice() == right.as_slice() } } -impl Eq for PoolArray {} +impl Eq for PoolArray {} /// RAII read guard. -pub struct ReadGuard<'a, T: Element> { +pub struct ReadGuard<'a, T: PoolElement> { access: *mut T::SysReadAccess, len: usize, _marker: std::marker::PhantomData<&'a T>, } -impl<'a, T: Element> ReadGuard<'a, T> { +impl<'a, T: PoolElement> ReadGuard<'a, T> { #[inline] unsafe fn new(arr: *const T::SysArray) -> Self { let len = (T::size_fn(get_api()))(arr) as usize; @@ -349,7 +349,7 @@ impl<'a, T: Element> ReadGuard<'a, T> { } } -unsafe impl<'a, T: Element> crate::core_types::access::Guard for ReadGuard<'a, T> { +unsafe impl<'a, T: PoolElement> crate::core_types::access::Guard for ReadGuard<'a, T> { type Target = T; #[inline] @@ -366,7 +366,7 @@ unsafe impl<'a, T: Element> crate::core_types::access::Guard for ReadGuard<'a, T } } -impl<'a, T: Element> Drop for ReadGuard<'a, T> { +impl<'a, T: PoolElement> Drop for ReadGuard<'a, T> { #[inline] fn drop(&mut self) { unsafe { @@ -375,7 +375,7 @@ impl<'a, T: Element> Drop for ReadGuard<'a, T> { } } -impl<'a, T: Element> Clone for ReadGuard<'a, T> { +impl<'a, T: PoolElement> Clone for ReadGuard<'a, T> { #[inline] fn clone(&self) -> Self { let access = unsafe { (T::read_access_copy_fn(get_api()))(self.access) }; @@ -389,13 +389,13 @@ impl<'a, T: Element> Clone for ReadGuard<'a, T> { } /// RAII write guard. -pub struct WriteGuard<'a, T: Element> { +pub struct WriteGuard<'a, T: PoolElement> { access: *mut T::SysWriteAccess, len: usize, _marker: std::marker::PhantomData<&'a T>, } -impl<'a, T: Element> WriteGuard<'a, T> { +impl<'a, T: PoolElement> WriteGuard<'a, T> { #[inline] unsafe fn new(arr: *mut T::SysArray) -> Self { let len = (T::size_fn(get_api()))(arr) as usize; @@ -409,7 +409,7 @@ impl<'a, T: Element> WriteGuard<'a, T> { } } -unsafe impl<'a, T: Element> crate::core_types::access::Guard for WriteGuard<'a, T> { +unsafe impl<'a, T: PoolElement> crate::core_types::access::Guard for WriteGuard<'a, T> { type Target = T; #[inline] @@ -425,9 +425,9 @@ unsafe impl<'a, T: Element> crate::core_types::access::Guard for WriteGuard<'a, } } -unsafe impl<'a, T: Element> crate::core_types::access::WritePtr for WriteGuard<'a, T> {} +unsafe impl<'a, T: PoolElement> crate::core_types::access::WritePtr for WriteGuard<'a, T> {} -impl<'a, T: Element> Drop for WriteGuard<'a, T> { +impl<'a, T: PoolElement> Drop for WriteGuard<'a, T> { #[inline] fn drop(&mut self) { unsafe { @@ -439,41 +439,41 @@ impl<'a, T: Element> Drop for WriteGuard<'a, T> { macros::decl_typed_array_element! { /// Trait for element types that can be contained in `PoolArray`. This trait is sealed /// and has no public interface. - pub trait Element: private::Sealed { .. } + pub trait PoolElement: private::Sealed { .. } } macros::impl_typed_array_element! { - impl Element for u8 => byte { .. } + impl PoolElement for u8 => byte { .. } } macros::impl_typed_array_element! { - impl Element for i32 => int { .. } + impl PoolElement for i32 => int { .. } } macros::impl_typed_array_element! { - impl Element for f32 => real { .. } + impl PoolElement for f32 => real { .. } } macros::impl_typed_array_element! { - impl Element for GodotString + impl PoolElement for GodotString as sys::godot_string ref *const sys::godot_string => string { .. } } macros::impl_typed_array_element! { - impl Element for Vector2 + impl PoolElement for Vector2 as sys::godot_vector2 ref *const sys::godot_vector2 => vector2 { .. } } macros::impl_typed_array_element! { - impl Element for Vector3 + impl PoolElement for Vector3 as sys::godot_vector3 ref *const sys::godot_vector3 => vector3 { .. } } macros::impl_typed_array_element! { - impl Element for Color + impl PoolElement for Color as sys::godot_color ref *const sys::godot_color => color @@ -495,7 +495,7 @@ mod serialize { use std::fmt::Formatter; use std::marker::PhantomData; - impl Serialize for PoolArray { + impl Serialize for PoolArray { #[inline] fn serialize(&self, ser: S) -> Result<::Ok, ::Error> where @@ -510,14 +510,14 @@ mod serialize { } } - impl<'de, T: Deserialize<'de> + Element> Deserialize<'de> for PoolArray { + impl<'de, T: Deserialize<'de> + PoolElement> Deserialize<'de> for PoolArray { #[inline] fn deserialize(deserializer: D) -> Result>::Error> where D: Deserializer<'de>, { struct TypedArrayVisitor(PhantomData); - impl<'de, T: Deserialize<'de> + Element> Visitor<'de> for TypedArrayVisitor { + impl<'de, T: Deserialize<'de> + PoolElement> Visitor<'de> for TypedArrayVisitor { type Value = PoolArray; fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { diff --git a/gdnative-core/src/core_types/variant.rs b/gdnative-core/src/core_types/variant.rs index b5eda7ce7..807d7ddc3 100644 --- a/gdnative-core/src/core_types/variant.rs +++ b/gdnative-core/src/core_types/variant.rs @@ -1348,7 +1348,7 @@ from_variant_from_sys!( impl FromVariant for Dictionary as Dictionary : godot_variant_as_dictionary; ); -impl ToVariant for PoolArray { +impl ToVariant for PoolArray { #[inline] fn to_variant(&self) -> Variant { unsafe { @@ -1359,9 +1359,9 @@ impl ToVariant for PoolArray { } } } -impl ToVariantEq for PoolArray {} +impl ToVariantEq for PoolArray {} -impl FromVariant for PoolArray { +impl FromVariant for PoolArray { #[inline] fn from_variant(variant: &Variant) -> Result { unsafe { From 479369a42423d3907bde21973e648231d97a05f5 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 13 Dec 2021 14:07:48 +0100 Subject: [PATCH 3/8] Rename ClassBuilder::build_*() -> *() --- examples/array_export/src/lib.rs | 8 ++++---- examples/spinning_cube/src/lib.rs | 4 ++-- gdnative-async/src/rt/bridge.rs | 4 +--- gdnative-async/src/rt/func_state.rs | 4 ++-- gdnative-core/src/export/class_builder.rs | 12 ++++-------- gdnative-derive/src/lib.rs | 4 ++-- gdnative-derive/src/methods.rs | 2 +- gdnative-derive/src/native_script.rs | 2 +- test/src/test_async.rs | 4 +--- test/src/test_register.rs | 8 ++++---- 10 files changed, 22 insertions(+), 30 deletions(-) diff --git a/examples/array_export/src/lib.rs b/examples/array_export/src/lib.rs index 9430aba55..8f9481299 100644 --- a/examples/array_export/src/lib.rs +++ b/examples/array_export/src/lib.rs @@ -14,12 +14,12 @@ impl ExportsArrays { fn register(builder: &ClassBuilder) { builder - .build_property::("single_array") + .property::("single_array") .with_setter(ExportsArrays::set_single_array) .done(); builder - .build_property::("single_array_range") + .property::("single_array_range") .with_setter(ExportsArrays::set_single_array_range) .with_hint(ArrayHint::with_element_hint::(IntHint::Range( RangeHint::new(-5, 5), @@ -27,7 +27,7 @@ impl ExportsArrays { .done(); builder - .build_property::("double_array") + .property::("double_array") .with_setter(ExportsArrays::set_double_array) .with_hint(ArrayHint::with_element_hint::( ArrayHint::new(), @@ -35,7 +35,7 @@ impl ExportsArrays { .done(); builder - .build_property::("double_array_range") + .property::("double_array_range") .with_setter(ExportsArrays::set_double_array_range) .with_hint(ArrayHint::with_element_hint::( ArrayHint::with_element_hint::(IntHint::Range(RangeHint::new(-5, 5))), diff --git a/examples/spinning_cube/src/lib.rs b/examples/spinning_cube/src/lib.rs index 8a961b3b7..bcee4f2c5 100644 --- a/examples/spinning_cube/src/lib.rs +++ b/examples/spinning_cube/src/lib.rs @@ -15,7 +15,7 @@ struct RustTest { fn register_properties(builder: &ClassBuilder) { builder - .build_property::("test/test_enum") + .property::("test/test_enum") .with_hint(StringHint::Enum(EnumHint::new(vec![ "Hello".into(), "World".into(), @@ -25,7 +25,7 @@ fn register_properties(builder: &ClassBuilder) { .done(); builder - .build_property("test/test_flags") + .property("test/test_flags") .with_hint(IntHint::Flags(EnumHint::new(vec![ "A".into(), "B".into(), diff --git a/gdnative-async/src/rt/bridge.rs b/gdnative-async/src/rt/bridge.rs index 8347277ac..86fbb6e78 100644 --- a/gdnative-async/src/rt/bridge.rs +++ b/gdnative-async/src/rt/bridge.rs @@ -134,8 +134,6 @@ impl Method for OnSignalFn { impl NativeClassMethods for SignalBridge { fn register(builder: &ClassBuilder) { - builder - .build_method("_on_signal", OnSignalFn) - .done_stateless(); + builder.method("_on_signal", OnSignalFn).done_stateless(); } } diff --git a/gdnative-async/src/rt/func_state.rs b/gdnative-async/src/rt/func_state.rs index 4b960dab9..6626f89ea 100644 --- a/gdnative-async/src/rt/func_state.rs +++ b/gdnative-async/src/rt/func_state.rs @@ -169,10 +169,10 @@ impl StaticArgsMethod for ResumeFn { impl NativeClassMethods for FuncState { fn register(builder: &ClassBuilder) { builder - .build_method("is_valid", StaticArgs::new(IsValidFn)) + .method("is_valid", StaticArgs::new(IsValidFn)) .done_stateless(); builder - .build_method("resume", StaticArgs::new(ResumeFn)) + .method("resume", StaticArgs::new(ResumeFn)) .done_stateless(); } } diff --git a/gdnative-core/src/export/class_builder.rs b/gdnative-core/src/export/class_builder.rs index 189d5c105..fd3a170bf 100644 --- a/gdnative-core/src/export/class_builder.rs +++ b/gdnative-core/src/export/class_builder.rs @@ -76,7 +76,7 @@ impl ClassBuilder { /// /// fn my_register(builder: &ClassBuilder) { /// builder - /// .build_method("my_method", MyMethod) + /// .method("my_method", MyMethod) /// .with_rpc_mode(RpcMode::RemoteSync) /// .done(); /// } @@ -95,11 +95,7 @@ impl ClassBuilder { /// ``` /// #[inline] - pub fn build_method<'a, F: Method>( - &'a self, - name: &'a str, - method: F, - ) -> MethodBuilder<'a, C, F> { + pub fn method<'a, F: Method>(&'a self, name: &'a str, method: F) -> MethodBuilder<'a, C, F> { MethodBuilder::new(self, name, method) } @@ -128,7 +124,7 @@ impl ClassBuilder { /// /// fn my_register(builder: &ClassBuilder) { /// builder - /// .build_property("foo") + /// .property("foo") /// .with_default(5) /// .with_hint((-10..=30).into()) /// .with_getter(MyType::get_foo) @@ -138,7 +134,7 @@ impl ClassBuilder { /// } /// ``` #[inline] - pub fn build_property<'a, T>(&'a self, name: &'a str) -> PropertyBuilder<'a, C, T> + pub fn property<'a, T>(&'a self, name: &'a str) -> PropertyBuilder<'a, C, T> where T: Export, { diff --git a/gdnative-derive/src/lib.rs b/gdnative-derive/src/lib.rs index 8b8938fe7..bd9b70d7f 100644 --- a/gdnative-derive/src/lib.rs +++ b/gdnative-derive/src/lib.rs @@ -50,7 +50,7 @@ mod variant; /// impl gdnative::export::NativeClassMethods for Foo { /// fn register(builder: &ClassBuilder) { /// use gdnative::export::*; -/// builder.build_method("foo", gdnative::export::godot_wrap_method!(Foo, fn foo(&self, _owner: &Reference, bar: i64) -> i64)) +/// builder.method("foo", gdnative::export::godot_wrap_method!(Foo, fn foo(&self, _owner: &Reference, bar: i64) -> i64)) /// .with_rpc_mode(RpcMode::Disabled) /// .done_stateless(); /// } @@ -177,7 +177,7 @@ pub fn profiled(meta: TokenStream, input: TokenStream) -> TokenStream { /// } /// fn my_register_function(builder: &ClassBuilder) { /// builder.add_signal(Signal { name: "foo", args: &[] }); -/// builder.build_property::("bar") +/// builder.property::("bar") /// .with_getter(|_, _| 42.0) /// .with_hint(FloatHint::Range(RangeHint::new(0.0, 100.0))) /// .done(); diff --git a/gdnative-derive/src/methods.rs b/gdnative-derive/src/methods.rs index 1a1d15ef3..730ff2f22 100644 --- a/gdnative-derive/src/methods.rs +++ b/gdnative-derive/src/methods.rs @@ -133,7 +133,7 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 { fn #name ( #( #args )* ) -> #ret_ty ); - #builder.build_method(#name_string, method) + #builder.method(#name_string, method) .with_rpc_mode(#rpc) .done_stateless(); } diff --git a/gdnative-derive/src/native_script.rs b/gdnative-derive/src/native_script.rs index 61214e973..a0da35852 100644 --- a/gdnative-derive/src/native_script.rs +++ b/gdnative-derive/src/native_script.rs @@ -79,7 +79,7 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result for ResumeAddFn { } fn register_methods(builder: &ClassBuilder) { - builder - .build_method("resume_add", Async::new(ResumeAddFn)) - .done(); + builder.method("resume_add", Async::new(ResumeAddFn)).done(); } diff --git a/test/src/test_register.rs b/test/src/test_register.rs index 0cc347093..d25a4a07c 100644 --- a/test/src/test_register.rs +++ b/test/src/test_register.rs @@ -61,7 +61,7 @@ impl NativeClass for RegisterProperty { } fn register_properties(builder: &ClassBuilder) { builder - .build_property("value") + .property("value") .with_default(42) .with_setter(RegisterProperty::set_value) .with_getter(RegisterProperty::get_value) @@ -155,15 +155,15 @@ where fn register_methods(builder: &ClassBuilder) { builder - .build_method("add_ints", StaticArgs::new(StatefulMixin { d: 42 })) + .method("add_ints", StaticArgs::new(StatefulMixin { d: 42 })) .done(); builder - .build_method("add_floats", StaticArgs::new(StatefulMixin { d: 4.0 })) + .method("add_floats", StaticArgs::new(StatefulMixin { d: 4.0 })) .done(); builder - .build_method( + .method( "add_vectors", StaticArgs::new(StatefulMixin { d: Vector2::new(1.0, 2.0), From 1954608e04037dd15fc5b703a57630f4f4d37b9e Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Tue, 14 Dec 2021 19:36:45 +0100 Subject: [PATCH 4/8] Hide Reference::init_ref() -- unsound, ref-counting to be done via Ref Closes #632 --- bindings_generator/src/methods.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings_generator/src/methods.rs b/bindings_generator/src/methods.rs index d00c40432..e802e2e32 100644 --- a/bindings_generator/src/methods.rs +++ b/bindings_generator/src/methods.rs @@ -136,7 +136,7 @@ impl MethodSig { } fn skip_method(method: &GodotMethod, name: &str) -> bool { - const METHODS: &[&str] = &["free", "reference", "unreference"]; + const METHODS: &[&str] = &["free", "reference", "unreference", "init_ref"]; METHODS.contains(&name) || method.is_virtual } From 35f4ad210bf95dfab3d47b9bff59fb781ed40387 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Tue, 14 Dec 2021 19:41:24 +0100 Subject: [PATCH 5/8] Remove FloatHint::Enum variant Closes #820 --- gdnative-core/src/export/property/hint.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/gdnative-core/src/export/property/hint.rs b/gdnative-core/src/export/property/hint.rs index 98ed102ac..4de05f7bf 100644 --- a/gdnative-core/src/export/property/hint.rs +++ b/gdnative-core/src/export/property/hint.rs @@ -265,8 +265,6 @@ pub enum FloatHint { Range(RangeHint), /// Hints that an integer or float property should be within an exponential range. ExpRange(RangeHint), - /// Hints that an integer, float or string property is an enumerated value to pick in a list. - Enum(EnumHint), /// Hints that a float property should be edited via an exponential easing function. ExpEasing(ExpEasingHint), } @@ -282,13 +280,11 @@ where let hint_kind = match &self { FH::Range(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_RANGE, FH::ExpRange(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_EXP_RANGE, - FH::Enum(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_ENUM, FH::ExpEasing(_) => sys::godot_property_hint_GODOT_PROPERTY_HINT_EXP_EASING, }; let hint_string = match self { FH::Range(range) | FH::ExpRange(range) => range.to_godot_hint_string(), - FH::Enum(e) => e.to_godot_hint_string(), FH::ExpEasing(e) => e.to_godot_hint_string(), }; @@ -320,13 +316,6 @@ where } } -impl From for FloatHint { - #[inline] - fn from(hint: EnumHint) -> Self { - Self::Enum(hint) - } -} - impl From for FloatHint { #[inline] fn from(hint: ExpEasingHint) -> Self { From 1c94e10912df21a40a90b78e052be8bfe86b700b Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 15 Dec 2021 19:45:43 +0100 Subject: [PATCH 6/8] Variant, String: rename forget() -> leak() Consistent with standard Vec::leak(), Box::leak() etc. --- gdnative-core/src/core_types/string.rs | 13 ++++++------- gdnative-core/src/core_types/variant.rs | 11 +++++------ gdnative-core/src/export/method.rs | 6 +++--- gdnative-core/src/export/property/accessor.rs | 10 +++++----- .../src/export/property/invalid_accessor.rs | 2 +- test/src/lib.rs | 2 +- 6 files changed, 21 insertions(+), 23 deletions(-) diff --git a/gdnative-core/src/core_types/string.rs b/gdnative-core/src/core_types/string.rs index 6601d9cb9..1adf1c355 100644 --- a/gdnative-core/src/core_types/string.rs +++ b/gdnative-core/src/core_types/string.rs @@ -237,15 +237,14 @@ impl GodotString { Self(unsafe { (get_api().godot_string_format)(&self.0, &values.0) }) } - /// Returns the internal ffi representation of the string and consumes - /// the rust object without running the destructor. + /// Returns the internal FFI representation of the string and consumes + /// the Rust object without running the destructor. /// - /// This should be only used when certain that the receiving side is - /// responsible for running the destructor for the object, otherwise - /// it is leaked. + /// The returned object has no `Drop` implementation. The caller is + /// responsible of manually ensuring destruction. #[doc(hidden)] #[inline] - pub fn forget(self) -> sys::godot_string { + pub fn leak(self) -> sys::godot_string { let v = self.0; forget(self); v @@ -285,7 +284,7 @@ impl GodotString { pub fn clone_from_sys(sys: sys::godot_string) -> Self { let sys_string = GodotString(sys); let this = sys_string.clone(); - sys_string.forget(); + sys_string.leak(); this } diff --git a/gdnative-core/src/core_types/variant.rs b/gdnative-core/src/core_types/variant.rs index 807d7ddc3..6fd95cede 100644 --- a/gdnative-core/src/core_types/variant.rs +++ b/gdnative-core/src/core_types/variant.rs @@ -524,15 +524,14 @@ impl Variant { unsafe { &mut *(ptr as *mut variant::Variant) } } - /// Returns the internal ffi representation of the variant and consumes - /// the rust object without running the destructor. + /// Returns the internal FFI representation of the variant and consumes + /// the Rust object without running the destructor. /// - /// This should be only used when certain that the receiving side is - /// responsible for running the destructor for the object, otherwise - /// it is leaked. + /// The returned object has no `Drop` implementation. The caller is + /// responsible of manually ensuring destruction. #[inline] #[doc(hidden)] - pub fn forget(self) -> sys::godot_variant { + pub fn leak(self) -> sys::godot_variant { let v = self.0; forget(self); v diff --git a/gdnative-core/src/export/method.rs b/gdnative-core/src/export/method.rs index e191e18d6..736954a88 100644 --- a/gdnative-core/src/export/method.rs +++ b/gdnative-core/src/export/method.rs @@ -549,7 +549,7 @@ unsafe extern "C" fn method_wrapper>( C::class_name(), ), ); - return Variant::nil().forget(); + return Variant::nil().leak(); } let this = match std::ptr::NonNull::new(this) { @@ -562,7 +562,7 @@ unsafe extern "C" fn method_wrapper>( C::class_name(), ), ); - return Variant::nil().forget(); + return Variant::nil().leak(); } }; @@ -586,7 +586,7 @@ unsafe extern "C" fn method_wrapper>( ); Variant::nil() }) - .forget() + .leak() } unsafe extern "C" fn free_func(method_data: *mut libc::c_void) { diff --git a/gdnative-core/src/export/property/accessor.rs b/gdnative-core/src/export/property/accessor.rs index 6849e2263..0f5a0faca 100644 --- a/gdnative-core/src/export/property/accessor.rs +++ b/gdnative-core/src/export/property/accessor.rs @@ -296,7 +296,7 @@ where "gdnative-core: user data pointer for {} is null (did the constructor fail?)", C::class_name(), ); - return Variant::nil().forget(); + return Variant::nil().leak(); } let this = match NonNull::new(this) { @@ -306,7 +306,7 @@ where "gdnative-core: owner pointer for {} is null", C::class_name(), ); - return Variant::nil().forget(); + return Variant::nil().leak(); } }; @@ -316,17 +316,17 @@ where let func = &*(method as *const F); match <(SelfArg, RetKind)>::map_get(&user_data, func, owner) { - Ok(variant) => variant.forget(), + Ok(variant) => variant.leak(), Err(err) => { godot_error!("gdnative-core: cannot call property getter: {:?}", err); - Variant::nil().forget() + Variant::nil().leak() } } }); result.unwrap_or_else(|_| { godot_error!("gdnative-core: property getter panicked (check stderr for output)"); - Variant::nil().forget() + Variant::nil().leak() }) } get.get_func = Some(invoke::); diff --git a/gdnative-core/src/export/property/invalid_accessor.rs b/gdnative-core/src/export/property/invalid_accessor.rs index 18cec2994..c343f5f60 100644 --- a/gdnative-core/src/export/property/invalid_accessor.rs +++ b/gdnative-core/src/export/property/invalid_accessor.rs @@ -70,7 +70,7 @@ extern "C" fn invalid_getter( property_name, class_name ); - Variant::nil().forget() + Variant::nil().leak() } extern "C" fn invalid_free_func(data: *mut libc::c_void) { diff --git a/test/src/lib.rs b/test/src/lib.rs index 4bdc9dcf0..7ed65e996 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -75,7 +75,7 @@ pub extern "C" fn run_tests( status &= test_variant_ops::run_tests(); status &= test_vararray_return::run_tests(); - gdnative::core_types::Variant::new(status).forget() + gdnative::core_types::Variant::new(status).leak() } fn test_underscore_method_binding() -> bool { From b0d28df7617f122d10ccc1ab77e5bb48304f637e Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 15 Dec 2021 20:47:04 +0100 Subject: [PATCH 7/8] Add SignalBuilder struct; rename SignalArgument -> SignalParam; integrate into ClassBuilder --- examples/dodge_the_creeps/src/hud.rs | 5 +- examples/dodge_the_creeps/src/player.rs | 5 +- examples/signals/src/lib.rs | 18 +--- gdnative-async/src/rt/func_state.rs | 25 ++--- gdnative-core/src/export/class_builder.rs | 121 ++++++++++++---------- gdnative-core/src/export/method.rs | 5 +- gdnative-core/src/export/mod.rs | 33 +++++- gdnative-core/src/export/property.rs | 4 +- gdnative-core/src/export/signal.rs | 109 +++++++++++++++++++ gdnative-derive/src/lib.rs | 4 +- gdnative/src/prelude.rs | 2 +- test/src/test_register.rs | 13 +-- 12 files changed, 234 insertions(+), 110 deletions(-) create mode 100644 gdnative-core/src/export/signal.rs diff --git a/examples/dodge_the_creeps/src/hud.rs b/examples/dodge_the_creeps/src/hud.rs index 8fe05be63..0a8194828 100644 --- a/examples/dodge_the_creeps/src/hud.rs +++ b/examples/dodge_the_creeps/src/hud.rs @@ -9,10 +9,7 @@ pub struct Hud; #[methods] impl Hud { fn register_hud(builder: &ClassBuilder) { - builder.add_signal(Signal { - name: "start_game", - args: &[], - }); + builder.signal("start_game").done(); } fn new(_owner: &CanvasLayer) -> Self { diff --git a/examples/dodge_the_creeps/src/player.rs b/examples/dodge_the_creeps/src/player.rs index e1873b99f..f09a818af 100644 --- a/examples/dodge_the_creeps/src/player.rs +++ b/examples/dodge_the_creeps/src/player.rs @@ -16,10 +16,7 @@ pub struct Player { #[methods] impl Player { fn register_player(builder: &ClassBuilder) { - builder.add_signal(Signal { - name: "hit", - args: &[], - }); + builder.signal("hit").done() } fn new(_owner: &Area2D) -> Self { diff --git a/examples/signals/src/lib.rs b/examples/signals/src/lib.rs index eb53250c1..94ff4979c 100644 --- a/examples/signals/src/lib.rs +++ b/examples/signals/src/lib.rs @@ -12,21 +12,13 @@ struct SignalEmitter { #[methods] impl SignalEmitter { fn register_signals(builder: &ClassBuilder) { - builder.add_signal(Signal { - name: "tick", - args: &[], - }); + builder.signal("tick").done(); - builder.add_signal(Signal { - name: "tick_with_data", + builder + .signal("tick_with_data") // Argument list used by the editor for GUI and generation of GDScript handlers. It can be omitted if the signal is only used from code. - args: &[SignalArgument { - name: "data", - default: Variant::new(100), - export_info: ExportInfo::new(VariantType::I64), - usage: PropertyUsage::DEFAULT, - }], - }); + .with_param_default("data", Variant::new(100)) + .done(); } fn new(_owner: &Node) -> Self { diff --git a/gdnative-async/src/rt/func_state.rs b/gdnative-async/src/rt/func_state.rs index 6626f89ea..32ac0a458 100644 --- a/gdnative-async/src/rt/func_state.rs +++ b/gdnative-async/src/rt/func_state.rs @@ -1,9 +1,8 @@ use gdnative_bindings::Reference; -use gdnative_core::core_types::{ToVariant, Variant, VariantType}; +use gdnative_core::core_types::{ToVariant, Variant}; use gdnative_core::export::user_data::{LocalCellData, Map, MapMut}; use gdnative_core::export::{ - ClassBuilder, ExportInfo, NativeClass, NativeClassMethods, PropertyUsage, Signal, - SignalArgument, StaticArgs, StaticArgsMethod, + ClassBuilder, NativeClass, NativeClassMethods, StaticArgs, StaticArgsMethod, }; use gdnative_core::godot_site; use gdnative_core::object::ownership::Unique; @@ -32,20 +31,12 @@ impl NativeClass for FuncState { } fn register_properties(builder: &ClassBuilder) { - builder.add_signal(Signal { - name: "completed", - args: &[SignalArgument { - name: "value", - default: Variant::nil(), - export_info: ExportInfo::new(VariantType::Nil), - usage: PropertyUsage::DEFAULT, - }], - }); - - builder.add_signal(Signal { - name: "resumable", - args: &[], - }); + builder + .signal("completed") + .with_param_untyped("value") + .done(); + + builder.signal("resumable").done(); } } diff --git a/gdnative-core/src/export/class_builder.rs b/gdnative-core/src/export/class_builder.rs index fd3a170bf..478c9925c 100644 --- a/gdnative-core/src/export/class_builder.rs +++ b/gdnative-core/src/export/class_builder.rs @@ -1,44 +1,21 @@ -//! Low-level API to register and export GDNative classes, methods and properties. -//! -//! ## Init and exit hooks -//! -//! Three endpoints are automatically invoked by the engine during startup and shutdown: -//! -//! - [`godot_gdnative_init`], -//! - [`godot_nativescript_init`], -//! - [`godot_gdnative_terminate`], -//! -//! All three must be present. To quickly define all three endpoints using the default names, -//! use [`godot_init`]. -//! -//! ## Registering script classes -//! -//! [`InitHandle`] is the registry of all your exported symbols. -//! To register script classes, call [`InitHandle::add_class`] or [`InitHandle::add_tool_class`] -//! in your [`godot_nativescript_init`] or [`godot_init`] callback: -//! -//! ```ignore -//! use gdnative::prelude::*; -//! -//! fn init(handle: InitHandle) { -//! handle.add_class::(); -//! } -//! -//! godot_init!(init); -//! ``` -//! -//! For full examples, see [`examples`](https://github.com/godot-rust/godot-rust/tree/master/examples) -//! in the godot-rust repository. - +use crate::core_types::GodotString; use std::ffi::CString; use std::marker::PhantomData; use std::ptr; -use crate::core_types::{GodotString, Variant}; use crate::export::*; use crate::object::NewRef; use crate::private::get_api; +// TODO unify string parameters across all buiders +// Potential candidates: +// * &str +// * impl Into +// * impl Into> + +/// Allows registration of exported properties, methods and signals. +/// +/// See member functions of this class for usage examples. #[derive(Debug)] pub struct ClassBuilder { pub(super) init_handle: *mut libc::c_void, @@ -141,23 +118,66 @@ impl ClassBuilder { PropertyBuilder::new(self, name) } + /// Returns a `SignalBuilder` which can be used to add a signal to the class being + /// registered. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use gdnative::prelude::*; + /// + /// #[derive(NativeClass)] + /// #[inherit(Node)] + /// #[register_with(Self::my_register)] + /// #[no_constructor] + /// struct MyType {} + /// + /// // Note: no #[methods] required + /// impl MyType { + /// fn my_register(builder: &ClassBuilder) { + /// // Add signal without parameters + /// builder + /// .signal("jumped") + /// .done(); + /// + /// // Add another signal with 1 parameter (untyped) + /// builder + /// .signal("fired") + /// .with_param_untyped("weapon_type") + /// .done(); + /// + /// // Add third signal with int + String parameters, the latter with a default value "Kerosene" + /// builder + /// .signal("used_jetpack") + /// .with_param("fuel_spent", VariantType::I64) + /// .with_param_default("fuel_type", Variant::new("Kerosene")) + /// .done(); + /// } + /// } + /// ``` + #[inline] + pub fn signal(&self, name: &str) -> SignalBuilder { + SignalBuilder::new(self, GodotString::from(name)) + } + #[inline] - pub fn add_signal(&self, signal: Signal) { + pub(crate) fn add_signal(&self, signal: Signal) { unsafe { - let name = GodotString::from_str(signal.name); - let owned = signal + let args_and_hints = signal .args .iter() .map(|arg| { - let arg_name = GodotString::from_str(arg.name); let hint_string = arg.export_info.hint_string.new_ref(); - (arg, arg_name, hint_string) + (arg, hint_string) }) .collect::>(); - let mut args = owned + + let mut sys_args = args_and_hints .iter() - .map(|(arg, arg_name, hint_string)| sys::godot_signal_argument { - name: arg_name.to_sys(), + .map(|(arg, hint_string)| sys::godot_signal_argument { + name: arg.name.to_sys(), type_: arg.default.get_type() as i32, hint: arg.export_info.hint_kind, hint_string: hint_string.to_sys(), @@ -165,13 +185,14 @@ impl ClassBuilder { default_value: arg.default.to_sys(), }) .collect::>(); + (get_api().godot_nativescript_register_signal)( self.init_handle, self.class_name.as_ptr(), &sys::godot_signal { - name: name.to_sys(), - num_args: args.len() as i32, - args: args.as_mut_ptr(), + name: signal.name.to_sys(), + num_args: sys_args.len() as i32, + args: sys_args.as_mut_ptr(), num_default_args: 0, default_args: ptr::null_mut(), }, @@ -211,15 +232,3 @@ impl ClassBuilder { } } } - -pub struct Signal<'l> { - pub name: &'l str, - pub args: &'l [SignalArgument<'l>], -} - -pub struct SignalArgument<'l> { - pub name: &'l str, - pub default: Variant, - pub export_info: ExportInfo, - pub usage: PropertyUsage, -} diff --git a/gdnative-core/src/export/method.rs b/gdnative-core/src/export/method.rs index 736954a88..35bec90ab 100644 --- a/gdnative-core/src/export/method.rs +++ b/gdnative-core/src/export/method.rs @@ -12,8 +12,9 @@ use crate::object::ownership::Shared; use crate::object::{Ref, TInstance, TRef}; /// Builder type used to register a method on a `NativeClass`. +#[must_use = "MethodBuilder left unbuilt -- did you forget to call done() or done_stateless()?"] pub struct MethodBuilder<'a, C, F> { - class_builder: &'a super::ClassBuilder, + class_builder: &'a ClassBuilder, name: &'a str, method: F, @@ -66,7 +67,7 @@ where F: Method + Copy + Default, { /// Register the method as a stateless method. Stateless methods do not have data - /// pointers and destructors and is thus slightly lighter. This is intended for ZSTs, + /// pointers and destructors and are thus slightly lighter. This is intended for ZSTs, /// but can be used with any `Method` type with `Copy + Default`. #[inline] pub fn done_stateless(self) { diff --git a/gdnative-core/src/export/mod.rs b/gdnative-core/src/export/mod.rs index 11821e4f4..41b03bbfb 100644 --- a/gdnative-core/src/export/mod.rs +++ b/gdnative-core/src/export/mod.rs @@ -2,18 +2,48 @@ //! //! NativeScript allows users to have their own scripts in a native language (in this case Rust). //! It is _not_ the same as GDNative, the native interface to call into Godot. -//! //! Symbols in this module allow registration, exporting and management of user-defined types //! which are wrapped in native scripts. //! //! If you are looking for how to manage Godot core types or classes (objects), check //! out the [`core_types`][crate::core_types] and [`object`][crate::object] modules, respectively. +//! +//! ## Init and exit hooks +//! +//! Three endpoints are automatically invoked by the engine during startup and shutdown: +//! +//! * [`godot_gdnative_init`], +//! * [`godot_nativescript_init`], +//! * [`godot_gdnative_terminate`], +//! +//! All three must be present. To quickly define all three endpoints using the default names, +//! use [`godot_init`]. +//! +//! ## Registering script classes +//! +//! [`InitHandle`] is the registry of all your exported symbols. +//! To register script classes, call [`InitHandle::add_class()`] or [`InitHandle::add_tool_class()`] +//! in your [`godot_nativescript_init`] or [`godot_init`] callback: +//! +//! ```ignore +//! use gdnative::prelude::*; +//! +//! fn init(handle: InitHandle) { +//! handle.add_class::(); +//! } +//! +//! godot_init!(init); +//! ``` +//! +//! For full examples, see [`examples`](https://github.com/godot-rust/godot-rust/tree/master/examples) +//! in the godot-rust repository. mod class; mod class_builder; mod macros; mod method; mod property; +mod signal; pub(crate) mod class_registry; pub(crate) mod emplace; @@ -26,3 +56,4 @@ pub use class::*; pub use class_builder::*; pub use method::*; pub use property::*; +pub use signal::*; diff --git a/gdnative-core/src/export/property.rs b/gdnative-core/src/export/property.rs index dc5e38117..5e465a303 100644 --- a/gdnative-core/src/export/property.rs +++ b/gdnative-core/src/export/property.rs @@ -64,7 +64,7 @@ impl ExportInfo { /// Builder type used to register a property on a `NativeClass`. #[derive(Debug)] -#[must_use] +#[must_use = "PropertyBuilder left unbuilt -- did you forget to call done()?"] pub struct PropertyBuilder<'a, C, T: Export, S = InvalidSetter<'a>, G = InvalidGetter<'a>> { name: &'a str, setter: S, @@ -161,6 +161,8 @@ where /// Provides a setter function with the signature `fn(&C, owner: C::Base, value: T)` /// where `C` is the `NativeClass` type being registered and `T` is the type of the property. + /// + /// "shr" stands for "shared reference", as opposed to the more common `&mut self`. #[inline] pub fn with_shr_setter( self, diff --git a/gdnative-core/src/export/signal.rs b/gdnative-core/src/export/signal.rs new file mode 100644 index 000000000..90e96a14f --- /dev/null +++ b/gdnative-core/src/export/signal.rs @@ -0,0 +1,109 @@ +use crate::core_types::{GodotString, Variant, VariantType}; +use crate::export::{ClassBuilder, ExportInfo, NativeClass, PropertyUsage}; + +/// Class to construct a signal. Make sure to call [`Self::done()`] in the end. +/// +/// Signal parameters can be added with the various `param*()` methods. +/// Keep in mind that unlike function parameters, signal parameters (both their lengths and types) +/// are not statically checked in Godot. The parameter signature you specify is simply to assist you +/// in the editor UI and with auto-generation of GDScript signal handlers. +#[must_use = "SignalBuilder left unbuilt -- did you forget to call done()?"] +pub struct SignalBuilder<'a, C> { + class_builder: &'a ClassBuilder, + name: GodotString, + args: Vec, +} + +impl<'a, C: NativeClass> SignalBuilder<'a, C> { + pub(super) fn new(class_builder: &'a ClassBuilder, signal_name: GodotString) -> Self { + Self { + class_builder, + name: signal_name, + args: vec![], + } + } + + /// Add a parameter for the signal with a name and type. + /// + /// Note that GDScript signal parameters are generally untyped and not checked at runtime. + /// The type is solely used for UI purposes. + #[inline] + pub fn with_param(self, parameter_name: &str, parameter_type: VariantType) -> Self { + self.with_param_custom(SignalParam { + name: parameter_name.into(), + default: Variant::nil(), + export_info: ExportInfo::new(parameter_type), + usage: PropertyUsage::DEFAULT, + }) + } + + /// Add a parameter for the signal with a name and default value. + /// + /// The type is inferred from the default value. + /// Note that GDScript signal parameters are generally untyped and not checked at runtime. + /// The type is solely used for UI purposes. + #[inline] + pub fn with_param_default(self, parameter_name: &str, default_value: Variant) -> Self { + let variant_type = default_value.get_type(); + + self.with_param_custom(SignalParam { + name: parameter_name.into(), + default: default_value, + export_info: ExportInfo::new(variant_type), + usage: PropertyUsage::DEFAULT, + }) + } + + /// Add a (untyped) parameter for the signal with a name. + /// + /// Types are not required or checked at runtime, but they help for editor UI and auto-generation of signal listeners. + #[inline] + pub fn with_param_untyped(self, parameter_name: &str) -> Self { + // Note: the use of 'Nil' to express "untyped" is not following official documentation and could be improved. + + self.with_param_custom(SignalParam { + name: parameter_name.into(), + default: Variant::nil(), + export_info: ExportInfo::new(VariantType::Nil), + usage: PropertyUsage::DEFAULT, + }) + } + + /// Add a parameter for the signal, manually configured. + #[inline] + pub fn with_param_custom(mut self, parameter: SignalParam) -> Self { + self.args.push(parameter); + self + } + + /// Finish registering the signal. + #[inline] + pub fn done(self) { + self.class_builder.add_signal(Signal { + name: self.name, + args: self.args, + }); + } +} + +pub(crate) struct Signal { + pub name: GodotString, + pub args: Vec, +} + +/// Parameter in a signal declaration. +/// +/// Instead of providing values for each field, check out the `param*()` methods in [`SignalBuilder`]. +pub struct SignalParam { + /// Parameter name. + pub name: GodotString, + + /// Default value, used when no argument is provided. + pub default: Variant, + + /// Metadata and UI hints about exporting, e.g. parameter type. + pub export_info: ExportInfo, + + /// In which context the signal parameter is used. + pub usage: PropertyUsage, +} diff --git a/gdnative-derive/src/lib.rs b/gdnative-derive/src/lib.rs index bd9b70d7f..7a6ed0288 100644 --- a/gdnative-derive/src/lib.rs +++ b/gdnative-derive/src/lib.rs @@ -176,8 +176,8 @@ pub fn profiled(meta: TokenStream, input: TokenStream) -> TokenStream { /// Self {} /// } /// fn my_register_function(builder: &ClassBuilder) { -/// builder.add_signal(Signal { name: "foo", args: &[] }); -/// builder.property::("bar") +/// builder.signal("my_sig").done(); +/// builder.property::("my_prop") /// .with_getter(|_, _| 42.0) /// .with_hint(FloatHint::Range(RangeHint::new(0.0, 100.0))) /// .done(); diff --git a/gdnative/src/prelude.rs b/gdnative/src/prelude.rs index f9247134f..a4fc4b74c 100644 --- a/gdnative/src/prelude.rs +++ b/gdnative/src/prelude.rs @@ -17,7 +17,7 @@ pub use gdnative_core::core_types::{ }; pub use gdnative_core::export::{ ClassBuilder, ExportInfo, Method, MethodBuilder, NativeClass, NativeClassMethods, - PropertyUsage, Signal, SignalArgument, + PropertyUsage, SignalBuilder, SignalParam, }; pub use gdnative_core::init::InitHandle; pub use gdnative_core::object::{ diff --git a/test/src/test_register.rs b/test/src/test_register.rs index d25a4a07c..160e40747 100644 --- a/test/src/test_register.rs +++ b/test/src/test_register.rs @@ -31,15 +31,10 @@ impl NativeClass for RegisterSignal { RegisterSignal } fn register_properties(builder: &ClassBuilder) { - builder.add_signal(Signal { - name: "progress", - args: &[SignalArgument { - name: "amount", - default: Variant::nil(), - export_info: ExportInfo::new(VariantType::I64), - usage: PropertyUsage::DEFAULT, - }], - }); + builder + .signal("progress") + .with_param("amount", VariantType::I64) + .done(); } } From 305b57fa14acfa435c82164f5519e25dfd9bd4a4 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sun, 19 Dec 2021 11:40:50 +0100 Subject: [PATCH 8/8] Fix signal parameter type not propagated to editor --- gdnative-core/src/export/class_builder.rs | 24 ++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/gdnative-core/src/export/class_builder.rs b/gdnative-core/src/export/class_builder.rs index 478c9925c..dbbd1f829 100644 --- a/gdnative-core/src/export/class_builder.rs +++ b/gdnative-core/src/export/class_builder.rs @@ -1,8 +1,8 @@ -use crate::core_types::GodotString; use std::ffi::CString; use std::marker::PhantomData; use std::ptr; +use crate::core_types::{GodotString, VariantType}; use crate::export::*; use crate::object::NewRef; use crate::private::get_api; @@ -176,13 +176,13 @@ impl ClassBuilder { let mut sys_args = args_and_hints .iter() - .map(|(arg, hint_string)| sys::godot_signal_argument { - name: arg.name.to_sys(), - type_: arg.default.get_type() as i32, - hint: arg.export_info.hint_kind, + .map(|(param, hint_string)| sys::godot_signal_argument { + name: param.name.to_sys(), + type_: Self::get_param_type(param) as i32, + hint: param.export_info.hint_kind, hint_string: hint_string.to_sys(), - usage: arg.usage.to_sys(), - default_value: arg.default.to_sys(), + usage: param.usage.to_sys(), + default_value: param.default.to_sys(), }) .collect::>(); @@ -200,6 +200,16 @@ impl ClassBuilder { } } + /// Returns the declared parameter type, or the default value's type, or Nil (in that order) + fn get_param_type(arg: &SignalParam) -> VariantType { + let export_type = arg.export_info.variant_type; + if export_type != VariantType::Nil { + export_type + } else { + arg.default.get_type() + } + } + pub(crate) fn add_method(&self, method: ScriptMethod) { let method_name = CString::new(method.name).unwrap();