From 7c224f0cdbb0eb992a6ea1b1d0e7d9ed477f3575 Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 16:23:12 -0400 Subject: [PATCH 01/20] Code organization Moves impl blocks directly after types, organizes like-types together, presents shared functionality first --- crates/bevy_ecs/src/event.rs | 265 ++++++++++++++++++----------------- 1 file changed, 137 insertions(+), 128 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 889d9d22e8b4c..4174dcd4346a6 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -141,134 +141,6 @@ impl Default for Events { } } -fn map_instance_event_with_id(event_instance: &EventInstance) -> (&T, EventId) { - (&event_instance.event, event_instance.event_id) -} - -fn map_instance_event(event_instance: &EventInstance) -> &T { - &event_instance.event -} - -/// Reads events of type `T` in order and tracks which events have already been read. -#[derive(SystemParam)] -pub struct EventReader<'a, T: Component> { - last_event_count: Local<'a, (usize, PhantomData)>, - events: Res<'a, Events>, -} - -/// Sends events of type `T`. -#[derive(SystemParam)] -pub struct EventWriter<'a, T: Component> { - events: ResMut<'a, Events>, -} - -impl<'a, T: Component> EventWriter<'a, T> { - pub fn send(&mut self, event: T) { - self.events.send(event); - } - - pub fn send_batch(&mut self, events: impl Iterator) { - self.events.extend(events); - } -} - -pub struct ManualEventReader { - last_event_count: usize, - _marker: PhantomData, -} - -impl Default for ManualEventReader { - fn default() -> Self { - ManualEventReader { - last_event_count: 0, - _marker: Default::default(), - } - } -} - -impl ManualEventReader { - /// See [`EventReader::iter`] - pub fn iter<'a>(&mut self, events: &'a Events) -> impl DoubleEndedIterator { - internal_event_reader(&mut self.last_event_count, events).map(|(e, _)| e) - } - - /// See [`EventReader::iter_with_id`] - pub fn iter_with_id<'a>( - &mut self, - events: &'a Events, - ) -> impl DoubleEndedIterator)> { - internal_event_reader(&mut self.last_event_count, events) - } -} - -/// Like [`iter_with_id`](EventReader::iter_with_id) except not emitting any traces for read -/// messages. -fn internal_event_reader<'a, T>( - last_event_count: &mut usize, - events: &'a Events, -) -> impl DoubleEndedIterator)> { - // if the reader has seen some of the events in a buffer, find the proper index offset. - // otherwise read all events in the buffer - let a_index = if *last_event_count > events.a_start_event_count { - *last_event_count - events.a_start_event_count - } else { - 0 - }; - let b_index = if *last_event_count > events.b_start_event_count { - *last_event_count - events.b_start_event_count - } else { - 0 - }; - *last_event_count = events.event_count; - match events.state { - State::A => events - .events_b - .get(b_index..) - .unwrap_or_else(|| &[]) - .iter() - .map(map_instance_event_with_id) - .chain( - events - .events_a - .get(a_index..) - .unwrap_or_else(|| &[]) - .iter() - .map(map_instance_event_with_id), - ), - State::B => events - .events_a - .get(a_index..) - .unwrap_or_else(|| &[]) - .iter() - .map(map_instance_event_with_id) - .chain( - events - .events_b - .get(b_index..) - .unwrap_or_else(|| &[]) - .iter() - .map(map_instance_event_with_id), - ), - } -} - -impl<'a, T: Component> EventReader<'a, T> { - /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's - /// event counter, which means subsequent event reads will not include events that happened - /// before now. - pub fn iter(&mut self) -> impl DoubleEndedIterator { - self.iter_with_id().map(|(event, _id)| event) - } - - /// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events. - pub fn iter_with_id(&mut self) -> impl DoubleEndedIterator)> { - internal_event_reader(&mut self.last_event_count.0, &self.events).map(|(event, id)| { - trace!("EventReader::iter() -> {}", id); - (event, id) - }) - } -} - impl Events { /// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read /// the event. @@ -374,6 +246,143 @@ impl Events { } } +fn map_instance_event_with_id(event_instance: &EventInstance) -> (&T, EventId) { + (&event_instance.event, event_instance.event_id) +} + +fn map_instance_event(event_instance: &EventInstance) -> &T { + &event_instance.event +} + +/// Like [`iter_with_id`](EventReader::iter_with_id) except not emitting any traces for read +/// messages. +fn internal_event_reader<'a, T>( + last_event_count: &mut usize, + events: &'a Events, +) -> impl DoubleEndedIterator)> { + // if the reader has seen some of the events in a buffer, find the proper index offset. + // otherwise read all events in the buffer + let a_index = if *last_event_count > events.a_start_event_count { + *last_event_count - events.a_start_event_count + } else { + 0 + }; + let b_index = if *last_event_count > events.b_start_event_count { + *last_event_count - events.b_start_event_count + } else { + 0 + }; + *last_event_count = events.event_count; + match events.state { + State::A => events + .events_b + .get(b_index..) + .unwrap_or_else(|| &[]) + .iter() + .map(map_instance_event_with_id) + .chain( + events + .events_a + .get(a_index..) + .unwrap_or_else(|| &[]) + .iter() + .map(map_instance_event_with_id), + ), + State::B => events + .events_a + .get(a_index..) + .unwrap_or_else(|| &[]) + .iter() + .map(map_instance_event_with_id) + .chain( + events + .events_b + .get(b_index..) + .unwrap_or_else(|| &[]) + .iter() + .map(map_instance_event_with_id), + ), + } +} +/// Sends events of type `T`. +pub struct EventWriter<'a, T: Component> { + events: &'a mut Events, +} + +impl<'a, T: Component> SystemParam for EventWriter<'a, T> { + type Fetch = ResMutState>; +} + +impl<'a, T: Component> EventWriter<'a, T> { + pub fn new(events: &'a mut Events) -> Self { + EventWriter::<'a, T> { events } + } + + pub fn send(&mut self, event: T) { + self.events.send(event); + } + + pub fn send_batch(&mut self, events: impl Iterator) { + self.events.extend(events); + } +} + +/// Reads events of type `T` in order and tracks which events have already been read. +pub struct EventReader<'a, T: Component> { + last_event_count: Local<'a, (usize, PhantomData)>, + events: &'a Events, +} + +impl<'a, T: Component> SystemParam for EventReader<'a, T> { + type Fetch = ResState>; +} + +impl<'a, T: Component> EventReader<'a, T> { + /// Iterates over the events this EventReader has not seen yet. This updates the EventReader's + /// event counter, which means subsequent event reads will not include events that happened + /// before now. + pub fn iter(&mut self) -> impl DoubleEndedIterator { + self.iter_with_id().map(|(event, _id)| event) + } + + /// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events. + pub fn iter_with_id(&mut self) -> impl DoubleEndedIterator)> { + internal_event_reader(&mut self.last_event_count.0, &self.events).map(|(event, id)| { + trace!("EventReader::iter() -> {}", id); + (event, id) + }) + } +} + +pub struct ManualEventReader { + last_event_count: usize, + _marker: PhantomData, +} + +impl Default for ManualEventReader { + fn default() -> Self { + ManualEventReader { + last_event_count: 0, + _marker: Default::default(), + } + } +} + +impl ManualEventReader { + /// See [`EventReader::iter`] + pub fn iter<'a>(&mut self, events: &'a Events) -> impl DoubleEndedIterator { + internal_event_reader(&mut self.last_event_count, events).map(|(e, _)| e) + } + + /// See [`EventReader::iter_with_id`] + pub fn iter_with_id<'a>( + &mut self, + events: &'a Events, + ) -> impl DoubleEndedIterator)> { + internal_event_reader(&mut self.last_event_count, events) + } +} + #[cfg(test)] mod tests { use super::*; From 8be5e903cefcc61cd87a59cead4b35fd228ec4b4 Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 16:23:37 -0400 Subject: [PATCH 02/20] Added EventConsumer Also added handy drain_with_id method --- crates/bevy_ecs/src/event.rs | 55 +++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 4174dcd4346a6..587062c65f8c1 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -207,20 +207,21 @@ impl Events { } /// Creates a draining iterator that removes all events. - pub fn drain(&mut self) -> impl Iterator + '_ { - let map = |i: EventInstance| i.event; - match self.state { - State::A => self - .events_b - .drain(..) - .map(map) - .chain(self.events_a.drain(..).map(map)), - State::B => self - .events_a - .drain(..) - .map(map) - .chain(self.events_b.drain(..).map(map)), - } + pub fn drain(&mut self) -> impl DoubleEndedIterator + '_ { + self.drain_with_id().map(|(e, _)| e) + } + + /// Creates a draining iterator that returns both events and their ids + pub fn drain_with_id(&mut self) -> impl DoubleEndedIterator)> + '_ { + let event_instances = match self.state { + BufferState::A => self.events_b.drain(..).chain(self.events_a.drain(..)), + BufferState::B => self.events_a.drain(..).chain(self.events_b.drain(..)), + }; + + event_instances.map(|ei| { + trace!("Events::drain_with_id -> {}", ei.event_id); + (ei.event, ei.event_id) + }) } pub fn extend(&mut self, events: I) @@ -354,6 +355,32 @@ impl<'a, T: Component> EventReader<'a, T> { } } +/// Reads and consumes all events of type T +/// +/// Useful for manual event cleanup when [AppBuilder::add_event::] is omitted, +/// allowing events to accumulate on your components or resources until consumed. +/// Note: due to the draining nature of this reader, you probably only want one +/// EventConsumer per event storage location + event type combination. +pub struct EventConsumer<'a, T: Component> { + events: &'a mut Events, +} + +impl<'a, T: Component> SystemParam for EventConsumer<'a, T> { + type Fetch = ResMutState>; +} + +impl<'a, T: Component> EventConsumer<'a, T> { + /// Drains all available events this EventConsumer has access to into an iterator + pub fn drain(self) -> impl DoubleEndedIterator + 'a { + self.events.drain() + } + + /// Drains all available events this EventConsumer has access to into an iterator and returns the id + pub fn drain_with_id(self) -> impl DoubleEndedIterator)> + 'a { + self.events.drain_with_id() + } +} + pub struct ManualEventReader { last_event_count: usize, _marker: PhantomData, From 0140b6340770b4770082f70529df0613606a64fb Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 16:34:08 -0400 Subject: [PATCH 03/20] Renamed State to BufferState for clarity Event buffer's state should not clash with State concept --- crates/bevy_ecs/src/event.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 587062c65f8c1..27d88be5efefc 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -51,7 +51,7 @@ struct EventInstance { } #[derive(Debug)] -enum State { +enum BufferState { A, B, } @@ -125,7 +125,7 @@ pub struct Events { a_start_event_count: usize, b_start_event_count: usize, event_count: usize, - state: State, + state: BufferState, } impl Default for Events { @@ -136,7 +136,7 @@ impl Default for Events { event_count: 0, events_a: Vec::new(), events_b: Vec::new(), - state: State::A, + state: BufferState::A, } } } @@ -154,8 +154,8 @@ impl Events { let event_instance = EventInstance { event_id, event }; match self.state { - State::A => self.events_a.push(event_instance), - State::B => self.events_b.push(event_instance), + BufferState::A => self.events_a.push(event_instance), + BufferState::B => self.events_b.push(event_instance), } self.event_count += 1; @@ -182,14 +182,14 @@ impl Events { /// called once per frame/update. pub fn update(&mut self) { match self.state { - State::A => { + BufferState::A => { self.events_b = Vec::new(); - self.state = State::B; + self.state = BufferState::B; self.b_start_event_count = self.event_count; } - State::B => { + BufferState::B => { self.events_a = Vec::new(); - self.state = State::A; + self.state = BufferState::A; self.a_start_event_count = self.event_count; } } @@ -241,8 +241,8 @@ impl Events { /// happen after this call and before the next `update()` call will be dropped. pub fn iter_current_update_events(&self) -> impl DoubleEndedIterator { match self.state { - State::A => self.events_a.iter().map(map_instance_event), - State::B => self.events_b.iter().map(map_instance_event), + BufferState::A => self.events_a.iter().map(map_instance_event), + BufferState::B => self.events_b.iter().map(map_instance_event), } } } @@ -275,7 +275,7 @@ fn internal_event_reader<'a, T>( }; *last_event_count = events.event_count; match events.state { - State::A => events + BufferState::A => events .events_b .get(b_index..) .unwrap_or_else(|| &[]) @@ -289,7 +289,7 @@ fn internal_event_reader<'a, T>( .iter() .map(map_instance_event_with_id), ), - State::B => events + BufferState::B => events .events_a .get(a_index..) .unwrap_or_else(|| &[]) From 9ae131f1d578cdc5a967aed0dc6dd1f22357f6b6 Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 16:47:05 -0400 Subject: [PATCH 04/20] Reverted to SystemParam only form of EventReader etc. --- crates/bevy_ecs/src/event.rs | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 27d88be5efefc..2e3ca727ec0b0 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -306,19 +306,12 @@ fn internal_event_reader<'a, T>( } } /// Sends events of type `T`. +#[derive(SystemParam)] pub struct EventWriter<'a, T: Component> { - events: &'a mut Events, -} - -impl<'a, T: Component> SystemParam for EventWriter<'a, T> { - type Fetch = ResMutState>; + events: ResMut<'a, Events>, } impl<'a, T: Component> EventWriter<'a, T> { - pub fn new(events: &'a mut Events) -> Self { - EventWriter::<'a, T> { events } - } - pub fn send(&mut self, event: T) { self.events.send(event); } @@ -329,13 +322,10 @@ impl<'a, T: Component> EventWriter<'a, T> { } /// Reads events of type `T` in order and tracks which events have already been read. +#[derive(SystemParam)] pub struct EventReader<'a, T: Component> { last_event_count: Local<'a, (usize, PhantomData)>, - events: &'a Events, -} - -impl<'a, T: Component> SystemParam for EventReader<'a, T> { - type Fetch = ResState>; + events: Res<'a, Events>, } impl<'a, T: Component> EventReader<'a, T> { @@ -361,22 +351,19 @@ impl<'a, T: Component> EventReader<'a, T> { /// allowing events to accumulate on your components or resources until consumed. /// Note: due to the draining nature of this reader, you probably only want one /// EventConsumer per event storage location + event type combination. +#[derive(SystemParam)] pub struct EventConsumer<'a, T: Component> { - events: &'a mut Events, -} - -impl<'a, T: Component> SystemParam for EventConsumer<'a, T> { - type Fetch = ResMutState>; + events: ResMut<'a, Events>, } impl<'a, T: Component> EventConsumer<'a, T> { /// Drains all available events this EventConsumer has access to into an iterator - pub fn drain(self) -> impl DoubleEndedIterator + 'a { + pub fn drain(self) -> impl DoubleEndedIterator { self.events.drain() } /// Drains all available events this EventConsumer has access to into an iterator and returns the id - pub fn drain_with_id(self) -> impl DoubleEndedIterator)> + 'a { + pub fn drain_with_id(self) -> impl DoubleEndedIterator)> { self.events.drain_with_id() } } From 620bbbcf980b87b893fa1018d81fbcbea378723d Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 17:08:07 -0400 Subject: [PATCH 05/20] Add into_inner for ResMut Direct copy of into_inner for Mut --- crates/bevy_ecs/src/system/system_param.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 5dce8e4b2e477..24bf7e5edbb0d 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -367,6 +367,13 @@ impl<'w, T: Component> DerefMut for ResMut<'w, T> { } } +impl<'a, T: Component> ResMut<'a, T> { + pub fn into_inner(self) -> &'a mut T { + self.ticks.set_changed(self.change_tick); + self.value + } +} + /// The [`SystemParamState`] of [`ResMut`]. pub struct ResMutState { component_id: ComponentId, From 42dad934d0f03a449c2ccaef111829059d026afe Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 17:08:28 -0400 Subject: [PATCH 06/20] Fix lifetime errors in drain methods --- crates/bevy_ecs/src/event.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 2e3ca727ec0b0..1e6a12f99ec2a 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -358,13 +358,15 @@ pub struct EventConsumer<'a, T: Component> { impl<'a, T: Component> EventConsumer<'a, T> { /// Drains all available events this EventConsumer has access to into an iterator - pub fn drain(self) -> impl DoubleEndedIterator { - self.events.drain() + pub fn drain(self) -> impl DoubleEndedIterator + 'a { + // into_inner is needed to ensure the lifetime is not bound to the implicit .deref_mut() call + self.events.into_inner().drain() } /// Drains all available events this EventConsumer has access to into an iterator and returns the id - pub fn drain_with_id(self) -> impl DoubleEndedIterator)> { - self.events.drain_with_id() + pub fn drain_with_id(self) -> impl DoubleEndedIterator)> + 'a { + // into_inner is needed to ensure the lifetime is not bound to the implicit .deref_mut() call + self.events.into_inner().drain_with_id() } } From 766e25c044f6721c659512e3270517fee242f722 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 8 May 2021 02:29:31 -0400 Subject: [PATCH 07/20] Added test for Event* as system parameters --- crates/bevy_ecs/src/event.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 1e6a12f99ec2a..81eda82d90a55 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -402,12 +402,45 @@ impl ManualEventReader { #[cfg(test)] mod tests { use super::*; + use crate::schedule::{Stage, SystemStage}; + use crate::system::IntoSystem; + use crate::world::World; #[derive(Copy, Clone, PartialEq, Eq, Debug)] struct TestEvent { i: usize, } + #[test] + fn event_system_params() { + let world = World::default(); + struct E; + fn writes(ew: EventWriter) { + ew.send(E) + } + fn reads(er: EventReader) { + er.iter(); + } + fn consumes(ec: EventConsumer) { + ec.drain(); + } + + let mut stage1 = SystemStage::parallel(); + stage1.add_system(writes.system()); + stage1.add_system(reads.system()); + + stage1.run(&mut World::default()); + let current_events = world.get_resource::>().unwrap(); + assert!(current_events.events_a.len() == 1); + + let mut stage2 = SystemStage::parallel(); + stage2.add_system(consumes.system()); + + stage2.run(&mut World::default()); + let current_events = world.get_resource::>().unwrap(); + assert!(current_events.events_a.len() == 0); + } + #[test] fn test_events() { let mut events = Events::::default(); From 0ca992e45fdb0f1384f38ac0188e0c7117b73e57 Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 17:19:48 -0400 Subject: [PATCH 08/20] Comments explaining tiny map functions --- crates/bevy_ecs/src/event.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 81eda82d90a55..9b31cfc7fc452 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -247,10 +247,12 @@ impl Events { } } +// Used in internal_event_reader instead of a closure to ensure stable type fn map_instance_event_with_id(event_instance: &EventInstance) -> (&T, EventId) { (&event_instance.event, event_instance.event_id) } +// Used in internal_event_reader instead of a closure to ensure stable type fn map_instance_event(event_instance: &EventInstance) -> &T { &event_instance.event } From bb18f07c2b2a9c3d95ac34d770d31d65dae93d64 Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 18:05:36 -0400 Subject: [PATCH 09/20] Fixed test! --- crates/bevy_ecs/src/event.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 9b31cfc7fc452..d7d2019349a21 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -415,12 +415,14 @@ mod tests { #[test] fn event_system_params() { - let world = World::default(); struct E; - fn writes(ew: EventWriter) { + let mut world = World::default(); + world.insert_resource(Events::::default()); + + fn writes(mut ew: EventWriter) { ew.send(E) } - fn reads(er: EventReader) { + fn reads(mut er: EventReader) { er.iter(); } fn consumes(ec: EventConsumer) { @@ -430,15 +432,15 @@ mod tests { let mut stage1 = SystemStage::parallel(); stage1.add_system(writes.system()); stage1.add_system(reads.system()); + stage1.run(&mut world); - stage1.run(&mut World::default()); let current_events = world.get_resource::>().unwrap(); assert!(current_events.events_a.len() == 1); let mut stage2 = SystemStage::parallel(); stage2.add_system(consumes.system()); - stage2.run(&mut World::default()); + stage2.run(&mut world); let current_events = world.get_resource::>().unwrap(); assert!(current_events.events_a.len() == 0); } From 51b6195c35a7c4681172dd5a4f9b01b202ea37a0 Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 18:13:42 -0400 Subject: [PATCH 10/20] Rename ei to instance for clarity --- crates/bevy_ecs/src/event.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index d7d2019349a21..d6b844e80d08a 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -218,9 +218,9 @@ impl Events { BufferState::B => self.events_a.drain(..).chain(self.events_b.drain(..)), }; - event_instances.map(|ei| { - trace!("Events::drain_with_id -> {}", ei.event_id); - (ei.event, ei.event_id) + event_instances.map(|instance| { + trace!("Events::drain_with_id -> {}", instance.event_id); + (instance.event, instance.event_id) }) } From 1da3c348e766dae6ebf695c28ffd6137fe472663 Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 11 May 2021 18:39:58 -0400 Subject: [PATCH 11/20] Add event_consumer example --- Cargo.toml | 4 +++ crates/bevy_ecs/src/lib.rs | 2 +- examples/README.md | 1 + examples/ecs/event_consumer.rs | 65 ++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 examples/ecs/event_consumer.rs diff --git a/Cargo.toml b/Cargo.toml index 95141021a8077..b09e5fdc6530d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -259,6 +259,10 @@ path = "examples/ecs/change_detection.rs" name = "event" path = "examples/ecs/event.rs" +[[example]] +name = "event_consumer" +path = "examples/ecs/event_consumer.rs" + [[example]] name = "fixed_timestep" path = "examples/ecs/fixed_timestep.rs" diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index f45c6790a5780..9e6839424e53e 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -19,7 +19,7 @@ pub mod prelude { pub use crate::{ bundle::Bundle, entity::Entity, - event::{EventReader, EventWriter}, + event::{EventConsumer, EventReader, EventWriter}, query::{Added, ChangeTrackers, Changed, Or, QueryState, With, WithBundle, Without}, schedule::{ AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, diff --git a/examples/README.md b/examples/README.md index 096afc63e4193..e6971549315d8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -151,6 +151,7 @@ Example | File | Description `ecs_guide` | [`ecs/ecs_guide.rs`](./ecs/ecs_guide.rs) | Full guide to Bevy's ECS `change_detection` | [`ecs/change_detection.rs`](./ecs/change_detection.rs) | Change detection on components `event` | [`ecs/event.rs`](./ecs/event.rs) | Illustrates event creation, activation, and reception +`event_consumer` | [`ecs/event_consumer.rs`](./ecs/event_consumer.rs) | Shows how to consume events and avoid automatic event cleanup when skipping systems `fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick `hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities `parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` diff --git a/examples/ecs/event_consumer.rs b/examples/ecs/event_consumer.rs new file mode 100644 index 0000000000000..7a1fbbc1cbad0 --- /dev/null +++ b/examples/ecs/event_consumer.rs @@ -0,0 +1,65 @@ +use bevy::core::FixedTimestep; +use bevy::prelude::*; + +/// Events are automatically cleaned up after two frames when intialized via `.add_event`. +/// To bypass this, you can simply add the Events:: resource manually. +/// This is critical when working with systems that read events but do not run every tick, +/// such as those that operate with a FixedTimeStep run criteria. +/// +/// When you do so though, you need to be careful to clean up these events eventually, +/// otherwise the size of your vector of events will grow in an unbounded fashion. +/// +/// `EventConsumer::` provides a simple interface to do so, clearing all events that it reads +/// by draining them into a new vector. +/// You can combine it with other `EventReader`s as long as they read events before , +/// but only one `EventConsumer` system should be used per event type in most cases +/// as they will compete for events. +fn main() { + App::build() + .add_plugins(DefaultPlugins) + .add_event::() + .add_system( + event_trigger_system + .system() + .with_run_criteria(FixedTimestep::step(1.0)), + ) + .add_system(event_listener_system.system().label("listening")) + .add_system( + event_devourer_system + .system() + // Must occur after event_listener_system or some events may be missed + .after("listening") + .with_run_criteria(FixedTimestep::step(5.0)), + ) + .run(); +} + +struct MyEvent { + pub message: String, +} + +// sends MyEvent every second +fn event_trigger_system(time: Res