From 8f4589baa1838965b1459e20a88ef21b12cdd970 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 27 Jan 2024 23:18:22 -0500 Subject: [PATCH 01/20] Add basic example --- Cargo.toml | 11 ++ crates/bevy_core/src/lib.rs | 2 +- examples/ecs/send_and_receive_events.rs | 152 ++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 examples/ecs/send_and_receive_events.rs diff --git a/Cargo.toml b/Cargo.toml index 1b7bdf9f66311..1de13bc3181f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1399,6 +1399,17 @@ description = "Illustrates event creation, activation, and reception" category = "ECS (Entity Component System)" wasm = false +[[example]] +name = "send_and_receive_events" +path = "examples/ecs/send_and_receive_events.rs" +doc-scrape-examples = true + +[package.metadata.example.send_and_receive_events] +name = "Send and receive events" +description = "Demonstrates how to send and receive events of the same type in a single system" +category = "ECS (Entity Component System)" +wasm = false + [[example]] name = "fixed_timestep" path = "examples/ecs/fixed_timestep.rs" diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index c925bc99362da..5c669186d7934 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -140,7 +140,7 @@ fn tick_global_task_pools(_main_thread_marker: Option>) { /// [`FrameCount`] will wrap to 0 after exceeding [`u32::MAX`]. Within reasonable /// assumptions, one may exploit wrapping arithmetic to determine the number of frames /// that have elapsed between two observations – see [`u32::wrapping_sub()`]. -#[derive(Default, Resource, Clone, Copy)] +#[derive(Debug, Default, Resource, Clone, Copy)] pub struct FrameCount(pub u32); /// Adds frame counting functionality to Apps. diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs new file mode 100644 index 0000000000000..13d9b5d58d7f9 --- /dev/null +++ b/examples/ecs/send_and_receive_events.rs @@ -0,0 +1,152 @@ +//! From time-to-time, you may find that you want to both send and receive and event of the same type in a single system. +//! +//! Of course, this results in an error: the borrows of [`EventWriter`] and [`EventReader`] overlap, +//! if and only if the [`Event`] type is the same. +//! One system parameter borrows the [`Events`] resource mutably, and another system parameter borrows the [`Events`] resource immutably immutably. +//! If Bevy allowed this, there would be Undefined Behavior! +//! +//! 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`]. +//! +//! In the first case, you're being careful to only check out one of the [`EventWriter`] or [`EventReader`] at a time. +//! By "temporally" seperating them, you avoid the overlap. +//! +//! In the second case, you only ever have one access to the underlying [`Events`] resource at a time. +//! But in exchange, you have to manually keep track of which events you've already read. +//! +//! Let's look at an example of each. + +use bevy::core::FrameCount; +use bevy::ecs::event::ManualEventReader; +use bevy::prelude::*; + +fn main() { + let mut app = App::new(); + app.add_plugins(MinimalPlugins) + .add_event::() + .add_systems( + Update, + ( + send_events, + debug_events, + send_and_receive_param_set, + debug_events, + send_and_receive_local_event_reader, + debug_events, + ) + .chain(), + ); + // We're just going to run a few frames, so we can see and understand the output. + app.update(); + // By running for longer than one frame, we can see that we're caching our cursor in the event queue properly. + app.update(); +} + +/// A dummy event type. +#[derive(Debug, Clone, Event)] +struct MyEvent { + re_emit_from_param_set: bool, + re_emit_from_local_event_reader: bool, + times_sent: u8, +} + +/// A system that sends all combinations of events. +fn send_events(mut events: EventWriter, frame_count: Res) { + info!("Sending events for frame {:?}", *frame_count); + + events.send(MyEvent { + re_emit_from_param_set: false, + re_emit_from_local_event_reader: false, + times_sent: 1, + }); + events.send(MyEvent { + re_emit_from_param_set: true, + re_emit_from_local_event_reader: false, + times_sent: 1, + }); + events.send(MyEvent { + re_emit_from_param_set: false, + re_emit_from_local_event_reader: true, + times_sent: 1, + }); + events.send(MyEvent { + re_emit_from_param_set: true, + re_emit_from_local_event_reader: true, + times_sent: 1, + }); +} + +/// A system that prints all events sent since the last time this system ran. +/// +/// Note that some events will be printed twice, because they were sent twice. +fn debug_events(mut events: EventReader) { + for event in events.read() { + println!("{:?}", event); + } +} + +/// A system that both sends and receives events using [`ParamSet`]. +fn send_and_receive_param_set( + // Note: we can make this even prettier using the `SystemParam` derive macro + // to make our own `SystemParam` type to put inside of the `ParamSet`. + mut param_set: ParamSet<(EventReader, EventWriter)>, + frame_count: Res, +) { + info!("Sending and receiving events for frame {:?}", *frame_count); + + // We must collect the events to re-emit, because we can't access the writer while we're iterating over the reader. + let mut events_to_re_emit = Vec::new(); + + // This is p0, as the first parameter in the `ParamSet` is the reader. + for event in param_set.p0().read() { + if event.re_emit_from_param_set { + events_to_re_emit.push(event.clone()); + } + } + + // This is p1, as the second parameter in the `ParamSet` is the writer. + for event in events_to_re_emit { + param_set.p1().send(MyEvent { + times_sent: event.times_sent + 1, + // This is struct update syntax! Here, we're copying all of the fields from `event`, + // except for the `times_sent` field, which we're incrementing. + ..event + }); + } +} + +/// A system that both sends and receives events using a [`Local`] [`ManualEventReader`]. +fn send_and_receive_local_event_reader( + // The Local SystemParam stores state inside the system itself, rather than in the world. + // ManualEventReader is the internal state of EventReader, which tracks which events have been seen. + mut local_event_reader: Local>, + // We can access the `Events` resource mutably, allowing us to both read and write its contents. + mut events: ResMut>, + frame_count: Res, +) { + info!("Sending and receiving events for frame {:?}", *frame_count); + + // We must collect the events to re-emit, because we can't mutate events while we're iterating over the events. + let mut events_to_re_emit = Vec::new(); + + for event in local_event_reader.read(&events) { + if event.re_emit_from_local_event_reader { + // For simplicity, we're cloning the event. + // In this case, since we have mutable access to the `Events` resource, + // we could also just mutate the event in-place, + // or drain the event queue into our `events_to_re_emit` vector. + events_to_re_emit.push(event.clone()); + } + } + + for event in events_to_re_emit { + events.send(MyEvent { + times_sent: event.times_sent + 1, + // This is struct update syntax! Here, we're copying all of the fields from `event`, + // except for the `times_sent` field, which we're incrementing. + ..event + }); + } +} From b723820788570453b9b9564b691dc0a4c927f894 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 27 Jan 2024 23:22:14 -0500 Subject: [PATCH 02/20] Add more docs to ManualEventReader --- crates/bevy_ecs/src/event.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 8b25ab2a2cbc8..5372201edaef0 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -556,7 +556,14 @@ impl<'w, E: Event> EventWriter<'w, E> { } /// Stores the state for an [`EventReader`]. +/// /// Access to the [`Events`] resource is required to read any incoming events. +/// +/// In almost all cases, you should just use an [`EventReader`], +/// which will automatically manage the state for you. +/// +/// However, to see how to manually track events (and why you might want to do so), +/// take a look at the [send_and_receive_events example](https://github.com/bevyengine/bevy/blob/latest/examples/ecs/send_and_receive_events.rs). #[derive(Debug)] pub struct ManualEventReader { last_event_count: usize, From d21df14032719b5a542d5de513d25e012879ea05 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 27 Jan 2024 23:26:43 -0500 Subject: [PATCH 03/20] Better debug statements --- examples/ecs/send_and_receive_events.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index 13d9b5d58d7f9..2fac4be7223da 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -94,7 +94,10 @@ fn send_and_receive_param_set( mut param_set: ParamSet<(EventReader, EventWriter)>, frame_count: Res, ) { - info!("Sending and receiving events for frame {:?}", *frame_count); + info!( + "Sending and receiving events for frame {} with a `ParamSet`", + frame_count.0 + ); // We must collect the events to re-emit, because we can't access the writer while we're iterating over the reader. let mut events_to_re_emit = Vec::new(); @@ -126,7 +129,10 @@ fn send_and_receive_local_event_reader( mut events: ResMut>, frame_count: Res, ) { - info!("Sending and receiving events for frame {:?}", *frame_count); + info!( + "Sending and receiving events for frame {} with a `Local", + frame_count.0 + ); // We must collect the events to re-emit, because we can't mutate events while we're iterating over the events. let mut events_to_re_emit = Vec::new(); From 81b379aae3e65fefeadf0024525ed79ac4bcd217 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 08:42:18 -0500 Subject: [PATCH 04/20] Generate README again --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index 350485648f9b4..175fc632a4a3c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -239,6 +239,7 @@ Example | Description [Parallel Query](../examples/ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` [Removal Detection](../examples/ecs/removal_detection.rs) | Query for entities that had a specific component removed earlier in the current frame [Run Conditions](../examples/ecs/run_conditions.rs) | Run systems only when one or multiple conditions are met +[Send and receive events](../examples/ecs/send_and_receive_events.rs) | Demonstrates how to send and receive events of the same type in a single system [Startup System](../examples/ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) [State](../examples/ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state [System Closure](../examples/ecs/system_closure.rs) | Show how to use closures as systems, and how to configure `Local` variables by capturing external state From 0910d0e9468f118a0046e06f13f13c34ab473d7b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 08:46:36 -0500 Subject: [PATCH 05/20] Add clarifying simple case that works --- examples/ecs/send_and_receive_events.rs | 42 +++++++++++++++++-------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index 2fac4be7223da..3ea06f6f5d58c 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -25,7 +25,8 @@ use bevy::prelude::*; fn main() { let mut app = App::new(); app.add_plugins(MinimalPlugins) - .add_event::() + .add_event::() + .add_systems(Update, read_and_write_different_type) .add_systems( Update, ( @@ -44,34 +45,49 @@ fn main() { app.update(); } +#[derive(Event)] +struct A; + +#[derive(Event)] +struct B; + +// This works fine, because the types are different, +// so the borrows of the `EventWriter` and `EventReader` don't overlap. +// Note that these borrowing rules are checked at system initialization time, +// not at compile time, as Bevy uses internal unsafe code to split the `World` into disjoint pieces. +fn read_and_write_different_type(mut a: EventWriter, mut b: EventReader) { + for _ in b.read() {} + a.send(A); +} + /// A dummy event type. #[derive(Debug, Clone, Event)] -struct MyEvent { +struct DebugEvent { re_emit_from_param_set: bool, re_emit_from_local_event_reader: bool, times_sent: u8, } /// A system that sends all combinations of events. -fn send_events(mut events: EventWriter, frame_count: Res) { +fn send_events(mut events: EventWriter, frame_count: Res) { info!("Sending events for frame {:?}", *frame_count); - events.send(MyEvent { + events.send(DebugEvent { re_emit_from_param_set: false, re_emit_from_local_event_reader: false, times_sent: 1, }); - events.send(MyEvent { + events.send(DebugEvent { re_emit_from_param_set: true, re_emit_from_local_event_reader: false, times_sent: 1, }); - events.send(MyEvent { + events.send(DebugEvent { re_emit_from_param_set: false, re_emit_from_local_event_reader: true, times_sent: 1, }); - events.send(MyEvent { + events.send(DebugEvent { re_emit_from_param_set: true, re_emit_from_local_event_reader: true, times_sent: 1, @@ -81,7 +97,7 @@ fn send_events(mut events: EventWriter, frame_count: Res) { /// A system that prints all events sent since the last time this system ran. /// /// Note that some events will be printed twice, because they were sent twice. -fn debug_events(mut events: EventReader) { +fn debug_events(mut events: EventReader) { for event in events.read() { println!("{:?}", event); } @@ -91,7 +107,7 @@ fn debug_events(mut events: EventReader) { fn send_and_receive_param_set( // Note: we can make this even prettier using the `SystemParam` derive macro // to make our own `SystemParam` type to put inside of the `ParamSet`. - mut param_set: ParamSet<(EventReader, EventWriter)>, + mut param_set: ParamSet<(EventReader, EventWriter)>, frame_count: Res, ) { info!( @@ -111,7 +127,7 @@ fn send_and_receive_param_set( // This is p1, as the second parameter in the `ParamSet` is the writer. for event in events_to_re_emit { - param_set.p1().send(MyEvent { + param_set.p1().send(DebugEvent { times_sent: event.times_sent + 1, // This is struct update syntax! Here, we're copying all of the fields from `event`, // except for the `times_sent` field, which we're incrementing. @@ -124,9 +140,9 @@ fn send_and_receive_param_set( fn send_and_receive_local_event_reader( // The Local SystemParam stores state inside the system itself, rather than in the world. // ManualEventReader is the internal state of EventReader, which tracks which events have been seen. - mut local_event_reader: Local>, + mut local_event_reader: Local>, // We can access the `Events` resource mutably, allowing us to both read and write its contents. - mut events: ResMut>, + mut events: ResMut>, frame_count: Res, ) { info!( @@ -148,7 +164,7 @@ fn send_and_receive_local_event_reader( } for event in events_to_re_emit { - events.send(MyEvent { + events.send(DebugEvent { times_sent: event.times_sent + 1, // This is struct update syntax! Here, we're copying all of the fields from `event`, // except for the `times_sent` field, which we're incrementing. From a7a8e2e92046920de3add0fe2899b022aa916697 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 15:13:25 -0500 Subject: [PATCH 06/20] Style Co-authored-by: Joona Aalto --- examples/ecs/send_and_receive_events.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index 3ea06f6f5d58c..56cd382a767bf 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -138,8 +138,8 @@ fn send_and_receive_param_set( /// A system that both sends and receives events using a [`Local`] [`ManualEventReader`]. fn send_and_receive_local_event_reader( - // The Local SystemParam stores state inside the system itself, rather than in the world. - // ManualEventReader is the internal state of EventReader, which tracks which events have been seen. + // The `Local` `SystemParam` stores state inside the system itself, rather than in the world. + // `ManualEventReader` is the internal state of `EventReader`, which tracks which events have been seen. mut local_event_reader: Local>, // We can access the `Events` resource mutably, allowing us to both read and write its contents. mut events: ResMut>, From f893ebca72f6af86a6be3a2d4627b087d2086053 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 15:55:58 -0500 Subject: [PATCH 07/20] Phrasing --- examples/ecs/send_and_receive_events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index 56cd382a767bf..a99881a4f2a6b 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -10,7 +10,7 @@ //! 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`]. //! -//! In the first case, you're being careful to only check out one of the [`EventWriter`] or [`EventReader`] at a time. +//! In the first case, you're being careful to only check out only one of the [`EventWriter`] or [`EventReader`] at a time. //! By "temporally" seperating them, you avoid the overlap. //! //! In the second case, you only ever have one access to the underlying [`Events`] resource at a time. From ba4e8984cb28545daa13978f5ec5f2c46b1534a6 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 15:57:03 -0500 Subject: [PATCH 08/20] Typo Co-authored-by: ickk --- examples/ecs/send_and_receive_events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index a99881a4f2a6b..d4e616a14996b 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -2,7 +2,7 @@ //! //! Of course, this results in an error: the borrows of [`EventWriter`] and [`EventReader`] overlap, //! if and only if the [`Event`] type is the same. -//! One system parameter borrows the [`Events`] resource mutably, and another system parameter borrows the [`Events`] resource immutably immutably. +//! One system parameter borrows the [`Events`] resource mutably, and another system parameter borrows the [`Events`] resource immutably. //! If Bevy allowed this, there would be Undefined Behavior! //! //! There are two ways to solve this problem: From 13fb9f4af6cf48f4acd9fbdf1fcbfe90cf589534 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 15:57:38 -0500 Subject: [PATCH 09/20] Typo Co-authored-by: Joona Aalto --- examples/ecs/send_and_receive_events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index d4e616a14996b..b9f0df900ff75 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -1,4 +1,4 @@ -//! From time-to-time, you may find that you want to both send and receive and event of the same type in a single system. +//! From time to time, you may find that you want to both send and receive an event of the same type in a single system. //! //! Of course, this results in an error: the borrows of [`EventWriter`] and [`EventReader`] overlap, //! if and only if the [`Event`] type is the same. From 7f69a98699d35b11fe992c0c55faf90c7a7e3309 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 15:58:29 -0500 Subject: [PATCH 10/20] local_event_reader -> manual_event_reader --- examples/ecs/send_and_receive_events.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index b9f0df900ff75..92fa9a7d51171 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -34,7 +34,7 @@ fn main() { debug_events, send_and_receive_param_set, debug_events, - send_and_receive_local_event_reader, + send_and_receive_manual_event_reader, debug_events, ) .chain(), @@ -137,7 +137,7 @@ fn send_and_receive_param_set( } /// A system that both sends and receives events using a [`Local`] [`ManualEventReader`]. -fn send_and_receive_local_event_reader( +fn send_and_receive_manual_event_reader( // The `Local` `SystemParam` stores state inside the system itself, rather than in the world. // `ManualEventReader` is the internal state of `EventReader`, which tracks which events have been seen. mut local_event_reader: Local>, From f032eb19b778a9aed4561e56d35a207ffd280bf4 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 15:59:14 -0500 Subject: [PATCH 11/20] Explain UB --- examples/ecs/send_and_receive_events.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index 92fa9a7d51171..fed33f6688abd 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -3,7 +3,8 @@ //! Of course, this results in an error: the borrows of [`EventWriter`] and [`EventReader`] overlap, //! if and only if the [`Event`] type is the same. //! One system parameter borrows the [`Events`] resource mutably, and another system parameter borrows the [`Events`] resource immutably. -//! If Bevy allowed this, there would be Undefined Behavior! +//! If Bevy allowed this, this would violate Rust's rules against aliased mutability. +//! In other words, this would be Undefined Behavior (UB)! //! //! There are two ways to solve this problem: //! From b1161ec210f5fe45fe93d54f288d0e7291cd9a1b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 15:59:45 -0500 Subject: [PATCH 12/20] Remove note on SystemParam derive --- examples/ecs/send_and_receive_events.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index fed33f6688abd..ed08c8df7e103 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -106,8 +106,6 @@ fn debug_events(mut events: EventReader) { /// A system that both sends and receives events using [`ParamSet`]. fn send_and_receive_param_set( - // Note: we can make this even prettier using the `SystemParam` derive macro - // to make our own `SystemParam` type to put inside of the `ParamSet`. mut param_set: ParamSet<(EventReader, EventWriter)>, frame_count: Res, ) { From 3e8cec1a51b564a5ded7c85a3ad35e0e18df0ed0 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 16:05:07 -0500 Subject: [PATCH 13/20] re_emit -> resend --- crates/bevy_ecs/src/event.rs | 34 ++++++++++++++++++++-- examples/ecs/send_and_receive_events.rs | 38 ++++++++++++------------- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 5372201edaef0..f749724385a5a 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -562,8 +562,38 @@ impl<'w, E: Event> EventWriter<'w, E> { /// In almost all cases, you should just use an [`EventReader`], /// which will automatically manage the state for you. /// -/// However, to see how to manually track events (and why you might want to do so), -/// take a look at the [send_and_receive_events example](https://github.com/bevyengine/bevy/blob/latest/examples/ecs/send_and_receive_events.rs). +/// 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 +/// +/// ```rust +/// use bevy_ecs::prelude::*; +/// use bevy_ecs::event::{Event, Events, ManualEventReader}; +/// +/// #[derive(Event, Clone, Debug)] +/// struct MyEvent; +/// +/// /// A system that both sends and receives events using a [`Local`] [`ManualEventReader`]. +/// fn send_and_receive_manual_event_reader( +/// // The `Local` `SystemParam` stores state inside the system itself, rather than in the world. +/// // `ManualEventReader` is the internal state of `EventReader`, which tracks which events have been seen. +/// mut local_event_reader: Local>, +/// // We can access the `Events` resource mutably, allowing us to both read and write its contents. +/// mut events: ResMut>, +/// ) { +/// // We must collect the events to re-emit, because we can't mutate events while we're iterating over the events. +/// let mut events_to_re_emit = Vec::new(); +/// +/// for event in local_event_reader.read(&events) { +/// events_to_re_emit.push(event.clone()); +/// } +/// +/// for event in events_to_re_emit { +/// events.send(MyEvent); +/// } +/// } +/// ``` #[derive(Debug)] pub struct ManualEventReader { last_event_count: usize, diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index ed08c8df7e103..70caddfa766d9 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -64,8 +64,8 @@ fn read_and_write_different_type(mut a: EventWriter, mut b: EventReader) { /// A dummy event type. #[derive(Debug, Clone, Event)] struct DebugEvent { - re_emit_from_param_set: bool, - re_emit_from_local_event_reader: bool, + resend_from_param_set: bool, + resend_from_local_event_reader: bool, times_sent: u8, } @@ -74,23 +74,23 @@ fn send_events(mut events: EventWriter, frame_count: Res info!("Sending events for frame {:?}", *frame_count); events.send(DebugEvent { - re_emit_from_param_set: false, - re_emit_from_local_event_reader: false, + resend_from_param_set: false, + resend_from_local_event_reader: false, times_sent: 1, }); events.send(DebugEvent { - re_emit_from_param_set: true, - re_emit_from_local_event_reader: false, + resend_from_param_set: true, + resend_from_local_event_reader: false, times_sent: 1, }); events.send(DebugEvent { - re_emit_from_param_set: false, - re_emit_from_local_event_reader: true, + resend_from_param_set: false, + resend_from_local_event_reader: true, times_sent: 1, }); events.send(DebugEvent { - re_emit_from_param_set: true, - re_emit_from_local_event_reader: true, + resend_from_param_set: true, + resend_from_local_event_reader: true, times_sent: 1, }); } @@ -115,17 +115,17 @@ fn send_and_receive_param_set( ); // We must collect the events to re-emit, because we can't access the writer while we're iterating over the reader. - let mut events_to_re_emit = Vec::new(); + 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.re_emit_from_param_set { - events_to_re_emit.push(event.clone()); + 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 event in events_to_re_emit { + for event in events_to_resend { param_set.p1().send(DebugEvent { times_sent: event.times_sent + 1, // This is struct update syntax! Here, we're copying all of the fields from `event`, @@ -150,19 +150,19 @@ fn send_and_receive_manual_event_reader( ); // We must collect the events to re-emit, because we can't mutate events while we're iterating over the events. - let mut events_to_re_emit = Vec::new(); + let mut events_to_resend = Vec::new(); for event in local_event_reader.read(&events) { - if event.re_emit_from_local_event_reader { + if event.resend_from_local_event_reader { // For simplicity, we're cloning the event. // In this case, since we have mutable access to the `Events` resource, // we could also just mutate the event in-place, - // or drain the event queue into our `events_to_re_emit` vector. - events_to_re_emit.push(event.clone()); + // or drain the event queue into our `events_to_resend` vector. + events_to_resend.push(event.clone()); } } - for event in events_to_re_emit { + for event in events_to_resend { events.send(DebugEvent { times_sent: event.times_sent + 1, // This is struct update syntax! Here, we're copying all of the fields from `event`, From acc72fe987cd5d528b03532614183302555ec358 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 16:05:55 -0500 Subject: [PATCH 14/20] Tiny doc test rather than link --- crates/bevy_ecs/src/event.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index f749724385a5a..b0d68269d2623 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -580,7 +580,7 @@ impl<'w, E: Event> EventWriter<'w, E> { /// // `ManualEventReader` is the internal state of `EventReader`, which tracks which events have been seen. /// mut local_event_reader: Local>, /// // We can access the `Events` resource mutably, allowing us to both read and write its contents. -/// mut events: ResMut>, +/// mut events: ResMut>, /// ) { /// // We must collect the events to re-emit, because we can't mutate events while we're iterating over the events. /// let mut events_to_re_emit = Vec::new(); @@ -593,6 +593,8 @@ impl<'w, E: Event> EventWriter<'w, E> { /// events.send(MyEvent); /// } /// } +/// +/// # bevy_ecs::system::assert_is_system(send_and_receive_manual_event_reader); /// ``` #[derive(Debug)] pub struct ManualEventReader { From 943c3b27a78dda39b742c7611873cef3fe169293 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 16:07:41 -0500 Subject: [PATCH 15/20] Clearer system naming --- examples/ecs/send_and_receive_events.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index 70caddfa766d9..744d5fb978f22 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -27,7 +27,7 @@ fn main() { let mut app = App::new(); app.add_plugins(MinimalPlugins) .add_event::() - .add_systems(Update, read_and_write_different_type) + .add_systems(Update, read_and_write_different_event_types) .add_systems( Update, ( @@ -56,7 +56,7 @@ struct B; // so the borrows of the `EventWriter` and `EventReader` don't overlap. // Note that these borrowing rules are checked at system initialization time, // not at compile time, as Bevy uses internal unsafe code to split the `World` into disjoint pieces. -fn read_and_write_different_type(mut a: EventWriter, mut b: EventReader) { +fn read_and_write_different_event_types(mut a: EventWriter, mut b: EventReader) { for _ in b.read() {} a.send(A); } From dbb11bee1c3187665850dc17f87a7340dc3901e2 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 16:09:18 -0500 Subject: [PATCH 16/20] Missed rename Co-authored-by: Joona Aalto --- examples/ecs/send_and_receive_events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index 744d5fb978f22..eedef768f29b2 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -149,7 +149,7 @@ fn send_and_receive_manual_event_reader( frame_count.0 ); - // We must collect the events to re-emit, because we can't mutate events while we're iterating over the events. + // 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) { From 87d6304fa7040a5fae906a6db0bd1d7815d8943a Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 16:09:38 -0500 Subject: [PATCH 17/20] rust annotation not needed Co-authored-by: Joona Aalto --- crates/bevy_ecs/src/event.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index b0d68269d2623..27ba63792cd2d 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -567,7 +567,7 @@ impl<'w, E: Event> EventWriter<'w, E> { /// /// # Example /// -/// ```rust +/// ``` /// use bevy_ecs::prelude::*; /// use bevy_ecs::event::{Event, Events, ManualEventReader}; /// From 4df1e20f1f6e4b190949395d1d48771c1c1fc1f4 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 16:11:42 -0500 Subject: [PATCH 18/20] Simplify event resending (thanks icck) --- examples/ecs/send_and_receive_events.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index 744d5fb978f22..4239bed607ca6 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -125,13 +125,9 @@ fn send_and_receive_param_set( } // This is p1, as the second parameter in the `ParamSet` is the writer. - for event in events_to_resend { - param_set.p1().send(DebugEvent { - times_sent: event.times_sent + 1, - // This is struct update syntax! Here, we're copying all of the fields from `event`, - // except for the `times_sent` field, which we're incrementing. - ..event - }); + for mut event in events_to_resend { + event.times_sent += 1; + param_set.p1().send(event); } } @@ -162,12 +158,8 @@ fn send_and_receive_manual_event_reader( } } - for event in events_to_resend { - events.send(DebugEvent { - times_sent: event.times_sent + 1, - // This is struct update syntax! Here, we're copying all of the fields from `event`, - // except for the `times_sent` field, which we're incrementing. - ..event - }); + for mut event in events_to_resend { + event.times_sent += 1; + events.send(event); } } From b3e9ccae38ecee5f9dc6b05c01be30390549532e Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 16:12:42 -0500 Subject: [PATCH 19/20] Missed emission --- examples/ecs/send_and_receive_events.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index 28948e1c3b844..b186937f029ee 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -114,7 +114,7 @@ fn send_and_receive_param_set( frame_count.0 ); - // We must collect the events to re-emit, because we can't access the writer while we're iterating over the reader. + // 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. From 219d68c096eeb0d49b76ab613d95880ac3f8289a Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 28 Jan 2024 16:19:03 -0500 Subject: [PATCH 20/20] Stop emitting! Co-authored-by: Joona Aalto --- crates/bevy_ecs/src/event.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 27ba63792cd2d..554b17195fbeb 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -582,14 +582,14 @@ impl<'w, E: Event> EventWriter<'w, E> { /// // We can access the `Events` resource mutably, allowing us to both read and write its contents. /// mut events: ResMut>, /// ) { -/// // We must collect the events to re-emit, because we can't mutate events while we're iterating over the events. -/// let mut events_to_re_emit = Vec::new(); +/// // 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_re_emit.push(event.clone()); +/// events_to_resend.push(event.clone()); /// } /// -/// for event in events_to_re_emit { +/// for event in events_to_resend { /// events.send(MyEvent); /// } /// }