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