Skip to main content

songbird/events/
store.rs

1use super::*;
2use crate::{
3    constants::*,
4    tracks::{ReadyState, TrackHandle, TrackState},
5};
6use std::collections::{BinaryHeap, HashMap};
7use tracing::info;
8
9#[derive(Debug, Default)]
10/// Storage for [`EventData`], designed to be used for both local and global contexts.
11///
12/// Timed events are stored in a binary heap for fast selection, and have custom `Eq`,
13/// `Ord`, etc. implementations to support (only) this.
14///
15/// [`EventData`]: EventData
16pub struct EventStore {
17    timed: BinaryHeap<EventData>,
18    untimed: HashMap<UntimedEvent, Vec<EventData>>,
19    local_only: bool,
20}
21
22impl EventStore {
23    /// Creates a new event store to be used globally.
24    #[must_use]
25    pub fn new() -> Self {
26        Self::default()
27    }
28
29    /// Creates a new event store to be used within a [`Track`].
30    ///
31    /// This is usually automatically installed by the driver once
32    /// a track has been registered.
33    ///
34    /// [`Track`]: crate::tracks::Track
35    #[must_use]
36    pub fn new_local() -> Self {
37        EventStore {
38            local_only: true,
39            ..Default::default()
40        }
41    }
42
43    /// Add an event to this store.
44    ///
45    /// Updates `evt` according to [`EventData::compute_activation`].
46    ///
47    /// [`EventData::compute_activation`]: EventData::compute_activation
48    pub fn add_event(&mut self, mut evt: EventData, now: Duration) {
49        evt.compute_activation(now);
50
51        if self.local_only && evt.event.is_global_only() {
52            return;
53        }
54
55        match evt.event {
56            Event::Core(c) => {
57                self.untimed.entry(c.into()).or_default().push(evt);
58            },
59            Event::Track(t) => {
60                self.untimed.entry(t.into()).or_default().push(evt);
61            },
62            Event::Delayed(_) | Event::Periodic(_, _) => {
63                self.timed.push(evt);
64            },
65            _ => {
66                // Event cancelled.
67            },
68        }
69    }
70
71    /// Processes all events due up to and including `now`.
72    pub(crate) async fn process_timed(&mut self, now: Duration, ctx: EventContext<'_>) {
73        while let Some(evt) = self.timed.peek() {
74            if evt
75                .fire_time
76                .as_ref()
77                .expect("Timed event must have a fire_time.")
78                > &now
79            {
80                break;
81            }
82            let mut evt = self
83                .timed
84                .pop()
85                .expect("Can only succeed due to peek = Some(...).");
86
87            let old_evt_type = evt.event;
88            if let Some(new_evt_type) = evt.action.act(&ctx).await {
89                evt.event = new_evt_type;
90                self.add_event(evt, now);
91            } else if let Event::Periodic(d, _) = old_evt_type {
92                evt.event = Event::Periodic(d, None);
93                self.add_event(evt, now);
94            }
95        }
96    }
97
98    /// Processes all events due up to and including `now`.
99    pub(crate) fn timed_event_ready(&self, now: Duration) -> bool {
100        self.timed.peek().is_some_and(|evt| {
101            evt.fire_time
102                .as_ref()
103                .expect("Timed event must have a fire_time.")
104                <= &now
105        })
106    }
107
108    /// Processes all events attached to the given track event.
109    pub(crate) async fn process_untimed(
110        &mut self,
111        now: Duration,
112        untimed_event: UntimedEvent,
113        ctx: EventContext<'_>,
114    ) {
115        // move a Vec in and out: not too expensive, but could be better.
116        // Although it's obvious that moving an event out of one vec and into
117        // another necessitates that they be different event types, thus entries,
118        // convincing the compiler of this is non-trivial without making them dedicated
119        // fields.
120        let events = self.untimed.remove(&untimed_event);
121        if let Some(mut events) = events {
122            // TODO: Possibly use tombstones to prevent realloc/memcpys?
123            // i.e., never shrink array, replace ended tracks with <DEAD>,
124            // maintain a "first-track" stack and freelist alongside.
125            let mut i = 0;
126            while i < events.len() {
127                let evt = &mut events[i];
128                // Only remove/readd if the event type changes (i.e., Some AND new != old)
129                if let Some(new_evt_type) = evt.action.act(&ctx).await {
130                    if evt.event == new_evt_type {
131                        let mut evt = events.remove(i);
132
133                        evt.event = new_evt_type;
134                        self.add_event(evt, now);
135                    } else {
136                        i += 1;
137                    }
138                } else {
139                    i += 1;
140                };
141            }
142            self.untimed.insert(untimed_event, events);
143        }
144    }
145}
146
147#[derive(Debug, Default)]
148pub(crate) struct GlobalEvents {
149    pub(crate) store: EventStore,
150    pub(crate) time: Duration,
151    pub(crate) awaiting_tick: HashMap<TrackEvent, Vec<usize>>,
152}
153
154impl GlobalEvents {
155    pub(crate) fn add_event(&mut self, evt: EventData) {
156        self.store.add_event(evt, self.time);
157    }
158
159    pub(crate) async fn fire_core_event(&mut self, evt: CoreEvent, ctx: EventContext<'_>) {
160        self.store.process_untimed(self.time, evt.into(), ctx).await;
161    }
162
163    pub(crate) fn fire_track_event(&mut self, evt: TrackEvent, index: usize) {
164        let holder = self.awaiting_tick.entry(evt).or_default();
165
166        holder.push(index);
167    }
168
169    pub(crate) fn remove_handlers(&mut self) {
170        self.store = EventStore::new();
171    }
172
173    pub(crate) async fn tick(
174        &mut self,
175        events: &mut [EventStore],
176        states: &mut [TrackState],
177        handles: &mut [TrackHandle],
178    ) {
179        // Global timed events
180        self.time += TIMESTEP_LENGTH;
181        if self.store.timed_event_ready(self.time) {
182            let global_ctx: Vec<(&TrackState, &TrackHandle)> =
183                states.iter().zip(handles.iter()).collect();
184            self.store
185                .process_timed(self.time, EventContext::Track(&global_ctx[..]))
186                .await;
187        }
188
189        // Local timed events
190        for (i, state) in states.iter_mut().enumerate() {
191            if state.playing.is_playing() && state.ready == ReadyState::Playable {
192                state.step_frame();
193
194                let event_store = events
195                    .get_mut(i)
196                    .expect("Missing store index for Tick (local timed).");
197                let handle = handles
198                    .get_mut(i)
199                    .expect("Missing handle index for Tick (local timed).");
200
201                event_store
202                    .process_timed(state.play_time, EventContext::Track(&[(state, handle)]))
203                    .await;
204            }
205        }
206
207        for (evt, indices) in &self.awaiting_tick {
208            let untimed = (*evt).into();
209
210            if !indices.is_empty() {
211                info!("Firing {:?} for {:?}", evt, indices);
212            }
213
214            // Local untimed track events.
215            for &i in indices {
216                let event_store = events
217                    .get_mut(i)
218                    .expect("Missing store index for Tick (local untimed).");
219                let handle = handles
220                    .get_mut(i)
221                    .expect("Missing handle index for Tick (local untimed).");
222                let state = states
223                    .get_mut(i)
224                    .expect("Missing state index for Tick (local untimed).");
225
226                event_store
227                    .process_untimed(
228                        state.position,
229                        untimed,
230                        EventContext::Track(&[(state, handle)]),
231                    )
232                    .await;
233            }
234
235            // Global untimed track events.
236            if self.store.untimed.contains_key(&untimed) && !indices.is_empty() {
237                let global_ctx: Vec<(&TrackState, &TrackHandle)> = indices
238                    .iter()
239                    .map(|i| {
240                        (
241                            states
242                                .get(*i)
243                                .expect("Missing state index for Tick (global untimed)"),
244                            handles
245                                .get(*i)
246                                .expect("Missing handle index for Tick (global untimed)"),
247                        )
248                    })
249                    .collect();
250
251                self.store
252                    .process_untimed(self.time, untimed, EventContext::Track(&global_ctx[..]))
253                    .await;
254            }
255        }
256
257        // Now drain vecs.
258        for indices in self.awaiting_tick.values_mut() {
259            indices.clear();
260        }
261    }
262}