Skip to main content

rusteal_core/
dynamic_call.rs

1use 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}