From ec1aa48fc6d4f7683e41d7aa9fff252882549cfa Mon Sep 17 00:00:00 2001 From: Bob Gardner Date: Mon, 8 Jul 2024 07:53:06 -0700 Subject: [PATCH] Created an EventMutator for when you want to mutate an event before reading (#13818) # Objective - Often in games you will want to create chains of systems that modify some event. For example, a chain of damage systems that handle a DamageEvent and modify the underlying value before the health system finally consumes the event. Right now this requires either: * Using a component added to the entity * Consuming and refiring events Neither is ideal when really all we want to do is read the events value, modify it, and write it back. ## Solution - Create an EventMutator class similar to EventReader but with ResMut and iterators that return &mut so that events can be mutated. ## Testing - I replicated all the existing tests for EventReader to make sure behavior was the same (I believe) and added a number of tests specific to testing that 1) events can actually be mutated, and that 2) EventReader sees changes from EventMutator for events it hasn't already seen. ## Migration Guide Users currently using `ManualEventReader` should use `EventCursor` instead. `ManualEventReader` will be removed in Bevy 0.16. Additionally, `Events::get_reader` has been replaced by `Events::get_cursor`. Users currently directly accessing the `Events` resource for mutation should move to `EventMutator` if possible. --------- Co-authored-by: poopy Co-authored-by: Alice Cecile --- crates/bevy_app/src/app.rs | 4 +- crates/bevy_asset/src/lib.rs | 4 +- crates/bevy_ecs/src/event/collections.rs | 36 +- crates/bevy_ecs/src/event/event_cursor.rs | 150 +++++ crates/bevy_ecs/src/event/iterators.rs | 16 +- crates/bevy_ecs/src/event/mod.rs | 664 +++++++++++++-------- crates/bevy_ecs/src/event/mut_iterators.rs | 275 +++++++++ crates/bevy_ecs/src/event/mutator.rs | 142 +++++ crates/bevy_ecs/src/event/reader.rs | 120 +--- crates/bevy_ecs/src/lib.rs | 2 +- crates/bevy_ecs/src/removal_detection.rs | 18 +- crates/bevy_scene/src/scene_spawner.rs | 4 +- crates/bevy_state/src/app.rs | 4 +- crates/bevy_winit/src/state.rs | 4 +- examples/ecs/event.rs | 163 +++-- examples/ecs/send_and_receive_events.rs | 12 +- tests/how_to_test_systems.rs | 2 +- 17 files changed, 1163 insertions(+), 457 deletions(-) create mode 100644 crates/bevy_ecs/src/event/event_cursor.rs create mode 100644 crates/bevy_ecs/src/event/mut_iterators.rs create mode 100644 crates/bevy_ecs/src/event/mutator.rs diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 4bb725e257..cf1fafc699 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -4,7 +4,7 @@ use crate::{ }; pub use bevy_derive::AppLabel; use bevy_ecs::{ - event::{event_update_system, ManualEventReader}, + event::{event_update_system, EventCursor}, intern::Interned, prelude::*, schedule::{ScheduleBuildSettings, ScheduleLabel}, @@ -818,7 +818,7 @@ impl App { /// This should be called after every [`update()`](App::update) otherwise you risk /// dropping possible [`AppExit`] events. pub fn should_exit(&self) -> Option { - let mut reader = ManualEventReader::default(); + let mut reader = EventCursor::default(); let events = self.world().get_resource::>()?; let mut events = reader.read(events); diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index a13144015b..5a1c4c7756 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -454,7 +454,7 @@ mod tests { use bevy_core::TaskPoolPlugin; use bevy_ecs::prelude::*; use bevy_ecs::{ - event::ManualEventReader, + event::EventCursor, schedule::{LogLevel, ScheduleBuildSettings}, }; use bevy_log::LogPlugin; @@ -1300,7 +1300,7 @@ mod tests { gate_opener.open(b_path); gate_opener.open(c_path); - let mut reader = ManualEventReader::default(); + let mut reader = EventCursor::default(); run_app_until(&mut app, |world| { let events = world.resource::>>(); let asset_server = world.resource::(); diff --git a/crates/bevy_ecs/src/event/collections.rs b/crates/bevy_ecs/src/event/collections.rs index 68928d4c15..6a5f88b6e0 100644 --- a/crates/bevy_ecs/src/event/collections.rs +++ b/crates/bevy_ecs/src/event/collections.rs @@ -1,6 +1,6 @@ use crate as bevy_ecs; use bevy_ecs::{ - event::{Event, EventId, EventInstance, ManualEventReader}, + event::{Event, EventCursor, EventId, EventInstance}, system::Resource, }; #[cfg(feature = "bevy_reflect")] @@ -153,15 +153,37 @@ impl Events { self.send(Default::default()) } - /// Gets a new [`ManualEventReader`]. This will include all events already in the event buffers. - pub fn get_reader(&self) -> ManualEventReader { - ManualEventReader::default() + /// Gets a new [`EventCursor`]. This will include all events already in the event buffers. + pub fn get_cursor(&self) -> EventCursor { + EventCursor::default() } - /// Gets a new [`ManualEventReader`]. This will ignore all events already in the event buffers. + /// Gets a new [`EventCursor`]. This will ignore all events already in the event buffers. /// It will read all future events. - pub fn get_reader_current(&self) -> ManualEventReader { - ManualEventReader { + pub fn get_cursor_current(&self) -> EventCursor { + EventCursor { + last_event_count: self.event_count, + ..Default::default() + } + } + + #[deprecated( + since = "0.14.0", + note = "`get_reader` has been deprecated. Please use `get_cursor` instead." + )] + /// Gets a new [`EventCursor`]. This will include all events already in the event buffers. + pub fn get_reader(&self) -> EventCursor { + EventCursor::default() + } + + #[deprecated( + since = "0.14.0", + note = "`get_reader_current` has been replaced. Please use `get_cursor_current` instead." + )] + /// Gets a new [`EventCursor`]. This will ignore all events already in the event buffers. + /// It will read all future events. + pub fn get_reader_current(&self) -> EventCursor { + EventCursor { last_event_count: self.event_count, ..Default::default() } diff --git a/crates/bevy_ecs/src/event/event_cursor.rs b/crates/bevy_ecs/src/event/event_cursor.rs new file mode 100644 index 0000000000..1081298cf4 --- /dev/null +++ b/crates/bevy_ecs/src/event/event_cursor.rs @@ -0,0 +1,150 @@ +use crate as bevy_ecs; +use bevy_ecs::event::{ + Event, EventIterator, EventIteratorWithId, EventMutIterator, EventMutIteratorWithId, Events, +}; +#[cfg(feature = "multi_threaded")] +use bevy_ecs::event::{EventMutParIter, EventParIter}; +use std::marker::PhantomData; + +// Deprecated in favor of `EventCursor`, there is no nice way to deprecate this +// because generic constraints are not allowed in type aliases, so this will always +// 'dead code'. Hence the `#[allow(dead_code)]`. +#[deprecated( + since = "0.14.0", + note = "`ManualEventReader` has been replaced. Please use `EventCursor` instead." +)] +#[doc(alias = "EventCursor")] +#[allow(dead_code)] +pub type ManualEventReader = EventCursor; + +/// Stores the state for an [`EventReader`] or [`EventMutator`]. +/// +/// Access to the [`Events`] resource is required to read any incoming events. +/// +/// In almost all cases, you should just use an [`EventReader`] or [`EventMutator`], +/// which will automatically manage the state for you. +/// +/// However, this type can be useful if you need to manually track events, +/// such as when you're attempting to send and receive events of the same type in the same system. +/// +/// # Example +/// +/// ``` +/// use bevy_ecs::prelude::*; +/// use bevy_ecs::event::{Event, Events, EventCursor}; +/// +/// #[derive(Event, Clone, Debug)] +/// struct MyEvent; +/// +/// /// A system that both sends and receives events using a [`Local`] [`EventCursor`]. +/// fn send_and_receive_events( +/// // The `Local` `SystemParam` stores state inside the system itself, rather than in the world. +/// // `EventCursor` is the internal state of `EventMutator`, which tracks which events have been seen. +/// mut local_event_reader: Local>, +/// // We can access the `Events` resource mutably, allowing us to both read and write its contents. +/// mut events: ResMut>, +/// ) { +/// // We must collect the events to resend, because we can't mutate events while we're iterating over the events. +/// let mut events_to_resend = Vec::new(); +/// +/// for event in local_event_reader.read(&mut events) { +/// events_to_resend.push(event.clone()); +/// } +/// +/// for event in events_to_resend { +/// events.send(MyEvent); +/// } +/// } +/// +/// # bevy_ecs::system::assert_is_system(send_and_receive_events); +/// ``` +#[derive(Debug)] +pub struct EventCursor { + pub(super) last_event_count: usize, + pub(super) _marker: PhantomData, +} + +impl Default for EventCursor { + fn default() -> Self { + EventCursor { + last_event_count: 0, + _marker: Default::default(), + } + } +} + +impl Clone for EventCursor { + fn clone(&self) -> Self { + EventCursor { + last_event_count: self.last_event_count, + _marker: PhantomData, + } + } +} + +#[allow(clippy::len_without_is_empty)] // Check fails since the is_empty implementation has a signature other than `(&self) -> bool` +impl EventCursor { + /// See [`EventReader::read`] + pub fn read<'a>(&'a mut self, events: &'a Events) -> EventIterator<'a, E> { + self.read_with_id(events).without_id() + } + + /// See [`EventMutator::read`] + pub fn read_mut<'a>(&'a mut self, events: &'a mut Events) -> EventMutIterator<'a, E> { + self.read_mut_with_id(events).without_id() + } + + /// See [`EventReader::read_with_id`] + pub fn read_with_id<'a>(&'a mut self, events: &'a Events) -> EventIteratorWithId<'a, E> { + EventIteratorWithId::new(self, events) + } + + /// See [`EventMutator::read_with_id`] + pub fn read_mut_with_id<'a>( + &'a mut self, + events: &'a mut Events, + ) -> EventMutIteratorWithId<'a, E> { + EventMutIteratorWithId::new(self, events) + } + + /// See [`EventReader::par_read`] + #[cfg(feature = "multi_threaded")] + pub fn par_read<'a>(&'a mut self, events: &'a Events) -> EventParIter<'a, E> { + EventParIter::new(self, events) + } + + /// See [`EventMutator::par_read`] + #[cfg(feature = "multi_threaded")] + pub fn par_read_mut<'a>(&'a mut self, events: &'a mut Events) -> EventMutParIter<'a, E> { + EventMutParIter::new(self, events) + } + + /// See [`EventReader::len`] + pub fn len(&self, events: &Events) -> usize { + // The number of events in this reader is the difference between the most recent event + // and the last event seen by it. This will be at most the number of events contained + // with the events (any others have already been dropped) + // TODO: Warn when there are dropped events, or return e.g. a `Result` + events + .event_count + .saturating_sub(self.last_event_count) + .min(events.len()) + } + + /// Amount of events we missed. + pub fn missed_events(&self, events: &Events) -> usize { + events + .oldest_event_count() + .saturating_sub(self.last_event_count) + } + + /// See [`EventReader::is_empty()`] + pub fn is_empty(&self, events: &Events) -> bool { + self.len(events) == 0 + } + + /// See [`EventReader::clear()`] + pub fn clear(&mut self, events: &Events) { + self.last_event_count = events.event_count; + } +} diff --git a/crates/bevy_ecs/src/event/iterators.rs b/crates/bevy_ecs/src/event/iterators.rs index 1c985805ab..843a01509e 100644 --- a/crates/bevy_ecs/src/event/iterators.rs +++ b/crates/bevy_ecs/src/event/iterators.rs @@ -1,11 +1,11 @@ use crate as bevy_ecs; #[cfg(feature = "multi_threaded")] use bevy_ecs::batching::BatchingStrategy; -use bevy_ecs::event::{Event, EventId, EventInstance, Events, ManualEventReader}; +use bevy_ecs::event::{Event, EventCursor, EventId, EventInstance, Events}; use bevy_utils::detailed_trace; use std::{iter::Chain, slice::Iter}; -/// An iterator that yields any unread events from an [`EventReader`] or [`ManualEventReader`]. +/// An iterator that yields any unread events from an [`EventReader`] or [`EventCursor`]. #[derive(Debug)] pub struct EventIterator<'a, E: Event> { iter: EventIteratorWithId<'a, E>, @@ -43,17 +43,17 @@ impl<'a, E: Event> ExactSizeIterator for EventIterator<'a, E> { } } -/// An iterator that yields any unread events (and their IDs) from an [`EventReader`] or [`ManualEventReader`]. +/// An iterator that yields any unread events (and their IDs) from an [`EventReader`] or [`EventCursor`]. #[derive(Debug)] pub struct EventIteratorWithId<'a, E: Event> { - reader: &'a mut ManualEventReader, + reader: &'a mut EventCursor, chain: Chain>, Iter<'a, EventInstance>>, unread: usize, } impl<'a, E: Event> EventIteratorWithId<'a, E> { /// Creates a new iterator that yields any `events` that have not yet been seen by `reader`. - pub fn new(reader: &'a mut ManualEventReader, events: &'a Events) -> Self { + pub fn new(reader: &'a mut EventCursor, events: &'a Events) -> Self { let a_index = reader .last_event_count .saturating_sub(events.events_a.start_event_count); @@ -139,10 +139,10 @@ impl<'a, E: Event> ExactSizeIterator for EventIteratorWithId<'a, E> { } /// A parallel iterator over `Event`s. -#[derive(Debug)] #[cfg(feature = "multi_threaded")] +#[derive(Debug)] pub struct EventParIter<'a, E: Event> { - reader: &'a mut ManualEventReader, + reader: &'a mut EventCursor, slices: [&'a [EventInstance]; 2], batching_strategy: BatchingStrategy, unread: usize, @@ -151,7 +151,7 @@ pub struct EventParIter<'a, E: Event> { #[cfg(feature = "multi_threaded")] impl<'a, E: Event> EventParIter<'a, E> { /// Creates a new parallel iterator over `events` that have not yet been seen by `reader`. - pub fn new(reader: &'a mut ManualEventReader, events: &'a Events) -> Self { + pub fn new(reader: &'a mut EventCursor, events: &'a Events) -> Self { let a_index = reader .last_event_count .saturating_sub(events.events_a.start_event_count); diff --git a/crates/bevy_ecs/src/event/mod.rs b/crates/bevy_ecs/src/event/mod.rs index a959fd5e77..195d66c539 100644 --- a/crates/bevy_ecs/src/event/mod.rs +++ b/crates/bevy_ecs/src/event/mod.rs @@ -1,7 +1,10 @@ //! Event handling types. mod base; mod collections; +mod event_cursor; mod iterators; +mod mut_iterators; +mod mutator; mod reader; mod registry; mod update; @@ -11,10 +14,15 @@ pub(crate) use base::EventInstance; pub use base::{Event, EventId}; pub use bevy_ecs_macros::Event; pub use collections::{Events, SendBatchIds}; +pub use event_cursor::EventCursor; #[cfg(feature = "multi_threaded")] pub use iterators::EventParIter; pub use iterators::{EventIterator, EventIteratorWithId}; -pub use reader::{EventReader, ManualEventReader}; +#[cfg(feature = "multi_threaded")] +pub use mut_iterators::EventMutParIter; +pub use mut_iterators::{EventMutIterator, EventMutIteratorWithId}; +pub use mutator::EventMutator; +pub use reader::EventReader; pub use registry::{EventRegistry, ShouldUpdateEvents}; pub use update::{ event_update_condition, event_update_system, signal_event_update_system, EventUpdates, @@ -32,6 +40,13 @@ mod tests { i: usize, } + #[derive(Event, Clone, PartialEq, Debug, Default)] + struct EmptyTestEvent; + + fn get_events(events: &Events, cursor: &mut EventCursor) -> Vec { + cursor.read(events).cloned().collect::>() + } + #[test] fn test_events() { let mut events = Events::::default(); @@ -41,9 +56,9 @@ mod tests { // this reader will miss event_0 and event_1 because it wont read them over the course of // two updates - let mut reader_missed: ManualEventReader = events.get_reader(); + let mut reader_missed: EventCursor = events.get_cursor(); - let mut reader_a: ManualEventReader = events.get_reader(); + let mut reader_a: EventCursor = events.get_cursor(); events.send(event_0); @@ -58,7 +73,7 @@ mod tests { "second iteration of reader_a created before event results in zero events" ); - let mut reader_b: ManualEventReader = events.get_reader(); + let mut reader_b: EventCursor = events.get_cursor(); assert_eq!( get_events(&events, &mut reader_b), @@ -73,7 +88,7 @@ mod tests { events.send(event_1); - let mut reader_c = events.get_reader(); + let mut reader_c = events.get_cursor(); assert_eq!( get_events(&events, &mut reader_c), @@ -94,7 +109,7 @@ mod tests { events.update(); - let mut reader_d = events.get_reader(); + let mut reader_d = events.get_cursor(); events.send(event_2); @@ -123,35 +138,28 @@ mod tests { ); } - fn get_events( - events: &Events, - reader: &mut ManualEventReader, - ) -> Vec { - reader.read(events).cloned().collect::>() - } - - #[derive(Event, PartialEq, Eq, Debug)] - struct E(usize); - - fn events_clear_and_read_impl(clear_func: impl FnOnce(&mut Events)) { - let mut events = Events::::default(); - let mut reader = events.get_reader(); + // Events Collection + fn events_clear_and_read_impl(clear_func: impl FnOnce(&mut Events)) { + let mut events = Events::::default(); + let mut reader = events.get_cursor(); assert!(reader.read(&events).next().is_none()); - events.send(E(0)); - assert_eq!(*reader.read(&events).next().unwrap(), E(0)); + events.send(TestEvent { i: 0 }); + assert_eq!(*reader.read(&events).next().unwrap(), TestEvent { i: 0 }); assert_eq!(reader.read(&events).next(), None); - events.send(E(1)); + events.send(TestEvent { i: 1 }); clear_func(&mut events); assert!(reader.read(&events).next().is_none()); - events.send(E(2)); + events.send(TestEvent { i: 2 }); events.update(); - events.send(E(3)); + events.send(TestEvent { i: 3 }); - assert!(reader.read(&events).eq([E(2), E(3)].iter())); + assert!(reader + .read(&events) + .eq([TestEvent { i: 2 }, TestEvent { i: 3 }].iter())); } #[test] @@ -162,243 +170,21 @@ mod tests { #[test] fn test_events_drain_and_read() { events_clear_and_read_impl(|events| { - assert!(events.drain().eq(vec![E(0), E(1)].into_iter())); + assert!(events + .drain() + .eq(vec![TestEvent { i: 0 }, TestEvent { i: 1 }].into_iter())); }); } #[test] - fn test_events_extend_impl() { - let mut events = Events::::default(); - let mut reader = events.get_reader(); - - events.extend(vec![TestEvent { i: 0 }, TestEvent { i: 1 }]); - assert!(reader - .read(&events) - .eq([TestEvent { i: 0 }, TestEvent { i: 1 }].iter())); - } - - #[test] - fn test_events_empty() { - let mut events = Events::::default(); - assert!(events.is_empty()); - - events.send(TestEvent { i: 0 }); - assert!(!events.is_empty()); - - events.update(); - assert!(!events.is_empty()); - - // events are only empty after the second call to update - // due to double buffering. - events.update(); - assert!(events.is_empty()); - } - - #[test] - fn test_event_reader_len_empty() { - let events = Events::::default(); - assert_eq!(events.get_reader().len(&events), 0); - assert!(events.get_reader().is_empty(&events)); - } - - #[test] - fn test_event_reader_len_filled() { - let mut events = Events::::default(); - events.send(TestEvent { i: 0 }); - assert_eq!(events.get_reader().len(&events), 1); - assert!(!events.get_reader().is_empty(&events)); - } - - #[test] - fn test_event_iter_len_updated() { - let mut events = Events::::default(); - events.send(TestEvent { i: 0 }); - events.send(TestEvent { i: 1 }); - events.send(TestEvent { i: 2 }); - let mut reader = events.get_reader(); - let mut iter = reader.read(&events); - assert_eq!(iter.len(), 3); - iter.next(); - assert_eq!(iter.len(), 2); - iter.next(); - assert_eq!(iter.len(), 1); - iter.next(); - assert_eq!(iter.len(), 0); - } - - #[test] - fn test_event_reader_len_current() { - let mut events = Events::::default(); - events.send(TestEvent { i: 0 }); - let reader = events.get_reader_current(); - dbg!(&reader); - dbg!(&events); - assert!(reader.is_empty(&events)); - events.send(TestEvent { i: 0 }); - assert_eq!(reader.len(&events), 1); - assert!(!reader.is_empty(&events)); - } - - #[test] - fn test_event_reader_len_update() { - let mut events = Events::::default(); - events.send(TestEvent { i: 0 }); - events.send(TestEvent { i: 0 }); - let reader = events.get_reader(); - assert_eq!(reader.len(&events), 2); - events.update(); - events.send(TestEvent { i: 0 }); - assert_eq!(reader.len(&events), 3); - events.update(); - assert_eq!(reader.len(&events), 1); - events.update(); - assert!(reader.is_empty(&events)); - } - - #[test] - fn test_event_reader_clear() { - use bevy_ecs::prelude::*; - - let mut world = World::new(); - let mut events = Events::::default(); - events.send(TestEvent { i: 0 }); - world.insert_resource(events); - - let mut reader = IntoSystem::into_system(|mut events: EventReader| -> bool { - if !events.is_empty() { - events.clear(); - false - } else { - true - } - }); - reader.initialize(&mut world); - - let is_empty = reader.run((), &mut world); - assert!(!is_empty, "EventReader should not be empty"); - let is_empty = reader.run((), &mut world); - assert!(is_empty, "EventReader should be empty"); - } - - #[test] - fn test_event_registry_can_add_and_remove_events_to_world() { - use bevy_ecs::prelude::*; - - let mut world = World::new(); - EventRegistry::register_event::(&mut world); - - let has_events = world.get_resource::>().is_some(); - - assert!(has_events, "Should have the events resource"); - - EventRegistry::deregister_events::(&mut world); - - let has_events = world.get_resource::>().is_some(); - - assert!(!has_events, "Should not have the events resource"); - } - - #[test] - fn test_update_drain() { - let mut events = Events::::default(); - let mut reader = events.get_reader(); - - events.send(TestEvent { i: 0 }); - events.send(TestEvent { i: 1 }); - assert_eq!(reader.read(&events).count(), 2); - - let mut old_events = Vec::from_iter(events.update_drain()); - assert!(old_events.is_empty()); - - events.send(TestEvent { i: 2 }); - assert_eq!(reader.read(&events).count(), 1); - - old_events.extend(events.update_drain()); - assert_eq!(old_events.len(), 2); - - old_events.extend(events.update_drain()); - assert_eq!( - old_events, - &[TestEvent { i: 0 }, TestEvent { i: 1 }, TestEvent { i: 2 }] - ); - } - - #[allow(clippy::iter_nth_zero)] - #[test] - fn test_event_iter_nth() { - use bevy_ecs::prelude::*; - - let mut world = World::new(); - world.init_resource::>(); - - world.send_event(TestEvent { i: 0 }); - world.send_event(TestEvent { i: 1 }); - world.send_event(TestEvent { i: 2 }); - world.send_event(TestEvent { i: 3 }); - world.send_event(TestEvent { i: 4 }); - - let mut schedule = Schedule::default(); - schedule.add_systems(|mut events: EventReader| { - let mut iter = events.read(); - - assert_eq!(iter.next(), Some(&TestEvent { i: 0 })); - assert_eq!(iter.nth(2), Some(&TestEvent { i: 3 })); - assert_eq!(iter.nth(1), None); - - assert!(events.is_empty()); - }); - schedule.run(&mut world); - } - - #[test] - fn test_event_iter_last() { - use bevy_ecs::prelude::*; - - let mut world = World::new(); - world.init_resource::>(); - - let mut reader = - IntoSystem::into_system(|mut events: EventReader| -> Option { - events.read().last().copied() - }); - reader.initialize(&mut world); - - let last = reader.run((), &mut world); - assert!(last.is_none(), "EventReader should be empty"); - - world.send_event(TestEvent { i: 0 }); - let last = reader.run((), &mut world); - assert_eq!(last, Some(TestEvent { i: 0 })); - - world.send_event(TestEvent { i: 1 }); - world.send_event(TestEvent { i: 2 }); - world.send_event(TestEvent { i: 3 }); - let last = reader.run((), &mut world); - assert_eq!(last, Some(TestEvent { i: 3 })); - - let last = reader.run((), &mut world); - assert!(last.is_none(), "EventReader should be empty"); - } - - #[derive(Event, Clone, PartialEq, Debug, Default)] - struct EmptyTestEvent; - - #[test] - fn test_firing_empty_event() { + fn test_events_send_default() { let mut events = Events::::default(); events.send_default(); - let mut reader = events.get_reader(); + let mut reader = events.get_cursor(); assert_eq!(get_events(&events, &mut reader), vec![EmptyTestEvent]); } - #[test] - fn ensure_reader_readonly() { - fn reader_system(_: EventReader) {} - - assert_is_read_only_system(reader_system); - } - #[test] fn test_send_events_ids() { let mut events = Events::::default(); @@ -438,9 +224,244 @@ mod tests { ); } + #[test] + fn test_event_registry_can_add_and_remove_events_to_world() { + use bevy_ecs::prelude::*; + + let mut world = World::new(); + EventRegistry::register_event::(&mut world); + + let has_events = world.get_resource::>().is_some(); + assert!(has_events, "Should have the events resource"); + + EventRegistry::deregister_events::(&mut world); + + let has_events = world.get_resource::>().is_some(); + assert!(!has_events, "Should not have the events resource"); + } + + #[test] + fn test_events_update_drain() { + let mut events = Events::::default(); + let mut reader = events.get_cursor(); + + events.send(TestEvent { i: 0 }); + events.send(TestEvent { i: 1 }); + assert_eq!(reader.read(&events).count(), 2); + + let mut old_events = Vec::from_iter(events.update_drain()); + assert!(old_events.is_empty()); + + events.send(TestEvent { i: 2 }); + assert_eq!(reader.read(&events).count(), 1); + + old_events.extend(events.update_drain()); + assert_eq!(old_events.len(), 2); + + old_events.extend(events.update_drain()); + assert_eq!( + old_events, + &[TestEvent { i: 0 }, TestEvent { i: 1 }, TestEvent { i: 2 }] + ); + } + + #[test] + fn test_events_empty() { + let mut events = Events::::default(); + assert!(events.is_empty()); + + events.send(TestEvent { i: 0 }); + assert!(!events.is_empty()); + + events.update(); + assert!(!events.is_empty()); + + // events are only empty after the second call to update + // due to double buffering. + events.update(); + assert!(events.is_empty()); + } + + #[test] + fn test_events_extend_impl() { + let mut events = Events::::default(); + let mut reader = events.get_cursor(); + + events.extend(vec![TestEvent { i: 0 }, TestEvent { i: 1 }]); + assert!(reader + .read(&events) + .eq([TestEvent { i: 0 }, TestEvent { i: 1 }].iter())); + } + + // Cursor + #[test] + fn test_event_cursor_read() { + let mut events = Events::::default(); + let mut cursor = events.get_cursor(); + assert!(cursor.read(&events).next().is_none()); + + events.send(TestEvent { i: 0 }); + let sent_event = cursor.read(&events).next().unwrap(); + assert_eq!(sent_event, &TestEvent { i: 0 }); + assert!(cursor.read(&events).next().is_none()); + + events.send(TestEvent { i: 2 }); + let sent_event = cursor.read(&events).next().unwrap(); + assert_eq!(sent_event, &TestEvent { i: 2 }); + assert!(cursor.read(&events).next().is_none()); + + events.clear(); + assert!(cursor.read(&events).next().is_none()); + } + + #[test] + fn test_event_cursor_read_mut() { + let mut events = Events::::default(); + let mut write_cursor = events.get_cursor(); + let mut read_cursor = events.get_cursor(); + assert!(write_cursor.read_mut(&mut events).next().is_none()); + assert!(read_cursor.read(&events).next().is_none()); + + events.send(TestEvent { i: 0 }); + let sent_event = write_cursor.read_mut(&mut events).next().unwrap(); + assert_eq!(sent_event, &mut TestEvent { i: 0 }); + *sent_event = TestEvent { i: 1 }; // Mutate whole event + assert_eq!( + read_cursor.read(&events).next().unwrap(), + &TestEvent { i: 1 } + ); + assert!(read_cursor.read(&events).next().is_none()); + + events.send(TestEvent { i: 2 }); + let sent_event = write_cursor.read_mut(&mut events).next().unwrap(); + assert_eq!(sent_event, &mut TestEvent { i: 2 }); + sent_event.i = 3; // Mutate sub value + assert_eq!( + read_cursor.read(&events).next().unwrap(), + &TestEvent { i: 3 } + ); + assert!(read_cursor.read(&events).next().is_none()); + + events.clear(); + assert!(write_cursor.read(&events).next().is_none()); + assert!(read_cursor.read(&events).next().is_none()); + } + + #[test] + fn test_event_cursor_clear() { + let mut events = Events::::default(); + let mut reader = events.get_cursor(); + + events.send(TestEvent { i: 0 }); + assert_eq!(reader.len(&events), 1); + reader.clear(&events); + assert_eq!(reader.len(&events), 0); + } + + #[test] + fn test_event_cursor_len_update() { + let mut events = Events::::default(); + events.send(TestEvent { i: 0 }); + events.send(TestEvent { i: 0 }); + let reader = events.get_cursor(); + assert_eq!(reader.len(&events), 2); + events.update(); + events.send(TestEvent { i: 0 }); + assert_eq!(reader.len(&events), 3); + events.update(); + assert_eq!(reader.len(&events), 1); + events.update(); + assert!(reader.is_empty(&events)); + } + + #[test] + fn test_event_cursor_len_current() { + let mut events = Events::::default(); + events.send(TestEvent { i: 0 }); + let reader = events.get_cursor_current(); + assert!(reader.is_empty(&events)); + events.send(TestEvent { i: 0 }); + assert_eq!(reader.len(&events), 1); + assert!(!reader.is_empty(&events)); + } + + #[test] + fn test_event_cursor_iter_len_updated() { + let mut events = Events::::default(); + events.send(TestEvent { i: 0 }); + events.send(TestEvent { i: 1 }); + events.send(TestEvent { i: 2 }); + let mut reader = events.get_cursor(); + let mut iter = reader.read(&events); + assert_eq!(iter.len(), 3); + iter.next(); + assert_eq!(iter.len(), 2); + iter.next(); + assert_eq!(iter.len(), 1); + iter.next(); + assert_eq!(iter.len(), 0); + } + + #[test] + fn test_event_cursor_len_empty() { + let events = Events::::default(); + assert_eq!(events.get_cursor().len(&events), 0); + assert!(events.get_cursor().is_empty(&events)); + } + + #[test] + fn test_event_cursor_len_filled() { + let mut events = Events::::default(); + events.send(TestEvent { i: 0 }); + assert_eq!(events.get_cursor().len(&events), 1); + assert!(!events.get_cursor().is_empty(&events)); + } + #[cfg(feature = "multi_threaded")] #[test] - fn test_events_par_iter() { + fn test_event_cursor_par_read() { + use crate::prelude::*; + use std::sync::atomic::{AtomicUsize, Ordering}; + + #[derive(Resource)] + struct Counter(AtomicUsize); + + let mut world = World::new(); + world.init_resource::>(); + for _ in 0..100 { + world.send_event(TestEvent { i: 1 }); + } + + let mut schedule = Schedule::default(); + + schedule.add_systems( + |mut cursor: Local>, + events: Res>, + counter: ResMut| { + cursor.par_read(&events).for_each(|event| { + counter.0.fetch_add(event.i, Ordering::Relaxed); + }); + }, + ); + + world.insert_resource(Counter(AtomicUsize::new(0))); + schedule.run(&mut world); + let counter = world.remove_resource::().unwrap(); + assert_eq!(counter.0.into_inner(), 100); + + world.insert_resource(Counter(AtomicUsize::new(0))); + schedule.run(&mut world); + let counter = world.remove_resource::().unwrap(); + assert_eq!( + counter.0.into_inner(), + 0, + "par_read should have consumed events but didn't" + ); + } + + #[cfg(feature = "multi_threaded")] + #[test] + fn test_event_cursor_par_read_mut() { use crate::prelude::*; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -454,8 +475,11 @@ mod tests { } let mut schedule = Schedule::default(); schedule.add_systems( - |mut events: EventReader, counter: ResMut| { - events.par_read().for_each(|event| { + |mut cursor: Local>, + mut events: ResMut>, + counter: ResMut| { + cursor.par_read_mut(&mut events).for_each(|event| { + event.i += 1; counter.0.fetch_add(event.i, Ordering::Relaxed); }); }, @@ -463,11 +487,137 @@ mod tests { world.insert_resource(Counter(AtomicUsize::new(0))); schedule.run(&mut world); let counter = world.remove_resource::().unwrap(); - assert_eq!(counter.0.into_inner(), 100); + assert_eq!(counter.0.into_inner(), 200, "Initial run failed"); world.insert_resource(Counter(AtomicUsize::new(0))); schedule.run(&mut world); let counter = world.remove_resource::().unwrap(); - assert_eq!(counter.0.into_inner(), 0); + assert_eq!( + counter.0.into_inner(), + 0, + "par_read_mut should have consumed events but didn't" + ); + } + + // Reader & Mutator + #[test] + fn ensure_reader_readonly() { + fn reader_system(_: EventReader) {} + + assert_is_read_only_system(reader_system); + } + + #[test] + fn test_event_reader_iter_last() { + use bevy_ecs::prelude::*; + + let mut world = World::new(); + world.init_resource::>(); + + let mut reader = + IntoSystem::into_system(|mut events: EventReader| -> Option { + events.read().last().copied() + }); + reader.initialize(&mut world); + + let last = reader.run((), &mut world); + assert!(last.is_none(), "EventReader should be empty"); + + world.send_event(TestEvent { i: 0 }); + let last = reader.run((), &mut world); + assert_eq!(last, Some(TestEvent { i: 0 })); + + world.send_event(TestEvent { i: 1 }); + world.send_event(TestEvent { i: 2 }); + world.send_event(TestEvent { i: 3 }); + let last = reader.run((), &mut world); + assert_eq!(last, Some(TestEvent { i: 3 })); + + let last = reader.run((), &mut world); + assert!(last.is_none(), "EventReader should be empty"); + } + + #[test] + fn test_event_mutator_iter_last() { + use bevy_ecs::prelude::*; + + let mut world = World::new(); + world.init_resource::>(); + + let mut mutator = + IntoSystem::into_system(|mut events: EventMutator| -> Option { + events.read().last().copied() + }); + mutator.initialize(&mut world); + + let last = mutator.run((), &mut world); + assert!(last.is_none(), "EventMutator should be empty"); + + world.send_event(TestEvent { i: 0 }); + let last = mutator.run((), &mut world); + assert_eq!(last, Some(TestEvent { i: 0 })); + + world.send_event(TestEvent { i: 1 }); + world.send_event(TestEvent { i: 2 }); + world.send_event(TestEvent { i: 3 }); + let last = mutator.run((), &mut world); + assert_eq!(last, Some(TestEvent { i: 3 })); + + let last = mutator.run((), &mut world); + assert!(last.is_none(), "EventMutator should be empty"); + } + + #[allow(clippy::iter_nth_zero)] + #[test] + fn test_event_reader_iter_nth() { + use bevy_ecs::prelude::*; + + let mut world = World::new(); + world.init_resource::>(); + + world.send_event(TestEvent { i: 0 }); + world.send_event(TestEvent { i: 1 }); + world.send_event(TestEvent { i: 2 }); + world.send_event(TestEvent { i: 3 }); + world.send_event(TestEvent { i: 4 }); + + let mut schedule = Schedule::default(); + schedule.add_systems(|mut events: EventReader| { + let mut iter = events.read(); + + assert_eq!(iter.next(), Some(&TestEvent { i: 0 })); + assert_eq!(iter.nth(2), Some(&TestEvent { i: 3 })); + assert_eq!(iter.nth(1), None); + + assert!(events.is_empty()); + }); + schedule.run(&mut world); + } + + #[allow(clippy::iter_nth_zero)] + #[test] + fn test_event_mutator_iter_nth() { + use bevy_ecs::prelude::*; + + let mut world = World::new(); + world.init_resource::>(); + + world.send_event(TestEvent { i: 0 }); + world.send_event(TestEvent { i: 1 }); + world.send_event(TestEvent { i: 2 }); + world.send_event(TestEvent { i: 3 }); + world.send_event(TestEvent { i: 4 }); + + let mut schedule = Schedule::default(); + schedule.add_systems(|mut events: EventReader| { + let mut iter = events.read(); + + assert_eq!(iter.next(), Some(&TestEvent { i: 0 })); + assert_eq!(iter.nth(2), Some(&TestEvent { i: 3 })); + assert_eq!(iter.nth(1), None); + + assert!(events.is_empty()); + }); + schedule.run(&mut world); } } diff --git a/crates/bevy_ecs/src/event/mut_iterators.rs b/crates/bevy_ecs/src/event/mut_iterators.rs new file mode 100644 index 0000000000..677ae2e106 --- /dev/null +++ b/crates/bevy_ecs/src/event/mut_iterators.rs @@ -0,0 +1,275 @@ +use crate as bevy_ecs; +#[cfg(feature = "multi_threaded")] +use bevy_ecs::batching::BatchingStrategy; +use bevy_ecs::event::{Event, EventCursor, EventId, EventInstance, Events}; +use bevy_utils::detailed_trace; +use std::{iter::Chain, slice::IterMut}; + +/// An iterator that yields any unread events from an [`EventMutator`] or [`EventCursor`]. +#[derive(Debug)] +pub struct EventMutIterator<'a, E: Event> { + iter: EventMutIteratorWithId<'a, E>, +} + +impl<'a, E: Event> Iterator for EventMutIterator<'a, E> { + type Item = &'a mut E; + fn next(&mut self) -> Option { + self.iter.next().map(|(event, _)| event) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.count() + } + + fn last(self) -> Option + where + Self: Sized, + { + self.iter.last().map(|(event, _)| event) + } + + fn nth(&mut self, n: usize) -> Option { + self.iter.nth(n).map(|(event, _)| event) + } +} + +impl<'a, E: Event> ExactSizeIterator for EventMutIterator<'a, E> { + fn len(&self) -> usize { + self.iter.len() + } +} + +/// An iterator that yields any unread events (and their IDs) from an [`EventMutator`] or [`EventCursor`]. +#[derive(Debug)] +pub struct EventMutIteratorWithId<'a, E: Event> { + mutator: &'a mut EventCursor, + chain: Chain>, IterMut<'a, EventInstance>>, + unread: usize, +} + +impl<'a, E: Event> EventMutIteratorWithId<'a, E> { + /// Creates a new iterator that yields any `events` that have not yet been seen by `mutator`. + pub fn new(mutator: &'a mut EventCursor, events: &'a mut Events) -> Self { + let a_index = mutator + .last_event_count + .saturating_sub(events.events_a.start_event_count); + let b_index = mutator + .last_event_count + .saturating_sub(events.events_b.start_event_count); + let a = events.events_a.get_mut(a_index..).unwrap_or_default(); + let b = events.events_b.get_mut(b_index..).unwrap_or_default(); + + let unread_count = a.len() + b.len(); + + mutator.last_event_count = events.event_count - unread_count; + // Iterate the oldest first, then the newer events + let chain = a.iter_mut().chain(b.iter_mut()); + + Self { + mutator, + chain, + unread: unread_count, + } + } + + /// Iterate over only the events. + pub fn without_id(self) -> EventMutIterator<'a, E> { + EventMutIterator { iter: self } + } +} + +impl<'a, E: Event> Iterator for EventMutIteratorWithId<'a, E> { + type Item = (&'a mut E, EventId); + fn next(&mut self) -> Option { + match self + .chain + .next() + .map(|instance| (&mut instance.event, instance.event_id)) + { + Some(item) => { + detailed_trace!("EventMutator::iter() -> {}", item.1); + self.mutator.last_event_count += 1; + self.unread -= 1; + Some(item) + } + None => None, + } + } + + fn size_hint(&self) -> (usize, Option) { + self.chain.size_hint() + } + + fn count(self) -> usize { + self.mutator.last_event_count += self.unread; + self.unread + } + + fn last(self) -> Option + where + Self: Sized, + { + let EventInstance { event_id, event } = self.chain.last()?; + self.mutator.last_event_count += self.unread; + Some((event, *event_id)) + } + + fn nth(&mut self, n: usize) -> Option { + if let Some(EventInstance { event_id, event }) = self.chain.nth(n) { + self.mutator.last_event_count += n + 1; + self.unread -= n + 1; + Some((event, *event_id)) + } else { + self.mutator.last_event_count += self.unread; + self.unread = 0; + None + } + } +} + +impl<'a, E: Event> ExactSizeIterator for EventMutIteratorWithId<'a, E> { + fn len(&self) -> usize { + self.unread + } +} + +/// A parallel iterator over `Event`s. +#[derive(Debug)] +#[cfg(feature = "multi_threaded")] +pub struct EventMutParIter<'a, E: Event> { + mutator: &'a mut EventCursor, + slices: [&'a mut [EventInstance]; 2], + batching_strategy: BatchingStrategy, + unread: usize, +} + +#[cfg(feature = "multi_threaded")] +impl<'a, E: Event> EventMutParIter<'a, E> { + /// Creates a new parallel iterator over `events` that have not yet been seen by `mutator`. + pub fn new(mutator: &'a mut EventCursor, events: &'a mut Events) -> Self { + let a_index = mutator + .last_event_count + .saturating_sub(events.events_a.start_event_count); + let b_index = mutator + .last_event_count + .saturating_sub(events.events_b.start_event_count); + let a = events.events_a.get_mut(a_index..).unwrap_or_default(); + let b = events.events_b.get_mut(b_index..).unwrap_or_default(); + + let unread_count = a.len() + b.len(); + mutator.last_event_count = events.event_count - unread_count; + + Self { + mutator, + slices: [a, b], + batching_strategy: BatchingStrategy::default(), + unread: unread_count, + } + } + + /// Changes the batching strategy used when iterating. + /// + /// For more information on how this affects the resultant iteration, see + /// [`BatchingStrategy`]. + pub fn batching_strategy(mut self, strategy: BatchingStrategy) -> Self { + self.batching_strategy = strategy; + self + } + + /// Runs the provided closure for each unread event in parallel. + /// + /// Unlike normal iteration, the event order is not guaranteed in any form. + /// + /// # Panics + /// If the [`ComputeTaskPool`] is not initialized. If using this from an event reader that is being + /// initialized and run from the ECS scheduler, this should never panic. + /// + /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool + pub fn for_each(self, func: FN) { + self.for_each_with_id(move |e, _| func(e)); + } + + /// Runs the provided closure for each unread event in parallel, like [`for_each`](Self::for_each), + /// but additionally provides the `EventId` to the closure. + /// + /// Note that the order of iteration is not guaranteed, but `EventId`s are ordered by send order. + /// + /// # Panics + /// If the [`ComputeTaskPool`] is not initialized. If using this from an event reader that is being + /// initialized and run from the ECS scheduler, this should never panic. + /// + /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool + pub fn for_each_with_id) + Send + Sync + Clone>( + mut self, + func: FN, + ) { + #[cfg(target_arch = "wasm32")] + { + self.into_iter().for_each(|(e, i)| func(e, i)); + } + + #[cfg(not(target_arch = "wasm32"))] + { + let pool = bevy_tasks::ComputeTaskPool::get(); + let thread_count = pool.thread_num(); + if thread_count <= 1 { + return self.into_iter().for_each(|(e, i)| func(e, i)); + } + + let batch_size = self + .batching_strategy + .calc_batch_size(|| self.len(), thread_count); + let chunks = self.slices.map(|s| s.chunks_mut(batch_size)); + + pool.scope(|scope| { + for batch in chunks.into_iter().flatten() { + let func = func.clone(); + scope.spawn(async move { + for event in batch { + func(&mut event.event, event.event_id); + } + }); + } + }); + + // Events are guaranteed to be read at this point. + self.mutator.last_event_count += self.unread; + self.unread = 0; + } + } + + /// Returns the number of [`Event`]s to be iterated. + pub fn len(&self) -> usize { + self.slices.iter().map(|s| s.len()).sum() + } + + /// Returns [`true`] if there are no events remaining in this iterator. + pub fn is_empty(&self) -> bool { + self.slices.iter().all(|x| x.is_empty()) + } +} + +#[cfg(feature = "multi_threaded")] +impl<'a, E: Event> IntoIterator for EventMutParIter<'a, E> { + type IntoIter = EventMutIteratorWithId<'a, E>; + type Item = ::Item; + + fn into_iter(self) -> Self::IntoIter { + let EventMutParIter { + mutator: reader, + slices: [a, b], + .. + } = self; + let unread = a.len() + b.len(); + let chain = a.iter_mut().chain(b); + EventMutIteratorWithId { + mutator: reader, + chain, + unread, + } + } +} diff --git a/crates/bevy_ecs/src/event/mutator.rs b/crates/bevy_ecs/src/event/mutator.rs new file mode 100644 index 0000000000..bf518a2768 --- /dev/null +++ b/crates/bevy_ecs/src/event/mutator.rs @@ -0,0 +1,142 @@ +use crate as bevy_ecs; +#[cfg(feature = "multi_threaded")] +use bevy_ecs::event::EventMutParIter; +use bevy_ecs::{ + event::{Event, EventCursor, EventMutIterator, EventMutIteratorWithId, Events}, + system::{Local, ResMut, SystemParam}, +}; + +/// Mutably reads events of type `T` keeping track of which events have already been read +/// by each system allowing multiple systems to read the same events. Ideal for chains of systems +/// that all want to modify the same events. +/// +/// # Usage +/// +/// `EventMutators`s are usually declared as a [`SystemParam`]. +/// ``` +/// # use bevy_ecs::prelude::*; +/// +/// #[derive(Event, Debug)] +/// pub struct MyEvent(pub u32); // Custom event type. +/// fn my_system(mut reader: EventMutator) { +/// for event in reader.read() { +/// event.0 += 1; +/// println!("received event: {:?}", event); +/// } +/// } +/// ``` +/// +/// # Concurrency +/// +/// Multiple systems with `EventMutator` of the same event type can not run concurrently. +/// They also can not be executed in parallel with [`EventReader`] or [`EventWriter`]. +/// +/// # Clearing, Reading, and Peeking +/// +/// Events are stored in a double buffered queue that switches each frame. This switch also clears the previous +/// frame's events. Events should be read each frame otherwise they may be lost. For manual control over this +/// behavior, see [`Events`]. +/// +/// Most of the time systems will want to use [`EventMutator::read()`]. This function creates an iterator over +/// all events that haven't been read yet by this system, marking the event as read in the process. +/// +#[derive(SystemParam, Debug)] +pub struct EventMutator<'w, 's, E: Event> { + pub(super) reader: Local<'s, EventCursor>, + events: ResMut<'w, Events>, +} + +impl<'w, 's, E: Event> EventMutator<'w, 's, E> { + /// Iterates over the events this [`EventMutator`] has not seen yet. This updates the + /// [`EventMutator`]'s event counter, which means subsequent event reads will not include events + /// that happened before now. + pub fn read(&mut self) -> EventMutIterator<'_, E> { + self.reader.read_mut(&mut self.events) + } + + /// Like [`read`](Self::read), except also returning the [`EventId`] of the events. + pub fn read_with_id(&mut self) -> EventMutIteratorWithId<'_, E> { + self.reader.read_mut_with_id(&mut self.events) + } + + /// Returns a parallel iterator over the events this [`EventMutator`] has not seen yet. + /// See also [`for_each`](EventParIter::for_each). + /// + /// # Example + /// ``` + /// # use bevy_ecs::prelude::*; + /// # use std::sync::atomic::{AtomicUsize, Ordering}; + /// + /// #[derive(Event)] + /// struct MyEvent { + /// value: usize, + /// } + /// + /// #[derive(Resource, Default)] + /// struct Counter(AtomicUsize); + /// + /// // setup + /// let mut world = World::new(); + /// world.init_resource::>(); + /// world.insert_resource(Counter::default()); + /// + /// let mut schedule = Schedule::default(); + /// schedule.add_systems(|mut events: EventMutator, counter: Res| { + /// events.par_read().for_each(|MyEvent { value }| { + /// counter.0.fetch_add(*value, Ordering::Relaxed); + /// }); + /// }); + /// for value in 0..100 { + /// world.send_event(MyEvent { value }); + /// } + /// schedule.run(&mut world); + /// let Counter(counter) = world.remove_resource::().unwrap(); + /// // all events were processed + /// assert_eq!(counter.into_inner(), 4950); + /// ``` + /// + #[cfg(feature = "multi_threaded")] + pub fn par_read(&mut self) -> EventMutParIter<'_, E> { + self.reader.par_read_mut(&mut self.events) + } + + /// Determines the number of events available to be read from this [`EventMutator`] without consuming any. + pub fn len(&self) -> usize { + self.reader.len(&self.events) + } + + /// Returns `true` if there are no events available to read. + /// + /// # Example + /// + /// The following example shows a useful pattern where some behavior is triggered if new events are available. + /// [`EventMutator::clear()`] is used so the same events don't re-trigger the behavior the next time the system runs. + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # + /// #[derive(Event)] + /// struct CollisionEvent; + /// + /// fn play_collision_sound(mut events: EventMutator) { + /// if !events.is_empty() { + /// events.clear(); + /// // Play a sound + /// } + /// } + /// # bevy_ecs::system::assert_is_system(play_collision_sound); + /// ``` + pub fn is_empty(&self) -> bool { + self.reader.is_empty(&self.events) + } + + /// Consumes all available events. + /// + /// This means these events will not appear in calls to [`EventMutator::read()`] or + /// [`EventMutator::read_with_id()`] and [`EventMutator::is_empty()`] will return `true`. + /// + /// For usage, see [`EventMutator::is_empty()`]. + pub fn clear(&mut self) { + self.reader.clear(&self.events); + } +} diff --git a/crates/bevy_ecs/src/event/reader.rs b/crates/bevy_ecs/src/event/reader.rs index 2152613fb9..08b0879659 100644 --- a/crates/bevy_ecs/src/event/reader.rs +++ b/crates/bevy_ecs/src/event/reader.rs @@ -2,20 +2,19 @@ use crate as bevy_ecs; #[cfg(feature = "multi_threaded")] use bevy_ecs::event::EventParIter; use bevy_ecs::{ - event::{Event, EventIterator, EventIteratorWithId, Events}, + event::{Event, EventCursor, EventIterator, EventIteratorWithId, Events}, system::{Local, Res, SystemParam}, }; -use std::marker::PhantomData; /// Reads events of type `T` in order and tracks which events have already been read. /// /// # Concurrency /// /// Unlike [`EventWriter`], systems with `EventReader` param can be executed concurrently -/// (but not concurrently with `EventWriter` systems for the same event type). +/// (but not concurrently with `EventWriter` or `EventMutator` systems for the same event type). #[derive(SystemParam, Debug)] pub struct EventReader<'w, 's, E: Event> { - pub(super) reader: Local<'s, ManualEventReader>, + pub(super) reader: Local<'s, EventCursor>, events: Res<'w, Events>, } @@ -113,116 +112,3 @@ impl<'w, 's, E: Event> EventReader<'w, 's, E> { self.reader.clear(&self.events); } } - -/// Stores the state for an [`EventReader`]. -/// -/// Access to the [`Events`] resource is required to read any incoming events. -/// -/// In almost all cases, you should just use an [`EventReader`], -/// which will automatically manage the state for you. -/// -/// However, this type can be useful if you need to manually track events, -/// such as when you're attempting to send and receive events of the same type in the same system. -/// -/// # Example -/// -/// ``` -/// use bevy_ecs::prelude::*; -/// use bevy_ecs::event::{Event, Events, ManualEventReader}; -/// -/// #[derive(Event, Clone, Debug)] -/// struct MyEvent; -/// -/// /// A system that both sends and receives events using a [`Local`] [`ManualEventReader`]. -/// fn send_and_receive_manual_event_reader( -/// // The `Local` `SystemParam` stores state inside the system itself, rather than in the world. -/// // `ManualEventReader` is the internal state of `EventReader`, which tracks which events have been seen. -/// mut local_event_reader: Local>, -/// // We can access the `Events` resource mutably, allowing us to both read and write its contents. -/// mut events: ResMut>, -/// ) { -/// // We must collect the events to resend, because we can't mutate events while we're iterating over the events. -/// let mut events_to_resend = Vec::new(); -/// -/// for event in local_event_reader.read(&events) { -/// events_to_resend.push(event.clone()); -/// } -/// -/// for event in events_to_resend { -/// events.send(MyEvent); -/// } -/// } -/// -/// # bevy_ecs::system::assert_is_system(send_and_receive_manual_event_reader); -/// ``` -#[derive(Debug)] -pub struct ManualEventReader { - pub(super) last_event_count: usize, - pub(super) _marker: PhantomData, -} - -impl Default for ManualEventReader { - fn default() -> Self { - ManualEventReader { - last_event_count: 0, - _marker: Default::default(), - } - } -} - -impl Clone for ManualEventReader { - fn clone(&self) -> Self { - ManualEventReader { - last_event_count: self.last_event_count, - _marker: PhantomData, - } - } -} - -#[allow(clippy::len_without_is_empty)] // Check fails since the is_empty implementation has a signature other than `(&self) -> bool` -impl ManualEventReader { - /// See [`EventReader::read`] - pub fn read<'a>(&'a mut self, events: &'a Events) -> EventIterator<'a, E> { - self.read_with_id(events).without_id() - } - - /// See [`EventReader::read_with_id`] - pub fn read_with_id<'a>(&'a mut self, events: &'a Events) -> EventIteratorWithId<'a, E> { - EventIteratorWithId::new(self, events) - } - - /// See [`EventReader::par_read`] - #[cfg(feature = "multi_threaded")] - pub fn par_read<'a>(&'a mut self, events: &'a Events) -> EventParIter<'a, E> { - EventParIter::new(self, events) - } - - /// See [`EventReader::len`] - pub fn len(&self, events: &Events) -> usize { - // The number of events in this reader is the difference between the most recent event - // and the last event seen by it. This will be at most the number of events contained - // with the events (any others have already been dropped) - // TODO: Warn when there are dropped events, or return e.g. a `Result` - events - .event_count - .saturating_sub(self.last_event_count) - .min(events.len()) - } - - /// Amount of events we missed. - pub fn missed_events(&self, events: &Events) -> usize { - events - .oldest_event_count() - .saturating_sub(self.last_event_count) - } - - /// See [`EventReader::is_empty()`] - pub fn is_empty(&self, events: &Events) -> bool { - self.len(events) == 0 - } - - /// See [`EventReader::clear()`] - pub fn clear(&mut self, events: &Events) { - self.last_event_count = events.event_count; - } -} diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 307c7a94a1..da573baf04 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -46,7 +46,7 @@ pub mod prelude { change_detection::{DetectChanges, DetectChangesMut, Mut, Ref}, component::Component, entity::{Entity, EntityMapper}, - event::{Event, EventReader, EventWriter, Events}, + event::{Event, EventMutator, EventReader, EventWriter, Events}, observer::{Observer, Trigger}, query::{Added, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without}, removal_detection::RemovedComponents, diff --git a/crates/bevy_ecs/src/removal_detection.rs b/crates/bevy_ecs/src/removal_detection.rs index 2e009de3d7..f55af1f5c6 100644 --- a/crates/bevy_ecs/src/removal_detection.rs +++ b/crates/bevy_ecs/src/removal_detection.rs @@ -4,7 +4,7 @@ use crate::{ self as bevy_ecs, component::{Component, ComponentId, ComponentIdFor, Tick}, entity::Entity, - event::{Event, EventId, EventIterator, EventIteratorWithId, Events, ManualEventReader}, + event::{Event, EventCursor, EventId, EventIterator, EventIteratorWithId, Events}, prelude::Local, storage::SparseSet, system::{ReadOnlySystemParam, SystemMeta, SystemParam}, @@ -30,14 +30,14 @@ impl From for Entity { } } -/// Wrapper around a [`ManualEventReader`] so that we +/// Wrapper around a [`EventCursor`] so that we /// can differentiate events between components. #[derive(Debug)] pub struct RemovedComponentReader where T: Component, { - reader: ManualEventReader, + reader: EventCursor, marker: PhantomData, } @@ -51,7 +51,7 @@ impl Default for RemovedComponentReader { } impl Deref for RemovedComponentReader { - type Target = ManualEventReader; + type Target = EventCursor; fn deref(&self) -> &Self::Target { &self.reader } @@ -172,13 +172,13 @@ fn map_id_events( // For all practical purposes, the api surface of `RemovedComponents` // should be similar to `EventReader` to reduce confusion. impl<'w, 's, T: Component> RemovedComponents<'w, 's, T> { - /// Fetch underlying [`ManualEventReader`]. - pub fn reader(&self) -> &ManualEventReader { + /// Fetch underlying [`EventCursor`]. + pub fn reader(&self) -> &EventCursor { &self.reader } - /// Fetch underlying [`ManualEventReader`] mutably. - pub fn reader_mut(&mut self) -> &mut ManualEventReader { + /// Fetch underlying [`EventCursor`] mutably. + pub fn reader_mut(&mut self) -> &mut EventCursor { &mut self.reader } @@ -187,7 +187,7 @@ impl<'w, 's, T: Component> RemovedComponents<'w, 's, T> { self.event_sets.get(self.component_id.get()) } - /// Destructures to get a mutable reference to the `ManualEventReader` + /// Destructures to get a mutable reference to the `EventCursor` /// and a reference to `Events`. /// /// This is necessary since Rust can't detect destructuring through methods and most diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 9e43b8711d..45791eb1d6 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -3,7 +3,7 @@ use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ entity::Entity, - event::{Event, Events, ManualEventReader}, + event::{Event, EventCursor, Events}, reflect::AppTypeRegistry, system::Resource, world::{Command, Mut, World}, @@ -64,7 +64,7 @@ impl InstanceId { pub struct SceneSpawner { pub(crate) spawned_dynamic_scenes: HashMap, HashSet>, pub(crate) spawned_instances: HashMap, - scene_asset_event_reader: ManualEventReader>, + scene_asset_event_reader: EventCursor>, dynamic_scenes_to_spawn: Vec<(Handle, InstanceId, Option)>, scenes_to_spawn: Vec<(Handle, InstanceId, Option)>, scenes_to_despawn: Vec>, diff --git a/crates/bevy_state/src/app.rs b/crates/bevy_state/src/app.rs index a239bfd5be..1044e0203d 100644 --- a/crates/bevy_state/src/app.rs +++ b/crates/bevy_state/src/app.rs @@ -256,7 +256,7 @@ mod tests { assert_eq!(world.resource::>().0, TestState::B); let events = world.resource::>>(); assert_eq!(events.len(), 1); - let mut reader = events.get_reader(); + let mut reader = events.get_cursor(); let last = reader.read(events).last().unwrap(); assert_eq!(last.exited, None); assert_eq!(last.entered, Some(TestState::B)); @@ -276,7 +276,7 @@ mod tests { assert_eq!(world.resource::>().0, TestState::C); let events = world.resource::>>(); assert_eq!(events.len(), 1); - let mut reader = events.get_reader(); + let mut reader = events.get_cursor(); let last = reader.read(events).last().unwrap(); assert_eq!(last.exited, None); assert_eq!(last.entered, Some(TestState::C)); diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index dcba31bb41..8b866d5979 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -2,7 +2,7 @@ use approx::relative_eq; use bevy_app::{App, AppExit, PluginsState}; use bevy_ecs::change_detection::{DetectChanges, NonSendMut, Res}; use bevy_ecs::entity::Entity; -use bevy_ecs::event::{EventWriter, ManualEventReader}; +use bevy_ecs::event::{EventCursor, EventWriter}; use bevy_ecs::prelude::*; use bevy_ecs::system::SystemState; use bevy_ecs::world::FromWorld; @@ -408,7 +408,7 @@ impl ApplicationHandler for WinitAppRunnerState { create_windows(event_loop, create_window.get_mut(self.world_mut())); create_window.apply(self.world_mut()); - let mut redraw_event_reader = ManualEventReader::::default(); + let mut redraw_event_reader = EventCursor::::default(); let mut focused_windows_state: SystemState<(Res, Query<(Entity, &Window)>)> = SystemState::new(self.world_mut()); diff --git a/examples/ecs/event.rs b/examples/ecs/event.rs index 7d5abd1856..e085a874cb 100644 --- a/examples/ecs/event.rs +++ b/examples/ecs/event.rs @@ -1,63 +1,144 @@ -//! This example creates a new event, a system that triggers the event once per second, -//! and a system that prints a message whenever the event is received. +//! This example shows how to send, mutate, and receive, events. As well as showing +//! how to you might control system ordering so that events are processed in a specific order. +//! It does this by simulating a damage over time effect that you might find in a game. use bevy::prelude::*; -fn main() { - App::new() - .add_plugins(DefaultPlugins) - .add_event::() - .add_event::() - .init_resource::() - .add_systems(Update, (event_trigger, event_listener, sound_player)) - .run(); +// In order to send or receive events first you must define them +// This event should be sent when something attempts to deal damage to another entity. +#[derive(Event, Debug)] +struct DealDamage { + pub amount: i32, } -#[derive(Event)] -struct MyEvent { - pub message: String, -} +// This event should be sent when an entity receives damage. +#[derive(Event, Debug, Default)] +struct DamageReceived; -#[derive(Event, Default)] -struct PlaySound; +// This event should be sent when an entity blocks damage with armor. +#[derive(Event, Debug, Default)] +struct ArmorBlockedDamage; -#[derive(Resource)] -struct EventTriggerState { - event_timer: Timer, -} +// This resource represents a timer used to determine when to deal damage +// By default it repeats once per second +#[derive(Resource, Deref, DerefMut)] +struct DamageTimer(pub Timer); -impl Default for EventTriggerState { +impl Default for DamageTimer { fn default() -> Self { - EventTriggerState { - event_timer: Timer::from_seconds(1.0, TimerMode::Repeating), + DamageTimer(Timer::from_seconds(1.0, TimerMode::Repeating)) + } +} + +// Next we define systems that send, mutate, and receive events +// This system reads 'DamageTimer', updates it, then sends a 'DealDamage' event +// if the timer has finished. +// +// Events are sent using an 'EventWriter' by calling 'send' or 'send_default'. +// The 'send_default' method will send the event with the default value if the event +// has a 'Default' implementation. +fn deal_damage_over_time( + time: Res