use std::ptr::NonNull;
use crate::core_types::{
FromVariant, FromVariantError, GodotString, OwnedToVariant, ToVariant, Variant,
};
use crate::nativescript::init::ClassBuilder;
use crate::nativescript::Map;
use crate::nativescript::MapMut;
use crate::nativescript::UserData;
use crate::object::{
AssumeSafeLifetime, LifetimeConstraint, QueueFree, RawObject, Ref, RefImplBound, SafeAsRaw,
SafeDeref, TRef,
};
use crate::object::{GodotObject, Instanciable};
use crate::private::{get_api, ReferenceCountedClassPlaceholder};
use crate::ref_kind::{ManuallyManaged, RefCounted};
use crate::thread_access::{NonUniqueThreadAccess, Shared, ThreadAccess, ThreadLocal, Unique};
pub trait NativeClass: Sized + 'static {
type Base: GodotObject;
type UserData: UserData<Target = Self>;
fn class_name() -> &'static str;
fn init(owner: TRef<'_, Self::Base, Shared>) -> Self;
#[inline]
fn register_properties(_builder: &ClassBuilder<Self>) {}
#[inline]
fn new_instance() -> Instance<Self, Unique>
where
Self::Base: Instanciable,
{
Instance::new()
}
}
pub trait NativeClassMethods: NativeClass {
fn register(builder: &ClassBuilder<Self>);
}
pub trait OwnerArg<'a, T: GodotObject, Access: ThreadAccess + 'static>: private::Sealed {
#[doc(hidden)]
fn from_safe_ref(owner: TRef<'a, T, Access>) -> Self;
}
impl<'a, T> private::Sealed for &'a T where T: GodotObject {}
impl<'a, T, Access> OwnerArg<'a, T, Access> for &'a T
where
T: GodotObject,
Access: ThreadAccess + 'static,
{
#[inline]
fn from_safe_ref(owner: TRef<'a, T, Access>) -> Self {
owner.as_ref()
}
}
impl<'a, T, Access> private::Sealed for TRef<'a, T, Access>
where
T: GodotObject,
Access: ThreadAccess + 'static,
{
}
impl<'a, T, Access> OwnerArg<'a, T, Access> for TRef<'a, T, Access>
where
T: GodotObject,
Access: ThreadAccess + 'static,
{
#[inline]
fn from_safe_ref(owner: TRef<'a, T, Access>) -> Self {
owner
}
}
#[derive(Debug)]
pub struct Instance<T: NativeClass, Access: ThreadAccess> {
owner: Ref<T::Base, Access>,
script: T::UserData,
}
#[derive(Debug)]
pub struct RefInstance<'a, T: NativeClass, Access: ThreadAccess> {
owner: TRef<'a, T::Base, Access>,
script: T::UserData,
}
impl<T: NativeClass> Instance<T, Unique> {
#[inline]
#[allow(clippy::new_without_default)]
pub fn new() -> Self
where
T::Base: Instanciable,
{
unsafe {
let gd_api = get_api();
let nativescript_methods = crate::private::NativeScriptMethodTable::get(gd_api);
assert_ne!(
std::ptr::null(),
nativescript_methods.set_class_name,
"NativeScript::set_class_name must be available"
);
assert_ne!(
std::ptr::null(),
nativescript_methods.set_library,
"NativeScript::set_library must be available"
);
assert_ne!(
std::ptr::null(),
nativescript_methods.new,
"NativeScript::new must be available"
);
let class_name = b"NativeScript\0".as_ptr() as *const libc::c_char;
let ctor = (gd_api.godot_get_class_constructor)(class_name).unwrap();
let native_script =
NonNull::new(ctor()).expect("NativeScript constructor should not return null");
let native_script =
RawObject::<ReferenceCountedClassPlaceholder>::from_sys_ref_unchecked(
native_script,
);
native_script.init_ref_count();
let script_class_name = GodotString::from(T::class_name());
let mut args: [*const libc::c_void; 1] = [script_class_name.sys() as *const _];
(gd_api.godot_method_bind_ptrcall)(
nativescript_methods.set_class_name,
native_script.sys().as_ptr(),
args.as_mut_ptr(),
std::ptr::null_mut(),
);
let mut args: [*const libc::c_void; 1] = [crate::private::get_gdnative_library_sys()];
(gd_api.godot_method_bind_ptrcall)(
nativescript_methods.set_library,
native_script.sys().as_ptr(),
args.as_mut_ptr(),
std::ptr::null_mut(),
);
let mut args: [*const sys::godot_variant; 0] = [];
let variant = (gd_api.godot_method_bind_call)(
nativescript_methods.new,
native_script.sys().as_ptr(),
args.as_mut_ptr(),
0,
std::ptr::null_mut(),
);
let variant = Variant::from_sys(variant);
let owner = variant
.try_to_object::<T::Base>()
.expect("base object should be of the correct type (is the script registered?)")
.assume_unique();
let script_ptr =
(gd_api.godot_nativescript_get_userdata)(owner.sys()) as *const libc::c_void;
assert_ne!(
std::ptr::null(),
script_ptr,
"script instance should not be null (did the constructor fail?)"
);
let script = T::UserData::clone_from_user_data_unchecked(script_ptr);
native_script.unref();
Instance { owner, script }
}
}
}
impl<T: NativeClass, Access: ThreadAccess> Instance<T, Access> {
#[inline]
pub fn into_base(self) -> Ref<T::Base, Access> {
self.owner
}
#[inline]
pub fn into_script(self) -> T::UserData {
self.script
}
#[inline]
pub fn decouple(self) -> (Ref<T::Base, Access>, T::UserData) {
(self.owner, self.script)
}
#[inline]
pub fn base(&self) -> &Ref<T::Base, Access> {
&self.owner
}
#[inline]
pub fn script(&self) -> &T::UserData {
&self.script
}
}
impl<T: NativeClass, Access: ThreadAccess> Instance<T, Access>
where
RefImplBound: SafeAsRaw<<T::Base as GodotObject>::RefKind, Access>,
{
#[inline]
pub fn try_from_base(owner: Ref<T::Base, Access>) -> Result<Self, Ref<T::Base, Access>> {
let user_data = match try_get_user_data_ptr::<T>(owner.as_raw()) {
Some(user_data) => user_data,
None => return Err(owner),
};
let script = unsafe { T::UserData::clone_from_user_data_unchecked(user_data) };
Ok(Instance { owner, script })
}
#[inline]
pub fn from_base(owner: Ref<T::Base, Access>) -> Option<Self> {
Self::try_from_base(owner).ok()
}
}
impl<T: NativeClass, Access: ThreadAccess> Instance<T, Access>
where
RefImplBound: SafeDeref<<T::Base as GodotObject>::RefKind, Access>,
{
#[inline]
pub fn map<F, U>(&self, op: F) -> Result<U, <T::UserData as Map>::Err>
where
T::UserData: Map,
F: FnOnce(&T, TRef<'_, T::Base, Access>) -> U,
{
self.script.map(|script| op(script, self.owner.as_ref()))
}
#[inline]
pub fn map_mut<F, U>(&self, op: F) -> Result<U, <T::UserData as MapMut>::Err>
where
T::UserData: MapMut,
F: FnOnce(&mut T, TRef<'_, T::Base, Access>) -> U,
{
self.script
.map_mut(|script| op(script, self.owner.as_ref()))
}
}
impl<T: NativeClass> Instance<T, Shared> {
#[inline]
pub unsafe fn assume_safe<'a, 'r>(&'r self) -> RefInstance<'a, T, Shared>
where
AssumeSafeLifetime<'a, 'r>: LifetimeConstraint<<T::Base as GodotObject>::RefKind>,
{
RefInstance {
owner: self.owner.assume_safe(),
script: self.script.clone(),
}
}
}
impl<T: NativeClass> Instance<T, Shared>
where
T::Base: GodotObject<RefKind = ManuallyManaged>,
{
#[inline]
#[allow(clippy::trivially_copy_pass_by_ref)]
pub unsafe fn is_instance_sane(&self) -> bool {
self.owner.is_instance_sane()
}
}
impl<T: NativeClass> Instance<T, Unique>
where
T::Base: GodotObject<RefKind = ManuallyManaged>,
{
#[inline]
pub fn free(self) {
self.into_base().free()
}
}
impl<T: NativeClass> Instance<T, Unique>
where
T::Base: GodotObject<RefKind = ManuallyManaged> + QueueFree,
{
#[inline]
pub fn queue_free(self) {
self.into_base().queue_free()
}
}
impl<T: NativeClass> Instance<T, Unique> {
#[inline]
pub fn into_shared(self) -> Instance<T, Shared> {
Instance {
owner: self.owner.into_shared(),
script: self.script,
}
}
}
impl<T: NativeClass> Instance<T, Unique>
where
T::Base: GodotObject<RefKind = RefCounted>,
{
#[inline]
pub fn into_thread_local(self) -> Instance<T, ThreadLocal> {
Instance {
owner: self.owner.into_thread_local(),
script: self.script,
}
}
}
impl<T: NativeClass> Instance<T, Shared> {
#[inline]
pub unsafe fn assume_unique(self) -> Instance<T, Unique> {
Instance {
owner: self.owner.assume_unique(),
script: self.script,
}
}
}
impl<T: NativeClass> Instance<T, Shared>
where
T::Base: GodotObject<RefKind = RefCounted>,
{
#[inline]
pub unsafe fn assume_thread_local(self) -> Instance<T, ThreadLocal> {
Instance {
owner: self.owner.assume_thread_local(),
script: self.script,
}
}
}
impl<'a, T: NativeClass, Access: ThreadAccess> RefInstance<'a, T, Access> {
#[inline]
pub fn base(&self) -> TRef<'a, T::Base, Access> {
self.owner
}
#[inline]
pub fn script(&self) -> &T::UserData {
&self.script
}
#[inline]
pub fn try_from_base(owner: TRef<'a, T::Base, Access>) -> Option<Self> {
let user_data = try_get_user_data_ptr::<T>(owner.as_raw())?;
unsafe { Some(Self::from_raw_unchecked(owner, user_data)) }
}
#[doc(hidden)]
#[inline]
pub unsafe fn from_raw_unchecked(
owner: TRef<'a, T::Base, Access>,
user_data: *mut libc::c_void,
) -> Self {
let script = T::UserData::clone_from_user_data_unchecked(user_data);
RefInstance { owner, script }
}
}
impl<'a, T: NativeClass, Access: NonUniqueThreadAccess> RefInstance<'a, T, Access> {
#[inline]
pub fn claim(self) -> Instance<T, Access> {
Instance {
owner: self.owner.claim(),
script: self.script,
}
}
}
impl<'a, T: NativeClass, Access: ThreadAccess> RefInstance<'a, T, Access>
where
T::Base: GodotObject,
{
#[inline]
pub fn map<F, U>(&self, op: F) -> Result<U, <T::UserData as Map>::Err>
where
T::UserData: Map,
F: FnOnce(&T, TRef<'_, T::Base, Access>) -> U,
{
self.script.map(|script| op(script, self.owner))
}
#[inline]
pub fn map_mut<F, U>(&self, op: F) -> Result<U, <T::UserData as MapMut>::Err>
where
T::UserData: MapMut,
F: FnOnce(&mut T, TRef<'_, T::Base, Access>) -> U,
{
self.script.map_mut(|script| op(script, self.owner))
}
}
impl<T, Access: ThreadAccess> Clone for Instance<T, Access>
where
T: NativeClass,
Ref<T::Base, Access>: Clone,
{
#[inline]
fn clone(&self) -> Self {
Instance {
owner: self.owner.clone(),
script: self.script.clone(),
}
}
}
impl<'a, T, Access: ThreadAccess> Clone for RefInstance<'a, T, Access>
where
T: NativeClass,
{
#[inline]
fn clone(&self) -> Self {
RefInstance {
owner: self.owner,
script: self.script.clone(),
}
}
}
impl<T, Access: ThreadAccess> ToVariant for Instance<T, Access>
where
T: NativeClass,
Ref<T::Base, Access>: ToVariant,
{
#[inline]
fn to_variant(&self) -> Variant {
self.owner.to_variant()
}
}
impl<T> OwnedToVariant for Instance<T, Unique>
where
T: NativeClass,
{
#[inline]
fn owned_to_variant(self) -> Variant {
self.into_base().owned_to_variant()
}
}
impl<T> FromVariant for Instance<T, Shared>
where
T: NativeClass,
T::Base: GodotObject<RefKind = RefCounted>,
{
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
let owner = Ref::<T::Base, Shared>::from_variant(variant)?;
Self::from_base(owner).ok_or(FromVariantError::InvalidInstance {
expected: T::class_name(),
})
}
}
fn try_get_user_data_ptr<T: NativeClass>(owner: &RawObject<T::Base>) -> Option<*mut libc::c_void> {
unsafe {
let api = get_api();
let owner_ptr = owner.sys().as_ptr();
let type_tag = (api.godot_nativescript_get_type_tag)(owner_ptr);
if type_tag.is_null() {
return None;
}
if !crate::nativescript::type_tag::check::<T>(type_tag) {
return None;
}
Some((api.godot_nativescript_get_userdata)(owner_ptr))
}
}
mod private {
pub trait Sealed {}
}