Skip to main content

galeon_engine/
event.rs

1// SPDX-License-Identifier: AGPL-3.0-only OR Commercial
2
3use std::any::TypeId;
4
5use crate::system_param::{Access, SystemParam};
6use crate::world::UnsafeWorldCell;
7
8// =============================================================================
9// Events<T> — double-buffered typed event queue
10// =============================================================================
11
12/// Double-buffered typed event queue.
13///
14/// Events are written to `current` during the tick they are sent. At the start
15/// of the next `Schedule::run()`, `World::update_events()` swaps the buffers:
16/// `current` becomes `previous` and the old `previous` is cleared. Systems
17/// that use `EventReader<T>` iterate over `previous`, so they always read
18/// events from the *previous* tick.
19///
20/// ```text
21/// Tick N:   EventWriter sends → current
22/// tick N+1: update_events() → current becomes previous, cleared current
23///           EventReader reads previous (events from tick N)
24/// Tick N+2: update_events() → previous is cleared
25/// ```
26///
27/// Register an event type with [`World::add_event::<T>()`] before using
28/// `EventWriter<T>` or `EventReader<T>` in systems.
29pub struct Events<T: 'static> {
30    /// Events written during the previous tick — readable by `EventReader`.
31    previous: Vec<T>,
32    /// Events being written this tick — by `EventWriter`.
33    current: Vec<T>,
34}
35
36impl<T: 'static> Events<T> {
37    /// Create an empty double buffer.
38    pub fn new() -> Self {
39        Self {
40            previous: Vec::new(),
41            current: Vec::new(),
42        }
43    }
44
45    /// Send an event. It will be readable by `EventReader` on the next tick.
46    pub fn send(&mut self, event: T) {
47        self.current.push(event);
48    }
49
50    /// Iterate over events sent during the previous tick.
51    pub fn read(&self) -> impl Iterator<Item = &T> {
52        self.previous.iter()
53    }
54
55    /// Iterate over events in the current (not yet swapped) buffer.
56    ///
57    /// For render extraction that runs *after* a tick completes: events
58    /// sent during tick N live in `current` until the next `update()`.
59    pub fn read_current(&self) -> impl Iterator<Item = &T> {
60        self.current.iter()
61    }
62
63    /// Number of events in the current (not yet swapped) buffer.
64    ///
65    /// Used by render event extractors with offset tracking to avoid
66    /// re-reading events that were already captured in a prior flush.
67    pub fn current_len(&self) -> usize {
68        self.current.len()
69    }
70
71    /// Advance the double buffer.
72    ///
73    /// The previous buffer is cleared. The current buffer becomes the new
74    /// previous buffer. Called automatically by `World::update_events()` at
75    /// the start of each `Schedule::run()`.
76    pub fn update(&mut self) {
77        // Move current → previous (swap), then clear current.
78        // Using swap + clear avoids a heap allocation: we reuse the old
79        // previous buffer (now cleared) as the new current buffer.
80        std::mem::swap(&mut self.previous, &mut self.current);
81        self.current.clear();
82    }
83
84    /// Number of readable events (in the previous buffer).
85    pub fn len(&self) -> usize {
86        self.previous.len()
87    }
88
89    /// Returns `true` if there are no readable events.
90    pub fn is_empty(&self) -> bool {
91        self.previous.is_empty()
92    }
93}
94
95impl<T: 'static> Default for Events<T> {
96    fn default() -> Self {
97        Self::new()
98    }
99}
100
101// =============================================================================
102// EventWriter<'w, T> — exclusive write access as a SystemParam
103// =============================================================================
104
105/// Exclusive write access to the `Events<T>` resource.
106///
107/// Use this in a system to send events that other systems can read on the
108/// next schedule tick via [`EventReader<T>`].
109///
110/// ```rust,ignore
111/// fn fire_cannon(mut writer: EventWriter<'_, CannonFired>) {
112///     writer.send(CannonFired { power: 9000 });
113/// }
114/// ```
115pub struct EventWriter<'w, T: Send + 'static> {
116    events: &'w mut Events<T>,
117}
118
119impl<'w, T: Send + 'static> EventWriter<'w, T> {
120    /// Send an event. Readable by `EventReader<T>` systems on the next tick.
121    pub fn send(&mut self, event: T) {
122        self.events.send(event);
123    }
124}
125
126// SAFETY: access() reports ResWrite(Events<T>). fetch() only touches the
127// Events<T> resource field via get_resource_mut — no other field is accessed.
128unsafe impl<T: Send + 'static> SystemParam for EventWriter<'_, T> {
129    type Item<'w> = EventWriter<'w, T>;
130
131    fn access() -> Vec<Access> {
132        vec![Access::ResWrite(TypeId::of::<Events<T>>())]
133    }
134
135    unsafe fn fetch<'w>(world: UnsafeWorldCell) -> Self::Item<'w> {
136        EventWriter {
137            events: unsafe { world.get_resource_mut::<Events<T>>() },
138        }
139    }
140}
141
142// =============================================================================
143// EventReader<'w, T> — shared read access as a SystemParam
144// =============================================================================
145
146/// Shared read access to the `Events<T>` resource.
147///
148/// Iterates over events sent by `EventWriter<T>` on the *previous* tick.
149///
150/// ```rust,ignore
151/// fn on_cannon_fired(reader: EventReader<'_, CannonFired>) {
152///     for ev in reader.read() {
153///         println!("cannon fired with power {}", ev.power);
154///     }
155/// }
156/// ```
157pub struct EventReader<'w, T: Send + 'static> {
158    events: &'w Events<T>,
159}
160
161impl<'w, T: Send + 'static> EventReader<'w, T> {
162    /// Iterate over events from the previous tick.
163    pub fn read(&self) -> impl Iterator<Item = &T> {
164        self.events.read()
165    }
166
167    /// Number of readable events.
168    pub fn len(&self) -> usize {
169        self.events.len()
170    }
171
172    /// Returns `true` if there are no readable events.
173    pub fn is_empty(&self) -> bool {
174        self.events.is_empty()
175    }
176}
177
178// SAFETY: access() reports ResRead(Events<T>). fetch() only reads the
179// Events<T> resource via get_resource — no mutation occurs.
180unsafe impl<T: Send + 'static> SystemParam for EventReader<'_, T> {
181    type Item<'w> = EventReader<'w, T>;
182
183    fn access() -> Vec<Access> {
184        vec![Access::ResRead(TypeId::of::<Events<T>>())]
185    }
186
187    unsafe fn fetch<'w>(world: UnsafeWorldCell) -> Self::Item<'w> {
188        EventReader {
189            events: unsafe { world.get_resource::<Events<T>>() },
190        }
191    }
192}
193
194// =============================================================================
195// Tests
196// =============================================================================
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201    use crate::system_param::{SystemParam, has_conflicts};
202    use crate::world::World;
203
204    #[derive(Debug, PartialEq)]
205    struct DamageEvent {
206        amount: u32,
207    }
208
209    #[derive(Debug, PartialEq)]
210    struct SpawnEvent {
211        x: f32,
212    }
213
214    // -------------------------------------------------------------------------
215    // Events<T> unit tests
216    // -------------------------------------------------------------------------
217
218    #[test]
219    fn events_send_and_read() {
220        let mut events: Events<DamageEvent> = Events::new();
221
222        // No events readable yet.
223        assert!(events.is_empty());
224        assert_eq!(events.len(), 0);
225
226        // Send an event.
227        events.send(DamageEvent { amount: 42 });
228
229        // Not yet readable — still in current.
230        assert!(events.is_empty());
231
232        // Advance the buffer.
233        events.update();
234
235        // Now readable in previous.
236        assert!(!events.is_empty());
237        assert_eq!(events.len(), 1);
238        let collected: Vec<_> = events.read().collect();
239        assert_eq!(collected, vec![&DamageEvent { amount: 42 }]);
240    }
241
242    #[test]
243    fn read_current_returns_unswaped_events() {
244        let mut events: Events<DamageEvent> = Events::new();
245
246        events.send(DamageEvent { amount: 7 });
247
248        // read() only sees previous (empty).
249        assert_eq!(events.read().count(), 0);
250        // read_current() sees the not-yet-swapped buffer.
251        let current: Vec<_> = events.read_current().collect();
252        assert_eq!(current, vec![&DamageEvent { amount: 7 }]);
253
254        // After update, event moves to previous.
255        events.update();
256        assert_eq!(events.read().count(), 1);
257        assert_eq!(events.read_current().count(), 0);
258    }
259
260    #[test]
261    fn events_double_buffer_semantics() {
262        let mut events: Events<DamageEvent> = Events::new();
263
264        // Tick N: send event.
265        events.send(DamageEvent { amount: 10 });
266        events.update(); // current → previous
267
268        // Tick N+1 start: event is in previous → readable.
269        assert_eq!(events.len(), 1);
270
271        // Tick N+1: no new events, advance again.
272        events.update(); // previous is cleared, empty current stays current
273
274        // Tick N+2 start: previous is now cleared.
275        assert!(events.is_empty());
276    }
277
278    #[test]
279    fn events_multiple_sends_same_tick() {
280        let mut events: Events<DamageEvent> = Events::new();
281
282        events.send(DamageEvent { amount: 1 });
283        events.send(DamageEvent { amount: 2 });
284        events.send(DamageEvent { amount: 3 });
285        events.update();
286
287        assert_eq!(events.len(), 3);
288        let amounts: Vec<u32> = events.read().map(|e| e.amount).collect();
289        assert_eq!(amounts, vec![1, 2, 3]);
290    }
291
292    // -------------------------------------------------------------------------
293    // Access declaration tests
294    // -------------------------------------------------------------------------
295
296    #[test]
297    fn event_writer_access_is_res_write() {
298        let access = <EventWriter<'_, DamageEvent> as SystemParam>::access();
299        assert_eq!(access.len(), 1);
300        assert_eq!(
301            access[0],
302            Access::ResWrite(TypeId::of::<Events<DamageEvent>>())
303        );
304    }
305
306    #[test]
307    fn event_reader_access_is_res_read() {
308        let access = <EventReader<'_, DamageEvent> as SystemParam>::access();
309        assert_eq!(access.len(), 1);
310        assert_eq!(
311            access[0],
312            Access::ResRead(TypeId::of::<Events<DamageEvent>>())
313        );
314    }
315
316    // -------------------------------------------------------------------------
317    // Conflict detection tests
318    // -------------------------------------------------------------------------
319
320    #[test]
321    fn event_writer_reader_different_types_no_conflict() {
322        // EventWriter<DamageEvent> and EventReader<SpawnEvent> — different
323        // Events<T> TypeIds — must not conflict.
324        let writer_access = <EventWriter<'_, DamageEvent> as SystemParam>::access();
325        let reader_access = <EventReader<'_, SpawnEvent> as SystemParam>::access();
326        assert!(!has_conflicts(&writer_access, &reader_access));
327    }
328
329    #[test]
330    fn event_writer_reader_same_type_conflicts() {
331        // EventWriter<DamageEvent> and EventReader<DamageEvent> both touch
332        // Events<DamageEvent> — ResWrite + ResRead on the same TypeId → conflict.
333        let writer_access = <EventWriter<'_, DamageEvent> as SystemParam>::access();
334        let reader_access = <EventReader<'_, DamageEvent> as SystemParam>::access();
335        assert!(has_conflicts(&writer_access, &reader_access));
336    }
337
338    #[test]
339    fn event_reader_reader_same_type_no_conflict() {
340        // Two EventReaders on the same type — ResRead + ResRead → no conflict.
341        let a = <EventReader<'_, DamageEvent> as SystemParam>::access();
342        let b = <EventReader<'_, DamageEvent> as SystemParam>::access();
343        assert!(!has_conflicts(&a, &b));
344    }
345
346    // -------------------------------------------------------------------------
347    // SystemParam fetch tests
348    // -------------------------------------------------------------------------
349
350    #[test]
351    fn event_writer_fetch_and_send() {
352        let mut world = World::new();
353        world.add_event::<DamageEvent>();
354
355        let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
356        unsafe {
357            let mut writer = <EventWriter<'_, DamageEvent> as SystemParam>::fetch(cell);
358            writer.send(DamageEvent { amount: 99 });
359        }
360
361        // Advance to make current → previous.
362        world.update_events();
363
364        assert_eq!(world.resource::<Events<DamageEvent>>().len(), 1);
365    }
366
367    #[test]
368    fn event_reader_fetch_and_read() {
369        let mut world = World::new();
370        world.add_event::<DamageEvent>();
371
372        // Send an event directly through the resource.
373        world
374            .resource_mut::<Events<DamageEvent>>()
375            .send(DamageEvent { amount: 7 });
376        world.update_events();
377
378        let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
379        unsafe {
380            let reader = <EventReader<'_, DamageEvent> as SystemParam>::fetch(cell);
381            assert_eq!(reader.len(), 1);
382            let ev = reader.read().next().unwrap();
383            assert_eq!(ev.amount, 7);
384        }
385    }
386
387    #[test]
388    fn add_event_duplicate_no_updater_duplication() {
389        let mut world = World::new();
390        world.add_event::<DamageEvent>();
391        world.add_event::<DamageEvent>(); // no-op
392
393        world
394            .resource_mut::<Events<DamageEvent>>()
395            .send(DamageEvent { amount: 5 });
396        world.update_events();
397
398        // If the updater were duplicated, the second would clear previous.
399        assert_eq!(world.resource::<Events<DamageEvent>>().len(), 1);
400    }
401
402    #[test]
403    fn add_event_duplicate_does_not_drop_queued_events() {
404        let mut world = World::new();
405        world.add_event::<DamageEvent>();
406
407        // Queue an event, then call add_event again — must not reset the buffer.
408        world
409            .resource_mut::<Events<DamageEvent>>()
410            .send(DamageEvent { amount: 99 });
411        world.add_event::<DamageEvent>(); // no-op
412
413        world.update_events();
414        assert_eq!(world.resource::<Events<DamageEvent>>().len(), 1);
415        assert_eq!(
416            world
417                .resource::<Events<DamageEvent>>()
418                .read()
419                .next()
420                .unwrap()
421                .amount,
422            99
423        );
424    }
425
426    #[test]
427    fn add_event_after_take_resource_restores_without_duplicate_updater() {
428        let mut world = World::new();
429        world.add_event::<DamageEvent>();
430
431        // Remove the resource via the public API.
432        let _old: Events<DamageEvent> = world.take_resource();
433
434        // Re-register — must restore the resource and not duplicate the updater.
435        world.add_event::<DamageEvent>();
436
437        world
438            .resource_mut::<Events<DamageEvent>>()
439            .send(DamageEvent { amount: 77 });
440        world.update_events();
441
442        // One updater → event survives in previous. Two would clear it.
443        assert_eq!(world.resource::<Events<DamageEvent>>().len(), 1);
444        assert_eq!(
445            world
446                .resource::<Events<DamageEvent>>()
447                .read()
448                .next()
449                .unwrap()
450                .amount,
451            77
452        );
453    }
454}