2024-06-10 21:45:01 +00:00
|
|
|
//! Event handling types.
|
|
|
|
mod base;
|
|
|
|
mod collections;
|
|
|
|
mod iterators;
|
|
|
|
mod reader;
|
|
|
|
mod registry;
|
|
|
|
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};
|
2024-06-25 15:36:06 +00:00
|
|
|
#[cfg(feature = "multi_threaded")]
|
|
|
|
pub use iterators::EventParIter;
|
|
|
|
pub use iterators::{EventIterator, EventIteratorWithId};
|
2024-06-10 21:45:01 +00:00
|
|
|
pub use reader::{EventReader, ManualEventReader};
|
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};
|
2024-06-10 21:45:01 +00:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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: ManualEventReader<TestEvent> = events.get_reader();
|
|
|
|
|
|
|
|
let mut reader_a: ManualEventReader<TestEvent> = events.get_reader();
|
|
|
|
|
|
|
|
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: ManualEventReader<TestEvent> = events.get_reader();
|
|
|
|
|
|
|
|
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_reader();
|
|
|
|
|
|
|
|
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_reader();
|
|
|
|
|
|
|
|
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"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
assert!(reader.read(&events).next().is_none());
|
|
|
|
|
|
|
|
events.send(E(0));
|
|
|
|
assert_eq!(*reader.read(&events).next().unwrap(), E(0));
|
|
|
|
assert_eq!(reader.read(&events).next(), None);
|
|
|
|
|
|
|
|
events.send(E(1));
|
|
|
|
clear_func(&mut events);
|
|
|
|
assert!(reader.read(&events).next().is_none());
|
|
|
|
|
|
|
|
events.send(E(2));
|
|
|
|
events.update();
|
|
|
|
events.send(E(3));
|
|
|
|
|
|
|
|
assert!(reader.read(&events).eq([E(2), E(3)].iter()));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_events_clear_and_read() {
|
|
|
|
events_clear_and_read_impl(|events| events.clear());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_events_drain_and_read() {
|
|
|
|
events_clear_and_read_impl(|events| {
|
|
|
|
assert!(events.drain().eq(vec![E(0), E(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_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() {
|
|
|
|
let mut events = Events::<EmptyTestEvent>::default();
|
|
|
|
events.send_default();
|
|
|
|
|
|
|
|
let mut reader = events.get_reader();
|
|
|
|
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();
|
|
|
|
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"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "multi_threaded")]
|
|
|
|
#[test]
|
|
|
|
fn test_events_par_iter() {
|
|
|
|
use crate::prelude::*;
|
2024-06-25 15:36:06 +00:00
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
|
|
|
|
#[derive(Resource)]
|
|
|
|
struct Counter(AtomicUsize);
|
2024-06-10 21:45:01 +00:00
|
|
|
|
|
|
|
let mut world = World::new();
|
|
|
|
world.init_resource::<Events<TestEvent>>();
|
2024-06-25 15:36:06 +00:00
|
|
|
for _ in 0..100 {
|
|
|
|
world.send_event(TestEvent { i: 1 });
|
2024-06-10 21:45:01 +00:00
|
|
|
}
|
|
|
|
let mut schedule = Schedule::default();
|
2024-06-25 15:36:06 +00:00
|
|
|
schedule.add_systems(
|
|
|
|
|mut events: EventReader<TestEvent>, counter: ResMut<Counter>| {
|
|
|
|
events.par_read().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);
|
2024-06-10 21:45:01 +00:00
|
|
|
|
2024-06-25 15:36:06 +00:00
|
|
|
world.insert_resource(Counter(AtomicUsize::new(0)));
|
2024-06-10 21:45:01 +00:00
|
|
|
schedule.run(&mut world);
|
2024-06-25 15:36:06 +00:00
|
|
|
let counter = world.remove_resource::<Counter>().unwrap();
|
|
|
|
assert_eq!(counter.0.into_inner(), 0);
|
2024-06-10 21:45:01 +00:00
|
|
|
}
|
|
|
|
}
|