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> ObjectPointer for SubclassOf<T> {
93    #[inline]
94    unsafe fn from_object_handle(handle: UObjectHandle) -> Self {
95        unsafe { Self::from_raw(UClassHandle(handle.0)) }
96    }
97
98    #[inline]
99    fn object_handle(&self) -> UObjectHandle {
100        UObjectHandle(self.handle.0)
101    }
102}
103
104impl<T: UeClass> Default for SubclassOf<T> {
105    fn default() -> Self {
106        Self::null()
107    }
108}
109
110impl<T: UeClass> std::fmt::Debug for SubclassOf<T> {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        f.debug_tuple("SubclassOf").field(&self.handle.0).finish()
113    }
114}
115
116// A class is a UObject: in a container it travels as an 8-byte object pointer.
117unsafe impl<T: UeClass> ContainerElement for SubclassOf<T> {
118    const BUF_SIZE: u32 = std::mem::size_of::<UObjectHandle>() as u32;
119
120    #[inline]
121    unsafe fn read_from_buf(buf: *const u8, _written: u32) -> Self {
122        unsafe {
123            let handle = (buf as *const UObjectHandle).read_unaligned();
124            Self::from_raw(UClassHandle(handle.0))
125        }
126    }
127
128    #[inline]
129    unsafe fn write_to_buf(&self, buf: *mut u8) -> u32 {
130        unsafe {
131            (buf as *mut UObjectHandle).write_unaligned(UObjectHandle(self.handle.0));
132            std::mem::size_of::<UObjectHandle>() as u32
133        }
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    struct Probe;
142    impl UeClass for Probe {
143        fn static_class() -> UClassHandle {
144            UClassHandle::from_addr(0x1000)
145        }
146    }
147
148    #[test]
149    fn compares_by_class() {
150        let base = SubclassOf::<Probe>::base();
151        assert_eq!(base, SubclassOf::<Probe>::base());
152        assert_ne!(base, SubclassOf::<Probe>::null());
153        assert!(SubclassOf::<Probe>::default().is_null());
154        assert_eq!(base.raw(), Probe::static_class());
155    }
156
157    struct Child;
158    impl UeClass for Child {
159        fn static_class() -> UClassHandle {
160            UClassHandle::from_addr(0x2000)
161        }
162    }
163    impl HasParent for Child {
164        type Parent = Probe;
165    }
166
167    #[test]
168    fn travels_as_an_object_pointer_and_upcasts() {
169        let child = SubclassOf::<Child>::base();
170        let handle = child.object_handle();
171        assert_eq!(handle.to_addr(), 0x2000);
172        assert_eq!(unsafe { SubclassOf::<Child>::from_object_handle(handle) }, child);
173        let as_parent: SubclassOf<Probe> = child.upcast();
174        assert_eq!(as_parent.raw(), Child::static_class());
175    }
176
177    #[test]
178    fn round_trips_through_a_container_buffer() {
179        let base = SubclassOf::<Probe>::base();
180        let mut buf = [0u8; 8];
181        unsafe {
182            assert_eq!(base.write_to_buf(buf.as_mut_ptr()), 8);
183            assert_eq!(SubclassOf::<Probe>::read_from_buf(buf.as_ptr(), 8), base);
184        }
185    }
186}