rusteal_core/
dynamic_call.rs1use rusteal_ffi::{FPropertyHandle, UFunctionHandle, UObjectHandle};
2
3use crate::error::{RustealError, RustealResult, check_ffi};
4use crate::ffi_dispatch::{self, NATIVE_PTR_NULL, NativePtr, native_ptr_is_null};
5use crate::object_ref::UObjectRef;
6use crate::traits::UeClass;
7
8pub struct DynamicCall {
9 obj: UObjectHandle,
10 func: UFunctionHandle,
11 params: NativePtr,
12 delegate: FPropertyHandle,
13}
14
15impl DynamicCall {
16 pub fn new(obj: &UObjectRef<impl UeClass>, func_name: &str) -> RustealResult<Self> {
17 let h = obj.checked()?.raw();
18
19 let func = unsafe {
20 ffi_dispatch::reflection_find_function(h, func_name.as_ptr(), func_name.len() as u32)
21 };
22
23 if func.is_null() {
24 return Err(RustealError::FunctionNotFound(func_name.to_string()));
25 }
26
27 let params = unsafe { ffi_dispatch::reflection_alloc_params(func) };
28
29 Ok(DynamicCall {
30 obj: h,
31 func,
32 params,
33 delegate: FPropertyHandle::null(),
34 })
35 }
36
37 pub fn for_delegate(
38 obj: &UObjectRef<impl UeClass>,
39 delegate: FPropertyHandle,
40 ) -> RustealResult<Self> {
41 let h = obj.checked()?.raw();
42
43 if delegate.is_null() {
44 return Err(RustealError::PropertyNotFound("delegate".to_string()));
45 }
46
47 let func = unsafe { ffi_dispatch::reflection_get_delegate_signature(delegate) };
48
49 if func.is_null() {
50 return Err(RustealError::TypeMismatch);
51 }
52
53 let params = unsafe { ffi_dispatch::reflection_alloc_params(func) };
54
55 Ok(DynamicCall {
56 obj: h,
57 func,
58 params,
59 delegate,
60 })
61 }
62
63 pub fn set<T: Copy>(&mut self, name: &str, value: T) -> RustealResult<()> {
64 let (prop, offset) = self.find_param(name)?;
65 let _ = prop;
66
67 unsafe {
68 ffi_dispatch::native_mem_write(self.params, offset as usize, value);
69 }
70
71 Ok(())
72 }
73
74 pub fn set_struct<T: crate::traits::UeStruct>(
75 &mut self,
76 name: &str,
77 value: &crate::containers::OwnedStruct<T>,
78 ) -> RustealResult<()> {
79 let (_, offset) = self.find_param(name)?;
80
81 check_ffi(unsafe {
82 ffi_dispatch::reflection_copy_struct(
83 T::static_struct(),
84 self.params.add(offset as usize),
85 value.as_bytes().as_ptr(),
86 )
87 })
88 }
89
90 pub fn broadcast(self) -> RustealResult<()> {
91 if self.delegate.is_null() {
92 return Err(RustealError::InvalidOperation("not a delegate call".into()));
93 }
94
95 check_ffi(unsafe {
96 ffi_dispatch::delegate_broadcast_multicast(self.obj, self.delegate, self.params)
97 })
98 }
99
100 pub fn call(mut self) -> RustealResult<DynamicCallResult> {
101 let code =
102 unsafe { ffi_dispatch::reflection_call_function(self.obj, self.func, self.params) };
103
104 check_ffi(code)?;
105
106 let result = DynamicCallResult {
107 func: self.func,
108 params: self.params,
109 };
110
111 self.params = NATIVE_PTR_NULL;
112
113 Ok(result)
114 }
115
116 fn find_param(&self, name: &str) -> RustealResult<(FPropertyHandle, u32)> {
117 let prop = unsafe {
118 ffi_dispatch::reflection_get_function_param(self.func, name.as_ptr(), name.len() as u32)
119 };
120
121 if prop.is_null() {
122 return Err(RustealError::PropertyNotFound(name.to_string()));
123 }
124
125 let offset = unsafe { ffi_dispatch::reflection_get_property_offset(prop) };
126
127 Ok((prop, offset))
128 }
129}
130
131impl Drop for DynamicCall {
132 fn drop(&mut self) {
133 if !native_ptr_is_null(self.params) {
134 unsafe { ffi_dispatch::reflection_free_params(self.func, self.params) };
135 }
136 }
137}
138
139pub struct DynamicCallResult {
140 func: UFunctionHandle,
141 params: NativePtr,
142}
143
144impl DynamicCallResult {
145 pub fn get<T: Copy>(&self, name: &str) -> RustealResult<T> {
146 let prop = unsafe {
147 ffi_dispatch::reflection_get_function_param(self.func, name.as_ptr(), name.len() as u32)
148 };
149
150 if prop.is_null() {
151 return Err(RustealError::PropertyNotFound(name.to_string()));
152 }
153
154 let offset = unsafe { ffi_dispatch::reflection_get_property_offset(prop) };
155
156 let value = unsafe { ffi_dispatch::native_mem_read(self.params, offset as usize) };
157
158 Ok(value)
159 }
160}
161
162impl Drop for DynamicCallResult {
163 fn drop(&mut self) {
164 if !native_ptr_is_null(self.params) {
165 unsafe { ffi_dispatch::reflection_free_params(self.func, self.params) };
166 }
167 }
168}