Skip to main content

rusteal_core/
subclass_of.rs

1use std::marker::PhantomData;
2
3use rusteal_ffi::{UClassHandle, UObjectHandle};
4
5use crate::containers::ContainerElement;
6use crate::object_ref::ObjectPointer;
7use crate::traits::{HasParent, UeClass};
8
9#[repr(transparent)]
10pub struct SubclassOf<T: UeClass> {
11    handle: UClassHandle,
12    _marker: PhantomData<*const T>,
13}
14
15impl<T: UeClass> Clone for SubclassOf<T> {
16    fn clone(&self) -> Self {
17        *self
18    }
19}
20
21impl<T: UeClass> Copy for SubclassOf<T> {}
22
23impl<T: UeClass> PartialEq for SubclassOf<T> {
24    fn eq(&self, other: &Self) -> bool {
25        self.handle == other.handle
26    }
27}
28
29impl<T: UeClass> Eq for SubclassOf<T> {}
30
31impl<T: UeClass> std::hash::Hash for SubclassOf<T> {
32    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
33        self.handle.hash(state);
34    }
35}
36
37unsafe impl<T: UeClass> Send for SubclassOf<T> {}
38
39impl<T: UeClass> SubclassOf<T> {
40    pub fn null() -> Self {
41        Self {
42            handle: UClassHandle::null(),
43            _marker: PhantomData,
44        }
45    }
46
47    pub fn base() -> Self {
48        Self {
49            handle: T::static_class(),
50            _marker: PhantomData,
51        }
52    }
53
54    pub unsafe fn from_raw(handle: UClassHandle) -> Self {
55        Self {
56            handle,
57            _marker: PhantomData,
58        }
59    }
60
61    pub fn raw(&self) -> UClassHandle {
62        self.handle
63    }
64
65    pub fn is_null(&self) -> bool {
66        self.handle.is_null()
67    }
68}
69
70impl<T: HasParent> SubclassOf<T> {
71    #[inline]
72    pub fn upcast(self) -> SubclassOf<T::Parent> {
73        unsafe { SubclassOf::from_raw(self.handle) }
74    }
75}
76
77impl<T: UeClass> SubclassOf<T> {
78    #[inline]
79    pub fn upcast_to<U: UeClass>(self) -> SubclassOf<U>
80    where
81        T: crate::traits::Inherits<U>,
82    {
83        unsafe { SubclassOf::from_raw(self.handle) }
84    }
85}
86
87impl<T: UeClass> ObjectPointer for SubclassOf<T> {
88    #[inline]
89    unsafe fn from_object_handle(handle: UObjectHandle) -> Self {
90        unsafe { Self::from_raw(UClassHandle(handle.0)) }
91    }
92
93    #[inline]
94    fn object_handle(&self) -> UObjectHandle {
95        UObjectHandle(self.handle.0)
96    }
97}
98
99impl<T: UeClass> Default for SubclassOf<T> {
100    fn default() -> Self {
101        Self::null()
102    }
103}
104
105impl<T: UeClass> std::fmt::Debug for SubclassOf<T> {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        f.debug_tuple("SubclassOf").field(&self.handle.0).finish()
108    }
109}
110
111unsafe impl<T: UeClass> ContainerElement for SubclassOf<T> {
112    const BUF_SIZE: u32 = std::mem::size_of::<UObjectHandle>() as u32;
113
114    #[inline]
115    unsafe fn read_from_buf(buf: *const u8, _written: u32) -> Self {
116        unsafe {
117            let handle = (buf as *const UObjectHandle).read_unaligned();
118
119            Self::from_raw(UClassHandle(handle.0))
120        }
121    }
122
123    #[inline]
124    unsafe fn write_to_buf(&self, buf: *mut u8) -> u32 {
125        unsafe {
126            (buf as *mut UObjectHandle).write_unaligned(UObjectHandle(self.handle.0));
127
128            std::mem::size_of::<UObjectHandle>() as u32
129        }
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    struct Probe;
138    impl UeClass for Probe {
139        fn static_class() -> UClassHandle {
140            UClassHandle::from_addr(0x1000)
141        }
142    }
143
144    #[test]
145    fn compares_by_class() {
146        let base = SubclassOf::<Probe>::base();
147        assert_eq!(base, SubclassOf::<Probe>::base());
148        assert_ne!(base, SubclassOf::<Probe>::null());
149        assert!(SubclassOf::<Probe>::default().is_null());
150        assert_eq!(base.raw(), Probe::static_class());
151    }
152
153    struct Child;
154    impl UeClass for Child {
155        fn static_class() -> UClassHandle {
156            UClassHandle::from_addr(0x2000)
157        }
158    }
159    impl HasParent for Child {
160        type Parent = Probe;
161    }
162
163    #[test]
164    fn travels_as_an_object_pointer_and_upcasts() {
165        let child = SubclassOf::<Child>::base();
166        let handle = child.object_handle();
167        assert_eq!(handle.to_addr(), 0x2000);
168
169        assert_eq!(
170            unsafe { SubclassOf::<Child>::from_object_handle(handle) },
171            child
172        );
173
174        let as_parent: SubclassOf<Probe> = child.upcast();
175        assert_eq!(as_parent.raw(), Child::static_class());
176    }
177
178    #[test]
179    fn round_trips_through_a_container_buffer() {
180        let base = SubclassOf::<Probe>::base();
181        let mut buf = [0u8; 8];
182
183        unsafe {
184            assert_eq!(base.write_to_buf(buf.as_mut_ptr()), 8);
185            assert_eq!(SubclassOf::<Probe>::read_from_buf(buf.as_ptr(), 8), base);
186        }
187    }
188}