Skip to main content

rusteal_core/
delegate_registry.rs

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