rusteal_core/
dynamic_call.rs1use std::any::Any;
2
3use rusteal_ffi::{FNameHandle, FPropertyHandle, UFunctionHandle, UObjectHandle};
4
5use crate::error::{RustealError, RustealResult, check_ffi};
6use crate::ffi_dispatch::{self, NATIVE_PTR_NULL, NativePtr, native_ptr_is_null};
7use crate::fname::FName;
8use crate::object_ref::UObjectRef;
9use crate::traits::UeClass;
10
11pub struct DynamicCall {
12 obj: UObjectHandle,
13 func: UFunctionHandle,
14 params: NativePtr,
15 delegate: FPropertyHandle,
16}
17
18impl DynamicCall {
19 #[track_caller]
20 pub fn new(obj: &UObjectRef<impl UeClass>, func_name: &str) -> RustealResult<Self> {
21 Self::on_object(obj.checked()?.raw(), func_name)
22 }
23
24 #[track_caller]
25 pub(crate) fn on_object(h: UObjectHandle, func_name: &str) -> RustealResult<Self> {
26 let func = unsafe {
27 ffi_dispatch::reflection_find_function(h, func_name.as_ptr(), func_name.len() as u32)
28 };
29
30 if func.is_null() {
31 return Err(RustealError::function_not_found(func_name.to_string()).on_handle(h));
32 }
33
34 let params = unsafe { ffi_dispatch::reflection_alloc_params(func) };
35
36 Ok(DynamicCall {
37 obj: h,
38 func,
39 params,
40 delegate: FPropertyHandle::null(),
41 })
42 }
43
44 #[track_caller]
45 pub fn for_delegate(
46 obj: &UObjectRef<impl UeClass>,
47 delegate: FPropertyHandle,
48 ) -> RustealResult<Self> {
49 let h = obj.checked()?.raw();
50
51 if delegate.is_null() {
52 return Err(RustealError::property_not_found("delegate").on_handle(h));
53 }
54
55 let func = unsafe { ffi_dispatch::reflection_get_delegate_signature(delegate) };
56
57 if func.is_null() {
58 return Err(RustealError::type_mismatch()
59 .context("the property is not a delegate")
60 .on_handle(h));
61 }
62
63 let params = unsafe { ffi_dispatch::reflection_alloc_params(func) };
64
65 Ok(DynamicCall {
66 obj: h,
67 func,
68 params,
69 delegate,
70 })
71 }
72
73 #[track_caller]
74 pub fn set<T: Copy + 'static>(&mut self, name: &str, value: T) -> RustealResult<()> {
75 let (prop, offset) = self.find_param(name)?;
76
77 if let Some(handle) = as_fname_handle(&value) {
78 return check_ffi(unsafe {
79 ffi_dispatch::property_set_fname(params_as_container(self.params), prop, handle)
80 });
81 }
82
83 unsafe {
84 ffi_dispatch::native_mem_write(self.params, offset as usize, value);
85 }
86
87 Ok(())
88 }
89
90 #[track_caller]
91 pub fn set_struct<T: crate::traits::UeStruct>(
92 &mut self,
93 name: &str,
94 value: &crate::containers::OwnedStruct<T>,
95 ) -> RustealResult<()> {
96 let (_, offset) = self.find_param(name)?;
97
98 check_ffi(unsafe {
99 ffi_dispatch::reflection_copy_struct(
100 T::static_struct(),
101 self.params.add(offset as usize),
102 value.as_bytes().as_ptr(),
103 )
104 })
105 }
106
107 #[track_caller]
108 pub fn set_struct_ref<T: crate::traits::UeStruct>(
109 &mut self,
110 name: &str,
111 value: &crate::struct_ref::UStructRef<T>,
112 ) -> RustealResult<()> {
113 let (_, offset) = self.find_param(name)?;
114
115 check_ffi(unsafe {
116 ffi_dispatch::reflection_copy_struct(
117 T::static_struct(),
118 self.params.add(offset as usize),
119 value.as_ptr().0 as *const u8,
120 )
121 })
122 }
123
124 #[track_caller]
125 pub fn broadcast(self) -> RustealResult<()> {
126 if self.delegate.is_null() {
127 return Err(RustealError::invalid_operation("not a delegate call"));
128 }
129
130 check_ffi(unsafe {
131 ffi_dispatch::delegate_broadcast_multicast(self.obj, self.delegate, self.params)
132 })
133 }
134
135 #[track_caller]
136 pub fn call(mut self) -> RustealResult<DynamicCallResult> {
137 let code =
138 unsafe { ffi_dispatch::reflection_call_function(self.obj, self.func, self.params) };
139
140 check_ffi(code).map_err(|e| {
141 e.context(format!("calling {}", function_name(self.func)))
142 .on_handle(self.obj)
143 })?;
144
145 let result = DynamicCallResult {
146 func: self.func,
147 params: self.params,
148 };
149
150 self.params = NATIVE_PTR_NULL;
151
152 Ok(result)
153 }
154
155 #[track_caller]
156 fn find_param(&self, name: &str) -> RustealResult<(FPropertyHandle, u32)> {
157 let prop = unsafe {
158 ffi_dispatch::reflection_get_function_param(self.func, name.as_ptr(), name.len() as u32)
159 };
160
161 if prop.is_null() {
162 return Err(missing_param(self.func, name).on_handle(self.obj));
163 }
164
165 let offset = unsafe { ffi_dispatch::reflection_get_property_offset(prop) };
166
167 Ok((prop, offset))
168 }
169}
170
171impl Drop for DynamicCall {
172 fn drop(&mut self) {
173 if !native_ptr_is_null(self.params) {
174 unsafe { ffi_dispatch::reflection_free_params(self.func, self.params) };
175 }
176 }
177}
178
179pub struct DynamicCallResult {
180 func: UFunctionHandle,
181 params: NativePtr,
182}
183
184impl DynamicCallResult {
185 #[track_caller]
186 pub fn get<T: Copy + 'static>(&self, name: &str) -> RustealResult<T> {
187 let prop = unsafe {
188 ffi_dispatch::reflection_get_function_param(self.func, name.as_ptr(), name.len() as u32)
189 };
190
191 if prop.is_null() {
192 return Err(missing_param(self.func, name));
193 }
194
195 if is_fname::<T>() {
196 let mut handle = FNameHandle::default();
197
198 check_ffi(unsafe {
199 ffi_dispatch::property_get_fname(
200 params_as_container(self.params),
201 prop,
202 &mut handle,
203 )
204 })?;
205
206 let name = FName(handle);
207
208 return (&name as &dyn Any)
209 .downcast_ref::<T>()
210 .or_else(|| (&handle as &dyn Any).downcast_ref::<T>())
211 .copied()
212 .ok_or_else(|| RustealError::type_mismatch());
213 }
214
215 let offset = unsafe { ffi_dispatch::reflection_get_property_offset(prop) };
216
217 let value = unsafe { ffi_dispatch::native_mem_read(self.params, offset as usize) };
218
219 Ok(value)
220 }
221}
222
223fn function_name(func: UFunctionHandle) -> String {
224 crate::error::object_name(UObjectHandle(func.0)).unwrap_or_else(|| "a function".to_owned())
225}
226
227#[track_caller]
228fn missing_param(func: UFunctionHandle, name: &str) -> RustealError {
229 RustealError::property_not_found(format!("parameter {name} of {}", function_name(func)))
230}
231
232fn is_fname<T: 'static>() -> bool {
233 let id = std::any::TypeId::of::<T>();
234
235 id == std::any::TypeId::of::<FNameHandle>() || id == std::any::TypeId::of::<FName>()
236}
237
238fn as_fname_handle<T: 'static>(value: &T) -> Option<FNameHandle> {
239 let value = value as &dyn Any;
240
241 value
242 .downcast_ref::<FNameHandle>()
243 .copied()
244 .or_else(|| value.downcast_ref::<FName>().map(FName::handle))
245}
246
247fn params_as_container(params: NativePtr) -> UObjectHandle {
248 UObjectHandle(params as *mut std::ffi::c_void)
249}
250
251impl Drop for DynamicCallResult {
252 fn drop(&mut self) {
253 if !native_ptr_is_null(self.params) {
254 unsafe { ffi_dispatch::reflection_free_params(self.func, self.params) };
255 }
256 }
257}