rusteal_core/
delegate_registry.rs1use std::collections::HashMap;
2use std::sync::atomic::{AtomicU64, Ordering};
3use std::sync::{Mutex, OnceLock};
4
5use crate::lock_or_recover;
6
7use rusteal_ffi::{FPropertyHandle, RustealErrorCode, UObjectHandle};
8
9use crate::error::{RustealResult, check_ffi};
10use crate::ffi_dispatch::NativePtr;
11
12type DelegateCallback = Option<Box<dyn FnMut(NativePtr) + Send>>;
13
14static NEXT_ID: AtomicU64 = AtomicU64::new(1);
15static REGISTRY: OnceLock<Mutex<HashMap<u64, DelegateCallback>>> = OnceLock::new();
16
17fn registry() -> &'static Mutex<HashMap<u64, DelegateCallback>> {
18 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
19}
20
21pub fn register_callback(f: impl FnMut(NativePtr) + Send + 'static) -> u64 {
22 let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
23 lock_or_recover(registry()).insert(id, Some(Box::new(f)));
24 id
25}
26
27pub fn unregister_callback(id: u64) {
28 lock_or_recover(registry()).remove(&id);
29}
30
31pub fn clear_all() {
32 if let Some(reg) = REGISTRY.get() {
33 lock_or_recover(reg).clear();
34 }
35
36 NEXT_ID.store(1, Ordering::Relaxed);
37}
38
39pub fn invoke(callback_id: u64, params: NativePtr) {
40 let mut cb = {
41 let mut reg = lock_or_recover(registry());
42 reg.get_mut(&callback_id).and_then(|slot| slot.take())
43 };
44
45 if let Some(ref mut f) = cb {
46 f(params);
47 }
48
49 if let Some(f) = cb {
50 let mut reg = lock_or_recover(registry());
51
52 if let Some(slot) = reg.get_mut(&callback_id)
53 && slot.is_none()
54 {
55 *slot = Some(f);
56 }
57 }
58}
59
60pub struct DelegateBinding {
61 callback_id: u64,
62 owner: UObjectHandle,
63 prop: FPropertyHandle,
64 is_multicast: bool,
65}
66
67impl DelegateBinding {
68 pub fn new(
69 callback_id: u64,
70 owner: UObjectHandle,
71 prop: FPropertyHandle,
72 is_multicast: bool,
73 ) -> Self {
74 Self {
75 callback_id,
76 owner,
77 prop,
78 is_multicast,
79 }
80 }
81
82 pub fn callback_id(&self) -> u64 {
83 self.callback_id
84 }
85
86 pub fn unbind(self) {}
87
88 pub fn detach(self) {
89 std::mem::forget(self);
90 }
91}
92
93impl Drop for DelegateBinding {
94 fn drop(&mut self) {
95 unregister_callback(self.callback_id);
96
97 if crate::api::is_api_initialized() {
98 unsafe {
99 if self.is_multicast {
100 let _ = crate::ffi_dispatch::delegate_remove_multicast(
101 self.owner,
102 self.prop,
103 self.callback_id,
104 );
105 } else {
106 let _ = crate::ffi_dispatch::delegate_unbind_delegate(self.owner, self.prop);
107 }
108 }
109 }
110 }
111}
112
113pub fn bind_unicast(
114 owner: UObjectHandle,
115 prop: FPropertyHandle,
116 callback: impl FnMut(NativePtr) + Send + 'static,
117) -> RustealResult<DelegateBinding> {
118 let id = register_callback(callback);
119 let result = unsafe { crate::ffi_dispatch::delegate_bind_delegate(owner, prop, id) };
120
121 if result != RustealErrorCode::Ok {
122 unregister_callback(id);
123 check_ffi(result)?;
124 }
125
126 Ok(DelegateBinding::new(id, owner, prop, false))
127}
128
129pub fn bind_multicast(
130 owner: UObjectHandle,
131 prop: FPropertyHandle,
132 callback: impl FnMut(NativePtr) + Send + 'static,
133) -> RustealResult<DelegateBinding> {
134 let id = register_callback(callback);
135 let result = unsafe { crate::ffi_dispatch::delegate_add_multicast(owner, prop, id) };
136
137 if result != RustealErrorCode::Ok {
138 unregister_callback(id);
139 check_ffi(result)?;
140 }
141
142 Ok(DelegateBinding::new(id, owner, prop, true))
143}