Skip to main content

rusteal_core/
subclass_of.rs

1// SubclassOf<T>: a class reference restricted to `T` and its subclasses, as
2// UE's `TSubclassOf<T>`. The property that holds it only accepts such classes,
3// so a value read from it is `T` or a subclass (or null).
4
5use std::marker::PhantomData;
6
7use rusteal_ffi::{UClassHandle, UObjectHandle};
8
9use crate::containers::ContainerElement;
10use crate::object_ref::ObjectPointer;
11use crate::traits::{HasParent, UeClass};
12
13/// A reference to `T`'s class or one of its subclasses; null when unset.
14#[repr(transparent)]
15pub struct SubclassOf<T: UeClass> {
16    handle: UClassHandle,
17    _marker: PhantomData<*const T>,
18}
19
20// By hand: derives would require the same traits of `T`, which classes lack.
21impl<T: UeClass> Clone for SubclassOf<T> {
22    fn clone(&self) -> Self {
23        *self
24    }
25}
26
27impl<T: UeClass> Copy for SubclassOf<T> {}
28
29impl<T: UeClass> PartialEq for SubclassOf<T> {
30    fn eq(&self, other: &Self) -> bool {
31        self.handle == other.handle
32    }
33}
34
35impl<T: UeClass> Eq for SubclassOf<T> {}
36
37impl<T: UeClass> std::hash::Hash for SubclassOf<T> {
38    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
39        self.handle.hash(state);
40    }
41}
42
43// Send: a class handle is a raw identifier; !Sync as UObjectRef.
44unsafe impl<T: UeClass> Send for SubclassOf<T> {}
45
46impl<T: UeClass> SubclassOf<T> {
47    /// No class.
48    pub fn null() -> Self {
49        Self {
50            handle: UClassHandle::null(),
51            _marker: PhantomData,
52        }
53    }
54
55    /// `T`'s own class.
56    pub fn base() -> Self {
57        Self {
58            handle: T::static_class(),
59            _marker: PhantomData,
60        }
61    }
62
63    /// Wrap a raw class handle.
64    ///
65    /// # Safety
66    /// The handle must be null or a UClass that is `T` or a subclass of `T`.
67    pub unsafe fn from_raw(handle: UClassHandle) -> Self {
68        Self {
69            handle,
70            _marker: PhantomData,
71        }
72    }
73
74    /// The underlying class handle.
75    pub fn raw(&self) -> UClassHandle {
76        self.handle
77    }
78
79    pub fn is_null(&self) -> bool {
80        self.handle.is_null()
81    }
82}
83
84impl<T: HasParent> SubclassOf<T> {
85    /// The same class as a subclass of `T`'s parent. Zero-cost.
86    #[inline]
87    pub fn upcast(self) -> SubclassOf<T::Parent> {
88        unsafe { SubclassOf::from_raw(self.handle) }
89    }
90}
91
92impl<T: UeClass> SubclassOf<T> {
93    /// The same class as a subclass of any ancestor of `T`, checked at
94    /// compile time (`SubclassOf::<PlayerStart>::base().upcast_to::<Actor>()`).
95    #[inline]
96    pub fn upcast_to<U: UeClass>(self) -> SubclassOf<U>
97    where
98        T: crate::traits::Inherits<U>,
99    {
100        unsafe { SubclassOf::from_raw(self.handle) }
101    }
102}
103
104impl<T: UeClass> ObjectPointer for SubclassOf<T> {
105    #[inline]
106    unsafe fn from_object_handle(handle: UObjectHandle) -> Self {
107        unsafe { Self::from_raw(UClassHandle(handle.0)) }
108    }
109
110    #[inline]
111    fn object_handle(&self) -> UObjectHandle {
112        UObjectHandle(self.handle.0)
113    }
114}
115
116impl<T: UeClass> Default for SubclassOf<T> {
117    fn default() -> Self {
118        Self::null()
119    }
120}
121
122impl<T: UeClass> std::fmt::Debug for SubclassOf<T> {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        f.debug_tuple("SubclassOf").field(&self.handle.0).finish()
125    }
126}
127
128// A class is a UObject: in a container it travels as an 8-byte object pointer.
129unsafe impl<T: UeClass> ContainerElement for SubclassOf<T> {
130    const BUF_SIZE: u32 = std::mem::size_of::<UObjectHandle>() as u32;
131
132    #[inline]
133    unsafe fn read_from_buf(buf: *const u8, _written: u32) -> Self {
134        unsafe {
135            let handle = (buf as *const UObjectHandle).read_unaligned();
136            Self::from_raw(UClassHandle(handle.0))
137        }
138    }
139
140    #[inline]
141    unsafe fn write_to_buf(&self, buf: *mut u8) -> u32 {
142        unsafe {
143            (buf as *mut UObjectHandle).write_unaligned(UObjectHandle(self.handle.0));
144            std::mem::size_of::<UObjectHandle>() as u32
145        }
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    struct Probe;
154    impl UeClass for Probe {
155        fn static_class() -> UClassHandle {
156            UClassHandle::from_addr(0x1000)
157        }
158    }
159
160    #[test]
161    fn compares_by_class() {
162        let base = SubclassOf::<Probe>::base();
163        assert_eq!(base, SubclassOf::<Probe>::base());
164        assert_ne!(base, SubclassOf::<Probe>::null());
165        assert!(SubclassOf::<Probe>::default().is_null());
166        assert_eq!(base.raw(), Probe::static_class());
167    }
168
169    struct Child;
170    impl UeClass for Child {
171        fn static_class() -> UClassHandle {
172            UClassHandle::from_addr(0x2000)
173        }
174    }
175    impl HasParent for Child {
176        type Parent = Probe;
177    }
178
179    #[test]
180    fn travels_as_an_object_pointer_and_upcasts() {
181        let child = SubclassOf::<Child>::base();
182        let handle = child.object_handle();
183        assert_eq!(handle.to_addr(), 0x2000);
184        assert_eq!(unsafe { SubclassOf::<Child>::from_object_handle(handle) }, child);
185        let as_parent: SubclassOf<Probe> = child.upcast();
186        assert_eq!(as_parent.raw(), Child::static_class());
187    }
188
189    #[test]
190    fn round_trips_through_a_container_buffer() {
191        let base = SubclassOf::<Probe>::base();
192        let mut buf = [0u8; 8];
193        unsafe {
194            assert_eq!(base.write_to_buf(buf.as_mut_ptr()), 8);
195            assert_eq!(SubclassOf::<Probe>::read_from_buf(buf.as_ptr(), 8), base);
196        }
197    }
198}