use std::any::Any;
use std::{any, fmt};
use crate::init::InitLevel;
use crate::meta::ClassId;
use crate::obj::{
Bounds, DynGd, Gd, GodotClass, Inherits, NewAlloc, Singleton, UserClass, UserSingleton, bounds,
cap,
};
use crate::registry::callbacks;
use crate::registry::class::GodotGetVirtual;
use crate::{classes, sys};
#[derive(Debug)]
pub struct ClassPlugin {
pub(crate) class_name: ClassId,
pub(crate) init_level: InitLevel,
pub(crate) item: PluginItem,
}
impl ClassPlugin {
pub fn new<T: GodotClass>(item: PluginItem) -> Self {
Self {
class_name: T::class_id(),
init_level: T::INIT_LEVEL,
item,
}
}
}
#[derive(Copy, Clone)]
pub struct ErasedRegisterFn {
pub raw: fn(&mut dyn Any),
}
impl fmt::Debug for ErasedRegisterFn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "0x{:0>16x}", self.raw as usize)
}
}
#[derive(Copy, Clone)]
pub struct ErasedRegisterRpcsFn {
pub raw: fn(&mut dyn Any),
}
impl fmt::Debug for ErasedRegisterRpcsFn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "0x{:0>16x}", self.raw as usize)
}
}
pub type ErasedDynifyFn = unsafe fn(Gd<classes::Object>) -> ErasedDynGd;
pub struct ErasedDynGd {
pub boxed: Box<dyn Any>,
}
type GodotCreateFn = unsafe extern "C" fn(
_class_userdata: *mut std::ffi::c_void,
#[cfg(since_api = "4.4")] _notify_postinitialize: sys::GDExtensionBool,
) -> sys::GDExtensionObjectPtr;
#[derive(Clone, Debug)]
pub enum PluginItem {
Struct(Struct),
InherentImpl(InherentImpl),
ITraitImpl(ITraitImpl),
DynTraitImpl(DynTraitImpl),
}
fn set<T>(field: &mut Option<T>, value: T) {
assert!(field.is_none(), "attempted to set field more than once",);
*field = Some(value);
}
#[derive(Clone, Debug)]
pub struct Struct {
pub(crate) base_class_name: ClassId,
pub(crate) generated_create_fn: Option<GodotCreateFn>,
pub(crate) generated_recreate_fn: Option<
unsafe extern "C" fn(
p_class_userdata: *mut std::ffi::c_void,
p_object: sys::GDExtensionObjectPtr,
) -> sys::GDExtensionClassInstancePtr,
>,
pub(crate) register_properties_fn: ErasedRegisterFn,
pub(crate) reference_fn: sys::GDExtensionClassReference,
pub(crate) unreference_fn: sys::GDExtensionClassUnreference,
pub(crate) free_fn: unsafe extern "C" fn(
_class_user_data: *mut std::ffi::c_void,
instance: sys::GDExtensionClassInstancePtr,
),
pub(crate) register_singleton_fn: Option<fn()>,
pub(crate) unregister_singleton_fn: Option<fn()>,
pub(crate) default_get_virtual_fn: Option<GodotGetVirtual>,
pub(crate) is_tool: bool,
pub(crate) is_editor_plugin: bool,
pub(crate) is_internal: bool,
pub(crate) is_instantiable: bool,
}
impl Struct {
pub fn new<T: GodotClass + cap::ImplementsGodotExports>() -> Self {
let refcounted = <T::Memory as bounds::Memory>::IS_REF_COUNTED;
Self {
base_class_name: T::Base::class_id(),
generated_create_fn: None,
generated_recreate_fn: None,
register_properties_fn: ErasedRegisterFn {
raw: callbacks::register_user_properties::<T>,
},
free_fn: callbacks::free::<T>,
register_singleton_fn: None,
unregister_singleton_fn: None,
default_get_virtual_fn: None,
is_tool: false,
is_editor_plugin: false,
is_internal: false,
is_instantiable: false,
reference_fn: refcounted.then_some(callbacks::reference::<T>),
unreference_fn: refcounted.then_some(callbacks::unreference::<T>),
}
}
pub fn with_generated<T: GodotClass + cap::GodotDefault>(mut self) -> Self {
set(&mut self.generated_create_fn, callbacks::create::<T>);
set(&mut self.generated_recreate_fn, callbacks::recreate::<T>);
self
}
#[cfg(before_api = "4.5")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.5")))]
pub fn with_generated_no_default<T: GodotClass>(mut self) -> Self {
set(&mut self.generated_create_fn, callbacks::create_null::<T>);
set(
&mut self.generated_recreate_fn,
callbacks::recreate_null::<T>,
);
self
}
pub fn with_default_get_virtual_fn<T: GodotClass + UserClass>(mut self) -> Self {
set(
&mut self.default_get_virtual_fn,
callbacks::default_get_virtual::<T>,
);
self
}
pub fn with_tool(mut self) -> Self {
self.is_tool = true;
self
}
pub fn with_editor_plugin(mut self) -> Self {
self.is_editor_plugin = true;
self
}
pub fn with_singleton<T>(mut self) -> Self
where
T: UserSingleton
+ Bounds<Memory = bounds::MemManual, Declarer = bounds::DeclUser>
+ NewAlloc
+ Inherits<classes::Object>,
{
self.register_singleton_fn = Some(|| {
crate::classes::Engine::singleton()
.register_singleton(&T::class_id().to_string_name(), &T::new_alloc());
});
self.unregister_singleton_fn = Some(|| {
let singleton = T::singleton();
crate::classes::Engine::singleton()
.unregister_singleton(&T::class_id().to_string_name());
singleton.free();
});
self
}
pub fn with_internal(mut self) -> Self {
self.is_internal = true;
self
}
pub fn with_instantiable(mut self) -> Self {
self.is_instantiable = true;
self
}
}
#[derive(Clone, Debug)]
pub struct InherentImpl {
pub(crate) register_methods_constants_fn: ErasedRegisterFn,
#[cfg_attr(not(feature = "codegen-full"), expect(dead_code))]
pub(crate) register_rpcs_fn: Option<ErasedRegisterRpcsFn>,
}
impl InherentImpl {
pub fn new<T: cap::ImplementsGodotApi>() -> Self {
Self {
register_methods_constants_fn: ErasedRegisterFn {
raw: callbacks::register_user_methods_constants::<T>,
},
register_rpcs_fn: Some(ErasedRegisterRpcsFn {
raw: callbacks::register_user_rpcs::<T>,
}),
}
}
}
#[derive(Default, Clone, Debug)]
pub struct ITraitImpl {
pub(crate) user_register_fn: Option<ErasedRegisterFn>,
pub(crate) user_create_fn: Option<GodotCreateFn>,
pub(crate) user_recreate_fn: Option<
unsafe extern "C" fn(
p_class_userdata: *mut ::std::os::raw::c_void,
p_object: sys::GDExtensionObjectPtr,
) -> sys::GDExtensionClassInstancePtr,
>,
pub(crate) user_to_string_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr,
r_is_valid: *mut sys::GDExtensionBool,
r_out: sys::GDExtensionStringPtr,
),
>,
pub(crate) user_on_notification_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr,
p_what: i32,
p_reversed: sys::GDExtensionBool,
),
>,
pub(crate) user_set_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr,
p_name: sys::GDExtensionConstStringNamePtr,
p_value: sys::GDExtensionConstVariantPtr,
) -> sys::GDExtensionBool,
>,
pub(crate) user_get_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr,
p_name: sys::GDExtensionConstStringNamePtr,
r_ret: sys::GDExtensionVariantPtr,
) -> sys::GDExtensionBool,
>,
pub(crate) get_virtual_fn: Option<GodotGetVirtual>,
pub(crate) user_get_property_list_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr,
r_count: *mut u32,
) -> *const sys::GDExtensionPropertyInfo,
>,
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
pub(crate) user_free_property_list_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr,
p_list: *const sys::GDExtensionPropertyInfo,
),
>,
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
pub(crate) user_free_property_list_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr,
p_list: *const sys::GDExtensionPropertyInfo,
p_count: u32,
),
>,
pub(crate) user_property_can_revert_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr,
p_name: sys::GDExtensionConstStringNamePtr,
) -> sys::GDExtensionBool,
>,
pub(crate) user_property_get_revert_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr,
p_name: sys::GDExtensionConstStringNamePtr,
r_ret: sys::GDExtensionVariantPtr,
) -> sys::GDExtensionBool,
>,
pub(crate) validate_property_fn: Option<
unsafe extern "C" fn(
p_instance: sys::GDExtensionClassInstancePtr,
p_property: *mut sys::GDExtensionPropertyInfo,
) -> sys::GDExtensionBool,
>,
}
impl ITraitImpl {
pub fn new<T: GodotClass + cap::ImplementsGodotVirtual>() -> Self {
Self {
get_virtual_fn: Some(callbacks::get_virtual::<T>),
..Default::default()
}
}
pub fn with_register<T: GodotClass + cap::GodotRegisterClass>(mut self) -> Self {
set(
&mut self.user_register_fn,
ErasedRegisterFn {
raw: callbacks::register_class_by_builder::<T>,
},
);
self
}
pub fn with_create<T: GodotClass + cap::GodotDefault>(mut self) -> Self {
set(&mut self.user_create_fn, callbacks::create::<T>);
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
set(&mut self.user_recreate_fn, callbacks::recreate::<T>);
self
}
pub fn with_string<T: GodotClass + cap::GodotToString>(mut self) -> Self {
set(&mut self.user_to_string_fn, callbacks::to_string::<T>);
self
}
pub fn with_on_notification<T: GodotClass + cap::GodotNotification>(mut self) -> Self {
set(
&mut self.user_on_notification_fn,
callbacks::on_notification::<T>,
);
self
}
pub fn with_get_property<T: GodotClass + cap::GodotGet>(mut self) -> Self {
set(&mut self.user_get_fn, callbacks::get_property::<T>);
self
}
pub fn with_set_property<T: GodotClass + cap::GodotSet>(mut self) -> Self {
set(&mut self.user_set_fn, callbacks::set_property::<T>);
self
}
pub fn with_get_property_list<T: GodotClass + cap::GodotGetPropertyList>(mut self) -> Self {
set(
&mut self.user_get_property_list_fn,
callbacks::get_property_list::<T>,
);
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
set(
&mut self.user_free_property_list_fn,
callbacks::free_property_list::<T>,
);
self
}
pub fn with_property_get_revert<T: GodotClass + cap::GodotPropertyGetRevert>(mut self) -> Self {
set(
&mut self.user_property_get_revert_fn,
callbacks::property_get_revert::<T>,
);
set(
&mut self.user_property_can_revert_fn,
callbacks::property_can_revert::<T>,
);
self
}
pub fn with_validate_property<T: GodotClass + cap::GodotValidateProperty>(mut self) -> Self {
set(
&mut self.validate_property_fn,
callbacks::validate_property::<T>,
);
self
}
}
#[derive(Clone, Debug)]
pub struct DynTraitImpl {
class_name: ClassId,
pub(crate) parent_class_name: Option<ClassId>,
dyn_trait_typeid: any::TypeId,
erased_dynify_fn: ErasedDynifyFn,
}
impl DynTraitImpl {
pub fn new<T, D>() -> Self
where
T: GodotClass
+ Inherits<classes::Object>
+ crate::obj::AsDyn<D>
+ Bounds<Declarer = bounds::DeclUser>,
D: ?Sized + 'static,
{
Self {
class_name: T::class_id(),
parent_class_name: None,
dyn_trait_typeid: std::any::TypeId::of::<D>(),
erased_dynify_fn: callbacks::dynify_fn::<T, D>,
}
}
pub fn class_name(&self) -> &ClassId {
&self.class_name
}
pub fn dyn_trait_typeid(&self) -> any::TypeId {
self.dyn_trait_typeid
}
pub fn get_dyn_gd<T: GodotClass, D: ?Sized + 'static>(
&self,
object: Gd<T>,
) -> Result<DynGd<T, D>, Gd<T>> {
let dynamic_class = object.dynamic_class_string();
if dynamic_class != self.class_name.to_string_name() {
return Err(object);
}
let object = object.upcast_object();
let erased_dyn = unsafe { (self.erased_dynify_fn)(object) };
let dyn_gd_object = erased_dyn.boxed.downcast::<DynGd<classes::Object, D>>();
let dyn_gd_object = unsafe { dyn_gd_object.unwrap_unchecked() };
let dyn_gd_t = unsafe { dyn_gd_object.cast_unchecked::<T>() };
Ok(dyn_gd_t)
}
}