Skip to main content

rusteal_core/
struct_ref.rs

1// UStructRef<T>: lightweight typed wrapper for struct memory pointers.
2//
3// Unlike UObjectRef<T>, struct memory is not garbage-collected by UE.
4// UStructRef is just a typed raw pointer to a struct instance in memory
5// (e.g., inside a UObject property or a parameter buffer).
6
7use std::marker::PhantomData;
8
9use rusteal_ffi::UObjectHandle;
10
11use crate::traits::UeStruct;
12
13/// A typed, non-owning reference to a UE struct instance in memory.
14///
15/// This wraps a raw pointer to struct data (e.g., an FVector stored inside
16/// a UObject property). The struct memory is managed by its container —
17/// no validity check is needed (unlike UObjectRef).
18///
19/// PropertyApi methods accept `UObjectHandle` (`*mut c_void`) which works
20/// for both UObject pointers and raw struct memory pointers.
21pub struct UStructRef<T: UeStruct> {
22    ptr: *mut u8,
23    _marker: PhantomData<T>,
24}
25
26impl<T: UeStruct> UStructRef<T> {
27    /// Create from a raw pointer to struct memory.
28    ///
29    /// # Safety
30    /// The caller must ensure `ptr` points to valid memory containing a `T`.
31    #[inline]
32    pub unsafe fn from_raw(ptr: *mut u8) -> Self {
33        UStructRef {
34            ptr,
35            _marker: PhantomData,
36        }
37    }
38
39    /// Get the raw pointer as a `UObjectHandle`.
40    ///
41    /// PropertyApi methods take `UObjectHandle` which is `*mut c_void` —
42    /// this works for both UObject pointers and raw struct memory.
43    #[inline]
44    pub fn as_ptr(&self) -> UObjectHandle {
45        UObjectHandle(self.ptr as *mut std::ffi::c_void)
46    }
47
48    /// An owned copy of the struct, for the generated functions that take
49    /// `&OwnedStruct<T>` or to keep it past the call it came with.
50    pub fn to_owned(&self) -> crate::containers::OwnedStruct<T> {
51        crate::containers::OwnedStruct::copy_from(self)
52    }
53}
54
55/// Create a `UStructRef<T>` from a native parameter buffer pointer + byte offset.
56///
57/// Used by `#[uclass_impl]` macro for Override function struct parameters.
58///
59/// # Safety
60///
61/// `ptr` must point to a live parameter buffer and `offset` must be the offset
62/// of a `T` inside it, as UE laid the buffer out; the reference must not outlive
63/// the call the buffer belongs to.
64#[inline(always)]
65pub unsafe fn struct_ref_from_param<T: UeStruct>(ptr: *mut u8, offset: usize) -> UStructRef<T> {
66    unsafe { UStructRef::from_raw(ptr.add(offset)) }
67}
68
69impl<T: UeStruct> std::fmt::Debug for UStructRef<T> {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        f.debug_struct("UStructRef").field("ptr", &self.ptr).finish()
72    }
73}
74
75/// A scalar out parameter of an engine event a `#[ufunction(Override)]`
76/// implements (`float& NewCameraFOV`): `set` writes the value the engine reads
77/// back once the call returns. Struct out parameters are `UStructRef`s, written
78/// through their setters the same way.
79pub struct OutRef<T: Copy> {
80    ptr: *mut T,
81}
82
83impl<T: Copy> OutRef<T> {
84    /// The value the caller passed in.
85    #[inline]
86    pub fn get(&self) -> T {
87        unsafe { self.ptr.read_unaligned() }
88    }
89
90    /// The value the caller gets back.
91    #[inline]
92    pub fn set(&self, value: T) {
93        unsafe { self.ptr.write_unaligned(value) }
94    }
95}
96
97/// Create an `OutRef<T>` from a native parameter buffer pointer + byte offset.
98///
99/// Used by `#[uclass_impl]` for Override out parameters.
100///
101/// # Safety
102///
103/// As [`struct_ref_from_param`]: `offset` is a `T` inside the live buffer `ptr`.
104#[inline(always)]
105pub unsafe fn out_ref_from_param<T: Copy>(ptr: *mut u8, offset: usize) -> OutRef<T> {
106    OutRef { ptr: unsafe { ptr.add(offset) } as *mut T }
107}
108
109impl<T: Copy> std::fmt::Debug for OutRef<T> {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        f.debug_struct("OutRef").field("ptr", &self.ptr).finish()
112    }
113}