steamworks/callbacks/
mod.rs

1pub use persona_state_change::*;
2
3use az::WrappingCast;
4use futures::Stream;
5use parking_lot::Mutex;
6use slotmap::DenseSlotMap;
7use std::convert::TryFrom;
8use steamworks_sys as sys;
9
10mod persona_state_change;
11
12pub(crate) type CallbackStorage<T> =
13    Mutex<DenseSlotMap<slotmap::DefaultKey, futures::channel::mpsc::UnboundedSender<T>>>;
14
15pub(crate) unsafe fn dispatch_callbacks(
16    callback_dispatchers: &CallbackDispatchers,
17    callback_msg: sys::CallbackMsg_t,
18) {
19    unsafe {
20        match callback_msg.m_iCallback.wrapping_cast() {
21            sys::PersonaStateChange_t_k_iCallback => callback_dispatchers
22                .persona_state_change
23                .dispatch(callback_msg.m_pubParam, callback_msg.m_cubParam),
24            sys::SteamShutdown_t_k_iCallback => callback_dispatchers
25                .steam_shutdown
26                .dispatch(callback_msg.m_pubParam, callback_msg.m_cubParam),
27            _ => {}
28        }
29    }
30}
31
32pub(crate) fn register_to_receive_callback<T: Clone + Send + 'static>(
33    dispatcher: &impl CallbackDispatcher<MappedCallbackData = T>,
34) -> impl Stream<Item = T> + Send {
35    let (tx, rx) = futures::channel::mpsc::unbounded();
36    dispatcher.storage().lock().insert(tx);
37    rx
38}
39
40#[derive(Debug, Default)]
41pub(crate) struct CallbackDispatchers {
42    pub(crate) persona_state_change: PersonaStateChangeDispatcher,
43    pub(crate) steam_shutdown: SteamShutdownDispatcher,
44}
45
46impl CallbackDispatchers {
47    pub(crate) fn new() -> Self {
48        Self::default()
49    }
50}
51
52pub(crate) trait CallbackDispatcher {
53    type RawCallbackData;
54    type MappedCallbackData: Clone + Send + 'static;
55
56    fn storage(&self) -> &CallbackStorage<Self::MappedCallbackData>;
57    fn map_callback_data(raw: &Self::RawCallbackData) -> Self::MappedCallbackData;
58
59    unsafe fn dispatch(&self, callback_data: *const u8, callback_data_len: i32) {
60        assert!(!callback_data.is_null());
61        assert_eq!(
62            callback_data.align_offset(align_of::<Self::RawCallbackData>()),
63            0
64        );
65        assert_eq!(
66            usize::try_from(callback_data_len).unwrap(),
67            size_of::<Self::RawCallbackData>()
68        );
69
70        let raw = unsafe { &*(callback_data as *const Self::RawCallbackData) };
71        let mapped = Self::map_callback_data(raw);
72
73        let mut storage = self.storage().lock();
74        storage.retain(|_key, tx| match tx.unbounded_send(mapped.clone()) {
75            Err(e) if e.is_disconnected() => false,
76            Err(e) => panic!("{}", e),
77            Ok(()) => true,
78        });
79    }
80}
81
82#[derive(Debug, Default)]
83pub(crate) struct SteamShutdownDispatcher(CallbackStorage<()>);
84
85impl CallbackDispatcher for SteamShutdownDispatcher {
86    type RawCallbackData = sys::SteamShutdown_t;
87    type MappedCallbackData = ();
88
89    fn storage(&self) -> &CallbackStorage<()> {
90        &self.0
91    }
92
93    fn map_callback_data(_raw: &sys::SteamShutdown_t) {}
94}