bevy/crates/bevy_ecs/src/event/mod.rs

626 lines
20 KiB
Rust
Raw Normal View History

//! Event handling types.
mod base;
mod collections;
mod event_cursor;
mod iterators;
mod mut_iterators;
mod mutator;
mod reader;
mod registry;
Commands::send_event (#14933) # Objective sending events tends to be low-frequency so ergonomics can be prioritized over efficiency. add `Commands::send_event` to send any type of event without needing a writer in hand. i don't know how we feel about these kind of ergonomic things, i add this to all my projects and find it useful. adding `mut this_particular_event_writer: EventWriter<ThisParticularEvent>` every time i want to send something is unnecessarily cumbersome. it also simplifies the "send and receive in the same system" pattern significantly. basic example before: ```rs fn my_func( q: Query<(Entity, &State)>, mut damage_event_writer: EventWriter<DamageEvent>, mut heal_event_writer: EventWriter<HealEvent>, ) { for (entity, state) in q.iter() { if let Some(damage) = state.get_damage() { damage_event_writer.send(DamageEvent { entity, damage }); } if let Some(heal) = state.get_heal() { heal_event_writer.send(HealEvent { entity, heal }); } } } ``` basic example after: ```rs import bevy::ecs::event::SendEventEx; fn my_func( mut commands: Commands, q: Query<(Entity, &State)>, ) { for (entity, state) in q.iter() { if let Some(damage) = state.get_damage() { commands.send_event(DamageEvent { entity, damage }); } if let Some(heal) = state.get_heal() { commands.send_event(HealEvent { entity, heal }); } } } ``` send/receive in the same system before: ```rs fn send_and_receive_param_set( mut param_set: ParamSet<(EventReader<DebugEvent>, EventWriter<DebugEvent>)>, ) { // We must collect the events to resend, because we can't access the writer while we're iterating over the reader. let mut events_to_resend = Vec::new(); // This is p0, as the first parameter in the `ParamSet` is the reader. for event in param_set.p0().read() { if event.resend_from_param_set { events_to_resend.push(event.clone()); } } // This is p1, as the second parameter in the `ParamSet` is the writer. for mut event in events_to_resend { event.times_sent += 1; param_set.p1().send(event); } } ``` after: ```rs use bevy::ecs::event::SendEventEx; fn send_via_commands_and_receive( mut reader: EventReader<DebugEvent>, mut commands: Commands, ) { for event in reader.read() { if event.resend_via_commands { commands.send_event(DebugEvent { times_sent: event.times_sent + 1, ..event.clone() }); } } } ``` --------- Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2024-08-27 23:43:40 +00:00
mod send_event;
mod update;
mod writer;
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};
#[cfg(feature = "multi_threaded")]
pub use mut_iterators::EventMutParIter;
pub use mut_iterators::{EventMutIterator, EventMutIteratorWithId};
pub use mutator::EventMutator;
pub use reader::EventReader;
Ensure that events are updated even when using a bare-bones Bevy App (#13808) # Objective As discovered in https://github.com/Leafwing-Studios/leafwing-input-manager/issues/538, there appears to be some real weirdness going on in how event updates are processed between Bevy 0.13 and Bevy 0.14. To identify the cause and prevent regression, I've added tests to validate the intended behavior. My initial suspicion was that this would be fixed by https://github.com/bevyengine/bevy/pull/13762, but that doesn't seem to be the case. Instead, events appear to never be updated at all when using `bevy_app` by itself. This is part of the problem resolved by https://github.com/bevyengine/bevy/pull/11528, and introduced by https://github.com/bevyengine/bevy/pull/10077. After some investigation, it appears that `signal_event_update_system` is never added using a bare-bones `App`, and so event updates are always skipped. This can be worked around by adding your own copy to a later-in-the-frame schedule, but that's not a very good fix. ## Solution Ensure that if we're not using a `FixedUpdate` schedule, events are always updated every frame. To do this, I've modified the logic of `event_update_condition` and `event_update_system` to clearly and correctly differentiate between the two cases: where we're waiting for a "you should update now" signal and where we simply don't care. To encode this, I've added the `ShouldUpdateEvents` enum, replacing a simple `bool` in `EventRegistry`'s `needs_update` field. Now, both tests pass as expected, without having to manually add a system! ## Testing I've written two parallel unit tests to cover the intended behavior: 1. Test that `iter_current_update_events` works as expected in `bevy_ecs`. 2. Test that `iter_current_update_events` works as expected in `bevy_app` I've also added a test to verify that event updating works correctly in the presence of a fixed main schedule, and a second test to verify that fixed updating works at all to help future authors narrow down failures. ## Outstanding - [x] figure out why the `bevy_app` version of this test fails but the `bevy_ecs` version does not - [x] figure out why `EventRegistry::run_updates` isn't working properly - [x] figure out why `EventRegistry::run_updates` is never getting called - [x] figure out why `event_update_condition` is always returning false - [x] figure out why `EventRegistry::needs_update` is always false - [x] verify that the problem is a missing `signal_events_update_system` --------- Co-authored-by: Mike <mike.hsu@gmail.com>
2024-06-12 14:28:51 +00:00
pub use registry::{EventRegistry, ShouldUpdateEvents};
Commands::send_event (#14933) # Objective sending events tends to be low-frequency so ergonomics can be prioritized over efficiency. add `Commands::send_event` to send any type of event without needing a writer in hand. i don't know how we feel about these kind of ergonomic things, i add this to all my projects and find it useful. adding `mut this_particular_event_writer: EventWriter<ThisParticularEvent>` every time i want to send something is unnecessarily cumbersome. it also simplifies the "send and receive in the same system" pattern significantly. basic example before: ```rs fn my_func( q: Query<(Entity, &State)>, mut damage_event_writer: EventWriter<DamageEvent>, mut heal_event_writer: EventWriter<HealEvent>, ) { for (entity, state) in q.iter() { if let Some(damage) = state.get_damage() { damage_event_writer.send(DamageEvent { entity, damage }); } if let Some(heal) = state.get_heal() { heal_event_writer.send(HealEvent { entity, heal }); } } } ``` basic example after: ```rs import bevy::ecs::event::SendEventEx; fn my_func( mut commands: Commands, q: Query<(Entity, &State)>, ) { for (entity, state) in q.iter() { if let Some(damage) = state.get_damage() { commands.send_event(DamageEvent { entity, damage }); } if let Some(heal) = state.get_heal() { commands.send_event(HealEvent { entity, heal }); } } } ``` send/receive in the same system before: ```rs fn send_and_receive_param_set( mut param_set: ParamSet<(EventReader<DebugEvent>, EventWriter<DebugEvent>)>, ) { // We must collect the events to resend, because we can't access the writer while we're iterating over the reader. let mut events_to_resend = Vec::new(); // This is p0, as the first parameter in the `ParamSet` is the reader. for event in param_set.p0().read() { if event.resend_from_param_set { events_to_resend.push(event.clone()); } } // This is p1, as the second parameter in the `ParamSet` is the writer. for mut event in events_to_resend { event.times_sent += 1; param_set.p1().send(event); } } ``` after: ```rs use bevy::ecs::event::SendEventEx; fn send_via_commands_and_receive( mut reader: EventReader<DebugEvent>, mut commands: Commands, ) { for event in reader.read() { if event.resend_via_commands { commands.send_event(DebugEvent { times_sent: event.times_sent + 1, ..event.clone() }); } } } ``` --------- Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2024-08-27 23:43:40 +00:00
pub use send_event::SendEvent;
pub use update::{
event_update_condition, event_update_system, signal_event_update_system, EventUpdates,
};
pub use writer::EventWriter;
#[cfg(test)]
mod tests {
use crate as bevy_ecs;
use bevy_ecs::{event::*, system::assert_is_read_only_system};
use bevy_ecs_macros::Event;
#[derive(Event, Copy, Clone, PartialEq, Eq, Debug)]
struct TestEvent {
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();
let event_0 = TestEvent { i: 0 };
let event_1 = TestEvent { i: 1 };
let event_2 = TestEvent { i: 2 };
// this reader will miss event_0 and event_1 because it wont read them over the course of
// two updates
let mut reader_missed: EventCursor<TestEvent> = events.get_cursor();
let mut reader_a: EventCursor<TestEvent> = events.get_cursor();
events.send(event_0);
assert_eq!(
get_events(&events, &mut reader_a),
vec![event_0],
"reader_a created before event receives event"
);
assert_eq!(
get_events(&events, &mut reader_a),
vec![],
"second iteration of reader_a created before event results in zero events"
);
let mut reader_b: EventCursor<TestEvent> = events.get_cursor();
assert_eq!(
get_events(&events, &mut reader_b),
vec![event_0],
"reader_b created after event receives event"
);
assert_eq!(
get_events(&events, &mut reader_b),
vec![],
"second iteration of reader_b created after event results in zero events"
);
events.send(event_1);
let mut reader_c = events.get_cursor();
assert_eq!(
get_events(&events, &mut reader_c),
vec![event_0, event_1],
"reader_c created after two events receives both events"
);
assert_eq!(
get_events(&events, &mut reader_c),
vec![],
"second iteration of reader_c created after two event results in zero events"
);
assert_eq!(
get_events(&events, &mut reader_a),
vec![event_1],
"reader_a receives next unread event"
);
events.update();
let mut reader_d = events.get_cursor();
events.send(event_2);
assert_eq!(
get_events(&events, &mut reader_a),
vec![event_2],
"reader_a receives event created after update"
);
assert_eq!(
get_events(&events, &mut reader_b),
vec![event_1, event_2],
"reader_b receives events created before and after update"
);
assert_eq!(
get_events(&events, &mut reader_d),
vec![event_0, event_1, event_2],
"reader_d receives all events created before and after update"
);
events.update();
assert_eq!(
get_events(&events, &mut reader_missed),
vec![event_2],
"reader_missed missed events unread after two update() calls"
);
}
// 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(TestEvent { i: 0 });
assert_eq!(*reader.read(&events).next().unwrap(), TestEvent { i: 0 });
assert_eq!(reader.read(&events).next(), None);
events.send(TestEvent { i: 1 });
clear_func(&mut events);
assert!(reader.read(&events).next().is_none());
events.send(TestEvent { i: 2 });
events.update();
events.send(TestEvent { i: 3 });
assert!(reader
.read(&events)
.eq([TestEvent { i: 2 }, TestEvent { i: 3 }].iter()));
}
#[test]
fn test_events_clear_and_read() {
events_clear_and_read_impl(Events::clear);
}
#[test]
fn test_events_drain_and_read() {
events_clear_and_read_impl(|events| {
assert!(events
.drain()
.eq(vec![TestEvent { i: 0 }, TestEvent { i: 1 }].into_iter()));
});
}
#[test]
fn test_events_send_default() {
let mut events = Events::<EmptyTestEvent>::default();
events.send_default();
let mut reader = events.get_cursor();
assert_eq!(get_events(&events, &mut reader), vec![EmptyTestEvent]);
}
#[test]
fn test_send_events_ids() {
let mut events = Events::<TestEvent>::default();
let event_0 = TestEvent { i: 0 };
let event_1 = TestEvent { i: 1 };
let event_2 = TestEvent { i: 2 };
let event_0_id = events.send(event_0);
assert_eq!(
events.get_event(event_0_id.id),
Some((&event_0, event_0_id)),
"Getting a sent event by ID should return the original event"
);
let mut event_ids = events.send_batch([event_1, event_2]);
let event_id = event_ids.next().expect("Event 1 must have been sent");
assert_eq!(
events.get_event(event_id.id),
Some((&event_1, event_id)),
"Getting a sent event by ID should return the original event"
);
let event_id = event_ids.next().expect("Event 2 must have been sent");
assert_eq!(
events.get_event(event_id.id),
Some((&event_2, event_id)),
"Getting a sent event by ID should return the original event"
);
assert!(
event_ids.next().is_none(),
"Only sent two events; got more than two IDs"
);
}
#[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_event_cursor_par_read() {
use crate::prelude::*;
Add `core` and `alloc` over `std` Lints (#15281) # Objective - Fixes #6370 - Closes #6581 ## Solution - Added the following lints to the workspace: - `std_instead_of_core` - `std_instead_of_alloc` - `alloc_instead_of_core` - Used `cargo +nightly fmt` with [item level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A) to split all `use` statements into single items. - Used `cargo clippy --workspace --all-targets --all-features --fix --allow-dirty` to _attempt_ to resolve the new linting issues, and intervened where the lint was unable to resolve the issue automatically (usually due to needing an `extern crate alloc;` statement in a crate root). - Manually removed certain uses of `std` where negative feature gating prevented `--all-features` from finding the offending uses. - Used `cargo +nightly fmt` with [crate level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A) to re-merge all `use` statements matching Bevy's previous styling. - Manually fixed cases where the `fmt` tool could not re-merge `use` statements due to conditional compilation attributes. ## Testing - Ran CI locally ## Migration Guide The MSRV is now 1.81. Please update to this version or higher. ## Notes - This is a _massive_ change to try and push through, which is why I've outlined the semi-automatic steps I used to create this PR, in case this fails and someone else tries again in the future. - Making this change has no impact on user code, but does mean Bevy contributors will be warned to use `core` and `alloc` instead of `std` where possible. - This lint is a critical first step towards investigating `no_std` options for Bevy. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com>
2024-09-27 00:59:59 +00:00
use core::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::*;
Add `core` and `alloc` over `std` Lints (#15281) # Objective - Fixes #6370 - Closes #6581 ## Solution - Added the following lints to the workspace: - `std_instead_of_core` - `std_instead_of_alloc` - `alloc_instead_of_core` - Used `cargo +nightly fmt` with [item level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A) to split all `use` statements into single items. - Used `cargo clippy --workspace --all-targets --all-features --fix --allow-dirty` to _attempt_ to resolve the new linting issues, and intervened where the lint was unable to resolve the issue automatically (usually due to needing an `extern crate alloc;` statement in a crate root). - Manually removed certain uses of `std` where negative feature gating prevented `--all-features` from finding the offending uses. - Used `cargo +nightly fmt` with [crate level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A) to re-merge all `use` statements matching Bevy's previous styling. - Manually fixed cases where the `fmt` tool could not re-merge `use` statements due to conditional compilation attributes. ## Testing - Ran CI locally ## Migration Guide The MSRV is now 1.81. Please update to this version or higher. ## Notes - This is a _massive_ change to try and push through, which is why I've outlined the semi-automatic steps I used to create this PR, in case this fails and someone else tries again in the future. - Making this change has no impact on user code, but does mean Bevy contributors will be warned to use `core` and `alloc` instead of `std` where possible. - This lint is a critical first step towards investigating `no_std` options for Bevy. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com>
2024-09-27 00:59:59 +00:00
use core::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>>,
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);
});
},
);
world.insert_resource(Counter(AtomicUsize::new(0)));
schedule.run(&mut world);
let counter = world.remove_resource::<Counter>().unwrap();
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,
"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);
}
}