Skip to main content

rusteal_core/
dynamic_call.rs

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