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