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