Skip to main content

goud_engine/ecs/system/system_param/
event_params.rs

1//! [`SystemParam`] implementations for event access types.
2//!
3//! Provides [`EcsEventReader`] and [`EcsEventWriter`] as ECS system parameters
4//! for reading and writing events through the [`Events`] resource.
5
6use std::marker::PhantomData;
7
8use crate::core::event::{Event, Events};
9use crate::ecs::query::Access;
10use crate::ecs::resource::{Res, ResMut, ResourceId};
11use crate::ecs::World;
12
13use super::traits::{ReadOnlySystemParam, SystemParam, SystemParamState};
14
15// =============================================================================
16// EcsEventReader<E> - Immutable Event SystemParam
17// =============================================================================
18
19/// Cached state for [`EcsEventReader`].
20///
21/// Stores a per-system cursor that tracks which events have been read across
22/// system runs. Each system gets its own independent cursor.
23pub struct EcsEventReaderState<E: Event> {
24    /// Position in the read buffer up to which events have been consumed.
25    /// Reset to 0 on each `update()` cycle (buffer swap) since the read
26    /// buffer changes entirely.
27    cursor: usize,
28    _marker: PhantomData<fn() -> E>,
29}
30
31impl<E: Event> SystemParamState for EcsEventReaderState<E> {
32    fn init(_world: &mut World) -> Self {
33        Self {
34            cursor: 0,
35            _marker: PhantomData,
36        }
37    }
38}
39
40/// ECS system parameter for reading events of type `E`.
41///
42/// Wraps a reference to the [`Events<E>`] resource and a cursor stored in
43/// the system's cached state. Each system using `EcsEventReader<E>` gets its
44/// own cursor, so multiple systems can independently consume the same events.
45///
46/// # Example
47///
48/// ```ignore
49/// fn damage_system(mut reader: EcsEventReader<DamageEvent>) {
50///     for event in reader.read() {
51///         println!("Damage: {}", event.amount);
52///     }
53/// }
54/// ```
55///
56/// # Frame Lifecycle
57///
58/// Events written during frame N become readable after `Events::update()` is
59/// called. The cursor automatically resets when the read buffer changes.
60pub struct EcsEventReader<'w, 's, E: Event> {
61    events: &'w Events<E>,
62    cursor: &'s mut usize,
63}
64
65impl<'w, 's, E: Event> EcsEventReader<'w, 's, E> {
66    /// Returns an iterator over unread events since this system last ran.
67    ///
68    /// Advances the cursor so that the next call to `read()` (in a
69    /// subsequent system run) returns only newly available events.
70    pub fn read(&mut self) -> impl Iterator<Item = &'w E> {
71        let buffer = self.events.read_buffer();
72        // Clamp cursor to buffer length in case the buffer shrank (new frame)
73        let start = (*self.cursor).min(buffer.len());
74        *self.cursor = buffer.len();
75        buffer[start..].iter()
76    }
77
78    /// Returns `true` if there are no unread events for this system.
79    #[must_use]
80    pub fn is_empty(&self) -> bool {
81        let buffer = self.events.read_buffer();
82        (*self.cursor).min(buffer.len()) >= buffer.len()
83    }
84
85    /// Returns the number of unread events for this system.
86    #[must_use]
87    pub fn len(&self) -> usize {
88        let buffer = self.events.read_buffer();
89        buffer.len().saturating_sub(*self.cursor)
90    }
91}
92
93impl<E: Event> SystemParam for EcsEventReader<'_, '_, E> {
94    type State = EcsEventReaderState<E>;
95    type Item<'w, 's> = EcsEventReader<'w, 's, E>;
96
97    fn update_access(_state: &Self::State, access: &mut Access) {
98        access.add_resource_read(ResourceId::of::<Events<E>>());
99    }
100
101    fn get_param<'w, 's>(state: &'s mut Self::State, world: &'w World) -> Self::Item<'w, 's> {
102        let events: Res<'w, Events<E>> = world
103            .resource::<Events<E>>()
104            .expect("Events<E> resource not found. Insert it with world.insert_resource(Events::<E>::new()).");
105        let events_ref: &'w Events<E> = events.into_inner();
106        EcsEventReader {
107            events: events_ref,
108            cursor: &mut state.cursor,
109        }
110    }
111}
112
113/// `EcsEventReader<E>` is read-only.
114impl<E: Event> ReadOnlySystemParam for EcsEventReader<'_, '_, E> {}
115
116// =============================================================================
117// EcsEventWriter<E> - Mutable Event SystemParam
118// =============================================================================
119
120/// Cached state for [`EcsEventWriter`].
121///
122/// Marker state with no data -- the writer does not need a cursor.
123pub struct EcsEventWriterState<E: Event> {
124    _marker: PhantomData<fn() -> E>,
125}
126
127impl<E: Event> SystemParamState for EcsEventWriterState<E> {
128    fn init(_world: &mut World) -> Self {
129        Self {
130            _marker: PhantomData,
131        }
132    }
133}
134
135/// ECS system parameter for writing events of type `E`.
136///
137/// Wraps a mutable reference to the [`Events<E>`] resource, providing
138/// `send()` and `send_batch()` for emitting events.
139///
140/// # Example
141///
142/// ```ignore
143/// fn spawn_system(mut writer: EcsEventWriter<SpawnEvent>) {
144///     writer.send(SpawnEvent { entity_type: "enemy".into() });
145/// }
146/// ```
147pub struct EcsEventWriter<'w, E: Event> {
148    events: ResMut<'w, Events<E>>,
149}
150
151impl<E: Event> EcsEventWriter<'_, E> {
152    /// Sends a single event.
153    pub fn send(&mut self, event: E) {
154        self.events.send(event);
155    }
156
157    /// Sends multiple events in batch.
158    pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) {
159        self.events.send_batch(events);
160    }
161}
162
163impl<E: Event> SystemParam for EcsEventWriter<'_, E> {
164    type State = EcsEventWriterState<E>;
165    type Item<'w, 's> = EcsEventWriter<'w, E>;
166
167    fn update_access(_state: &Self::State, access: &mut Access) {
168        access.add_resource_write(ResourceId::of::<Events<E>>());
169    }
170
171    fn get_param<'w, 's>(_state: &'s mut Self::State, _world: &'w World) -> Self::Item<'w, 's> {
172        panic!("EcsEventWriter<E> requires mutable world access. Use get_param_mut instead.");
173    }
174
175    fn get_param_mut<'w, 's>(
176        _state: &'s mut Self::State,
177        world: &'w mut World,
178    ) -> Self::Item<'w, 's> {
179        let events = world
180            .resource_mut::<Events<E>>()
181            .expect("Events<E> resource not found. Insert it with world.insert_resource(Events::<E>::new()).");
182        EcsEventWriter { events }
183    }
184}
185
186// EcsEventWriter is NOT ReadOnlySystemParam -- intentionally omitted