mirror of
https://github.com/bevyengine/bevy
synced 2024-11-09 22:54:34 +00:00
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<T> 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 <gonesbird@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
8df10d2713
commit
ec1aa48fc6
17 changed files with 1163 additions and 457 deletions
|
@ -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<AppExit> {
|
||||
let mut reader = ManualEventReader::default();
|
||||
let mut reader = EventCursor::default();
|
||||
|
||||
let events = self.world().get_resource::<Events<AppExit>>()?;
|
||||
let mut events = reader.read(events);
|
||||
|
|
|
@ -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::<Events<AssetEvent<LoadedFolder>>>();
|
||||
let asset_server = world.resource::<AssetServer>();
|
||||
|
|
|
@ -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<E: Event> Events<E> {
|
|||
self.send(Default::default())
|
||||
}
|
||||
|
||||
/// Gets a new [`ManualEventReader`]. This will include all events already in the event buffers.
|
||||
pub fn get_reader(&self) -> ManualEventReader<E> {
|
||||
ManualEventReader::default()
|
||||
/// Gets a new [`EventCursor`]. This will include all events already in the event buffers.
|
||||
pub fn get_cursor(&self) -> EventCursor<E> {
|
||||
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<E> {
|
||||
ManualEventReader {
|
||||
pub fn get_cursor_current(&self) -> EventCursor<E> {
|
||||
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<E> {
|
||||
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<E> {
|
||||
EventCursor {
|
||||
last_event_count: self.event_count,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
150
crates/bevy_ecs/src/event/event_cursor.rs
Normal file
150
crates/bevy_ecs/src/event/event_cursor.rs
Normal file
|
@ -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<E> = EventCursor<E>;
|
||||
|
||||
/// Stores the state for an [`EventReader`] or [`EventMutator`].
|
||||
///
|
||||
/// Access to the [`Events<E>`] 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<T>` is the internal state of `EventMutator<T>`, which tracks which events have been seen.
|
||||
/// mut local_event_reader: Local<EventCursor<MyEvent>>,
|
||||
/// // We can access the `Events` resource mutably, allowing us to both read and write its contents.
|
||||
/// mut events: ResMut<Events<MyEvent>>,
|
||||
/// ) {
|
||||
/// // 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<E: Event> {
|
||||
pub(super) last_event_count: usize,
|
||||
pub(super) _marker: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: Event> Default for EventCursor<E> {
|
||||
fn default() -> Self {
|
||||
EventCursor {
|
||||
last_event_count: 0,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Event> Clone for EventCursor<E> {
|
||||
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<E: Event> EventCursor<E> {
|
||||
/// See [`EventReader::read`]
|
||||
pub fn read<'a>(&'a mut self, events: &'a Events<E>) -> 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<E>) -> 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<E>) -> 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<E>,
|
||||
) -> 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<E>) -> 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<E>) -> EventMutParIter<'a, E> {
|
||||
EventMutParIter::new(self, events)
|
||||
}
|
||||
|
||||
/// See [`EventReader::len`]
|
||||
pub fn len(&self, events: &Events<E>) -> 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<usize, (usize, usize)>`
|
||||
events
|
||||
.event_count
|
||||
.saturating_sub(self.last_event_count)
|
||||
.min(events.len())
|
||||
}
|
||||
|
||||
/// Amount of events we missed.
|
||||
pub fn missed_events(&self, events: &Events<E>) -> usize {
|
||||
events
|
||||
.oldest_event_count()
|
||||
.saturating_sub(self.last_event_count)
|
||||
}
|
||||
|
||||
/// See [`EventReader::is_empty()`]
|
||||
pub fn is_empty(&self, events: &Events<E>) -> bool {
|
||||
self.len(events) == 0
|
||||
}
|
||||
|
||||
/// See [`EventReader::clear()`]
|
||||
pub fn clear(&mut self, events: &Events<E>) {
|
||||
self.last_event_count = events.event_count;
|
||||
}
|
||||
}
|
|
@ -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<E>,
|
||||
reader: &'a mut EventCursor<E>,
|
||||
chain: Chain<Iter<'a, EventInstance<E>>, Iter<'a, EventInstance<E>>>,
|
||||
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<E>, events: &'a Events<E>) -> Self {
|
||||
pub fn new(reader: &'a mut EventCursor<E>, events: &'a Events<E>) -> 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<E>,
|
||||
reader: &'a mut EventCursor<E>,
|
||||
slices: [&'a [EventInstance<E>]; 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<E>, events: &'a Events<E>) -> Self {
|
||||
pub fn new(reader: &'a mut EventCursor<E>, events: &'a Events<E>) -> Self {
|
||||
let a_index = reader
|
||||
.last_event_count
|
||||
.saturating_sub(events.events_a.start_event_count);
|
||||
|
|
|
@ -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<E: Event + Clone>(events: &Events<E>, cursor: &mut EventCursor<E>) -> Vec<E> {
|
||||
cursor.read(events).cloned().collect::<Vec<E>>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_events() {
|
||||
let mut events = Events::<TestEvent>::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<TestEvent> = events.get_reader();
|
||||
let mut reader_missed: EventCursor<TestEvent> = events.get_cursor();
|
||||
|
||||
let mut reader_a: ManualEventReader<TestEvent> = events.get_reader();
|
||||
let mut reader_a: EventCursor<TestEvent> = 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<TestEvent> = events.get_reader();
|
||||
let mut reader_b: EventCursor<TestEvent> = 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<E: Event + Clone>(
|
||||
events: &Events<E>,
|
||||
reader: &mut ManualEventReader<E>,
|
||||
) -> Vec<E> {
|
||||
reader.read(events).cloned().collect::<Vec<E>>()
|
||||
}
|
||||
|
||||
#[derive(Event, PartialEq, Eq, Debug)]
|
||||
struct E(usize);
|
||||
|
||||
fn events_clear_and_read_impl(clear_func: impl FnOnce(&mut Events<E>)) {
|
||||
let mut events = Events::<E>::default();
|
||||
let mut reader = events.get_reader();
|
||||
// Events Collection
|
||||
fn events_clear_and_read_impl(clear_func: impl FnOnce(&mut Events<TestEvent>)) {
|
||||
let mut events = Events::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::default();
|
||||
events.send(TestEvent { i: 0 });
|
||||
world.insert_resource(events);
|
||||
|
||||
let mut reader = IntoSystem::into_system(|mut events: EventReader<TestEvent>| -> 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::<TestEvent>(&mut world);
|
||||
|
||||
let has_events = world.get_resource::<Events<TestEvent>>().is_some();
|
||||
|
||||
assert!(has_events, "Should have the events resource");
|
||||
|
||||
EventRegistry::deregister_events::<TestEvent>(&mut world);
|
||||
|
||||
let has_events = world.get_resource::<Events<TestEvent>>().is_some();
|
||||
|
||||
assert!(!has_events, "Should not have the events resource");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_drain() {
|
||||
let mut events = Events::<TestEvent>::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::<Events<TestEvent>>();
|
||||
|
||||
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<TestEvent>| {
|
||||
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::<Events<TestEvent>>();
|
||||
|
||||
let mut reader =
|
||||
IntoSystem::into_system(|mut events: EventReader<TestEvent>| -> Option<TestEvent> {
|
||||
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::<EmptyTestEvent>::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<EmptyTestEvent>) {}
|
||||
|
||||
assert_is_read_only_system(reader_system);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_send_events_ids() {
|
||||
let mut events = Events::<TestEvent>::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::<TestEvent>(&mut world);
|
||||
|
||||
let has_events = world.get_resource::<Events<TestEvent>>().is_some();
|
||||
assert!(has_events, "Should have the events resource");
|
||||
|
||||
EventRegistry::deregister_events::<TestEvent>(&mut world);
|
||||
|
||||
let has_events = world.get_resource::<Events<TestEvent>>().is_some();
|
||||
assert!(!has_events, "Should not have the events resource");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_events_update_drain() {
|
||||
let mut events = Events::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<TestEvent>::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::<Events<TestEvent>>();
|
||||
for _ in 0..100 {
|
||||
world.send_event(TestEvent { i: 1 });
|
||||
}
|
||||
|
||||
let mut schedule = Schedule::default();
|
||||
|
||||
schedule.add_systems(
|
||||
|mut cursor: Local<EventCursor<TestEvent>>,
|
||||
events: Res<Events<TestEvent>>,
|
||||
counter: ResMut<Counter>| {
|
||||
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::<Counter>().unwrap();
|
||||
assert_eq!(counter.0.into_inner(), 100);
|
||||
|
||||
world.insert_resource(Counter(AtomicUsize::new(0)));
|
||||
schedule.run(&mut world);
|
||||
let counter = world.remove_resource::<Counter>().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<TestEvent>, counter: ResMut<Counter>| {
|
||||
events.par_read().for_each(|event| {
|
||||
|mut cursor: Local<EventCursor<TestEvent>>,
|
||||
mut events: ResMut<Events<TestEvent>>,
|
||||
counter: ResMut<Counter>| {
|
||||
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::<Counter>().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::<Counter>().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<EmptyTestEvent>) {}
|
||||
|
||||
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::<Events<TestEvent>>();
|
||||
|
||||
let mut reader =
|
||||
IntoSystem::into_system(|mut events: EventReader<TestEvent>| -> Option<TestEvent> {
|
||||
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::<Events<TestEvent>>();
|
||||
|
||||
let mut mutator =
|
||||
IntoSystem::into_system(|mut events: EventMutator<TestEvent>| -> Option<TestEvent> {
|
||||
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::<Events<TestEvent>>();
|
||||
|
||||
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<TestEvent>| {
|
||||
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::<Events<TestEvent>>();
|
||||
|
||||
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<TestEvent>| {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
275
crates/bevy_ecs/src/event/mut_iterators.rs
Normal file
275
crates/bevy_ecs/src/event/mut_iterators.rs
Normal file
|
@ -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::Item> {
|
||||
self.iter.next().map(|(event, _)| event)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
|
||||
fn count(self) -> usize {
|
||||
self.iter.count()
|
||||
}
|
||||
|
||||
fn last(self) -> Option<Self::Item>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.iter.last().map(|(event, _)| event)
|
||||
}
|
||||
|
||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||
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<E>,
|
||||
chain: Chain<IterMut<'a, EventInstance<E>>, IterMut<'a, EventInstance<E>>>,
|
||||
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<E>, events: &'a mut Events<E>) -> 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<E>);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<usize>) {
|
||||
self.chain.size_hint()
|
||||
}
|
||||
|
||||
fn count(self) -> usize {
|
||||
self.mutator.last_event_count += self.unread;
|
||||
self.unread
|
||||
}
|
||||
|
||||
fn last(self) -> Option<Self::Item>
|
||||
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<Self::Item> {
|
||||
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<E>,
|
||||
slices: [&'a mut [EventInstance<E>]; 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<E>, events: &'a mut Events<E>) -> 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<FN: Fn(&'a mut E) + Send + Sync + Clone>(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<FN: Fn(&'a mut E, EventId<E>) + 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 = <Self::IntoIter as Iterator>::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,
|
||||
}
|
||||
}
|
||||
}
|
142
crates/bevy_ecs/src/event/mutator.rs
Normal file
142
crates/bevy_ecs/src/event/mutator.rs
Normal file
|
@ -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<MyEvent>) {
|
||||
/// for event in reader.read() {
|
||||
/// event.0 += 1;
|
||||
/// println!("received event: {:?}", event);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Concurrency
|
||||
///
|
||||
/// Multiple systems with `EventMutator<T>` 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<E>>,
|
||||
events: ResMut<'w, Events<E>>,
|
||||
}
|
||||
|
||||
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::<Events<MyEvent>>();
|
||||
/// world.insert_resource(Counter::default());
|
||||
///
|
||||
/// let mut schedule = Schedule::default();
|
||||
/// schedule.add_systems(|mut events: EventMutator<MyEvent>, counter: Res<Counter>| {
|
||||
/// 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::<Counter>().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<CollisionEvent>) {
|
||||
/// 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);
|
||||
}
|
||||
}
|
|
@ -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<T>`], systems with `EventReader<T>` param can be executed concurrently
|
||||
/// (but not concurrently with `EventWriter<T>` systems for the same event type).
|
||||
/// (but not concurrently with `EventWriter<T>` or `EventMutator<T>` systems for the same event type).
|
||||
#[derive(SystemParam, Debug)]
|
||||
pub struct EventReader<'w, 's, E: Event> {
|
||||
pub(super) reader: Local<'s, ManualEventReader<E>>,
|
||||
pub(super) reader: Local<'s, EventCursor<E>>,
|
||||
events: Res<'w, Events<E>>,
|
||||
}
|
||||
|
||||
|
@ -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<E>`] 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<T>` is the internal state of `EventReader<T>`, which tracks which events have been seen.
|
||||
/// mut local_event_reader: Local<ManualEventReader<MyEvent>>,
|
||||
/// // We can access the `Events` resource mutably, allowing us to both read and write its contents.
|
||||
/// mut events: ResMut<Events<MyEvent>>,
|
||||
/// ) {
|
||||
/// // 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<E: Event> {
|
||||
pub(super) last_event_count: usize,
|
||||
pub(super) _marker: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: Event> Default for ManualEventReader<E> {
|
||||
fn default() -> Self {
|
||||
ManualEventReader {
|
||||
last_event_count: 0,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Event> Clone for ManualEventReader<E> {
|
||||
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<E: Event> ManualEventReader<E> {
|
||||
/// See [`EventReader::read`]
|
||||
pub fn read<'a>(&'a mut self, events: &'a Events<E>) -> 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<E>) -> 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<E>) -> EventParIter<'a, E> {
|
||||
EventParIter::new(self, events)
|
||||
}
|
||||
|
||||
/// See [`EventReader::len`]
|
||||
pub fn len(&self, events: &Events<E>) -> 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<usize, (usize, usize)>`
|
||||
events
|
||||
.event_count
|
||||
.saturating_sub(self.last_event_count)
|
||||
.min(events.len())
|
||||
}
|
||||
|
||||
/// Amount of events we missed.
|
||||
pub fn missed_events(&self, events: &Events<E>) -> usize {
|
||||
events
|
||||
.oldest_event_count()
|
||||
.saturating_sub(self.last_event_count)
|
||||
}
|
||||
|
||||
/// See [`EventReader::is_empty()`]
|
||||
pub fn is_empty(&self, events: &Events<E>) -> bool {
|
||||
self.len(events) == 0
|
||||
}
|
||||
|
||||
/// See [`EventReader::clear()`]
|
||||
pub fn clear(&mut self, events: &Events<E>) {
|
||||
self.last_event_count = events.event_count;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<RemovedComponentEntity> for Entity {
|
|||
}
|
||||
}
|
||||
|
||||
/// Wrapper around a [`ManualEventReader<RemovedComponentEntity>`] so that we
|
||||
/// Wrapper around a [`EventCursor<RemovedComponentEntity>`] so that we
|
||||
/// can differentiate events between components.
|
||||
#[derive(Debug)]
|
||||
pub struct RemovedComponentReader<T>
|
||||
where
|
||||
T: Component,
|
||||
{
|
||||
reader: ManualEventReader<RemovedComponentEntity>,
|
||||
reader: EventCursor<RemovedComponentEntity>,
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ impl<T: Component> Default for RemovedComponentReader<T> {
|
|||
}
|
||||
|
||||
impl<T: Component> Deref for RemovedComponentReader<T> {
|
||||
type Target = ManualEventReader<RemovedComponentEntity>;
|
||||
type Target = EventCursor<RemovedComponentEntity>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.reader
|
||||
}
|
||||
|
@ -172,13 +172,13 @@ fn map_id_events(
|
|||
// For all practical purposes, the api surface of `RemovedComponents<T>`
|
||||
// should be similar to `EventReader<T>` to reduce confusion.
|
||||
impl<'w, 's, T: Component> RemovedComponents<'w, 's, T> {
|
||||
/// Fetch underlying [`ManualEventReader`].
|
||||
pub fn reader(&self) -> &ManualEventReader<RemovedComponentEntity> {
|
||||
/// Fetch underlying [`EventCursor`].
|
||||
pub fn reader(&self) -> &EventCursor<RemovedComponentEntity> {
|
||||
&self.reader
|
||||
}
|
||||
|
||||
/// Fetch underlying [`ManualEventReader`] mutably.
|
||||
pub fn reader_mut(&mut self) -> &mut ManualEventReader<RemovedComponentEntity> {
|
||||
/// Fetch underlying [`EventCursor`] mutably.
|
||||
pub fn reader_mut(&mut self) -> &mut EventCursor<RemovedComponentEntity> {
|
||||
&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
|
||||
|
|
|
@ -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<AssetId<DynamicScene>, HashSet<InstanceId>>,
|
||||
pub(crate) spawned_instances: HashMap<InstanceId, InstanceInfo>,
|
||||
scene_asset_event_reader: ManualEventReader<AssetEvent<DynamicScene>>,
|
||||
scene_asset_event_reader: EventCursor<AssetEvent<DynamicScene>>,
|
||||
dynamic_scenes_to_spawn: Vec<(Handle<DynamicScene>, InstanceId, Option<Entity>)>,
|
||||
scenes_to_spawn: Vec<(Handle<Scene>, InstanceId, Option<Entity>)>,
|
||||
scenes_to_despawn: Vec<AssetId<DynamicScene>>,
|
||||
|
|
|
@ -256,7 +256,7 @@ mod tests {
|
|||
assert_eq!(world.resource::<State<TestState>>().0, TestState::B);
|
||||
let events = world.resource::<Events<StateTransitionEvent<TestState>>>();
|
||||
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::<State<TestState>>().0, TestState::C);
|
||||
let events = world.resource::<Events<StateTransitionEvent<TestState>>>();
|
||||
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));
|
||||
|
|
|
@ -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<T: Event> ApplicationHandler<T> for WinitAppRunnerState<T> {
|
|||
create_windows(event_loop, create_window.get_mut(self.world_mut()));
|
||||
create_window.apply(self.world_mut());
|
||||
|
||||
let mut redraw_event_reader = ManualEventReader::<RequestRedraw>::default();
|
||||
let mut redraw_event_reader = EventCursor::<RequestRedraw>::default();
|
||||
|
||||
let mut focused_windows_state: SystemState<(Res<WinitSettings>, Query<(Entity, &Window)>)> =
|
||||
SystemState::new(self.world_mut());
|
||||
|
|
|
@ -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::<MyEvent>()
|
||||
.add_event::<PlaySound>()
|
||||
.init_resource::<EventTriggerState>()
|
||||
.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<T>' 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<Time>,
|
||||
mut state: ResMut<DamageTimer>,
|
||||
mut events: EventWriter<DealDamage>,
|
||||
) {
|
||||
if state.tick(time.delta()).finished() {
|
||||
// Events can be sent with 'send' and constructed just like any other object.
|
||||
events.send(DealDamage { amount: 10 });
|
||||
}
|
||||
}
|
||||
|
||||
// This system mutates the 'DealDamage' events to apply some armor value
|
||||
// It also sends an 'ArmorBlockedDamage' event if the value of 'DealDamage' is zero
|
||||
//
|
||||
// Events are mutated using an 'EventMutator<T>' by calling 'read'. This returns an iterator
|
||||
// over all the &mut T that this system has not read yet. Note, you can have multiple
|
||||
// 'EventReader', 'EventWriter', and 'EventMutator' in a given system, as long as the types (T) are different.
|
||||
fn apply_armor_to_damage(
|
||||
mut dmg_events: EventMutator<DealDamage>,
|
||||
mut armor_events: EventWriter<ArmorBlockedDamage>,
|
||||
) {
|
||||
for event in dmg_events.read() {
|
||||
event.amount -= 1;
|
||||
if event.amount <= 0 {
|
||||
// Zero-sized events can also be sent with 'send'
|
||||
armor_events.send(ArmorBlockedDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sends MyEvent and PlaySound every second
|
||||
fn event_trigger(
|
||||
time: Res<Time>,
|
||||
mut state: ResMut<EventTriggerState>,
|
||||
mut my_events: EventWriter<MyEvent>,
|
||||
mut play_sound_events: EventWriter<PlaySound>,
|
||||
// This system reads 'DealDamage' events and sends 'DamageReceived' if the amount is non-zero
|
||||
//
|
||||
// Events are read using an 'EventReader<T>' by calling 'read'. This returns an iterator over all the &T
|
||||
// that this system has not read yet, and must be 'mut' in order to track which events have been read.
|
||||
// Again, note you can have multiple 'EventReader', 'EventWriter', and 'EventMutator' in a given system,
|
||||
// as long as the types (T) are different.
|
||||
fn apply_damage_to_health(
|
||||
mut dmg_events: EventReader<DealDamage>,
|
||||
mut rcvd_events: EventWriter<DamageReceived>,
|
||||
) {
|
||||
if state.event_timer.tick(time.delta()).finished() {
|
||||
my_events.send(MyEvent {
|
||||
message: "MyEvent just happened!".to_string(),
|
||||
});
|
||||
play_sound_events.send_default();
|
||||
for event in dmg_events.read() {
|
||||
info!("Applying {} damage", event.amount);
|
||||
if event.amount > 0 {
|
||||
// Events with a 'Default' implementation can be sent with 'send_default'
|
||||
rcvd_events.send_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prints events as they come in
|
||||
fn event_listener(mut events: EventReader<MyEvent>) {
|
||||
for my_event in events.read() {
|
||||
info!("{}", my_event.message);
|
||||
// Finally these two systems read 'DamageReceived' events.
|
||||
//
|
||||
// The first system will play a sound.
|
||||
// The second system will spawn a particle effect.
|
||||
//
|
||||
// As before, events are read using an 'EventReader' by calling 'read'. This returns an iterator over all the &T
|
||||
// that this system has not read yet.
|
||||
fn play_damage_received_sound(mut dmg_events: EventReader<DamageReceived>) {
|
||||
for _ in dmg_events.read() {
|
||||
info!("Playing a sound.");
|
||||
}
|
||||
}
|
||||
|
||||
fn sound_player(mut play_sound_events: EventReader<PlaySound>) {
|
||||
for _ in play_sound_events.read() {
|
||||
info!("Playing a sound");
|
||||
// Note that both systems receive the same 'DamageReceived' events. Any number of systems can
|
||||
// receive the same event type.
|
||||
fn play_damage_received_particle_effect(mut dmg_events: EventReader<DamageReceived>) {
|
||||
for _ in dmg_events.read() {
|
||||
info!("Playing particle effect.");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
// Events must be added to the app before they can be used
|
||||
// using the 'add_event' method
|
||||
.add_event::<DealDamage>()
|
||||
.add_event::<ArmorBlockedDamage>()
|
||||
.add_event::<DamageReceived>()
|
||||
.init_resource::<DamageTimer>()
|
||||
// As always we must add our systems to the apps schedule.
|
||||
// Here we add our systems to the schedule using 'chain()' so that they run in order
|
||||
// This ensures that 'apply_armor_to_damage' runs before 'apply_damage_to_health'
|
||||
// It also ensures that 'EventWriters' are used before the associated 'EventReaders'
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
deal_damage_over_time,
|
||||
apply_armor_to_damage,
|
||||
apply_damage_to_health,
|
||||
)
|
||||
.chain(),
|
||||
)
|
||||
// These two systems are not guaranteed to run in order, nor are they guaranteed to run
|
||||
// after the above chain. They may even run in parallel with each other.
|
||||
// This means they may have a one frame delay in processing events compared to the above chain
|
||||
// In some instances this is fine. In other cases it can be an issue. See the docs for more information
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
play_damage_received_sound,
|
||||
play_damage_received_particle_effect,
|
||||
),
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
//! There are two ways to solve this problem:
|
||||
//!
|
||||
//! 1. Use [`ParamSet`] to check out the [`EventWriter`] and [`EventReader`] one at a time.
|
||||
//! 2. Use a [`Local`] [`ManualEventReader`] instead of an [`EventReader`], and use [`ResMut`] to access [`Events`].
|
||||
//! 2. Use a [`Local`] [`EventCursor`] instead of an [`EventReader`], and use [`ResMut`] to access [`Events`].
|
||||
//!
|
||||
//! In the first case, you're being careful to only check out only one of the [`EventWriter`] or [`EventReader`] at a time.
|
||||
//! By "temporally" separating them, you avoid the overlap.
|
||||
|
@ -20,7 +20,7 @@
|
|||
//! Let's look at an example of each.
|
||||
|
||||
use bevy::core::FrameCount;
|
||||
use bevy::ecs::event::ManualEventReader;
|
||||
use bevy::ecs::event::EventCursor;
|
||||
use bevy::prelude::*;
|
||||
|
||||
fn main() {
|
||||
|
@ -133,17 +133,17 @@ fn send_and_receive_param_set(
|
|||
}
|
||||
}
|
||||
|
||||
/// A system that both sends and receives events using a [`Local`] [`ManualEventReader`].
|
||||
/// A system that both sends and receives events using a [`Local`] [`EventCursor`].
|
||||
fn send_and_receive_manual_event_reader(
|
||||
// The `Local` `SystemParam` stores state inside the system itself, rather than in the world.
|
||||
// `ManualEventReader<T>` is the internal state of `EventReader<T>`, which tracks which events have been seen.
|
||||
mut local_event_reader: Local<ManualEventReader<DebugEvent>>,
|
||||
// `EventCursor<T>` is the internal state of `EventReader<T>`, which tracks which events have been seen.
|
||||
mut local_event_reader: Local<EventCursor<DebugEvent>>,
|
||||
// We can access the `Events` resource mutably, allowing us to both read and write its contents.
|
||||
mut events: ResMut<Events<DebugEvent>>,
|
||||
frame_count: Res<FrameCount>,
|
||||
) {
|
||||
println!(
|
||||
"Sending and receiving events for frame {} with a `Local<ManualEventReader>",
|
||||
"Sending and receiving events for frame {} with a `Local<EventCursor>",
|
||||
frame_count.0
|
||||
);
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ fn did_despawn_enemy() {
|
|||
|
||||
// Get `EnemyDied` event reader
|
||||
let enemy_died_events = app.world().resource::<Events<EnemyDied>>();
|
||||
let mut enemy_died_reader = enemy_died_events.get_reader();
|
||||
let mut enemy_died_reader = enemy_died_events.get_cursor();
|
||||
let enemy_died = enemy_died_reader.read(enemy_died_events).next().unwrap();
|
||||
|
||||
// Check the event has been sent
|
||||
|
|
Loading…
Reference in a new issue