rusteal_core/
subclass_of.rs1use 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#[repr(transparent)]
15pub struct SubclassOf<T: UeClass> {
16 handle: UClassHandle,
17 _marker: PhantomData<*const T>,
18}
19
20impl<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
43unsafe impl<T: UeClass> Send for SubclassOf<T> {}
45
46impl<T: UeClass> SubclassOf<T> {
47 pub fn null() -> Self {
49 Self {
50 handle: UClassHandle::null(),
51 _marker: PhantomData,
52 }
53 }
54
55 pub fn base() -> Self {
57 Self {
58 handle: T::static_class(),
59 _marker: PhantomData,
60 }
61 }
62
63 pub unsafe fn from_raw(handle: UClassHandle) -> Self {
68 Self {
69 handle,
70 _marker: PhantomData,
71 }
72 }
73
74 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 #[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
116unsafe 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}