gdnative_core/init/
init_handle.rs

1use crate::export::user_data::UserData;
2use crate::export::{
3    class_registry, emplace, ClassBuilder, NativeClass, NativeClassMethods, StaticallyNamed,
4};
5use crate::object::{GodotObject, RawObject, TRef};
6use crate::private::get_api;
7use std::borrow::Cow;
8use std::ffi::CString;
9use std::ptr;
10
11use super::InitLevel;
12
13/// A handle that can register new classes to the engine during initialization.
14///
15/// See [`godot_nativescript_init`](macro.godot_nativescript_init.html) and
16/// [`godot_init`](macro.godot_init.html).
17#[derive(Copy, Clone)]
18pub struct InitHandle {
19    handle: *mut libc::c_void,
20    init_level: InitLevel,
21}
22
23#[allow(deprecated)] // Remove once init(), register_properties() and register() have been renamed
24impl InitHandle {
25    #[doc(hidden)]
26    #[inline]
27    pub unsafe fn new(handle: *mut libc::c_void, init_level: InitLevel) -> Self {
28        InitHandle { handle, init_level }
29    }
30
31    /// Registers a new class to the engine.
32    #[inline]
33    pub fn add_class<C>(self)
34    where
35        C: NativeClassMethods + StaticallyNamed,
36    {
37        self.add_class_with::<C>(|_| {})
38    }
39
40    /// Registers a new class to the engine.
41    #[inline]
42    pub fn add_class_with<C>(self, f: impl FnOnce(&ClassBuilder<C>))
43    where
44        C: NativeClassMethods + StaticallyNamed,
45    {
46        self.add_maybe_tool_class_as_with::<C>(Cow::Borrowed(C::CLASS_NAME), false, |builder| {
47            C::nativeclass_register_monomorphized(builder);
48            f(builder);
49        })
50    }
51
52    /// Registers a new tool class to the engine.
53    #[inline]
54    pub fn add_tool_class<C>(self)
55    where
56        C: NativeClassMethods + StaticallyNamed,
57    {
58        self.add_tool_class_with::<C>(|_| {})
59    }
60
61    /// Registers a new tool class to the engine.
62    #[inline]
63    pub fn add_tool_class_with<C>(self, f: impl FnOnce(&ClassBuilder<C>))
64    where
65        C: NativeClassMethods + StaticallyNamed,
66    {
67        self.add_maybe_tool_class_as_with::<C>(Cow::Borrowed(C::CLASS_NAME), true, |builder| {
68            C::nativeclass_register_monomorphized(builder);
69            f(builder);
70        })
71    }
72
73    /// Registers a new class to the engine
74    ///
75    /// If the type implements [`StaticallyTyped`], that name is ignored in favor of the
76    /// name provided at registration.
77    #[inline]
78    pub fn add_class_as<C>(self, name: String)
79    where
80        C: NativeClassMethods,
81    {
82        self.add_class_as_with::<C>(name, |_| {})
83    }
84
85    /// Registers a new class to the engine
86    ///
87    /// If the type implements [`StaticallyTyped`], that name is ignored in favor of the
88    /// name provided at registration.
89    #[inline]
90    pub fn add_class_as_with<C>(self, name: String, f: impl FnOnce(&ClassBuilder<C>))
91    where
92        C: NativeClassMethods,
93    {
94        self.add_maybe_tool_class_as_with::<C>(Cow::Owned(name), false, f)
95    }
96
97    /// Registers a new tool class to the engine
98    ///
99    /// If the type implements [`StaticallyTyped`], that name is ignored in favor of the
100    /// name provided at registration.
101    #[inline]
102    pub fn add_tool_class_as<C>(self, name: String)
103    where
104        C: NativeClassMethods,
105    {
106        self.add_tool_class_as_with::<C>(name, |_| {})
107    }
108
109    /// Registers a new tool class to the engine
110    ///
111    /// If the type implements [`StaticallyTyped`], that name is ignored in favor of the
112    /// name provided at registration.
113    #[inline]
114    pub fn add_tool_class_as_with<C>(self, name: String, f: impl FnOnce(&ClassBuilder<C>))
115    where
116        C: NativeClassMethods,
117    {
118        self.add_maybe_tool_class_as_with::<C>(Cow::Owned(name), true, f)
119    }
120
121    #[inline]
122    fn add_maybe_tool_class_as_with<C>(
123        self,
124        name: Cow<'static, str>,
125        is_tool: bool,
126        f: impl FnOnce(&ClassBuilder<C>),
127    ) where
128        C: NativeClassMethods,
129    {
130        let c_class_name = CString::new(&*name).unwrap();
131
132        match class_registry::register_class_as::<C>(name, self.init_level) {
133            Ok(true) => {}
134            Ok(false) => return,
135            Err(e) => {
136                godot_error!("gdnative-core: ignoring new registration: {e}");
137                return;
138            }
139        };
140
141        unsafe {
142            let base_name = CString::new(C::Base::class_name()).unwrap();
143
144            let create = {
145                unsafe extern "C" fn constructor<C: NativeClass>(
146                    this: *mut sys::godot_object,
147                    _method_data: *mut libc::c_void,
148                ) -> *mut libc::c_void {
149                    use std::panic::{self, AssertUnwindSafe};
150
151                    let this = match ptr::NonNull::new(this) {
152                        Some(this) => this,
153                        None => {
154                            godot_error!(
155                                "gdnative-core: error constructing {}: owner pointer is null",
156                                class_registry::class_name_or_default::<C>(),
157                            );
158
159                            return ptr::null_mut();
160                        }
161                    };
162
163                    let owner = match RawObject::<C::Base>::try_from_sys_ref(this) {
164                        Some(owner) => owner,
165                        None => {
166                            godot_error!(
167                                "gdnative-core: error constructing {}: incompatible owner type, expecting {}",
168                                class_registry::class_name_or_default::<C>(),
169                                C::Base::class_name(),
170                            );
171                            return ptr::null_mut();
172                        }
173                    };
174
175                    let val = match panic::catch_unwind(AssertUnwindSafe(|| {
176                        emplace::take().unwrap_or_else(|| {
177                            C::nativeclass_init(TRef::new(C::Base::cast_ref(owner)))
178                        })
179                    })) {
180                        Ok(val) => val,
181                        Err(e) => {
182                            godot_error!(
183                                "gdnative-core: error constructing {}: constructor panicked",
184                                class_registry::class_name_or_default::<C>(),
185                            );
186                            crate::private::print_panic_error(e);
187                            return ptr::null_mut();
188                        }
189                    };
190
191                    let wrapper = C::UserData::new(val);
192                    C::UserData::into_user_data(wrapper) as *mut _
193                }
194
195                sys::godot_instance_create_func {
196                    create_func: Some(constructor::<C>),
197                    method_data: ptr::null_mut(),
198                    free_func: None,
199                }
200            };
201
202            let destroy = {
203                unsafe extern "C" fn destructor<C: NativeClass>(
204                    _this: *mut sys::godot_object,
205                    _method_data: *mut libc::c_void,
206                    user_data: *mut libc::c_void,
207                ) {
208                    if user_data.is_null() {
209                        godot_error!(
210                            "gdnative-core: user data pointer for {} is null (did the constructor fail?)",
211                            class_registry::class_name_or_default::<C>(),
212                        );
213                        return;
214                    }
215
216                    let wrapper = C::UserData::consume_user_data_unchecked(user_data);
217                    drop(wrapper)
218                }
219
220                sys::godot_instance_destroy_func {
221                    destroy_func: Some(destructor::<C>),
222                    method_data: ptr::null_mut(),
223                    free_func: None,
224                }
225            };
226
227            if is_tool {
228                (get_api().godot_nativescript_register_tool_class)(
229                    self.handle as *mut _,
230                    c_class_name.as_ptr() as *const _,
231                    base_name.as_ptr() as *const _,
232                    create,
233                    destroy,
234                );
235            } else {
236                (get_api().godot_nativescript_register_class)(
237                    self.handle as *mut _,
238                    c_class_name.as_ptr() as *const _,
239                    base_name.as_ptr() as *const _,
240                    create,
241                    destroy,
242                );
243            }
244
245            (get_api().godot_nativescript_set_type_tag)(
246                self.handle as *mut _,
247                c_class_name.as_ptr() as *const _,
248                crate::export::type_tag::create::<C>(),
249            );
250
251            let builder = ClassBuilder::new(self.handle, c_class_name);
252
253            C::nativeclass_register_properties(&builder);
254
255            // register methods
256            C::nativeclass_register(&builder);
257
258            f(&builder);
259        }
260    }
261}