Skip to content

Commit

Permalink
godot-rust#434 Hot reload: recreate callback implemented to make this…
Browse files Browse the repository at this point in the history
… feature work
  • Loading branch information
kkolyan committed Sep 30, 2023
1 parent 639aeb4 commit 08bfad5
Showing 1 changed file with 114 additions and 13 deletions.
127 changes: 114 additions & 13 deletions godot-core/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ use crate::out;
use std::any::Any;
use std::collections::HashMap;
use std::{fmt, ptr};
use godot_ffi::GDExtensionBool;
use sys::GDExtensionClassInstancePtr;
use sys::GDExtensionObjectPtr;

// TODO(bromeon): some information coming from the proc-macro API is deferred through PluginComponent, while others is directly
// translated to code. Consider moving more code to the PluginComponent, which allows for more dynamic registration and will
Expand Down Expand Up @@ -99,12 +102,21 @@ pub enum PluginComponent {
>,

/// User-defined `on_notification` function
#[cfg(before_api = "4.2")]
user_on_notification_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr, //
p_what: i32,
),
>,
#[cfg(since_api = "4.2")]
user_on_notification_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr, //
p_what: i32,
p_reversed: GDExtensionBool,
),
>,

/// Callback for other virtuals
get_virtual_fn: unsafe extern "C" fn(
Expand All @@ -125,7 +137,10 @@ struct ClassRegistrationInfo {
parent_class_name: Option<ClassName>,
generated_register_fn: Option<ErasedRegisterFn>,
user_register_fn: Option<ErasedRegisterFn>,
#[cfg(before_api = "4.2")]
godot_params: sys::GDExtensionClassCreationInfo,
#[cfg(since_api = "4.2")]
godot_params: sys::GDExtensionClassCreationInfo2,
init_level: InitLevel,
is_editor_plugin: bool,
}
Expand All @@ -143,6 +158,7 @@ pub fn register_class<

out!("Manually register class {}", std::any::type_name::<T>());

#[cfg(before_api = "4.2")]
let godot_params = sys::GDExtensionClassCreationInfo {
to_string_func: Some(callbacks::to_string::<T>),
notification_func: Some(callbacks::on_notification::<T>),
Expand All @@ -155,6 +171,20 @@ pub fn register_class<
class_userdata: ptr::null_mut(), // will be passed to create fn, but global per class
..default_creation_info()
};
#[cfg(since_api = "4.2")]
let godot_params = sys::GDExtensionClassCreationInfo2 {
to_string_func: Some(callbacks::to_string::<T>),
notification_func: Some(callbacks::on_notification::<T>),
reference_func: Some(callbacks::reference::<T>),
unreference_func: Some(callbacks::unreference::<T>),
create_instance_func: Some(callbacks::create::<T>),
recreate_instance_func: Some(callbacks::recreate::<T>),
free_instance_func: Some(callbacks::free::<T>),
get_virtual_func: Some(callbacks::get_virtual::<T>),
get_rid_func: None,
class_userdata: ptr::null_mut(), // will be passed to create fn, but global per class
..default_creation_info()
};

register_class_raw(ClassRegistrationInfo {
class_name: T::class_name(),
Expand Down Expand Up @@ -279,13 +309,23 @@ fn register_class_raw(info: ClassRegistrationInfo) {

unsafe {
// Try to register class...

#[cfg(before_api = "4.2")]
#[allow(clippy::let_unit_value)] // notifies us if Godot API ever adds a return type.
let _: () = interface_fn!(classdb_register_extension_class)(
sys::get_library(),
class_name.string_sys(),
parent_class_name.string_sys(),
ptr::addr_of!(info.godot_params),
);
#[cfg(since_api = "4.2")]
#[allow(clippy::let_unit_value)] // notifies us if Godot API ever adds a return type.
let _: () = interface_fn!(classdb_register_extension_class2)(
sys::get_library(),
class_name.string_sys(),
parent_class_name.string_sys(),
ptr::addr_of!(info.godot_params),
);

// ...then see if it worked.
// This is necessary because the above registration does not report errors (apart from console output).
Expand Down Expand Up @@ -329,29 +369,49 @@ pub mod callbacks {

pub unsafe extern "C" fn create<T: cap::GodotInit>(
_class_userdata: *mut std::ffi::c_void,
) -> sys::GDExtensionObjectPtr {
) -> GDExtensionObjectPtr {
create_custom(T::__godot_init)
}

pub(crate) fn create_custom<T, F>(make_user_instance: F) -> sys::GDExtensionObjectPtr
#[cfg(since_api = "4.2")]
pub unsafe extern "C" fn recreate<T: cap::GodotInit>(
_class_userdata: *mut std::ffi::c_void,
object: GDExtensionObjectPtr,
) -> GDExtensionClassInstancePtr {
create_rust_counterpart(T::__godot_init, object)
}

pub(crate) fn create_custom<T, F>(make_user_instance: F) -> GDExtensionObjectPtr
where
T: GodotClass,
F: FnOnce(Base<T::Base>) -> T,
{
let class_name = T::class_name();
let base_class_name = T::Base::class_name();

//out!("create callback: {}", class_name.backing);

let base_ptr =
unsafe { interface_fn!(classdb_construct_object)(base_class_name.string_sys()) };

create_rust_counterpart(make_user_instance, base_ptr);

// std::mem::forget(base_class_name);
base_ptr
}

fn create_rust_counterpart<T, F>(make_user_instance: F, base_ptr: GDExtensionObjectPtr) -> GDExtensionClassInstancePtr
where
T: GodotClass,
F: FnOnce(Base<T::Base>) -> T,
{
let class_name = T::class_name();

//out!("create callback: {}", class_name.backing);

let base = unsafe { Base::from_sys(base_ptr) };
let user_instance = make_user_instance(base);

let instance = InstanceStorage::<T>::construct(user_instance);
let instance_ptr = instance.into_raw();
let instance_ptr = instance_ptr as sys::GDExtensionClassInstancePtr;
let instance_ptr = instance_ptr as GDExtensionClassInstancePtr;

let binding_data_callbacks = crate::storage::nop_instance_callbacks();
unsafe {
Expand All @@ -365,13 +425,12 @@ pub mod callbacks {
}

// std::mem::forget(class_name);
// std::mem::forget(base_class_name);
base_ptr
instance_ptr
}

pub unsafe extern "C" fn free<T: GodotClass>(
_class_user_data: *mut std::ffi::c_void,
instance: sys::GDExtensionClassInstancePtr,
instance: GDExtensionClassInstancePtr,
) {
{
let storage = as_storage::<T>(instance);
Expand All @@ -394,7 +453,7 @@ pub mod callbacks {
}

pub unsafe extern "C" fn to_string<T: cap::GodotToString>(
instance: sys::GDExtensionClassInstancePtr,
instance: GDExtensionClassInstancePtr,
_is_valid: *mut sys::GDExtensionBool,
out_string: sys::GDExtensionStringPtr,
) {
Expand All @@ -409,23 +468,36 @@ pub mod callbacks {
string.move_string_ptr(out_string);
}

#[cfg(before_api = "4.2")]
pub unsafe extern "C" fn on_notification<T: cap::GodotNotification>(
instance: GDExtensionClassInstancePtr,
what: i32,
) {
let storage = as_storage::<T>(instance);
let mut instance = storage.get_mut();

T::__godot_notification(&mut *instance, what);
}

#[cfg(since_api = "4.2")]
pub unsafe extern "C" fn on_notification<T: cap::GodotNotification>(
instance: sys::GDExtensionClassInstancePtr,
instance: GDExtensionClassInstancePtr,
what: i32,
_reversed: GDExtensionBool,
) {
let storage = as_storage::<T>(instance);
let mut instance = storage.get_mut();

T::__godot_notification(&mut *instance, what);
}

pub unsafe extern "C" fn reference<T: GodotClass>(instance: sys::GDExtensionClassInstancePtr) {
pub unsafe extern "C" fn reference<T: GodotClass>(instance: GDExtensionClassInstancePtr) {
let storage = as_storage::<T>(instance);
storage.on_inc_ref();
}

pub unsafe extern "C" fn unreference<T: GodotClass>(
instance: sys::GDExtensionClassInstancePtr,
instance: GDExtensionClassInstancePtr,
) {
let storage = as_storage::<T>(instance);
storage.on_dec_ref();
Expand Down Expand Up @@ -486,6 +558,7 @@ fn default_registration_info(class_name: ClassName) -> ClassRegistrationInfo {
}
}

#[cfg(before_api = "4.2")]
fn default_creation_info() -> sys::GDExtensionClassCreationInfo {
sys::GDExtensionClassCreationInfo {
is_abstract: false as u8,
Expand All @@ -507,3 +580,31 @@ fn default_creation_info() -> sys::GDExtensionClassCreationInfo {
class_userdata: ptr::null_mut(),
}
}

#[cfg(since_api = "4.2")]
fn default_creation_info() -> sys::GDExtensionClassCreationInfo2 {
sys::GDExtensionClassCreationInfo2 {
is_abstract: false as u8,
is_virtual: false as u8,
set_func: None,
get_func: None,
get_property_list_func: None,
free_property_list_func: None,
property_can_revert_func: None,
property_get_revert_func: None,
validate_property_func: None,
notification_func: None,
to_string_func: None,
reference_func: None,
unreference_func: None,
create_instance_func: None,
free_instance_func: None,
recreate_instance_func: None,
get_virtual_func: None,
get_virtual_call_data_func: None,
call_virtual_with_data_func: None,
get_rid_func: None,
class_userdata: ptr::null_mut(),
is_exposed: false as u8
}
}

0 comments on commit 08bfad5

Please sign in to comment.