diff --git a/benches/benches/bevy_ecs/events/iter.rs b/benches/benches/bevy_ecs/events/iter.rs index dc20bc3395..9ad17ed8c8 100644 --- a/benches/benches/bevy_ecs/events/iter.rs +++ b/benches/benches/bevy_ecs/events/iter.rs @@ -1,6 +1,6 @@ use bevy_ecs::prelude::*; -#[derive(Event)] +#[derive(Event, BufferedEvent)] struct BenchEvent([u8; SIZE]); pub struct Benchmark(Events>); diff --git a/benches/benches/bevy_ecs/events/send.rs b/benches/benches/bevy_ecs/events/send.rs index fa996b50aa..be8934e789 100644 --- a/benches/benches/bevy_ecs/events/send.rs +++ b/benches/benches/bevy_ecs/events/send.rs @@ -1,6 +1,6 @@ use bevy_ecs::prelude::*; -#[derive(Event)] +#[derive(Event, BufferedEvent)] struct BenchEvent([u8; SIZE]); impl Default for BenchEvent { diff --git a/benches/benches/bevy_ecs/observers/propagation.rs b/benches/benches/bevy_ecs/observers/propagation.rs index e2be1afed4..808c3727d5 100644 --- a/benches/benches/bevy_ecs/observers/propagation.rs +++ b/benches/benches/bevy_ecs/observers/propagation.rs @@ -61,14 +61,10 @@ pub fn event_propagation(criterion: &mut Criterion) { group.finish(); } -#[derive(Clone, Component)] +#[derive(Event, EntityEvent, Clone, Component)] +#[entity_event(traversal = &'static ChildOf, auto_propagate)] struct TestEvent {} -impl Event for TestEvent { - type Traversal = &'static ChildOf; - const AUTO_PROPAGATE: bool = true; -} - fn send_events(world: &mut World, leaves: &[Entity]) { let target = leaves.iter().choose(&mut rand::thread_rng()).unwrap(); diff --git a/benches/benches/bevy_ecs/observers/simple.rs b/benches/benches/bevy_ecs/observers/simple.rs index b3006ec924..9c26b074e5 100644 --- a/benches/benches/bevy_ecs/observers/simple.rs +++ b/benches/benches/bevy_ecs/observers/simple.rs @@ -1,7 +1,7 @@ use core::hint::black_box; use bevy_ecs::{ - event::Event, + event::{EntityEvent, Event}, observer::{On, TriggerTargets}, world::World, }; @@ -13,7 +13,7 @@ fn deterministic_rand() -> ChaCha8Rng { ChaCha8Rng::seed_from_u64(42) } -#[derive(Clone, Event)] +#[derive(Clone, Event, EntityEvent)] struct EventBase; pub fn observe_simple(criterion: &mut Criterion) { diff --git a/crates/bevy_a11y/src/lib.rs b/crates/bevy_a11y/src/lib.rs index f8c46757dd..22b2f71f07 100644 --- a/crates/bevy_a11y/src/lib.rs +++ b/crates/bevy_a11y/src/lib.rs @@ -26,7 +26,8 @@ use accesskit::Node; use bevy_app::Plugin; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - prelude::{Component, Event}, + component::Component, + event::{BufferedEvent, Event}, resource::Resource, schedule::SystemSet, }; @@ -44,7 +45,7 @@ use serde::{Deserialize, Serialize}; use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; /// Wrapper struct for [`accesskit::ActionRequest`]. Required to allow it to be used as an `Event`. -#[derive(Event, Deref, DerefMut)] +#[derive(Event, BufferedEvent, Deref, DerefMut)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct ActionRequest(pub accesskit::ActionRequest); diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index dd68595961..ae7ce42ed6 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -324,13 +324,13 @@ impl AnimationClip { .push(variable_curve); } - /// Add a untargeted [`Event`] to this [`AnimationClip`]. + /// Add an [`EntityEvent`] with no [`AnimationTarget`] to this [`AnimationClip`]. /// /// The `event` will be cloned and triggered on the [`AnimationPlayer`] entity once the `time` (in seconds) /// is reached in the animation. /// /// See also [`add_event_to_target`](Self::add_event_to_target). - pub fn add_event(&mut self, time: f32, event: impl Event + Clone) { + pub fn add_event(&mut self, time: f32, event: impl EntityEvent + Clone) { self.add_event_fn( time, move |commands: &mut Commands, entity: Entity, _time: f32, _weight: f32| { @@ -339,7 +339,7 @@ impl AnimationClip { ); } - /// Add an [`Event`] to an [`AnimationTarget`] named by an [`AnimationTargetId`]. + /// Add an [`EntityEvent`] to an [`AnimationTarget`] named by an [`AnimationTargetId`]. /// /// The `event` will be cloned and triggered on the entity matching the target once the `time` (in seconds) /// is reached in the animation. @@ -349,7 +349,7 @@ impl AnimationClip { &mut self, target_id: AnimationTargetId, time: f32, - event: impl Event + Clone, + event: impl EntityEvent + Clone, ) { self.add_event_fn_to_target( target_id, @@ -360,19 +360,19 @@ impl AnimationClip { ); } - /// Add a untargeted event function to this [`AnimationClip`]. + /// Add an event function with no [`AnimationTarget`] to this [`AnimationClip`]. /// /// The `func` will trigger on the [`AnimationPlayer`] entity once the `time` (in seconds) /// is reached in the animation. /// - /// For a simpler [`Event`]-based alternative, see [`AnimationClip::add_event`]. + /// For a simpler [`EntityEvent`]-based alternative, see [`AnimationClip::add_event`]. /// See also [`add_event_to_target`](Self::add_event_to_target). /// /// ``` /// # use bevy_animation::AnimationClip; /// # let mut clip = AnimationClip::default(); /// clip.add_event_fn(1.0, |commands, entity, time, weight| { - /// println!("Animation Event Triggered {entity:#?} at time {time} with weight {weight}"); + /// println!("Animation event triggered {entity:#?} at time {time} with weight {weight}"); /// }) /// ``` pub fn add_event_fn( @@ -388,14 +388,14 @@ impl AnimationClip { /// The `func` will trigger on the entity matching the target once the `time` (in seconds) /// is reached in the animation. /// - /// For a simpler [`Event`]-based alternative, see [`AnimationClip::add_event_to_target`]. + /// For a simpler [`EntityEvent`]-based alternative, see [`AnimationClip::add_event_to_target`]. /// Use [`add_event`](Self::add_event) instead if you don't have a specific target. /// /// ``` /// # use bevy_animation::{AnimationClip, AnimationTargetId}; /// # let mut clip = AnimationClip::default(); /// clip.add_event_fn_to_target(AnimationTargetId::from_iter(["Arm", "Hand"]), 1.0, |commands, entity, time, weight| { - /// println!("Animation Event Triggered {entity:#?} at time {time} with weight {weight}"); + /// println!("Animation event triggered {entity:#?} at time {time} with weight {weight}"); /// }) /// ``` pub fn add_event_fn_to_target( @@ -1534,7 +1534,7 @@ mod tests { use super::*; - #[derive(Event, Reflect, Clone)] + #[derive(Event, EntityEvent, Reflect, Clone)] struct A; #[track_caller] diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 8f1ef3c1b4..af5183b159 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -344,7 +344,7 @@ impl App { self } - /// Initializes `T` event handling by inserting an event queue resource ([`Events::`]) + /// Initializes [`BufferedEvent`] handling for `T` by inserting an event queue resource ([`Events::`]) /// and scheduling an [`event_update_system`] in [`First`]. /// /// See [`Events`] for information on how to define events. @@ -355,7 +355,7 @@ impl App { /// # use bevy_app::prelude::*; /// # use bevy_ecs::prelude::*; /// # - /// # #[derive(Event)] + /// # #[derive(Event, BufferedEvent)] /// # struct MyEvent; /// # let mut app = App::new(); /// # @@ -363,7 +363,7 @@ impl App { /// ``` pub fn add_event(&mut self) -> &mut Self where - T: Event, + T: BufferedEvent, { self.main_mut().add_event::(); self @@ -1325,7 +1325,7 @@ impl App { /// # friends_allowed: bool, /// # }; /// # - /// # #[derive(Event)] + /// # #[derive(Event, EntityEvent)] /// # struct Invite; /// # /// # #[derive(Component)] @@ -1407,7 +1407,7 @@ fn run_once(mut app: App) -> AppExit { app.should_exit().unwrap_or(AppExit::Success) } -/// An event that indicates the [`App`] should exit. If one or more of these are present at the end of an update, +/// A [`BufferedEvent`] that indicates the [`App`] should exit. If one or more of these are present at the end of an update, /// the [runner](App::set_runner) will end and ([maybe](App::run)) return control to the caller. /// /// This event can be used to detect when an exit is requested. Make sure that systems listening @@ -1417,7 +1417,7 @@ fn run_once(mut app: App) -> AppExit { /// This type is roughly meant to map to a standard definition of a process exit code (0 means success, not 0 means error). Due to portability concerns /// (see [`ExitCode`](https://doc.rust-lang.org/std/process/struct.ExitCode.html) and [`process::exit`](https://doc.rust-lang.org/std/process/fn.exit.html#)) /// we only allow error codes between 1 and [255](u8::MAX). -#[derive(Event, Debug, Clone, Default, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, Default, PartialEq, Eq)] pub enum AppExit { /// [`App`] exited without any problems. #[default] @@ -1485,7 +1485,7 @@ mod tests { change_detection::{DetectChanges, ResMut}, component::Component, entity::Entity, - event::{Event, EventWriter, Events}, + event::{BufferedEvent, Event, EventWriter, Events}, lifecycle::RemovedComponents, query::With, resource::Resource, @@ -1851,7 +1851,7 @@ mod tests { } #[test] fn events_should_be_updated_once_per_update() { - #[derive(Event, Clone)] + #[derive(Event, BufferedEvent, Clone)] struct TestEvent; let mut app = App::new(); diff --git a/crates/bevy_app/src/sub_app.rs b/crates/bevy_app/src/sub_app.rs index c340b80654..56d6b43d38 100644 --- a/crates/bevy_app/src/sub_app.rs +++ b/crates/bevy_app/src/sub_app.rs @@ -338,7 +338,7 @@ impl SubApp { /// See [`App::add_event`]. pub fn add_event(&mut self) -> &mut Self where - T: Event, + T: BufferedEvent, { if !self.world.contains_resource::>() { EventRegistry::register_event::(self.world_mut()); diff --git a/crates/bevy_asset/src/event.rs b/crates/bevy_asset/src/event.rs index 087cb44b5a..42de19fe44 100644 --- a/crates/bevy_asset/src/event.rs +++ b/crates/bevy_asset/src/event.rs @@ -1,12 +1,12 @@ use crate::{Asset, AssetId, AssetLoadError, AssetPath, UntypedAssetId}; -use bevy_ecs::event::Event; +use bevy_ecs::event::{BufferedEvent, Event}; use bevy_reflect::Reflect; use core::fmt::Debug; -/// An event emitted when a specific [`Asset`] fails to load. +/// A [`BufferedEvent`] emitted when a specific [`Asset`] fails to load. /// /// For an untyped equivalent, see [`UntypedAssetLoadFailedEvent`]. -#[derive(Event, Clone, Debug)] +#[derive(Event, BufferedEvent, Clone, Debug)] pub struct AssetLoadFailedEvent { /// The stable identifier of the asset that failed to load. pub id: AssetId, @@ -24,7 +24,7 @@ impl AssetLoadFailedEvent { } /// An untyped version of [`AssetLoadFailedEvent`]. -#[derive(Event, Clone, Debug)] +#[derive(Event, BufferedEvent, Clone, Debug)] pub struct UntypedAssetLoadFailedEvent { /// The stable identifier of the asset that failed to load. pub id: UntypedAssetId, @@ -44,9 +44,9 @@ impl From<&AssetLoadFailedEvent> for UntypedAssetLoadFailedEvent { } } -/// Events that occur for a specific loaded [`Asset`], such as "value changed" events and "dependency" events. +/// [`BufferedEvent`]s that occur for a specific loaded [`Asset`], such as "value changed" events and "dependency" events. #[expect(missing_docs, reason = "Documenting the id fields is unhelpful.")] -#[derive(Event, Reflect)] +#[derive(Event, BufferedEvent, Reflect)] pub enum AssetEvent { /// Emitted whenever an [`Asset`] is added. Added { id: AssetId }, diff --git a/crates/bevy_core_widgets/src/core_button.rs b/crates/bevy_core_widgets/src/core_button.rs index 31afbc1aca..ec30b625f9 100644 --- a/crates/bevy_core_widgets/src/core_button.rs +++ b/crates/bevy_core_widgets/src/core_button.rs @@ -32,7 +32,7 @@ fn button_on_key_event( q_state: Query<(&CoreButton, Has)>, mut commands: Commands, ) { - if let Ok((bstate, disabled)) = q_state.get(trigger.target().unwrap()) { + if let Ok((bstate, disabled)) = q_state.get(trigger.target()) { if !disabled { let event = &trigger.event().input; if !event.repeat @@ -52,7 +52,7 @@ fn button_on_pointer_click( mut q_state: Query<(&CoreButton, Has, Has)>, mut commands: Commands, ) { - if let Ok((bstate, pressed, disabled)) = q_state.get_mut(trigger.target().unwrap()) { + if let Ok((bstate, pressed, disabled)) = q_state.get_mut(trigger.target()) { trigger.propagate(false); if pressed && !disabled { if let Some(on_click) = bstate.on_click { @@ -69,7 +69,7 @@ fn button_on_pointer_down( focus_visible: Option>, mut commands: Commands, ) { - if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target().unwrap()) { + if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target()) { trigger.propagate(false); if !disabled { if !pressed { @@ -78,7 +78,7 @@ fn button_on_pointer_down( // Clicking on a button makes it the focused input, // and hides the focus ring if it was visible. if let Some(mut focus) = focus { - focus.0 = trigger.target(); + focus.0 = (trigger.target() != Entity::PLACEHOLDER).then_some(trigger.target()); } if let Some(mut focus_visible) = focus_visible { focus_visible.0 = false; @@ -92,7 +92,7 @@ fn button_on_pointer_up( mut q_state: Query<(Entity, Has, Has), With>, mut commands: Commands, ) { - if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target().unwrap()) { + if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target()) { trigger.propagate(false); if !disabled && pressed { commands.entity(button).remove::(); @@ -105,7 +105,7 @@ fn button_on_pointer_drag_end( mut q_state: Query<(Entity, Has, Has), With>, mut commands: Commands, ) { - if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target().unwrap()) { + if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target()) { trigger.propagate(false); if !disabled && pressed { commands.entity(button).remove::(); @@ -118,7 +118,7 @@ fn button_on_pointer_cancel( mut q_state: Query<(Entity, Has, Has), With>, mut commands: Commands, ) { - if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target().unwrap()) { + if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target()) { trigger.propagate(false); if !disabled && pressed { commands.entity(button).remove::(); diff --git a/crates/bevy_core_widgets/src/core_slider.rs b/crates/bevy_core_widgets/src/core_slider.rs index b230442528..63a606be78 100644 --- a/crates/bevy_core_widgets/src/core_slider.rs +++ b/crates/bevy_core_widgets/src/core_slider.rs @@ -3,7 +3,8 @@ use core::ops::RangeInclusive; use accesskit::{Orientation, Role}; use bevy_a11y::AccessibilityNode; use bevy_app::{App, Plugin}; -use bevy_ecs::event::Event; +use bevy_ecs::entity::Entity; +use bevy_ecs::event::{EntityEvent, Event}; use bevy_ecs::hierarchy::{ChildOf, Children}; use bevy_ecs::lifecycle::Insert; use bevy_ecs::query::Has; @@ -211,13 +212,13 @@ pub(crate) fn slider_on_pointer_down( focus_visible: Option>, mut commands: Commands, ) { - if q_thumb.contains(trigger.target().unwrap()) { + if q_thumb.contains(trigger.target()) { // Thumb click, stop propagation to prevent track click. trigger.propagate(false); // Find the slider entity that's an ancestor of the thumb if let Some(slider_entity) = q_parents - .iter_ancestors(trigger.target().unwrap()) + .iter_ancestors(trigger.target()) .find(|entity| q_slider.contains(*entity)) { // Set focus to slider and hide focus ring @@ -229,14 +230,14 @@ pub(crate) fn slider_on_pointer_down( } } } else if let Ok((slider, value, range, step, node, node_target, transform, disabled)) = - q_slider.get(trigger.target().unwrap()) + q_slider.get(trigger.target()) { // Track click trigger.propagate(false); // Set focus to slider and hide focus ring if let Some(mut focus) = focus { - focus.0 = trigger.target(); + focus.0 = (trigger.target() != Entity::PLACEHOLDER).then_some(trigger.target()); } if let Some(mut focus_visible) = focus_visible { focus_visible.0 = false; @@ -248,7 +249,7 @@ pub(crate) fn slider_on_pointer_down( // Find thumb size by searching descendants for the first entity with CoreSliderThumb let thumb_size = q_children - .iter_descendants(trigger.target().unwrap()) + .iter_descendants(trigger.target()) .find_map(|child_id| q_thumb.get(child_id).ok().map(|thumb| thumb.size().x)) .unwrap_or(0.0); @@ -283,7 +284,7 @@ pub(crate) fn slider_on_pointer_down( commands.run_system_with(on_change, new_value); } else { commands - .entity(trigger.target().unwrap()) + .entity(trigger.target()) .insert(SliderValue(new_value)); } } @@ -300,7 +301,7 @@ pub(crate) fn slider_on_drag_start( With, >, ) { - if let Ok((value, mut drag, disabled)) = q_slider.get_mut(trigger.target().unwrap()) { + if let Ok((value, mut drag, disabled)) = q_slider.get_mut(trigger.target()) { trigger.propagate(false); if !disabled { drag.dragging = true; @@ -324,8 +325,7 @@ pub(crate) fn slider_on_drag( mut commands: Commands, ui_scale: Res, ) { - if let Ok((node, slider, range, transform, drag, disabled)) = - q_slider.get_mut(trigger.target().unwrap()) + if let Ok((node, slider, range, transform, drag, disabled)) = q_slider.get_mut(trigger.target()) { trigger.propagate(false); if drag.dragging && !disabled { @@ -334,7 +334,7 @@ pub(crate) fn slider_on_drag( let distance = transform.transform_vector2(distance); // Find thumb size by searching descendants for the first entity with CoreSliderThumb let thumb_size = q_children - .iter_descendants(trigger.target().unwrap()) + .iter_descendants(trigger.target()) .find_map(|child_id| q_thumb.get(child_id).ok().map(|thumb| thumb.size().x)) .unwrap_or(0.0); let slider_width = ((node.size().x - thumb_size) * node.inverse_scale_factor).max(1.0); @@ -349,7 +349,7 @@ pub(crate) fn slider_on_drag( commands.run_system_with(on_change, new_value); } else { commands - .entity(trigger.target().unwrap()) + .entity(trigger.target()) .insert(SliderValue(new_value)); } } @@ -360,7 +360,7 @@ pub(crate) fn slider_on_drag_end( mut trigger: On>, mut q_slider: Query<(&CoreSlider, &mut CoreSliderDragState)>, ) { - if let Ok((_slider, mut drag)) = q_slider.get_mut(trigger.target().unwrap()) { + if let Ok((_slider, mut drag)) = q_slider.get_mut(trigger.target()) { trigger.propagate(false); if drag.dragging { drag.dragging = false; @@ -379,7 +379,7 @@ fn slider_on_key_input( )>, mut commands: Commands, ) { - if let Ok((slider, value, range, step, disabled)) = q_slider.get(trigger.target().unwrap()) { + if let Ok((slider, value, range, step, disabled)) = q_slider.get(trigger.target()) { let event = &trigger.event().input; if !disabled && event.state == ButtonState::Pressed { let new_value = match event.key_code { @@ -400,14 +400,14 @@ fn slider_on_key_input( } pub(crate) fn slider_on_insert(trigger: On, mut world: DeferredWorld) { - let mut entity = world.entity_mut(trigger.target().unwrap()); + let mut entity = world.entity_mut(trigger.target()); if let Some(mut accessibility) = entity.get_mut::() { accessibility.set_orientation(Orientation::Horizontal); } } pub(crate) fn slider_on_insert_value(trigger: On, mut world: DeferredWorld) { - let mut entity = world.entity_mut(trigger.target().unwrap()); + let mut entity = world.entity_mut(trigger.target()); let value = entity.get::().unwrap().0; if let Some(mut accessibility) = entity.get_mut::() { accessibility.set_numeric_value(value.into()); @@ -415,7 +415,7 @@ pub(crate) fn slider_on_insert_value(trigger: On, mut world } pub(crate) fn slider_on_insert_range(trigger: On, mut world: DeferredWorld) { - let mut entity = world.entity_mut(trigger.target().unwrap()); + let mut entity = world.entity_mut(trigger.target()); let range = *entity.get::().unwrap(); if let Some(mut accessibility) = entity.get_mut::() { accessibility.set_min_numeric_value(range.start().into()); @@ -424,14 +424,14 @@ pub(crate) fn slider_on_insert_range(trigger: On, mut world } pub(crate) fn slider_on_insert_step(trigger: On, mut world: DeferredWorld) { - let mut entity = world.entity_mut(trigger.target().unwrap()); + let mut entity = world.entity_mut(trigger.target()); let step = entity.get::().unwrap().0; if let Some(mut accessibility) = entity.get_mut::() { accessibility.set_numeric_value_step(step.into()); } } -/// Event which can be triggered on a slider to modify the value (using the `on_change` callback). +/// An [`EntityEvent`] that can be triggered on a slider to modify its value (using the `on_change` callback). /// This can be used to control the slider via gamepad buttons or other inputs. The value will be /// clamped when the event is processed. /// @@ -456,7 +456,7 @@ pub(crate) fn slider_on_insert_step(trigger: On, mut world: /// commands.trigger_targets(SetSliderValue::Relative(-0.25), slider); /// } /// ``` -#[derive(Event)] +#[derive(Event, EntityEvent)] pub enum SetSliderValue { /// Set the slider value to a specific value. Absolute(f32), @@ -471,7 +471,7 @@ fn slider_on_set_value( q_slider: Query<(&CoreSlider, &SliderValue, &SliderRange, Option<&SliderStep>)>, mut commands: Commands, ) { - if let Ok((slider, value, range, step)) = q_slider.get(trigger.target().unwrap()) { + if let Ok((slider, value, range, step)) = q_slider.get(trigger.target()) { trigger.propagate(false); let new_value = match trigger.event() { SetSliderValue::Absolute(new_value) => range.clamp(*new_value), @@ -484,7 +484,7 @@ fn slider_on_set_value( commands.run_system_with(on_change, new_value); } else { commands - .entity(trigger.target().unwrap()) + .entity(trigger.target()) .insert(SliderValue(new_value)); } } diff --git a/crates/bevy_dev_tools/src/ci_testing/config.rs b/crates/bevy_dev_tools/src/ci_testing/config.rs index 6dc601f1cc..a2419dfaa5 100644 --- a/crates/bevy_dev_tools/src/ci_testing/config.rs +++ b/crates/bevy_dev_tools/src/ci_testing/config.rs @@ -49,7 +49,7 @@ pub enum CiTestingEvent { } /// A custom event that can be configured from a configuration file for CI testing. -#[derive(Event)] +#[derive(Event, BufferedEvent)] pub struct CiTestingCustomEvent(pub String); #[cfg(test)] diff --git a/crates/bevy_dev_tools/src/picking_debug.rs b/crates/bevy_dev_tools/src/picking_debug.rs index d11818dc6a..16233cd3dc 100644 --- a/crates/bevy_dev_tools/src/picking_debug.rs +++ b/crates/bevy_dev_tools/src/picking_debug.rs @@ -5,9 +5,9 @@ use bevy_color::prelude::*; use bevy_ecs::prelude::*; use bevy_picking::backend::HitData; use bevy_picking::hover::HoverMap; -use bevy_picking::pointer::{Location, PointerId, PointerPress}; +use bevy_picking::pointer::{Location, PointerId, PointerInput, PointerLocation, PointerPress}; use bevy_picking::prelude::*; -use bevy_picking::{pointer, PickingSystems}; +use bevy_picking::PickingSystems; use bevy_reflect::prelude::*; use bevy_render::prelude::*; use bevy_text::prelude::*; @@ -91,7 +91,7 @@ impl Plugin for DebugPickingPlugin { ( // This leaves room to easily change the log-level associated // with different events, should that be desired. - log_event_debug::.run_if(DebugPickingMode::is_noisy), + log_event_debug::.run_if(DebugPickingMode::is_noisy), log_pointer_event_debug::, log_pointer_event_debug::, log_pointer_event_debug::, @@ -121,7 +121,7 @@ impl Plugin for DebugPickingPlugin { } /// Listen for any event and logs it at the debug level -pub fn log_event_debug(mut events: EventReader) { +pub fn log_event_debug(mut events: EventReader) { for event in events.read() { debug!("{event:?}"); } @@ -214,7 +214,7 @@ pub fn update_debug_data( entity_names: Query, mut pointers: Query<( &PointerId, - &pointer::PointerLocation, + &PointerLocation, &PointerPress, &mut PointerDebug, )>, diff --git a/crates/bevy_ecs/README.md b/crates/bevy_ecs/README.md index 6ab8d45b7f..b085a79c21 100644 --- a/crates/bevy_ecs/README.md +++ b/crates/bevy_ecs/README.md @@ -277,26 +277,24 @@ world.spawn(PlayerBundle { }); ``` -### Events +### Buffered Events -Events offer a communication channel between one or more systems. Events can be sent using the system parameter `EventWriter` and received with `EventReader`. +Buffered events offer a communication channel between one or more systems. +They can be sent using the `EventWriter` system parameter and received with `EventReader`. ```rust use bevy_ecs::prelude::*; -#[derive(Event)] -struct MyEvent { - message: String, +#[derive(Event, BufferedEvent)] +struct Message(String); + +fn writer(mut writer: EventWriter) { + writer.write(Message("Hello!".to_string())); } -fn writer(mut writer: EventWriter) { - writer.write(MyEvent { - message: "hello!".to_string(), - }); -} - -fn reader(mut reader: EventReader) { - for event in reader.read() { +fn reader(mut reader: EventReader) { + for Message(message) in reader.read() { + println!("{}", message); } } ``` @@ -309,39 +307,41 @@ Observers are systems that listen for a "trigger" of a specific `Event`: use bevy_ecs::prelude::*; #[derive(Event)] -struct MyEvent { +struct Speak { message: String } let mut world = World::new(); -world.add_observer(|trigger: On| { - println!("{}", trigger.event().message); +world.add_observer(|trigger: On| { + println!("{}", trigger.message); }); world.flush(); -world.trigger(MyEvent { - message: "hello!".to_string(), +world.trigger(Speak { + message: "Hello!".to_string(), }); ``` -These differ from `EventReader` and `EventWriter` in that they are "reactive". Rather than happening at a specific point in a schedule, they happen _immediately_ whenever a trigger happens. Triggers can trigger other triggers, and they all will be evaluated at the same time! +These differ from `EventReader` and `EventWriter` in that they are "reactive". +Rather than happening at a specific point in a schedule, they happen _immediately_ whenever a trigger happens. +Triggers can trigger other triggers, and they all will be evaluated at the same time! -Events can also be triggered to target specific entities: +If the event is an `EntityEvent`, it can also be triggered to target specific entities: ```rust use bevy_ecs::prelude::*; -#[derive(Event)] +#[derive(Event, EntityEvent)] struct Explode; let mut world = World::new(); let entity = world.spawn_empty().id(); world.add_observer(|trigger: On, mut commands: Commands| { - println!("Entity {} goes BOOM!", trigger.target().unwrap()); - commands.entity(trigger.target().unwrap()).despawn(); + println!("Entity {} goes BOOM!", trigger.target()); + commands.entity(trigger.target()).despawn(); }); world.flush(); diff --git a/crates/bevy_ecs/examples/events.rs b/crates/bevy_ecs/examples/events.rs index fb01184048..ecdcb31a33 100644 --- a/crates/bevy_ecs/examples/events.rs +++ b/crates/bevy_ecs/examples/events.rs @@ -1,4 +1,4 @@ -//! In this example a system sends a custom event with a 50/50 chance during any frame. +//! In this example a system sends a custom buffered event with a 50/50 chance during any frame. //! If an event was sent, it will be printed by the console in a receiving system. #![expect(clippy::print_stdout, reason = "Allowed in examples.")] @@ -15,7 +15,7 @@ fn main() { // Create a schedule to store our systems let mut schedule = Schedule::default(); - // Events need to be updated in every frame in order to clear our buffers. + // Buffered events need to be updated every frame in order to clear our buffers. // This update should happen before we use the events. // Here, we use system sets to control the ordering. #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] @@ -37,7 +37,7 @@ fn main() { } // This is our event that we will send and receive in systems -#[derive(Event)] +#[derive(Event, BufferedEvent)] struct MyEvent { pub message: String, pub random_value: f32, diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index 6a693f2ce5..53ba284588 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -13,11 +13,28 @@ use syn::{ LitStr, Member, Path, Result, Token, Type, Visibility, }; -pub const EVENT: &str = "event"; +pub const EVENT: &str = "entity_event"; pub const AUTO_PROPAGATE: &str = "auto_propagate"; pub const TRAVERSAL: &str = "traversal"; pub fn derive_event(input: TokenStream) -> TokenStream { + let mut ast = parse_macro_input!(input as DeriveInput); + let bevy_ecs_path: Path = crate::bevy_ecs_path(); + + ast.generics + .make_where_clause() + .predicates + .push(parse_quote! { Self: Send + Sync + 'static }); + + let struct_name = &ast.ident; + let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + + TokenStream::from(quote! { + impl #impl_generics #bevy_ecs_path::event::Event for #struct_name #type_generics #where_clause {} + }) +} + +pub fn derive_entity_event(input: TokenStream) -> TokenStream { let mut ast = parse_macro_input!(input as DeriveInput); let mut auto_propagate = false; let mut traversal: Type = parse_quote!(()); @@ -49,13 +66,30 @@ pub fn derive_event(input: TokenStream) -> TokenStream { let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); TokenStream::from(quote! { - impl #impl_generics #bevy_ecs_path::event::Event for #struct_name #type_generics #where_clause { + impl #impl_generics #bevy_ecs_path::event::EntityEvent for #struct_name #type_generics #where_clause { type Traversal = #traversal; const AUTO_PROPAGATE: bool = #auto_propagate; } }) } +pub fn derive_buffered_event(input: TokenStream) -> TokenStream { + let mut ast = parse_macro_input!(input as DeriveInput); + let bevy_ecs_path: Path = crate::bevy_ecs_path(); + + ast.generics + .make_where_clause() + .predicates + .push(parse_quote! { Self: Send + Sync + 'static }); + + let struct_name = &ast.ident; + let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + + TokenStream::from(quote! { + impl #impl_generics #bevy_ecs_path::event::BufferedEvent for #struct_name #type_generics #where_clause {} + }) +} + pub fn derive_resource(input: TokenStream) -> TokenStream { let mut ast = parse_macro_input!(input as DeriveInput); let bevy_ecs_path: Path = crate::bevy_ecs_path(); diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index ee7163039d..8090cff7de 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -573,11 +573,23 @@ pub(crate) fn bevy_ecs_path() -> syn::Path { } /// Implement the `Event` trait. -#[proc_macro_derive(Event, attributes(event))] +#[proc_macro_derive(Event)] pub fn derive_event(input: TokenStream) -> TokenStream { component::derive_event(input) } +/// Implement the `EntityEvent` trait. +#[proc_macro_derive(EntityEvent, attributes(entity_event))] +pub fn derive_entity_event(input: TokenStream) -> TokenStream { + component::derive_entity_event(input) +} + +/// Implement the `BufferedEvent` trait. +#[proc_macro_derive(BufferedEvent)] +pub fn derive_buffered_event(input: TokenStream) -> TokenStream { + component::derive_buffered_event(input) +} + /// Implement the `Resource` trait. #[proc_macro_derive(Resource)] pub fn derive_resource(input: TokenStream) -> TokenStream { diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index 83bee583d7..006b738caf 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -230,7 +230,7 @@ pub trait DetectChangesMut: DetectChanges { /// #[derive(Resource, PartialEq, Eq)] /// pub struct Score(u32); /// - /// #[derive(Event, PartialEq, Eq)] + /// #[derive(Event, BufferedEvent, PartialEq, Eq)] /// pub struct ScoreChanged { /// current: u32, /// previous: u32, diff --git a/crates/bevy_ecs/src/event/base.rs b/crates/bevy_ecs/src/event/base.rs index 75e86f1b03..52839f369d 100644 --- a/crates/bevy_ecs/src/event/base.rs +++ b/crates/bevy_ecs/src/event/base.rs @@ -11,33 +11,81 @@ use core::{ marker::PhantomData, }; -/// Something that "happens" and might be read / observed by app logic. +/// Something that "happens" and can be processed by app logic. /// -/// Events can be stored in an [`Events`] resource -/// You can conveniently access events using the [`EventReader`] and [`EventWriter`] system parameter. +/// Events can be triggered on a [`World`] using a method like [`trigger`](World::trigger), +/// causing any global [`Observer`] watching that event to run. This allows for push-based +/// event handling where observers are immediately notified of events as they happen. /// -/// Events can also be "triggered" on a [`World`], which will then cause any [`Observer`] of that trigger to run. +/// Additional event handling behavior can be enabled by implementing the [`EntityEvent`] +/// and [`BufferedEvent`] traits: +/// +/// - [`EntityEvent`]s support targeting specific entities, triggering any observers watching those targets. +/// They are useful for entity-specific event handlers and can even be propagated from one entity to another. +/// - [`BufferedEvent`]s support a pull-based event handling system where events are written using an [`EventWriter`] +/// and read later using an [`EventReader`]. This is an alternative to observers that allows efficient batch processing +/// of events at fixed points in a schedule. /// /// Events must be thread-safe. /// -/// ## Derive -/// This trait can be derived. -/// Adding `auto_propagate` sets [`Self::AUTO_PROPAGATE`] to true. -/// Adding `traversal = "X"` sets [`Self::Traversal`] to be of type "X". +/// # Usage +/// +/// The [`Event`] trait can be derived: /// /// ``` -/// use bevy_ecs::prelude::*; -/// +/// # use bevy_ecs::prelude::*; +/// # /// #[derive(Event)] -/// #[event(auto_propagate)] -/// struct MyEvent; +/// struct Speak { +/// message: String, +/// } /// ``` /// +/// An [`Observer`] can then be added to listen for this event type: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # +/// # #[derive(Event)] +/// # struct Speak { +/// # message: String, +/// # } +/// # +/// # let mut world = World::new(); +/// # +/// world.add_observer(|trigger: On| { +/// println!("{}", trigger.message); +/// }); +/// ``` +/// +/// The event can be triggered on the [`World`] using the [`trigger`](World::trigger) method: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # +/// # #[derive(Event)] +/// # struct Speak { +/// # message: String, +/// # } +/// # +/// # let mut world = World::new(); +/// # +/// # world.add_observer(|trigger: On| { +/// # println!("{}", trigger.message); +/// # }); +/// # +/// # world.flush(); +/// # +/// world.trigger(Speak { +/// message: "Hello!".to_string(), +/// }); +/// ``` +/// +/// For events that additionally need entity targeting or buffering, consider also deriving +/// [`EntityEvent`] or [`BufferedEvent`], respectively. /// /// [`World`]: crate::world::World -/// [`ComponentId`]: crate::component::ComponentId /// [`Observer`]: crate::observer::Observer -/// [`Events`]: super::Events /// [`EventReader`]: super::EventReader /// [`EventWriter`]: super::EventWriter #[diagnostic::on_unimplemented( @@ -46,19 +94,6 @@ use core::{ note = "consider annotating `{Self}` with `#[derive(Event)]`" )] pub trait Event: Send + Sync + 'static { - /// The component that describes which [`Entity`] to propagate this event to next, when [propagation] is enabled. - /// - /// [`Entity`]: crate::entity::Entity - /// [propagation]: crate::observer::On::propagate - type Traversal: Traversal; - - /// When true, this event will always attempt to propagate when [triggered], without requiring a call - /// to [`On::propagate`]. - /// - /// [triggered]: crate::system::Commands::trigger_targets - /// [`On::propagate`]: crate::observer::On::propagate - const AUTO_PROPAGATE: bool = false; - /// Generates the [`ComponentId`] for this event type. /// /// If this type has already been registered, @@ -90,6 +125,209 @@ pub trait Event: Send + Sync + 'static { } } +/// An [`Event`] that can be targeted at specific entities. +/// +/// Entity events can be triggered on a [`World`] with specific entity targets using a method +/// like [`trigger_targets`](World::trigger_targets), causing any [`Observer`] watching the event +/// for those entities to run. +/// +/// Unlike basic [`Event`]s, entity events can optionally be propagated from one entity target to another +/// based on the [`EntityEvent::Traversal`] type associated with the event. This enables use cases +/// such as bubbling events to parent entities for UI purposes. +/// +/// Entity events must be thread-safe. +/// +/// # Usage +/// +/// The [`EntityEvent`] trait can be derived. The `event` attribute can be used to further configure +/// the propagation behavior: adding `auto_propagate` sets [`EntityEvent::AUTO_PROPAGATE`] to `true`, +/// while adding `traversal = X` sets [`EntityEvent::Traversal`] to be of type `X`. +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # +/// // When the `Damage` event is triggered on an entity, bubble the event up to ancestors. +/// #[derive(Event, EntityEvent)] +/// #[entity_event(traversal = &'static ChildOf, auto_propagate)] +/// struct Damage { +/// amount: f32, +/// } +/// ``` +/// +/// An [`Observer`] can then be added to listen for this event type for the desired entity: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # +/// # #[derive(Event, EntityEvent)] +/// # #[entity_event(traversal = &'static ChildOf, auto_propagate)] +/// # struct Damage { +/// # amount: f32, +/// # } +/// # +/// # #[derive(Component)] +/// # struct Health(f32); +/// # +/// # #[derive(Component)] +/// # struct Enemy; +/// # +/// # #[derive(Component)] +/// # struct ArmorPiece; +/// # +/// # let mut world = World::new(); +/// # +/// // Spawn an enemy entity. +/// let enemy = world.spawn((Enemy, Health(100.0))).id(); +/// +/// // Spawn some armor as a child of the enemy entity. +/// // When the armor takes damage, it will bubble the event up to the enemy, +/// // which can then handle the event with its own observer. +/// let armor_piece = world +/// .spawn((ArmorPiece, Health(25.0), ChildOf(enemy))) +/// .observe(|trigger: On, mut query: Query<&mut Health>| { +/// // Note: `On::target` only exists because this is an `EntityEvent`. +/// let mut health = query.get_mut(trigger.target()).unwrap(); +/// health.0 -= trigger.amount; +/// }) +/// .id(); +/// ``` +/// +/// The event can be triggered on the [`World`] using the [`trigger_targets`](World::trigger_targets) method, +/// providing the desired entity target(s): +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # +/// # #[derive(Event, EntityEvent)] +/// # #[entity_event(traversal = &'static ChildOf, auto_propagate)] +/// # struct Damage { +/// # amount: f32, +/// # } +/// # +/// # #[derive(Component)] +/// # struct Health(f32); +/// # +/// # #[derive(Component)] +/// # struct Enemy; +/// # +/// # #[derive(Component)] +/// # struct ArmorPiece; +/// # +/// # let mut world = World::new(); +/// # +/// # let enemy = world.spawn((Enemy, Health(100.0))).id(); +/// # let armor_piece = world +/// # .spawn((ArmorPiece, Health(25.0), ChildOf(enemy))) +/// # .observe(|trigger: On, mut query: Query<&mut Health>| { +/// # // Note: `On::target` only exists because this is an `EntityEvent`. +/// # let mut health = query.get_mut(trigger.target()).unwrap(); +/// # health.0 -= trigger.amount; +/// # }) +/// # .id(); +/// # +/// # world.flush(); +/// # +/// world.trigger_targets(Damage { amount: 10.0 }, armor_piece); +/// ``` +/// +/// [`World`]: crate::world::World +/// [`TriggerTargets`]: crate::observer::TriggerTargets +/// [`Observer`]: crate::observer::Observer +/// [`Events`]: super::Events +/// [`EventReader`]: super::EventReader +/// [`EventWriter`]: super::EventWriter +#[diagnostic::on_unimplemented( + message = "`{Self}` is not an `EntityEvent`", + label = "invalid `EntityEvent`", + note = "consider annotating `{Self}` with `#[derive(Event, EntityEvent)]`" +)] +pub trait EntityEvent: Event { + /// The component that describes which [`Entity`] to propagate this event to next, when [propagation] is enabled. + /// + /// [`Entity`]: crate::entity::Entity + /// [propagation]: crate::observer::On::propagate + type Traversal: Traversal; + + /// When true, this event will always attempt to propagate when [triggered], without requiring a call + /// to [`On::propagate`]. + /// + /// [triggered]: crate::system::Commands::trigger_targets + /// [`On::propagate`]: crate::observer::On::propagate + const AUTO_PROPAGATE: bool = false; +} + +/// A buffered [`Event`] for pull-based event handling. +/// +/// Buffered events can be written with [`EventWriter`] and read using the [`EventReader`] system parameter. +/// These events are stored in the [`Events`] resource, and require periodically polling the world for new events, +/// typically in a system that runs as part of a schedule. +/// +/// While the polling imposes a small overhead, buffered events are useful for efficiently batch processing +/// a large number of events at once. This can make them more efficient than [`Event`]s used by [`Observer`]s +/// for events that happen at a high frequency or in large quantities. +/// +/// Unlike [`Event`]s triggered for observers, buffered events are evaluated at fixed points in the schedule +/// rather than immediately when they are sent. This allows for more predictable scheduling and deferring +/// event processing to a later point in time. +/// +/// Buffered events do *not* trigger observers automatically when they are written via an [`EventWriter`]. +/// However, they can still also be triggered on a [`World`] in case you want both buffered and immediate +/// event handling for the same event. +/// +/// Buffered events must be thread-safe. +/// +/// # Usage +/// +/// The [`BufferedEvent`] trait can be derived: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # +/// #[derive(Event, BufferedEvent)] +/// struct Message(String); +/// ``` +/// +/// The event can then be written to the event buffer using an [`EventWriter`]: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # +/// # #[derive(Event, BufferedEvent)] +/// # struct Message(String); +/// # +/// fn write_hello(mut writer: EventWriter) { +/// writer.write(Message("Hello!".to_string())); +/// } +/// ``` +/// +/// Buffered events can be efficiently read using an [`EventReader`]: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # +/// # #[derive(Event, BufferedEvent)] +/// # struct Message(String); +/// # +/// fn read_messages(mut reader: EventReader) { +/// // Process all buffered events of type `Message`. +/// for Message(message) in reader.read() { +/// println!("{message}"); +/// } +/// } +/// ``` +/// +/// [`World`]: crate::world::World +/// [`Observer`]: crate::observer::Observer +/// [`Events`]: super::Events +/// [`EventReader`]: super::EventReader +/// [`EventWriter`]: super::EventWriter +#[diagnostic::on_unimplemented( + message = "`{Self}` is not an `BufferedEvent`", + label = "invalid `BufferedEvent`", + note = "consider annotating `{Self}` with `#[derive(Event, BufferedEvent)]`" +)] +pub trait BufferedEvent: Event {} + /// An internal type that implements [`Component`] for a given [`Event`] type. /// /// This exists so we can easily get access to a unique [`ComponentId`] for each [`Event`] type, @@ -116,7 +354,7 @@ struct EventWrapperComponent(PhantomData); derive(Reflect), reflect(Clone, Debug, PartialEq, Hash) )] -pub struct EventId { +pub struct EventId { /// Uniquely identifies the event associated with this ID. // This value corresponds to the order in which each event was added to the world. pub id: usize, @@ -126,21 +364,21 @@ pub struct EventId { pub(super) _marker: PhantomData, } -impl Copy for EventId {} +impl Copy for EventId {} -impl Clone for EventId { +impl Clone for EventId { fn clone(&self) -> Self { *self } } -impl fmt::Display for EventId { +impl fmt::Display for EventId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ::fmt(self, f) } } -impl fmt::Debug for EventId { +impl fmt::Debug for EventId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, @@ -151,27 +389,27 @@ impl fmt::Debug for EventId { } } -impl PartialEq for EventId { +impl PartialEq for EventId { fn eq(&self, other: &Self) -> bool { self.id == other.id } } -impl Eq for EventId {} +impl Eq for EventId {} -impl PartialOrd for EventId { +impl PartialOrd for EventId { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for EventId { +impl Ord for EventId { fn cmp(&self, other: &Self) -> Ordering { self.id.cmp(&other.id) } } -impl Hash for EventId { +impl Hash for EventId { fn hash(&self, state: &mut H) { Hash::hash(&self.id, state); } @@ -179,7 +417,7 @@ impl Hash for EventId { #[derive(Debug)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] -pub(crate) struct EventInstance { +pub(crate) struct EventInstance { pub event_id: EventId, pub event: E, } diff --git a/crates/bevy_ecs/src/event/collections.rs b/crates/bevy_ecs/src/event/collections.rs index 66447b7de4..7d1854149e 100644 --- a/crates/bevy_ecs/src/event/collections.rs +++ b/crates/bevy_ecs/src/event/collections.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use bevy_ecs::{ change_detection::MaybeLocation, - event::{Event, EventCursor, EventId, EventInstance}, + event::{BufferedEvent, EventCursor, EventId, EventInstance}, resource::Resource, }; use core::{ @@ -38,10 +38,11 @@ use { /// dropped silently. /// /// # Example -/// ``` -/// use bevy_ecs::event::{Event, Events}; /// -/// #[derive(Event)] +/// ``` +/// use bevy_ecs::event::{BufferedEvent, Event, Events}; +/// +/// #[derive(Event, BufferedEvent)] /// struct MyEvent { /// value: usize /// } @@ -91,7 +92,7 @@ use { /// [`event_update_system`]: super::event_update_system #[derive(Debug, Resource)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Resource, Default))] -pub struct Events { +pub struct Events { /// Holds the oldest still active events. /// Note that `a.start_event_count + a.len()` should always be equal to `events_b.start_event_count`. pub(crate) events_a: EventSequence, @@ -101,7 +102,7 @@ pub struct Events { } // Derived Default impl would incorrectly require E: Default -impl Default for Events { +impl Default for Events { fn default() -> Self { Self { events_a: Default::default(), @@ -111,7 +112,7 @@ impl Default for Events { } } -impl Events { +impl Events { /// Returns the index of the oldest event stored in the event buffer. pub fn oldest_event_count(&self) -> usize { self.events_a.start_event_count @@ -286,7 +287,7 @@ impl Events { } } -impl Extend for Events { +impl Extend for Events { #[track_caller] fn extend(&mut self, iter: I) where @@ -321,13 +322,13 @@ impl Extend for Events { #[derive(Debug)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Default))] -pub(crate) struct EventSequence { +pub(crate) struct EventSequence { pub(crate) events: Vec>, pub(crate) start_event_count: usize, } // Derived Default impl would incorrectly require E: Default -impl Default for EventSequence { +impl Default for EventSequence { fn default() -> Self { Self { events: Default::default(), @@ -336,7 +337,7 @@ impl Default for EventSequence { } } -impl Deref for EventSequence { +impl Deref for EventSequence { type Target = Vec>; fn deref(&self) -> &Self::Target { @@ -344,7 +345,7 @@ impl Deref for EventSequence { } } -impl DerefMut for EventSequence { +impl DerefMut for EventSequence { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.events } @@ -357,7 +358,7 @@ pub struct SendBatchIds { _marker: PhantomData, } -impl Iterator for SendBatchIds { +impl Iterator for SendBatchIds { type Item = EventId; fn next(&mut self) -> Option { @@ -377,7 +378,7 @@ impl Iterator for SendBatchIds { } } -impl ExactSizeIterator for SendBatchIds { +impl ExactSizeIterator for SendBatchIds { fn len(&self) -> usize { self.event_count.saturating_sub(self.last_count) } @@ -385,12 +386,11 @@ impl ExactSizeIterator for SendBatchIds { #[cfg(test)] mod tests { - use crate::event::Events; - use bevy_ecs_macros::Event; + use crate::event::{BufferedEvent, Event, Events}; #[test] fn iter_current_update_events_iterates_over_current_events() { - #[derive(Event, Clone)] + #[derive(Event, BufferedEvent, Clone)] struct TestEvent; let mut test_events = Events::::default(); diff --git a/crates/bevy_ecs/src/event/event_cursor.rs b/crates/bevy_ecs/src/event/event_cursor.rs index ff15ef4931..70e19a732c 100644 --- a/crates/bevy_ecs/src/event/event_cursor.rs +++ b/crates/bevy_ecs/src/event/event_cursor.rs @@ -1,5 +1,6 @@ use bevy_ecs::event::{ - Event, EventIterator, EventIteratorWithId, EventMutIterator, EventMutIteratorWithId, Events, + BufferedEvent, EventIterator, EventIteratorWithId, EventMutIterator, EventMutIteratorWithId, + Events, }; #[cfg(feature = "multi_threaded")] use bevy_ecs::event::{EventMutParIter, EventParIter}; @@ -19,9 +20,9 @@ use core::marker::PhantomData; /// /// ``` /// use bevy_ecs::prelude::*; -/// use bevy_ecs::event::{Event, Events, EventCursor}; +/// use bevy_ecs::event::{BufferedEvent, Events, EventCursor}; /// -/// #[derive(Event, Clone, Debug)] +/// #[derive(Event, BufferedEvent, Clone, Debug)] /// struct MyEvent; /// /// /// A system that both sends and receives events using a [`Local`] [`EventCursor`]. @@ -50,12 +51,12 @@ use core::marker::PhantomData; /// [`EventReader`]: super::EventReader /// [`EventMutator`]: super::EventMutator #[derive(Debug)] -pub struct EventCursor { +pub struct EventCursor { pub(super) last_event_count: usize, pub(super) _marker: PhantomData, } -impl Default for EventCursor { +impl Default for EventCursor { fn default() -> Self { EventCursor { last_event_count: 0, @@ -64,7 +65,7 @@ impl Default for EventCursor { } } -impl Clone for EventCursor { +impl Clone for EventCursor { fn clone(&self) -> Self { EventCursor { last_event_count: self.last_event_count, @@ -73,7 +74,7 @@ impl Clone for EventCursor { } } -impl EventCursor { +impl EventCursor { /// See [`EventReader::read`](super::EventReader::read) pub fn read<'a>(&'a mut self, events: &'a Events) -> EventIterator<'a, E> { self.read_with_id(events).without_id() diff --git a/crates/bevy_ecs/src/event/iterators.rs b/crates/bevy_ecs/src/event/iterators.rs index f9ee74b8b0..c90aed2a19 100644 --- a/crates/bevy_ecs/src/event/iterators.rs +++ b/crates/bevy_ecs/src/event/iterators.rs @@ -1,15 +1,15 @@ #[cfg(feature = "multi_threaded")] use bevy_ecs::batching::BatchingStrategy; -use bevy_ecs::event::{Event, EventCursor, EventId, EventInstance, Events}; +use bevy_ecs::event::{BufferedEvent, EventCursor, EventId, EventInstance, Events}; use core::{iter::Chain, slice::Iter}; /// An iterator that yields any unread events from an [`EventReader`](super::EventReader) or [`EventCursor`]. #[derive(Debug)] -pub struct EventIterator<'a, E: Event> { +pub struct EventIterator<'a, E: BufferedEvent> { iter: EventIteratorWithId<'a, E>, } -impl<'a, E: Event> Iterator for EventIterator<'a, E> { +impl<'a, E: BufferedEvent> Iterator for EventIterator<'a, E> { type Item = &'a E; fn next(&mut self) -> Option { self.iter.next().map(|(event, _)| event) @@ -35,7 +35,7 @@ impl<'a, E: Event> Iterator for EventIterator<'a, E> { } } -impl<'a, E: Event> ExactSizeIterator for EventIterator<'a, E> { +impl<'a, E: BufferedEvent> ExactSizeIterator for EventIterator<'a, E> { fn len(&self) -> usize { self.iter.len() } @@ -43,13 +43,13 @@ impl<'a, E: Event> ExactSizeIterator for EventIterator<'a, E> { /// An iterator that yields any unread events (and their IDs) from an [`EventReader`](super::EventReader) or [`EventCursor`]. #[derive(Debug)] -pub struct EventIteratorWithId<'a, E: Event> { +pub struct EventIteratorWithId<'a, E: BufferedEvent> { reader: &'a mut EventCursor, chain: Chain>, Iter<'a, EventInstance>>, unread: usize, } -impl<'a, E: Event> EventIteratorWithId<'a, E> { +impl<'a, E: BufferedEvent> 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 EventCursor, events: &'a Events) -> Self { let a_index = reader @@ -81,7 +81,7 @@ impl<'a, E: Event> EventIteratorWithId<'a, E> { } } -impl<'a, E: Event> Iterator for EventIteratorWithId<'a, E> { +impl<'a, E: BufferedEvent> Iterator for EventIteratorWithId<'a, E> { type Item = (&'a E, EventId); fn next(&mut self) -> Option { match self @@ -131,16 +131,16 @@ impl<'a, E: Event> Iterator for EventIteratorWithId<'a, E> { } } -impl<'a, E: Event> ExactSizeIterator for EventIteratorWithId<'a, E> { +impl<'a, E: BufferedEvent> ExactSizeIterator for EventIteratorWithId<'a, E> { fn len(&self) -> usize { self.unread } } -/// A parallel iterator over `Event`s. +/// A parallel iterator over `BufferedEvent`s. #[cfg(feature = "multi_threaded")] #[derive(Debug)] -pub struct EventParIter<'a, E: Event> { +pub struct EventParIter<'a, E: BufferedEvent> { reader: &'a mut EventCursor, slices: [&'a [EventInstance]; 2], batching_strategy: BatchingStrategy, @@ -149,7 +149,7 @@ pub struct EventParIter<'a, E: Event> { } #[cfg(feature = "multi_threaded")] -impl<'a, E: Event> EventParIter<'a, E> { +impl<'a, E: BufferedEvent> EventParIter<'a, E> { /// Creates a new parallel iterator over `events` that have not yet been seen by `reader`. pub fn new(reader: &'a mut EventCursor, events: &'a Events) -> Self { let a_index = reader @@ -248,7 +248,7 @@ impl<'a, E: Event> EventParIter<'a, E> { } } - /// Returns the number of [`Event`]s to be iterated. + /// Returns the number of [`BufferedEvent`]s to be iterated. pub fn len(&self) -> usize { self.slices.iter().map(|s| s.len()).sum() } @@ -260,7 +260,7 @@ impl<'a, E: Event> EventParIter<'a, E> { } #[cfg(feature = "multi_threaded")] -impl<'a, E: Event> IntoIterator for EventParIter<'a, E> { +impl<'a, E: BufferedEvent> IntoIterator for EventParIter<'a, E> { type IntoIter = EventIteratorWithId<'a, E>; type Item = ::Item; diff --git a/crates/bevy_ecs/src/event/mod.rs b/crates/bevy_ecs/src/event/mod.rs index 3bb422b7bb..fd624d1abf 100644 --- a/crates/bevy_ecs/src/event/mod.rs +++ b/crates/bevy_ecs/src/event/mod.rs @@ -11,8 +11,8 @@ mod update; mod writer; pub(crate) use base::EventInstance; -pub use base::{Event, EventId}; -pub use bevy_ecs_macros::Event; +pub use base::{BufferedEvent, EntityEvent, Event, EventId}; +pub use bevy_ecs_macros::{BufferedEvent, EntityEvent, Event}; pub use collections::{Events, SendBatchIds}; pub use event_cursor::EventCursor; #[cfg(feature = "multi_threaded")] @@ -38,17 +38,20 @@ pub use writer::EventWriter; mod tests { use alloc::{vec, vec::Vec}; use bevy_ecs::{event::*, system::assert_is_read_only_system}; - use bevy_ecs_macros::Event; + use bevy_ecs_macros::BufferedEvent; - #[derive(Event, Copy, Clone, PartialEq, Eq, Debug)] + #[derive(Event, BufferedEvent, Copy, Clone, PartialEq, Eq, Debug)] struct TestEvent { i: usize, } - #[derive(Event, Clone, PartialEq, Debug, Default)] + #[derive(Event, BufferedEvent, Clone, PartialEq, Debug, Default)] struct EmptyTestEvent; - fn get_events(events: &Events, cursor: &mut EventCursor) -> Vec { + fn get_events( + events: &Events, + cursor: &mut EventCursor, + ) -> Vec { cursor.read(events).cloned().collect::>() } diff --git a/crates/bevy_ecs/src/event/mut_iterators.rs b/crates/bevy_ecs/src/event/mut_iterators.rs index 3cb531ce78..3fa8378f23 100644 --- a/crates/bevy_ecs/src/event/mut_iterators.rs +++ b/crates/bevy_ecs/src/event/mut_iterators.rs @@ -1,17 +1,17 @@ #[cfg(feature = "multi_threaded")] use bevy_ecs::batching::BatchingStrategy; -use bevy_ecs::event::{Event, EventCursor, EventId, EventInstance, Events}; +use bevy_ecs::event::{BufferedEvent, EventCursor, EventId, EventInstance, Events}; use core::{iter::Chain, slice::IterMut}; /// An iterator that yields any unread events from an [`EventMutator`] or [`EventCursor`]. /// /// [`EventMutator`]: super::EventMutator #[derive(Debug)] -pub struct EventMutIterator<'a, E: Event> { +pub struct EventMutIterator<'a, E: BufferedEvent> { iter: EventMutIteratorWithId<'a, E>, } -impl<'a, E: Event> Iterator for EventMutIterator<'a, E> { +impl<'a, E: BufferedEvent> Iterator for EventMutIterator<'a, E> { type Item = &'a mut E; fn next(&mut self) -> Option { self.iter.next().map(|(event, _)| event) @@ -37,7 +37,7 @@ impl<'a, E: Event> Iterator for EventMutIterator<'a, E> { } } -impl<'a, E: Event> ExactSizeIterator for EventMutIterator<'a, E> { +impl<'a, E: BufferedEvent> ExactSizeIterator for EventMutIterator<'a, E> { fn len(&self) -> usize { self.iter.len() } @@ -47,13 +47,13 @@ impl<'a, E: Event> ExactSizeIterator for EventMutIterator<'a, E> { /// /// [`EventMutator`]: super::EventMutator #[derive(Debug)] -pub struct EventMutIteratorWithId<'a, E: Event> { +pub struct EventMutIteratorWithId<'a, E: BufferedEvent> { mutator: &'a mut EventCursor, chain: Chain>, IterMut<'a, EventInstance>>, unread: usize, } -impl<'a, E: Event> EventMutIteratorWithId<'a, E> { +impl<'a, E: BufferedEvent> EventMutIteratorWithId<'a, E> { /// Creates a new iterator that yields any `events` that have not yet been seen by `mutator`. pub fn new(mutator: &'a mut EventCursor, events: &'a mut Events) -> Self { let a_index = mutator @@ -84,7 +84,7 @@ impl<'a, E: Event> EventMutIteratorWithId<'a, E> { } } -impl<'a, E: Event> Iterator for EventMutIteratorWithId<'a, E> { +impl<'a, E: BufferedEvent> Iterator for EventMutIteratorWithId<'a, E> { type Item = (&'a mut E, EventId); fn next(&mut self) -> Option { match self @@ -134,16 +134,16 @@ impl<'a, E: Event> Iterator for EventMutIteratorWithId<'a, E> { } } -impl<'a, E: Event> ExactSizeIterator for EventMutIteratorWithId<'a, E> { +impl<'a, E: BufferedEvent> ExactSizeIterator for EventMutIteratorWithId<'a, E> { fn len(&self) -> usize { self.unread } } -/// A parallel iterator over `Event`s. +/// A parallel iterator over `BufferedEvent`s. #[derive(Debug)] #[cfg(feature = "multi_threaded")] -pub struct EventMutParIter<'a, E: Event> { +pub struct EventMutParIter<'a, E: BufferedEvent> { mutator: &'a mut EventCursor, slices: [&'a mut [EventInstance]; 2], batching_strategy: BatchingStrategy, @@ -152,7 +152,7 @@ pub struct EventMutParIter<'a, E: Event> { } #[cfg(feature = "multi_threaded")] -impl<'a, E: Event> EventMutParIter<'a, E> { +impl<'a, E: BufferedEvent> EventMutParIter<'a, E> { /// Creates a new parallel iterator over `events` that have not yet been seen by `mutator`. pub fn new(mutator: &'a mut EventCursor, events: &'a mut Events) -> Self { let a_index = mutator @@ -251,7 +251,7 @@ impl<'a, E: Event> EventMutParIter<'a, E> { } } - /// Returns the number of [`Event`]s to be iterated. + /// Returns the number of [`BufferedEvent`]s to be iterated. pub fn len(&self) -> usize { self.slices.iter().map(|s| s.len()).sum() } @@ -263,7 +263,7 @@ impl<'a, E: Event> EventMutParIter<'a, E> { } #[cfg(feature = "multi_threaded")] -impl<'a, E: Event> IntoIterator for EventMutParIter<'a, E> { +impl<'a, E: BufferedEvent> IntoIterator for EventMutParIter<'a, E> { type IntoIter = EventMutIteratorWithId<'a, E>; type Item = ::Item; diff --git a/crates/bevy_ecs/src/event/mutator.rs b/crates/bevy_ecs/src/event/mutator.rs index e95037af5b..a9c9459119 100644 --- a/crates/bevy_ecs/src/event/mutator.rs +++ b/crates/bevy_ecs/src/event/mutator.rs @@ -1,7 +1,7 @@ #[cfg(feature = "multi_threaded")] use bevy_ecs::event::EventMutParIter; use bevy_ecs::{ - event::{Event, EventCursor, EventMutIterator, EventMutIteratorWithId, Events}, + event::{BufferedEvent, EventCursor, EventMutIterator, EventMutIteratorWithId, Events}, system::{Local, ResMut, SystemParam}, }; @@ -15,7 +15,7 @@ use bevy_ecs::{ /// ``` /// # use bevy_ecs::prelude::*; /// -/// #[derive(Event, Debug)] +/// #[derive(Event, BufferedEvent, Debug)] /// pub struct MyEvent(pub u32); // Custom event type. /// fn my_system(mut reader: EventMutator) { /// for event in reader.read() { @@ -42,13 +42,13 @@ use bevy_ecs::{ /// [`EventReader`]: super::EventReader /// [`EventWriter`]: super::EventWriter #[derive(SystemParam, Debug)] -pub struct EventMutator<'w, 's, E: Event> { +pub struct EventMutator<'w, 's, E: BufferedEvent> { pub(super) reader: Local<'s, EventCursor>, - #[system_param(validation_message = "Event not initialized")] + #[system_param(validation_message = "BufferedEvent not initialized")] events: ResMut<'w, Events>, } -impl<'w, 's, E: Event> EventMutator<'w, 's, E> { +impl<'w, 's, E: BufferedEvent> 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. @@ -69,7 +69,7 @@ impl<'w, 's, E: Event> EventMutator<'w, 's, E> { /// # use bevy_ecs::prelude::*; /// # use std::sync::atomic::{AtomicUsize, Ordering}; /// - /// #[derive(Event)] + /// #[derive(Event, BufferedEvent)] /// struct MyEvent { /// value: usize, /// } @@ -116,7 +116,7 @@ impl<'w, 's, E: Event> EventMutator<'w, 's, E> { /// ``` /// # use bevy_ecs::prelude::*; /// # - /// #[derive(Event)] + /// #[derive(Event, BufferedEvent)] /// struct CollisionEvent; /// /// fn play_collision_sound(mut events: EventMutator) { diff --git a/crates/bevy_ecs/src/event/reader.rs b/crates/bevy_ecs/src/event/reader.rs index 995e2ca9e9..e15b3ea9e7 100644 --- a/crates/bevy_ecs/src/event/reader.rs +++ b/crates/bevy_ecs/src/event/reader.rs @@ -1,11 +1,11 @@ #[cfg(feature = "multi_threaded")] use bevy_ecs::event::EventParIter; use bevy_ecs::{ - event::{Event, EventCursor, EventIterator, EventIteratorWithId, Events}, + event::{BufferedEvent, EventCursor, EventIterator, EventIteratorWithId, Events}, system::{Local, Res, SystemParam}, }; -/// Reads events of type `T` in order and tracks which events have already been read. +/// Reads [`BufferedEvent`]s of type `T` in order and tracks which events have already been read. /// /// # Concurrency /// @@ -14,13 +14,13 @@ use bevy_ecs::{ /// /// [`EventWriter`]: super::EventWriter #[derive(SystemParam, Debug)] -pub struct EventReader<'w, 's, E: Event> { +pub struct EventReader<'w, 's, E: BufferedEvent> { pub(super) reader: Local<'s, EventCursor>, - #[system_param(validation_message = "Event not initialized")] + #[system_param(validation_message = "BufferedEvent not initialized")] events: Res<'w, Events>, } -impl<'w, 's, E: Event> EventReader<'w, 's, E> { +impl<'w, 's, E: BufferedEvent> EventReader<'w, 's, E> { /// Iterates over the events this [`EventReader`] has not seen yet. This updates the /// [`EventReader`]'s event counter, which means subsequent event reads will not include events /// that happened before now. @@ -41,7 +41,7 @@ impl<'w, 's, E: Event> EventReader<'w, 's, E> { /// # use bevy_ecs::prelude::*; /// # use std::sync::atomic::{AtomicUsize, Ordering}; /// - /// #[derive(Event)] + /// #[derive(Event, BufferedEvent)] /// struct MyEvent { /// value: usize, /// } @@ -88,7 +88,7 @@ impl<'w, 's, E: Event> EventReader<'w, 's, E> { /// ``` /// # use bevy_ecs::prelude::*; /// # - /// #[derive(Event)] + /// #[derive(Event, BufferedEvent)] /// struct CollisionEvent; /// /// fn play_collision_sound(mut events: EventReader) { diff --git a/crates/bevy_ecs/src/event/registry.rs b/crates/bevy_ecs/src/event/registry.rs index 0beb41cd25..7889de62da 100644 --- a/crates/bevy_ecs/src/event/registry.rs +++ b/crates/bevy_ecs/src/event/registry.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; use bevy_ecs::{ change_detection::{DetectChangesMut, MutUntyped}, component::{ComponentId, Tick}, - event::{Event, Events}, + event::{BufferedEvent, Events}, resource::Resource, world::World, }; @@ -45,7 +45,7 @@ impl EventRegistry { /// /// If no instance of the [`EventRegistry`] exists in the world, this will add one - otherwise it will use /// the existing instance. - pub fn register_event(world: &mut World) { + pub fn register_event(world: &mut World) { // By initializing the resource here, we can be sure that it is present, // and receive the correct, up-to-date `ComponentId` even if it was previously removed. let component_id = world.init_resource::>(); @@ -82,7 +82,7 @@ impl EventRegistry { } /// Removes an event from the world and its associated [`EventRegistry`]. - pub fn deregister_events(world: &mut World) { + pub fn deregister_events(world: &mut World) { let component_id = world.init_resource::>(); let mut registry = world.get_resource_or_init::(); registry diff --git a/crates/bevy_ecs/src/event/writer.rs b/crates/bevy_ecs/src/event/writer.rs index 5854ab34fb..4c38401eb4 100644 --- a/crates/bevy_ecs/src/event/writer.rs +++ b/crates/bevy_ecs/src/event/writer.rs @@ -1,9 +1,9 @@ use bevy_ecs::{ - event::{Event, EventId, Events, SendBatchIds}, + event::{BufferedEvent, EventId, Events, SendBatchIds}, system::{ResMut, SystemParam}, }; -/// Sends events of type `T`. +/// Sends [`BufferedEvent`]s of type `T`. /// /// # Usage /// @@ -11,7 +11,7 @@ use bevy_ecs::{ /// ``` /// # use bevy_ecs::prelude::*; /// -/// #[derive(Event)] +/// #[derive(Event, BufferedEvent)] /// pub struct MyEvent; // Custom event type. /// fn my_system(mut writer: EventWriter) { /// writer.write(MyEvent); @@ -21,8 +21,8 @@ use bevy_ecs::{ /// ``` /// # Observers /// -/// "Buffered" Events, such as those sent directly in [`Events`] or written using [`EventWriter`], do _not_ automatically -/// trigger any [`Observer`]s watching for that event, as each [`Event`] has different requirements regarding _if_ it will +/// "Buffered" events, such as those sent directly in [`Events`] or written using [`EventWriter`], do _not_ automatically +/// trigger any [`Observer`]s watching for that event, as each [`BufferedEvent`] has different requirements regarding _if_ it will /// be triggered, and if so, _when_ it will be triggered in the schedule. /// /// # Concurrency @@ -38,7 +38,7 @@ use bevy_ecs::{ /// /// ``` /// # use bevy_ecs::{prelude::*, event::Events}; -/// # #[derive(Event)] +/// # #[derive(Event, BufferedEvent)] /// # pub struct MyEvent; /// fn send_untyped(mut commands: Commands) { /// // Send an event of a specific type without having to declare that @@ -59,12 +59,12 @@ use bevy_ecs::{ /// /// [`Observer`]: crate::observer::Observer #[derive(SystemParam)] -pub struct EventWriter<'w, E: Event> { - #[system_param(validation_message = "Event not initialized")] +pub struct EventWriter<'w, E: BufferedEvent> { + #[system_param(validation_message = "BufferedEvent not initialized")] events: ResMut<'w, Events>, } -impl<'w, E: Event> EventWriter<'w, E> { +impl<'w, E: BufferedEvent> EventWriter<'w, E> { /// Writes an `event`, which can later be read by [`EventReader`](super::EventReader)s. /// This method returns the [ID](`EventId`) of the written `event`. /// diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index d8f88ed4a3..e5f0e908e5 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -60,7 +60,7 @@ pub mod world; pub use bevy_ptr as ptr; #[cfg(feature = "hotpatching")] -use event::Event; +use event::{BufferedEvent, Event}; /// The ECS prelude. /// @@ -78,7 +78,9 @@ pub mod prelude { component::Component, entity::{ContainsEntity, Entity, EntityMapper}, error::{BevyError, Result}, - event::{Event, EventMutator, EventReader, EventWriter, Events}, + event::{ + BufferedEvent, EntityEvent, Event, EventMutator, EventReader, EventWriter, Events, + }, hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children}, lifecycle::{ Add, Despawn, Insert, OnAdd, OnDespawn, OnInsert, OnRemove, OnReplace, Remove, @@ -137,7 +139,7 @@ pub mod __macro_exports { /// /// Systems should refresh their inner pointers. #[cfg(feature = "hotpatching")] -#[derive(Event, Default)] +#[derive(Event, BufferedEvent, Default)] pub struct HotPatched; #[cfg(test)] diff --git a/crates/bevy_ecs/src/lifecycle.rs b/crates/bevy_ecs/src/lifecycle.rs index 0c07995ce9..be5765beee 100644 --- a/crates/bevy_ecs/src/lifecycle.rs +++ b/crates/bevy_ecs/src/lifecycle.rs @@ -53,7 +53,10 @@ use crate::{ change_detection::MaybeLocation, component::{Component, ComponentId, ComponentIdFor, Tick}, entity::Entity, - event::{Event, EventCursor, EventId, EventIterator, EventIteratorWithId, Events}, + event::{ + BufferedEvent, EntityEvent, Event, EventCursor, EventId, EventIterator, + EventIteratorWithId, Events, + }, query::FilteredAccessSet, relationship::RelationshipHookMode, storage::SparseSet, @@ -325,7 +328,7 @@ pub const DESPAWN: ComponentId = ComponentId::new(4); /// Trigger emitted when a component is inserted onto an entity that does not already have that /// component. Runs before `Insert`. /// See [`crate::lifecycle::ComponentHooks::on_add`] for more information. -#[derive(Event, Debug)] +#[derive(Event, EntityEvent, Debug)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[doc(alias = "OnAdd")] @@ -334,7 +337,7 @@ pub struct Add; /// Trigger emitted when a component is inserted, regardless of whether or not the entity already /// had that component. Runs after `Add`, if it ran. /// See [`crate::lifecycle::ComponentHooks::on_insert`] for more information. -#[derive(Event, Debug)] +#[derive(Event, EntityEvent, Debug)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[doc(alias = "OnInsert")] @@ -345,7 +348,7 @@ pub struct Insert; /// /// Runs before the value is replaced, so you can still access the original component data. /// See [`crate::lifecycle::ComponentHooks::on_replace`] for more information. -#[derive(Event, Debug)] +#[derive(Event, EntityEvent, Debug)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[doc(alias = "OnReplace")] @@ -354,7 +357,7 @@ pub struct Replace; /// Trigger emitted when a component is removed from an entity, and runs before the component is /// removed, so you can still access the component data. /// See [`crate::lifecycle::ComponentHooks::on_remove`] for more information. -#[derive(Event, Debug)] +#[derive(Event, EntityEvent, Debug)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[doc(alias = "OnRemove")] @@ -362,7 +365,7 @@ pub struct Remove; /// Trigger emitted for each component on an entity when it is despawned. /// See [`crate::lifecycle::ComponentHooks::on_despawn`] for more information. -#[derive(Event, Debug)] +#[derive(Event, EntityEvent, Debug)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[doc(alias = "OnDespawn")] @@ -390,7 +393,7 @@ pub type OnDespawn = Despawn; /// Wrapper around [`Entity`] for [`RemovedComponents`]. /// Internally, `RemovedComponents` uses these as an `Events`. -#[derive(Event, Debug, Clone, Into)] +#[derive(Event, BufferedEvent, Debug, Clone, Into)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug, Clone))] pub struct RemovedComponentEntity(Entity); diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index 0e6d9d7781..5530b1f0fc 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -121,14 +121,18 @@ fn component_clone_observed_by(_source: &SourceComponent, ctx: &mut ComponentClo #[cfg(test)] mod tests { use crate::{ - entity::EntityCloner, event::Event, observer::On, resource::Resource, system::ResMut, + entity::EntityCloner, + event::{EntityEvent, Event}, + observer::On, + resource::Resource, + system::ResMut, world::World, }; #[derive(Resource, Default)] struct Num(usize); - #[derive(Event)] + #[derive(Event, EntityEvent)] struct E; #[test] diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 0d0ca66299..d34b686b95 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -54,7 +54,7 @@ //! ## Triggering observers //! //! Observers are most commonly triggered by [`Commands`], -//! via [`Commands::trigger`] (for untargeted events) or [`Commands::trigger_targets`] (for targeted events). +//! via [`Commands::trigger`] (for untargeted [`Event`]s) or [`Commands::trigger_targets`] (for targeted [`EntityEvent`]s). //! Like usual, equivalent methods are available on [`World`], allowing you to reduce overhead when working with exclusive world access. //! //! If your observer is configured to watch for a specific component or set of components instead, @@ -64,15 +64,14 @@ //! //! ## Observer bubbling //! -//! When events are targeted at an entity, they can optionally bubble to other targets, +//! When using an [`EntityEvent`] targeted at an entity, the event can optionally be propagated to other targets, //! typically up to parents in an entity hierarchy. //! -//! This behavior is controlled via [`Event::Traversal`] and [`Event::AUTO_PROPAGATE`], +//! This behavior is controlled via [`EntityEvent::Traversal`] and [`EntityEvent::AUTO_PROPAGATE`], //! with the details of the propagation path specified by the [`Traversal`](crate::traversal::Traversal) trait. //! -//! When auto-propagation is enabled, propagaion must be manually stopped to prevent the event from -//! continuing to other targets. -//! This can be done using the [`On::propagate`] method inside of your observer. +//! When auto-propagation is enabled, propagation must be manually stopped to prevent the event from +//! continuing to other targets. This can be done using the [`On::propagate`] method inside of your observer. //! //! ## Observer timing //! @@ -212,12 +211,6 @@ impl<'w, E, B: Bundle> On<'w, E, B> { Ptr::from(&self.event) } - /// Returns the [`Entity`] that was targeted by the `event` that triggered this observer. It may - /// be [`None`] if the trigger is not for a particular entity. - pub fn target(&self) -> Option { - self.trigger.target - } - /// Returns the components that triggered the observer, out of the /// components defined in `B`. Does not necessarily include all of them as /// `B` acts like an `OR` filter rather than an `AND` filter. @@ -251,14 +244,28 @@ impl<'w, E, B: Bundle> On<'w, E, B> { self.trigger.observer } + /// Returns the source code location that triggered this observer. + pub fn caller(&self) -> MaybeLocation { + self.trigger.caller + } +} + +impl<'w, E: EntityEvent, B: Bundle> On<'w, E, B> { + /// Returns the [`Entity`] that was targeted by the `event` that triggered this observer. + /// + /// If the event was not targeted at a specific entity, this will return [`Entity::PLACEHOLDER`]. + pub fn target(&self) -> Entity { + self.trigger.target.unwrap_or(Entity::PLACEHOLDER) + } + /// Enables or disables event propagation, allowing the same event to trigger observers on a chain of different entities. /// /// The path an event will propagate along is specified by its associated [`Traversal`] component. By default, events /// use `()` which ends the path immediately and prevents propagation. /// /// To enable propagation, you must: - /// + Set [`Event::Traversal`] to the component you want to propagate along. - /// + Either call `propagate(true)` in the first observer or set [`Event::AUTO_PROPAGATE`] to `true`. + /// + Set [`EntityEvent::Traversal`] to the component you want to propagate along. + /// + Either call `propagate(true)` in the first observer or set [`EntityEvent::AUTO_PROPAGATE`] to `true`. /// /// You can prevent an event from propagating further using `propagate(false)`. /// @@ -273,11 +280,6 @@ impl<'w, E, B: Bundle> On<'w, E, B> { pub fn get_propagate(&self) -> bool { *self.propagate } - - /// Returns the source code location that triggered this observer. - pub fn caller(&self) -> MaybeLocation { - self.trigger.caller - } } impl<'w, E: Debug, B: Bundle> Debug for On<'w, E, B> { @@ -723,7 +725,7 @@ impl World { let event_id = E::register_component_id(self); // SAFETY: We just registered `event_id` with the type of `event` unsafe { - self.trigger_targets_dynamic_ref_with_caller(event_id, &mut event, (), caller); + self.trigger_dynamic_ref_with_caller(event_id, &mut event, caller); } } @@ -735,20 +737,40 @@ impl World { pub fn trigger_ref(&mut self, event: &mut E) { let event_id = E::register_component_id(self); // SAFETY: We just registered `event_id` with the type of `event` - unsafe { self.trigger_targets_dynamic_ref(event_id, event, ()) }; + unsafe { self.trigger_dynamic_ref_with_caller(event_id, event, MaybeLocation::caller()) }; } - /// Triggers the given [`Event`] for the given `targets`, which will run any [`Observer`]s watching for it. + unsafe fn trigger_dynamic_ref_with_caller( + &mut self, + event_id: ComponentId, + event_data: &mut E, + caller: MaybeLocation, + ) { + let mut world = DeferredWorld::from(self); + // SAFETY: `event_data` is accessible as the type represented by `event_id` + unsafe { + world.trigger_observers_with_data::<_, ()>( + event_id, + None, + core::iter::empty::(), + event_data, + false, + caller, + ); + }; + } + + /// Triggers the given [`EntityEvent`] for the given `targets`, which will run any [`Observer`]s watching for it. /// /// While event types commonly implement [`Copy`], /// those that don't will be consumed and will no longer be accessible. /// If you need to use the event after triggering it, use [`World::trigger_targets_ref`] instead. #[track_caller] - pub fn trigger_targets(&mut self, event: E, targets: impl TriggerTargets) { + pub fn trigger_targets(&mut self, event: E, targets: impl TriggerTargets) { self.trigger_targets_with_caller(event, targets, MaybeLocation::caller()); } - pub(crate) fn trigger_targets_with_caller( + pub(crate) fn trigger_targets_with_caller( &mut self, mut event: E, targets: impl TriggerTargets, @@ -761,19 +783,23 @@ impl World { } } - /// Triggers the given [`Event`] as a mutable reference for the given `targets`, + /// Triggers the given [`EntityEvent`] as a mutable reference for the given `targets`, /// which will run any [`Observer`]s watching for it. /// /// Compared to [`World::trigger_targets`], this method is most useful when it's necessary to check /// or use the event after it has been modified by observers. #[track_caller] - pub fn trigger_targets_ref(&mut self, event: &mut E, targets: impl TriggerTargets) { + pub fn trigger_targets_ref( + &mut self, + event: &mut E, + targets: impl TriggerTargets, + ) { let event_id = E::register_component_id(self); // SAFETY: We just registered `event_id` with the type of `event` unsafe { self.trigger_targets_dynamic_ref(event_id, event, targets) }; } - /// Triggers the given [`Event`] for the given `targets`, which will run any [`Observer`]s watching for it. + /// Triggers the given [`EntityEvent`] for the given `targets`, which will run any [`Observer`]s watching for it. /// /// While event types commonly implement [`Copy`], /// those that don't will be consumed and will no longer be accessible. @@ -783,7 +809,7 @@ impl World { /// /// Caller must ensure that `event_data` is accessible as the type represented by `event_id`. #[track_caller] - pub unsafe fn trigger_targets_dynamic( + pub unsafe fn trigger_targets_dynamic( &mut self, event_id: ComponentId, mut event_data: E, @@ -795,7 +821,7 @@ impl World { }; } - /// Triggers the given [`Event`] as a mutable reference for the given `targets`, + /// Triggers the given [`EntityEvent`] as a mutable reference for the given `targets`, /// which will run any [`Observer`]s watching for it. /// /// Compared to [`World::trigger_targets_dynamic`], this method is most useful when it's necessary to check @@ -805,7 +831,7 @@ impl World { /// /// Caller must ensure that `event_data` is accessible as the type represented by `event_id`. #[track_caller] - pub unsafe fn trigger_targets_dynamic_ref( + pub unsafe fn trigger_targets_dynamic_ref( &mut self, event_id: ComponentId, event_data: &mut E, @@ -822,7 +848,7 @@ impl World { /// # Safety /// /// See `trigger_targets_dynamic_ref` - unsafe fn trigger_targets_dynamic_ref_with_caller( + unsafe fn trigger_targets_dynamic_ref_with_caller( &mut self, event_id: ComponentId, event_data: &mut E, @@ -1006,10 +1032,10 @@ mod tests { #[component(storage = "SparseSet")] struct S; - #[derive(Event)] + #[derive(Event, EntityEvent)] struct EventA; - #[derive(Event)] + #[derive(Event, EntityEvent)] struct EventWithData { counter: usize, } @@ -1033,8 +1059,8 @@ mod tests { } } - #[derive(Component, Event)] - #[event(traversal = &'static ChildOf, auto_propagate)] + #[derive(Component, Event, EntityEvent)] + #[entity_event(traversal = &'static ChildOf, auto_propagate)] struct EventPropagating; #[test] @@ -1132,20 +1158,20 @@ mod tests { world.add_observer( |obs: On, mut res: ResMut, mut commands: Commands| { res.observed("add_a"); - commands.entity(obs.target().unwrap()).insert(B); + commands.entity(obs.target()).insert(B); }, ); world.add_observer( |obs: On, mut res: ResMut, mut commands: Commands| { res.observed("remove_a"); - commands.entity(obs.target().unwrap()).remove::(); + commands.entity(obs.target()).remove::(); }, ); world.add_observer( |obs: On, mut res: ResMut, mut commands: Commands| { res.observed("add_b"); - commands.entity(obs.target().unwrap()).remove::(); + commands.entity(obs.target()).remove::(); }, ); world.add_observer(|_: On, mut res: ResMut| { @@ -1314,7 +1340,7 @@ mod tests { }; world.spawn_empty().observe(system); world.add_observer(move |obs: On, mut res: ResMut| { - assert_eq!(obs.target(), None); + assert_eq!(obs.target(), Entity::PLACEHOLDER); res.observed("event_a"); }); @@ -1341,7 +1367,7 @@ mod tests { .observe(|_: On, mut res: ResMut| res.observed("a_1")) .id(); world.add_observer(move |obs: On, mut res: ResMut| { - assert_eq!(obs.target().unwrap(), entity); + assert_eq!(obs.target(), entity); res.observed("a_2"); }); @@ -1761,7 +1787,7 @@ mod tests { world.add_observer( |trigger: On, query: Query<&A>, mut res: ResMut| { - if query.get(trigger.target().unwrap()).is_ok() { + if query.get(trigger.target()).is_ok() { res.observed("event"); } }, @@ -1784,7 +1810,7 @@ mod tests { fn observer_modifies_relationship() { fn on_add(trigger: On, mut commands: Commands) { commands - .entity(trigger.target().unwrap()) + .entity(trigger.target()) .with_related_entities::(|rsc| { rsc.spawn_empty(); }); diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index 283f516d51..4fd9f23556 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -21,10 +21,12 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: /// An [`Observer`] system. Add this [`Component`] to an [`Entity`] to turn it into an "observer". /// -/// Observers listen for a "trigger" of a specific [`Event`]. Events are triggered by calling [`World::trigger`] or [`World::trigger_targets`]. +/// Observers listen for a "trigger" of a specific [`Event`]. An event can be triggered on the [`World`] +/// by calling [`World::trigger`], or if the event is an [`EntityEvent`], it can also be triggered for specific +/// entity targets using [`World::trigger_targets`]. /// -/// Note that "buffered" events sent using [`EventReader`] and [`EventWriter`] are _not_ automatically triggered. They must be triggered at a specific -/// point in the schedule. +/// Note that [`BufferedEvent`]s sent using [`EventReader`] and [`EventWriter`] are _not_ automatically triggered. +/// They must be triggered at a specific point in the schedule. /// /// # Usage /// @@ -113,18 +115,19 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: /// recursively evaluated until there are no commands left, meaning nested triggers all /// evaluate at the same time! /// -/// Events can be triggered for entities, which will be passed to the [`Observer`]: +/// If the event is an [`EntityEvent`], it can be triggered for specific entities, +/// which will be passed to the [`Observer`]: /// /// ``` /// # use bevy_ecs::prelude::*; /// # let mut world = World::default(); /// # let entity = world.spawn_empty().id(); -/// #[derive(Event)] +/// #[derive(Event, EntityEvent)] /// struct Explode; /// /// world.add_observer(|trigger: On, mut commands: Commands| { -/// println!("Entity {} goes BOOM!", trigger.target().unwrap()); -/// commands.entity(trigger.target().unwrap()).despawn(); +/// println!("Entity {} goes BOOM!", trigger.target()); +/// commands.entity(trigger.target()).despawn(); /// }); /// /// world.flush(); @@ -139,7 +142,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: /// # let mut world = World::default(); /// # let e1 = world.spawn_empty().id(); /// # let e2 = world.spawn_empty().id(); -/// # #[derive(Event)] +/// # #[derive(Event, EntityEvent)] /// # struct Explode; /// world.trigger_targets(Explode, [e1, e2]); /// ``` @@ -153,11 +156,11 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: /// # let mut world = World::default(); /// # let e1 = world.spawn_empty().id(); /// # let e2 = world.spawn_empty().id(); -/// # #[derive(Event)] +/// # #[derive(Event, EntityEvent)] /// # struct Explode; /// world.entity_mut(e1).observe(|trigger: On, mut commands: Commands| { /// println!("Boom!"); -/// commands.entity(trigger.target().unwrap()).despawn(); +/// commands.entity(trigger.target()).despawn(); /// }); /// /// world.entity_mut(e2).observe(|trigger: On, mut commands: Commands| { @@ -175,7 +178,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: /// # use bevy_ecs::prelude::*; /// # let mut world = World::default(); /// # let entity = world.spawn_empty().id(); -/// # #[derive(Event)] +/// # #[derive(Event, EntityEvent)] /// # struct Explode; /// let mut observer = Observer::new(|trigger: On| {}); /// observer.watch_entity(entity); diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index 45eb4febb2..4983804dab 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -466,7 +466,7 @@ pub mod common_conditions { use super::{NotSystem, SystemCondition}; use crate::{ change_detection::DetectChanges, - event::{Event, EventReader}, + event::{BufferedEvent, EventReader}, lifecycle::RemovedComponents, prelude::{Component, Query, With}, query::QueryFilter, @@ -928,7 +928,7 @@ pub mod common_conditions { /// my_system.run_if(on_event::), /// ); /// - /// #[derive(Event)] + /// #[derive(Event, BufferedEvent)] /// struct MyEvent; /// /// fn my_system(mut counter: ResMut) { @@ -945,7 +945,7 @@ pub mod common_conditions { /// app.run(&mut world); /// assert_eq!(world.resource::().0, 1); /// ``` - pub fn on_event(mut reader: EventReader) -> bool { + pub fn on_event(mut reader: EventReader) -> bool { // The events need to be consumed, so that there are no false positives on subsequent // calls of the run condition. Simply checking `is_empty` would not be enough. // PERF: note that `count` is efficient (not actually looping/iterating), @@ -1328,6 +1328,7 @@ where #[cfg(test)] mod tests { use super::{common_conditions::*, SystemCondition}; + use crate::event::{BufferedEvent, Event}; use crate::query::With; use crate::{ change_detection::ResMut, @@ -1336,7 +1337,7 @@ mod tests { system::Local, world::World, }; - use bevy_ecs_macros::{Event, Resource}; + use bevy_ecs_macros::Resource; #[derive(Resource, Default)] struct Counter(usize); @@ -1447,7 +1448,7 @@ mod tests { #[derive(Component)] struct TestComponent; - #[derive(Event)] + #[derive(Event, BufferedEvent)] struct TestEvent; #[derive(Resource)] diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 8c5aa1d6fb..91f1b41312 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -784,8 +784,7 @@ mod tests { #[derive(Component)] struct B; - // An event type - #[derive(Event)] + #[derive(Event, BufferedEvent)] struct E; #[derive(Resource, Component)] diff --git a/crates/bevy_ecs/src/system/commands/command.rs b/crates/bevy_ecs/src/system/commands/command.rs index 68aa6c4310..84a2fdf4e9 100644 --- a/crates/bevy_ecs/src/system/commands/command.rs +++ b/crates/bevy_ecs/src/system/commands/command.rs @@ -9,7 +9,7 @@ use crate::{ change_detection::MaybeLocation, entity::Entity, error::Result, - event::{Event, Events}, + event::{BufferedEvent, EntityEvent, Event, Events}, observer::TriggerTargets, resource::Resource, schedule::ScheduleLabel, @@ -208,9 +208,7 @@ pub fn run_schedule(label: impl ScheduleLabel) -> impl Command { } } -/// A [`Command`] that sends a global [observer] [`Event`] without any targets. -/// -/// [observer]: crate::observer::Observer +/// A [`Command`] that sends a global [`Event`] without any targets. #[track_caller] pub fn trigger(event: impl Event) -> impl Command { let caller = MaybeLocation::caller(); @@ -219,11 +217,9 @@ pub fn trigger(event: impl Event) -> impl Command { } } -/// A [`Command`] that sends an [observer] [`Event`] for the given targets. -/// -/// [observer]: crate::observer::Observer +/// A [`Command`] that sends an [`EntityEvent`] for the given targets. pub fn trigger_targets( - event: impl Event, + event: impl EntityEvent, targets: impl TriggerTargets + Send + Sync + 'static, ) -> impl Command { let caller = MaybeLocation::caller(); @@ -232,9 +228,9 @@ pub fn trigger_targets( } } -/// A [`Command`] that sends an arbitrary [`Event`]. +/// A [`Command`] that sends an arbitrary [`BufferedEvent`]. #[track_caller] -pub fn send_event(event: E) -> impl Command { +pub fn send_event(event: E) -> impl Command { let caller = MaybeLocation::caller(); move |world: &mut World| { let mut events = world.resource_mut::>(); diff --git a/crates/bevy_ecs/src/system/commands/entity_command.rs b/crates/bevy_ecs/src/system/commands/entity_command.rs index 7414b85461..87bd2d858b 100644 --- a/crates/bevy_ecs/src/system/commands/entity_command.rs +++ b/crates/bevy_ecs/src/system/commands/entity_command.rs @@ -12,7 +12,7 @@ use crate::{ change_detection::MaybeLocation, component::{Component, ComponentId, ComponentInfo}, entity::{Entity, EntityClonerBuilder}, - event::Event, + event::EntityEvent, relationship::RelationshipHookMode, system::IntoObserverSystem, world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld}, @@ -218,7 +218,7 @@ pub fn despawn() -> impl EntityCommand { /// An [`EntityCommand`] that creates an [`Observer`](crate::observer::Observer) /// listening for events of type `E` targeting an entity #[track_caller] -pub fn observe( +pub fn observe( observer: impl IntoObserverSystem, ) -> impl EntityCommand { let caller = MaybeLocation::caller(); @@ -227,11 +227,11 @@ pub fn observe( } } -/// An [`EntityCommand`] that sends an [observer](crate::observer::Observer) [`Event`] targeting an entity. +/// An [`EntityCommand`] that sends an [`EntityEvent`] targeting an entity. /// -/// This will run any [`Observer`](crate::observer::Observer) of the given [`Event`] watching the entity. +/// This will run any [`Observer`](crate::observer::Observer) of the given [`EntityEvent`] watching the entity. #[track_caller] -pub fn trigger(event: impl Event) -> impl EntityCommand { +pub fn trigger(event: impl EntityEvent) -> impl EntityCommand { let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { let id = entity.id(); diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index d78836cc93..d36588d377 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -20,7 +20,7 @@ use crate::{ component::{Component, ComponentId, Mutable}, entity::{Entities, Entity, EntityClonerBuilder, EntityDoesNotExistError}, error::{ignore, warn, BevyError, CommandWithEntity, ErrorContext, HandleError}, - event::Event, + event::{BufferedEvent, EntityEvent, Event}, observer::{Observer, TriggerTargets}, resource::Resource, schedule::ScheduleLabel, @@ -1078,7 +1078,7 @@ impl<'w, 's> Commands<'w, 's> { self.queue(command::run_system_cached_with(system, input).handle_error_with(warn)); } - /// Sends a global [observer](Observer) [`Event`] without any targets. + /// Sends a global [`Event`] without any targets. /// /// This will run any [`Observer`] of the given [`Event`] that isn't scoped to specific targets. #[track_caller] @@ -1086,13 +1086,13 @@ impl<'w, 's> Commands<'w, 's> { self.queue(command::trigger(event)); } - /// Sends an [observer](Observer) [`Event`] for the given targets. + /// Sends an [`EntityEvent`] for the given targets. /// - /// This will run any [`Observer`] of the given [`Event`] watching those targets. + /// This will run any [`Observer`] of the given [`EntityEvent`] watching those targets. #[track_caller] pub fn trigger_targets( &mut self, - event: impl Event, + event: impl EntityEvent, targets: impl TriggerTargets + Send + Sync + 'static, ) { self.queue(command::trigger_targets(event, targets)); @@ -1119,7 +1119,7 @@ impl<'w, 's> Commands<'w, 's> { self.spawn(Observer::new(observer)) } - /// Sends an arbitrary [`Event`]. + /// Sends an arbitrary [`BufferedEvent`]. /// /// This is a convenience method for sending events /// without requiring an [`EventWriter`](crate::event::EventWriter). @@ -1132,7 +1132,7 @@ impl<'w, 's> Commands<'w, 's> { /// If these events are performance-critical or very frequently sent, /// consider using a typed [`EventWriter`](crate::event::EventWriter) instead. #[track_caller] - pub fn send_event(&mut self, event: E) -> &mut Self { + pub fn send_event(&mut self, event: E) -> &mut Self { self.queue(command::send_event(event)); self } @@ -1957,16 +1957,16 @@ impl<'a> EntityCommands<'a> { &mut self.commands } - /// Sends an [observer](Observer) [`Event`] targeting the entity. + /// Sends an [`EntityEvent`] targeting the entity. /// - /// This will run any [`Observer`] of the given [`Event`] watching this entity. + /// This will run any [`Observer`] of the given [`EntityEvent`] watching this entity. #[track_caller] - pub fn trigger(&mut self, event: impl Event) -> &mut Self { + pub fn trigger(&mut self, event: impl EntityEvent) -> &mut Self { self.queue(entity_command::trigger(event)) } /// Creates an [`Observer`] listening for events of type `E` targeting this entity. - pub fn observe( + pub fn observe( &mut self, observer: impl IntoObserverSystem, ) -> &mut Self { diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 13e531648d..4720860b23 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -131,7 +131,7 @@ impl SystemMeta { /// # use bevy_ecs::system::SystemState; /// # use bevy_ecs::event::Events; /// # -/// # #[derive(Event)] +/// # #[derive(Event, BufferedEvent)] /// # struct MyEvent; /// # #[derive(Resource)] /// # struct MyResource(u32); @@ -164,7 +164,7 @@ impl SystemMeta { /// # use bevy_ecs::system::SystemState; /// # use bevy_ecs::event::Events; /// # -/// # #[derive(Event)] +/// # #[derive(Event, BufferedEvent)] /// # struct MyEvent; /// #[derive(Resource)] /// struct CachedSystemState { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 8f36e6a8c2..153f85f336 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -57,7 +57,7 @@ use variadics_please::{all_tuples, all_tuples_enumerated}; /// # use bevy_ecs::prelude::*; /// # #[derive(Resource)] /// # struct SomeResource; -/// # #[derive(Event)] +/// # #[derive(Event, BufferedEvent)] /// # struct SomeEvent; /// # #[derive(Resource)] /// # struct SomeOtherResource; @@ -598,7 +598,7 @@ unsafe impl<'w, 's, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> Re /// ``` /// # use bevy_ecs::prelude::*; /// # -/// # #[derive(Event)] +/// # #[derive(Event, BufferedEvent)] /// # struct MyEvent; /// # impl MyEvent { /// # pub fn new() -> Self { Self } @@ -2841,7 +2841,7 @@ impl Display for SystemParamValidationError { #[cfg(test)] mod tests { use super::*; - use crate::system::assert_is_system; + use crate::{event::Event, system::assert_is_system}; use core::cell::RefCell; // Compile test for https://github.com/bevyengine/bevy/pull/2838. @@ -3087,11 +3087,11 @@ mod tests { } #[test] - #[should_panic = "Encountered an error in system `bevy_ecs::system::system_param::tests::missing_event_error::event_system`: Parameter `EventReader::events` failed validation: Event not initialized"] + #[should_panic = "Encountered an error in system `bevy_ecs::system::system_param::tests::missing_event_error::event_system`: Parameter `EventReader::events` failed validation: BufferedEvent not initialized"] fn missing_event_error() { - use crate::prelude::{Event, EventReader}; + use crate::prelude::{BufferedEvent, EventReader}; - #[derive(Event)] + #[derive(Event, BufferedEvent)] pub struct MissingEvent; let mut schedule = crate::schedule::Schedule::default(); diff --git a/crates/bevy_ecs/src/traversal.rs b/crates/bevy_ecs/src/traversal.rs index 86772a3b18..d6527a10fa 100644 --- a/crates/bevy_ecs/src/traversal.rs +++ b/crates/bevy_ecs/src/traversal.rs @@ -17,7 +17,7 @@ use crate::{entity::Entity, query::ReadOnlyQueryData, relationship::Relationship /// parameter `D` is the event type given in `On`. This allows traversal to differ depending on event /// data. /// -/// [specify the direction]: crate::event::Event::Traversal +/// [specify the direction]: crate::event::EntityEvent::Traversal /// [event propagation]: crate::observer::On::propagate /// [observers]: crate::observer::Observer pub trait Traversal: ReadOnlyQueryData { diff --git a/crates/bevy_ecs/src/world/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index acf48812c1..3e55dd8087 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -5,7 +5,7 @@ use crate::{ change_detection::{MaybeLocation, MutUntyped}, component::{ComponentId, Mutable}, entity::Entity, - event::{Event, EventId, Events, SendBatchIds}, + event::{BufferedEvent, EntityEvent, Event, EventId, Events, SendBatchIds}, lifecycle::{HookContext, INSERT, REPLACE}, observer::{Observers, TriggerTargets}, prelude::{Component, QueryState}, @@ -496,27 +496,27 @@ impl<'w> DeferredWorld<'w> { unsafe { self.world.get_non_send_resource_mut() } } - /// Sends an [`Event`]. + /// Sends a [`BufferedEvent`]. /// This method returns the [ID](`EventId`) of the sent `event`, /// or [`None`] if the `event` could not be sent. #[inline] - pub fn send_event(&mut self, event: E) -> Option> { + pub fn send_event(&mut self, event: E) -> Option> { self.send_event_batch(core::iter::once(event))?.next() } - /// Sends the default value of the [`Event`] of type `E`. + /// Sends the default value of the [`BufferedEvent`] of type `E`. /// This method returns the [ID](`EventId`) of the sent `event`, /// or [`None`] if the `event` could not be sent. #[inline] - pub fn send_event_default(&mut self) -> Option> { + pub fn send_event_default(&mut self) -> Option> { self.send_event(E::default()) } - /// Sends a batch of [`Event`]s from an iterator. + /// Sends a batch of [`BufferedEvent`]s from an iterator. /// This method returns the [IDs](`EventId`) of the sent `events`, /// or [`None`] if the `event` could not be sent. #[inline] - pub fn send_event_batch( + pub fn send_event_batch( &mut self, events: impl IntoIterator, ) -> Option> { @@ -807,15 +807,23 @@ impl<'w> DeferredWorld<'w> { } } - /// Sends a "global" [observer](crate::observer::Observer) [`Event`] without any targets. + /// Sends a global [`Event`] without any targets. + /// + /// This will run any [`Observer`] of the given [`Event`] that isn't scoped to specific targets. + /// + /// [`Observer`]: crate::observer::Observer pub fn trigger(&mut self, trigger: impl Event) { self.commands().trigger(trigger); } - /// Sends an [observer](crate::observer::Observer) [`Event`] with the given `targets`. + /// Sends an [`EntityEvent`] with the given `targets` + /// + /// This will run any [`Observer`] of the given [`EntityEvent`] watching those targets. + /// + /// [`Observer`]: crate::observer::Observer pub fn trigger_targets( &mut self, - trigger: impl Event, + trigger: impl EntityEvent, targets: impl TriggerTargets + Send + Sync + 'static, ) { self.commands().trigger_targets(trigger, targets); diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 44d4604331..ab470efcc7 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -13,7 +13,7 @@ use crate::{ ContainsEntity, Entity, EntityCloner, EntityClonerBuilder, EntityEquivalent, EntityIdLocation, EntityLocation, }, - event::Event, + event::EntityEvent, lifecycle::{DESPAWN, REMOVE, REPLACE}, observer::Observer, query::{Access, DebugCheckedUnwrap, ReadOnlyQueryData}, @@ -2626,7 +2626,7 @@ impl<'w> EntityWorldMut<'w> { /// # Panics /// /// If the entity has been despawned while this `EntityWorldMut` is still alive. - pub fn trigger(&mut self, event: impl Event) -> &mut Self { + pub fn trigger(&mut self, event: impl EntityEvent) -> &mut Self { self.assert_not_despawned(); self.world.trigger_targets(event, self.entity); self.world.flush(); @@ -2643,14 +2643,14 @@ impl<'w> EntityWorldMut<'w> { /// /// Panics if the given system is an exclusive system. #[track_caller] - pub fn observe( + pub fn observe( &mut self, observer: impl IntoObserverSystem, ) -> &mut Self { self.observe_with_caller(observer, MaybeLocation::caller()) } - pub(crate) fn observe_with_caller( + pub(crate) fn observe_with_caller( &mut self, observer: impl IntoObserverSystem, caller: MaybeLocation, @@ -5739,7 +5739,7 @@ mod tests { assert_eq!((&mut X(8), &mut Y(9)), (x_component, y_component)); } - #[derive(Event)] + #[derive(Event, EntityEvent)] struct TestEvent; #[test] @@ -5748,9 +5748,7 @@ mod tests { let entity = world .spawn_empty() .observe(|trigger: On, mut commands: Commands| { - commands - .entity(trigger.target().unwrap()) - .insert(TestComponent(0)); + commands.entity(trigger.target()).insert(TestComponent(0)); }) .id(); @@ -5769,7 +5767,7 @@ mod tests { fn location_on_despawned_entity_panics() { let mut world = World::new(); world.add_observer(|trigger: On, mut commands: Commands| { - commands.entity(trigger.target().unwrap()).despawn(); + commands.entity(trigger.target()).despawn(); }); let entity = world.spawn_empty().id(); let mut a = world.entity_mut(entity); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index dbc537fc8e..ed7c1f2cdd 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -19,6 +19,7 @@ pub use crate::{ }; use crate::{ error::{DefaultErrorHandler, ErrorHandler}, + event::BufferedEvent, lifecycle::{ComponentHooks, ADD, DESPAWN, INSERT, REMOVE, REPLACE}, prelude::{Add, Despawn, Insert, Remove, Replace}, }; @@ -2598,27 +2599,27 @@ impl World { Some(result) } - /// Sends an [`Event`]. + /// Sends a [`BufferedEvent`]. /// This method returns the [ID](`EventId`) of the sent `event`, /// or [`None`] if the `event` could not be sent. #[inline] - pub fn send_event(&mut self, event: E) -> Option> { + pub fn send_event(&mut self, event: E) -> Option> { self.send_event_batch(core::iter::once(event))?.next() } - /// Sends the default value of the [`Event`] of type `E`. + /// Sends the default value of the [`BufferedEvent`] of type `E`. /// This method returns the [ID](`EventId`) of the sent `event`, /// or [`None`] if the `event` could not be sent. #[inline] - pub fn send_event_default(&mut self) -> Option> { + pub fn send_event_default(&mut self) -> Option> { self.send_event(E::default()) } - /// Sends a batch of [`Event`]s from an iterator. + /// Sends a batch of [`BufferedEvent`]s from an iterator. /// This method returns the [IDs](`EventId`) of the sent `events`, /// or [`None`] if the `event` could not be sent. #[inline] - pub fn send_event_batch( + pub fn send_event_batch( &mut self, events: impl IntoIterator, ) -> Option> { diff --git a/crates/bevy_input/src/gamepad.rs b/crates/bevy_input/src/gamepad.rs index 2b0148909c..7d2f551201 100644 --- a/crates/bevy_input/src/gamepad.rs +++ b/crates/bevy_input/src/gamepad.rs @@ -10,7 +10,7 @@ use bevy_ecs::{ change_detection::DetectChangesMut, component::Component, entity::Entity, - event::{Event, EventReader, EventWriter}, + event::{BufferedEvent, Event, EventReader, EventWriter}, name::Name, system::{Commands, Query}, }; @@ -32,7 +32,7 @@ use thiserror::Error; /// the in-frame relative ordering of events is important. /// /// This event is produced by `bevy_input`. -#[derive(Event, Debug, Clone, PartialEq, From)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, From)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -59,7 +59,7 @@ pub enum GamepadEvent { /// the in-frame relative ordering of events is important. /// /// This event type is used by `bevy_input` to feed its components. -#[derive(Event, Debug, Clone, PartialEq, From)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, From)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -80,7 +80,7 @@ pub enum RawGamepadEvent { } /// [`GamepadButton`] changed event unfiltered by [`GamepadSettings`]. -#[derive(Event, Debug, Copy, Clone, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Copy, Clone, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -112,7 +112,7 @@ impl RawGamepadButtonChangedEvent { } /// [`GamepadAxis`] changed event unfiltered by [`GamepadSettings`]. -#[derive(Event, Debug, Copy, Clone, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Copy, Clone, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -143,9 +143,9 @@ impl RawGamepadAxisChangedEvent { } } -/// A Gamepad connection event. Created when a connection to a gamepad +/// A [`Gamepad`] connection event. Created when a connection to a gamepad /// is established and when a gamepad is disconnected. -#[derive(Event, Debug, Clone, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -184,7 +184,7 @@ impl GamepadConnectionEvent { } /// [`GamepadButton`] event triggered by a digital state change. -#[derive(Event, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -216,7 +216,7 @@ impl GamepadButtonStateChangedEvent { } /// [`GamepadButton`] event triggered by an analog state change. -#[derive(Event, Debug, Clone, Copy, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -251,7 +251,7 @@ impl GamepadButtonChangedEvent { } /// [`GamepadAxis`] event triggered by an analog state change. -#[derive(Event, Debug, Clone, Copy, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr( feature = "bevy_reflect", @@ -1774,7 +1774,7 @@ impl GamepadRumbleIntensity { #[doc(alias = "force feedback")] #[doc(alias = "vibration")] #[doc(alias = "vibrate")] -#[derive(Event, Clone)] +#[derive(Event, BufferedEvent, Clone)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone))] pub enum GamepadRumbleRequest { /// Add a rumble to the given gamepad. diff --git a/crates/bevy_input/src/gestures.rs b/crates/bevy_input/src/gestures.rs index 5cd14d4634..9daa21d525 100644 --- a/crates/bevy_input/src/gestures.rs +++ b/crates/bevy_input/src/gestures.rs @@ -1,6 +1,6 @@ //! Gestures functionality, from touchscreens and touchpads. -use bevy_ecs::event::Event; +use bevy_ecs::event::{BufferedEvent, Event}; use bevy_math::Vec2; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; @@ -17,7 +17,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; /// /// - Only available on **`macOS`** and **`iOS`**. /// - On **`iOS`**, must be enabled first -#[derive(Event, Debug, Clone, Copy, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -39,7 +39,7 @@ pub struct PinchGesture(pub f32); /// /// - Only available on **`macOS`** and **`iOS`**. /// - On **`iOS`**, must be enabled first -#[derive(Event, Debug, Clone, Copy, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -58,7 +58,7 @@ pub struct RotationGesture(pub f32); /// /// - Only available on **`macOS`** and **`iOS`**. /// - On **`iOS`**, must be enabled first -#[derive(Event, Debug, Clone, Copy, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -76,7 +76,7 @@ pub struct DoubleTapGesture; /// ## Platform-specific /// /// - On **`iOS`**, must be enabled first -#[derive(Event, Debug, Clone, Copy, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), diff --git a/crates/bevy_input/src/keyboard.rs b/crates/bevy_input/src/keyboard.rs index ea5452fb53..909880ac7a 100644 --- a/crates/bevy_input/src/keyboard.rs +++ b/crates/bevy_input/src/keyboard.rs @@ -69,7 +69,7 @@ use crate::{ButtonInput, ButtonState}; use bevy_ecs::{ change_detection::DetectChangesMut, entity::Entity, - event::{Event, EventReader}, + event::{BufferedEvent, Event, EventReader}, system::ResMut, }; @@ -94,7 +94,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; /// /// The event is consumed inside of the [`keyboard_input_system`] /// to update the [`ButtonInput`](ButtonInput) resource. -#[derive(Event, Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -139,7 +139,7 @@ pub struct KeyboardInput { /// when, for example, switching between windows with 'Alt-Tab' or using any other /// OS specific key combination that leads to Bevy window losing focus and not receiving any /// input events -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone, PartialEq))] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr( diff --git a/crates/bevy_input/src/mouse.rs b/crates/bevy_input/src/mouse.rs index 3a377d9329..e6b52bf51d 100644 --- a/crates/bevy_input/src/mouse.rs +++ b/crates/bevy_input/src/mouse.rs @@ -4,7 +4,7 @@ use crate::{ButtonInput, ButtonState}; use bevy_ecs::{ change_detection::DetectChangesMut, entity::Entity, - event::{Event, EventReader}, + event::{BufferedEvent, Event, EventReader}, resource::Resource, system::ResMut, }; @@ -26,7 +26,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; /// /// The event is read inside of the [`mouse_button_input_system`] /// to update the [`ButtonInput`] resource. -#[derive(Event, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -91,7 +91,7 @@ pub enum MouseButton { /// However, the event data does not make it possible to distinguish which device it is referring to. /// /// [`DeviceEvent::MouseMotion`]: https://docs.rs/winit/latest/winit/event/enum.DeviceEvent.html#variant.MouseMotion -#[derive(Event, Debug, Clone, Copy, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -140,7 +140,7 @@ pub enum MouseScrollUnit { /// A mouse wheel event. /// /// This event is the translated version of the `WindowEvent::MouseWheel` from the `winit` crate. -#[derive(Event, Debug, Clone, Copy, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), diff --git a/crates/bevy_input/src/touch.rs b/crates/bevy_input/src/touch.rs index 28f3159d53..df1cf3764f 100644 --- a/crates/bevy_input/src/touch.rs +++ b/crates/bevy_input/src/touch.rs @@ -2,7 +2,7 @@ use bevy_ecs::{ entity::Entity, - event::{Event, EventReader}, + event::{BufferedEvent, Event, EventReader}, resource::Resource, system::ResMut, }; @@ -37,7 +37,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; /// /// This event is the translated version of the `WindowEvent::Touch` from the `winit` crate. /// It is available to the end user and can be used for game logic. -#[derive(Event, Debug, Clone, Copy, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), diff --git a/crates/bevy_input_focus/src/lib.rs b/crates/bevy_input_focus/src/lib.rs index d146b1cc56..1fe7c4b7ef 100644 --- a/crates/bevy_input_focus/src/lib.rs +++ b/crates/bevy_input_focus/src/lib.rs @@ -137,21 +137,16 @@ pub struct InputFocusVisible(pub bool); /// /// To set up your own bubbling input event, add the [`dispatch_focused_input::`](dispatch_focused_input) system to your app, /// in the [`InputFocusSystems::Dispatch`] system set during [`PreUpdate`]. -#[derive(Clone, Debug, Component)] +#[derive(Event, EntityEvent, Clone, Debug, Component)] +#[entity_event(traversal = WindowTraversal, auto_propagate)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Clone))] -pub struct FocusedInput { +pub struct FocusedInput { /// The underlying input event. pub input: E, /// The primary window entity. window: Entity, } -impl Event for FocusedInput { - type Traversal = WindowTraversal; - - const AUTO_PROPAGATE: bool = true; -} - #[derive(QueryData)] /// These are for accessing components defined on the targeted entity pub struct WindowTraversal { @@ -159,7 +154,7 @@ pub struct WindowTraversal { window: Option<&'static Window>, } -impl Traversal> for WindowTraversal { +impl Traversal> for WindowTraversal { fn traverse(item: Self::Item<'_>, event: &FocusedInput) -> Option { let WindowTraversalItem { child_of, window } = item; @@ -230,7 +225,7 @@ pub fn set_initial_focus( /// System which dispatches bubbled input events to the focused entity, or to the primary window /// if no entity has focus. -pub fn dispatch_focused_input( +pub fn dispatch_focused_input( mut key_events: EventReader, focus: Res, windows: Query>, @@ -384,7 +379,7 @@ mod tests { trigger: On>, mut query: Query<&mut GatherKeyboardEvents>, ) { - if let Ok(mut gather) = query.get_mut(trigger.target().unwrap()) { + if let Ok(mut gather) = query.get_mut(trigger.target()) { if let Key::Character(c) = &trigger.input.logical_key { gather.0.push_str(c.as_str()); } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 7cd0287d81..83d28a7da7 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -551,7 +551,7 @@ pub(crate) fn add_light_view_entities( trigger: On, mut commands: Commands, ) { - if let Ok(mut v) = commands.get_entity(trigger.target().unwrap()) { + if let Ok(mut v) = commands.get_entity(trigger.target()) { v.insert(LightViewEntities::default()); } } @@ -561,7 +561,7 @@ pub(crate) fn extracted_light_removed( trigger: On, mut commands: Commands, ) { - if let Ok(mut v) = commands.get_entity(trigger.target().unwrap()) { + if let Ok(mut v) = commands.get_entity(trigger.target()) { v.try_remove::(); } } @@ -571,7 +571,7 @@ pub(crate) fn remove_light_view_entities( query: Query<&LightViewEntities>, mut commands: Commands, ) { - if let Ok(entities) = query.get(trigger.target().unwrap()) { + if let Ok(entities) = query.get(trigger.target()) { for v in entities.0.values() { for e in v.iter().copied() { if let Ok(mut v) = commands.get_entity(e) { diff --git a/crates/bevy_picking/src/backend.rs b/crates/bevy_picking/src/backend.rs index 9e28cc6d7c..28693314d9 100644 --- a/crates/bevy_picking/src/backend.rs +++ b/crates/bevy_picking/src/backend.rs @@ -55,7 +55,7 @@ pub mod prelude { /// Note that systems reading these events in [`PreUpdate`](bevy_app::PreUpdate) will not report ordering /// ambiguities with picking backends. Take care to ensure such systems are explicitly ordered /// against [`PickingSystems::Backend`](crate::PickingSystems::Backend), or better, avoid reading `PointerHits` in `PreUpdate`. -#[derive(Event, Debug, Clone, Reflect)] +#[derive(Event, BufferedEvent, Debug, Clone, Reflect)] #[reflect(Debug, Clone)] pub struct PointerHits { /// The pointer associated with this hit test. diff --git a/crates/bevy_picking/src/events.rs b/crates/bevy_picking/src/events.rs index 61f9da041d..393e4a9edc 100644 --- a/crates/bevy_picking/src/events.rs +++ b/crates/bevy_picking/src/events.rs @@ -59,7 +59,8 @@ use crate::{ /// /// The documentation for the [`pointer_events`] explains the events this module exposes and /// the order in which they fire. -#[derive(Clone, PartialEq, Debug, Reflect, Component)] +#[derive(Event, BufferedEvent, EntityEvent, Clone, PartialEq, Debug, Reflect, Component)] +#[entity_event(traversal = PointerTraversal, auto_propagate)] #[reflect(Component, Debug, Clone)] pub struct Pointer { /// The original target of this picking event, before bubbling @@ -106,15 +107,6 @@ where } } -impl Event for Pointer -where - E: Debug + Clone + Reflect, -{ - type Traversal = PointerTraversal; - - const AUTO_PROPAGATE: bool = true; -} - impl core::fmt::Display for Pointer { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_fmt(format_args!( diff --git a/crates/bevy_picking/src/lib.rs b/crates/bevy_picking/src/lib.rs index a54ecb61d9..74a765fbcd 100644 --- a/crates/bevy_picking/src/lib.rs +++ b/crates/bevy_picking/src/lib.rs @@ -48,20 +48,20 @@ //! # use bevy_ecs::prelude::*; //! # use bevy_transform::prelude::*; //! # use bevy_picking::prelude::*; -//! # #[derive(Event)] +//! # #[derive(Event, BufferedEvent)] //! # struct Greeting; //! fn setup(mut commands: Commands) { //! commands.spawn(Transform::default()) //! // Spawn your entity here, e.g. a `Mesh3d`. //! // When dragged, mutate the `Transform` component on the dragged target entity: //! .observe(|trigger: On>, mut transforms: Query<&mut Transform>| { -//! let mut transform = transforms.get_mut(trigger.target().unwrap()).unwrap(); +//! let mut transform = transforms.get_mut(trigger.target()).unwrap(); //! let drag = trigger.event(); //! transform.rotate_local_y(drag.delta.x / 50.0); //! }) //! .observe(|trigger: On>, mut commands: Commands| { -//! println!("Entity {} goes BOOM!", trigger.target().unwrap()); -//! commands.entity(trigger.target().unwrap()).despawn(); +//! println!("Entity {} goes BOOM!", trigger.target()); +//! commands.entity(trigger.target()).despawn(); //! }) //! .observe(|trigger: On>, mut events: EventWriter| { //! events.write(Greeting); diff --git a/crates/bevy_picking/src/pointer.rs b/crates/bevy_picking/src/pointer.rs index faba90cbb9..0406cb61f5 100644 --- a/crates/bevy_picking/src/pointer.rs +++ b/crates/bevy_picking/src/pointer.rs @@ -269,7 +269,7 @@ pub enum PointerAction { } /// An input event effecting a pointer. -#[derive(Event, Debug, Clone, Reflect)] +#[derive(Event, BufferedEvent, Debug, Clone, Reflect)] #[reflect(Clone)] pub struct PointerInput { /// The id of the pointer. diff --git a/crates/bevy_render/src/gpu_readback.rs b/crates/bevy_render/src/gpu_readback.rs index c05861f3da..adcb883559 100644 --- a/crates/bevy_render/src/gpu_readback.rs +++ b/crates/bevy_render/src/gpu_readback.rs @@ -15,14 +15,14 @@ use async_channel::{Receiver, Sender}; use bevy_app::{App, Plugin}; use bevy_asset::Handle; use bevy_derive::{Deref, DerefMut}; -use bevy_ecs::schedule::IntoScheduleConfigs; use bevy_ecs::{ change_detection::ResMut, entity::Entity, - event::Event, + event::EntityEvent, prelude::{Component, Resource, World}, system::{Query, Res}, }; +use bevy_ecs::{event::Event, schedule::IntoScheduleConfigs}; use bevy_image::{Image, TextureFormatPixelInfo}; use bevy_platform::collections::HashMap; use bevy_reflect::Reflect; @@ -96,7 +96,7 @@ impl Readback { /// /// The event contains the data as a `Vec`, which can be interpreted as the raw bytes of the /// requested buffer or texture. -#[derive(Event, Deref, DerefMut, Reflect, Debug)] +#[derive(Event, EntityEvent, Deref, DerefMut, Reflect, Debug)] #[reflect(Debug)] pub struct ReadbackComplete(pub Vec); diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index ace2a97bf8..6dceaba3c0 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -95,14 +95,14 @@ impl Plugin for SyncWorldPlugin { app.init_resource::(); app.add_observer( |trigger: On, mut pending: ResMut| { - pending.push(EntityRecord::Added(trigger.target().unwrap())); + pending.push(EntityRecord::Added(trigger.target())); }, ); app.add_observer( |trigger: On, mut pending: ResMut, query: Query<&RenderEntity>| { - if let Ok(e) = query.get(trigger.target().unwrap()) { + if let Ok(e) = query.get(trigger.target()) { pending.push(EntityRecord::Removed(*e)); }; }, @@ -514,14 +514,14 @@ mod tests { main_world.add_observer( |trigger: On, mut pending: ResMut| { - pending.push(EntityRecord::Added(trigger.target().unwrap())); + pending.push(EntityRecord::Added(trigger.target())); }, ); main_world.add_observer( |trigger: On, mut pending: ResMut, query: Query<&RenderEntity>| { - if let Ok(e) = query.get(trigger.target().unwrap()) { + if let Ok(e) = query.get(trigger.target()) { pending.push(EntityRecord::Removed(*e)); }; }, diff --git a/crates/bevy_render/src/view/window/screenshot.rs b/crates/bevy_render/src/view/window/screenshot.rs index f7699e1648..33b76d269d 100644 --- a/crates/bevy_render/src/view/window/screenshot.rs +++ b/crates/bevy_render/src/view/window/screenshot.rs @@ -39,7 +39,7 @@ use std::{ use tracing::{error, info, warn}; use wgpu::{CommandEncoder, Extent3d, TextureFormat}; -#[derive(Event, Deref, DerefMut, Reflect, Debug)] +#[derive(Event, EntityEvent, Deref, DerefMut, Reflect, Debug)] #[reflect(Debug)] pub struct ScreenshotCaptured(pub Image); diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 4e17af510f..a15d00f116 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -2,7 +2,7 @@ use crate::{DynamicScene, Scene}; use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; use bevy_ecs::{ entity::{Entity, EntityHashMap}, - event::{Event, EventCursor, Events}, + event::{EntityEvent, Event, EventCursor, Events}, hierarchy::ChildOf, reflect::AppTypeRegistry, resource::Resource, @@ -25,7 +25,7 @@ use bevy_ecs::{ /// See also [`On`], [`SceneSpawner::instance_is_ready`]. /// /// [`On`]: bevy_ecs::observer::On -#[derive(Clone, Copy, Debug, Eq, PartialEq, Event, Reflect)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Event, EntityEvent, Reflect)] #[reflect(Debug, PartialEq, Clone)] pub struct SceneInstanceReady { /// Instance which has been spawned. @@ -734,7 +734,7 @@ mod tests { ); assert_eq!( trigger.target(), - scene_entity, + scene_entity.unwrap_or(Entity::PLACEHOLDER), "`SceneInstanceReady` triggered on the wrong parent entity" ); assert!( diff --git a/crates/bevy_state/src/state/transitions.rs b/crates/bevy_state/src/state/transitions.rs index dfe711f245..1ee21826c3 100644 --- a/crates/bevy_state/src/state/transitions.rs +++ b/crates/bevy_state/src/state/transitions.rs @@ -1,7 +1,7 @@ use core::{marker::PhantomData, mem}; use bevy_ecs::{ - event::{Event, EventReader, EventWriter}, + event::{BufferedEvent, Event, EventReader, EventWriter}, schedule::{IntoScheduleConfigs, Schedule, ScheduleLabel, Schedules, SystemSet}, system::{Commands, In, ResMut}, world::World, @@ -55,11 +55,11 @@ pub struct OnTransition { #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)] pub struct StateTransition; -/// Event sent when any state transition of `S` happens. +/// A [`BufferedEvent`] sent when any state transition of `S` happens. /// This includes identity transitions, where `exited` and `entered` have the same value. /// /// If you know exactly what state you want to respond to ahead of time, consider [`OnEnter`], [`OnTransition`], or [`OnExit`] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Event)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Event, BufferedEvent)] pub struct StateTransitionEvent { /// The state being exited. pub exited: Option, diff --git a/crates/bevy_state/src/state_scoped_events.rs b/crates/bevy_state/src/state_scoped_events.rs index b11d8e79df..adba1ca6b6 100644 --- a/crates/bevy_state/src/state_scoped_events.rs +++ b/crates/bevy_state/src/state_scoped_events.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use bevy_app::{App, SubApp}; use bevy_ecs::{ - event::{Event, EventReader, Events}, + event::{BufferedEvent, EventReader, Events}, resource::Resource, system::Commands, world::World, @@ -12,7 +12,7 @@ use bevy_platform::collections::HashMap; use crate::state::{OnEnter, OnExit, StateTransitionEvent, States}; -fn clear_event_queue(w: &mut World) { +fn clear_event_queue(w: &mut World) { if let Some(mut queue) = w.get_resource_mut::>() { queue.clear(); } @@ -33,7 +33,7 @@ struct StateScopedEvents { } impl StateScopedEvents { - fn add_event(&mut self, state: S, transition_type: TransitionType) { + fn add_event(&mut self, state: S, transition_type: TransitionType) { let map = match transition_type { TransitionType::OnExit => &mut self.on_exit, TransitionType::OnEnter => &mut self.on_enter, @@ -106,7 +106,7 @@ fn clear_events_on_enter_state( }); } -fn clear_events_on_state_transition( +fn clear_events_on_state_transition( app: &mut SubApp, _p: PhantomData, state: S, @@ -128,7 +128,7 @@ fn clear_events_on_state_transition( /// Extension trait for [`App`] adding methods for registering state scoped events. pub trait StateScopedEventsAppExt { - /// Clears an [`Event`] when exiting the specified `state`. + /// Clears an [`BufferedEvent`] when exiting the specified `state`. /// /// Note that event cleanup is ambiguously ordered relative to /// [`DespawnOnExitState`](crate::prelude::DespawnOnExitState) entity cleanup, @@ -136,9 +136,9 @@ pub trait StateScopedEventsAppExt { /// All of these (state scoped entities and events cleanup, and `OnExit`) /// occur within schedule [`StateTransition`](crate::prelude::StateTransition) /// and system set `StateTransitionSystems::ExitSchedules`. - fn clear_events_on_exit_state(&mut self, state: impl States) -> &mut Self; + fn clear_events_on_exit_state(&mut self, state: impl States) -> &mut Self; - /// Clears an [`Event`] when entering the specified `state`. + /// Clears an [`BufferedEvent`] when entering the specified `state`. /// /// Note that event cleanup is ambiguously ordered relative to /// [`DespawnOnEnterState`](crate::prelude::DespawnOnEnterState) entity cleanup, @@ -146,11 +146,11 @@ pub trait StateScopedEventsAppExt { /// All of these (state scoped entities and events cleanup, and `OnEnter`) /// occur within schedule [`StateTransition`](crate::prelude::StateTransition) /// and system set `StateTransitionSystems::EnterSchedules`. - fn clear_events_on_enter_state(&mut self, state: impl States) -> &mut Self; + fn clear_events_on_enter_state(&mut self, state: impl States) -> &mut Self; } impl StateScopedEventsAppExt for App { - fn clear_events_on_exit_state(&mut self, state: impl States) -> &mut Self { + fn clear_events_on_exit_state(&mut self, state: impl States) -> &mut Self { clear_events_on_state_transition( self.main_mut(), PhantomData::, @@ -160,7 +160,7 @@ impl StateScopedEventsAppExt for App { self } - fn clear_events_on_enter_state(&mut self, state: impl States) -> &mut Self { + fn clear_events_on_enter_state(&mut self, state: impl States) -> &mut Self { clear_events_on_state_transition( self.main_mut(), PhantomData::, @@ -172,12 +172,12 @@ impl StateScopedEventsAppExt for App { } impl StateScopedEventsAppExt for SubApp { - fn clear_events_on_exit_state(&mut self, state: impl States) -> &mut Self { + fn clear_events_on_exit_state(&mut self, state: impl States) -> &mut Self { clear_events_on_state_transition(self, PhantomData::, state, TransitionType::OnExit); self } - fn clear_events_on_enter_state(&mut self, state: impl States) -> &mut Self { + fn clear_events_on_enter_state(&mut self, state: impl States) -> &mut Self { clear_events_on_state_transition(self, PhantomData::, state, TransitionType::OnEnter); self } @@ -187,6 +187,7 @@ impl StateScopedEventsAppExt for SubApp { mod tests { use super::*; use crate::app::StatesPlugin; + use bevy_ecs::event::{BufferedEvent, Event}; use bevy_state::prelude::*; #[derive(States, Default, Clone, Hash, Eq, PartialEq, Debug)] @@ -196,10 +197,10 @@ mod tests { B, } - #[derive(Event, Debug)] + #[derive(Event, BufferedEvent, Debug)] struct StandardEvent; - #[derive(Event, Debug)] + #[derive(Event, BufferedEvent, Debug)] struct StateScopedEvent; #[test] diff --git a/crates/bevy_time/src/lib.rs b/crates/bevy_time/src/lib.rs index 6e7b3b3991..c4118b876c 100644 --- a/crates/bevy_time/src/lib.rs +++ b/crates/bevy_time/src/lib.rs @@ -185,7 +185,10 @@ mod tests { use crate::{Fixed, Time, TimePlugin, TimeUpdateStrategy, Virtual}; use bevy_app::{App, FixedUpdate, Startup, Update}; use bevy_ecs::{ - event::{Event, EventReader, EventRegistry, EventWriter, Events, ShouldUpdateEvents}, + event::{ + BufferedEvent, Event, EventReader, EventRegistry, EventWriter, Events, + ShouldUpdateEvents, + }, resource::Resource, system::{Local, Res, ResMut}, }; @@ -193,7 +196,7 @@ mod tests { use core::time::Duration; use std::println; - #[derive(Event)] + #[derive(Event, BufferedEvent)] struct TestEvent { sender: std::sync::mpsc::Sender, } @@ -206,7 +209,7 @@ mod tests { } } - #[derive(Event)] + #[derive(Event, BufferedEvent)] struct DummyEvent; #[derive(Resource, Default)] diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index d0845e22d3..8c4456f0e3 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -119,7 +119,7 @@ use { /// # use bevy_ecs::prelude::*; /// # use bevy_time::prelude::*; /// # -/// #[derive(Event)] +/// #[derive(Event, BufferedEvent)] /// struct PauseEvent(bool); /// /// fn pause_system(mut time: ResMut>, mut events: EventReader) { diff --git a/crates/bevy_ui/src/interaction_states.rs b/crates/bevy_ui/src/interaction_states.rs index 04d5ba041d..6659204da4 100644 --- a/crates/bevy_ui/src/interaction_states.rs +++ b/crates/bevy_ui/src/interaction_states.rs @@ -19,7 +19,7 @@ use bevy_ecs::{ pub struct InteractionDisabled; pub(crate) fn on_add_disabled(trigger: On, mut world: DeferredWorld) { - let mut entity = world.entity_mut(trigger.target().unwrap()); + let mut entity = world.entity_mut(trigger.target()); if let Some(mut accessibility) = entity.get_mut::() { accessibility.set_disabled(); } @@ -29,7 +29,7 @@ pub(crate) fn on_remove_disabled( trigger: On, mut world: DeferredWorld, ) { - let mut entity = world.entity_mut(trigger.target().unwrap()); + let mut entity = world.entity_mut(trigger.target()); if let Some(mut accessibility) = entity.get_mut::() { accessibility.clear_disabled(); } @@ -53,7 +53,7 @@ impl Checked { } pub(crate) fn on_insert_is_checked(trigger: On, mut world: DeferredWorld) { - let mut entity = world.entity_mut(trigger.target().unwrap()); + let mut entity = world.entity_mut(trigger.target()); let checked = entity.get::().unwrap().get(); if let Some(mut accessibility) = entity.get_mut::() { accessibility.set_toggled(match checked { @@ -64,7 +64,7 @@ pub(crate) fn on_insert_is_checked(trigger: On, mut world: Defe } pub(crate) fn on_remove_is_checked(trigger: On, mut world: DeferredWorld) { - let mut entity = world.entity_mut(trigger.target().unwrap()); + let mut entity = world.entity_mut(trigger.target()); if let Some(mut accessibility) = entity.get_mut::() { accessibility.set_toggled(accesskit::Toggled::False); } diff --git a/crates/bevy_window/src/event.rs b/crates/bevy_window/src/event.rs index 026b85dc32..5a320439d7 100644 --- a/crates/bevy_window/src/event.rs +++ b/crates/bevy_window/src/event.rs @@ -1,5 +1,8 @@ use alloc::string::String; -use bevy_ecs::{entity::Entity, event::Event}; +use bevy_ecs::{ + entity::Entity, + event::{BufferedEvent, Event}, +}; use bevy_input::{ gestures::*, keyboard::{KeyboardFocusLost, KeyboardInput}, @@ -23,7 +26,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; use crate::WindowTheme; /// A window event that is sent whenever a window's logical size has changed. -#[derive(Event, Debug, Clone, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -45,7 +48,7 @@ pub struct WindowResized { /// An event that indicates all of the application's windows should be redrawn, /// even if their control flow is set to `Wait` and there have been no window events. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -61,7 +64,7 @@ pub struct RequestRedraw; /// An event that is sent whenever a new window is created. /// /// To create a new window, spawn an entity with a [`crate::Window`] on it. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -87,7 +90,7 @@ pub struct WindowCreated { /// /// [`WindowPlugin`]: crate::WindowPlugin /// [`Window`]: crate::Window -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -105,7 +108,7 @@ pub struct WindowCloseRequested { /// An event that is sent whenever a window is closed. This will be sent when /// the window entity loses its [`Window`](crate::window::Window) component or is despawned. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -126,7 +129,7 @@ pub struct WindowClosed { /// An event that is sent whenever a window is closing. This will be sent when /// after a [`WindowCloseRequested`] event is received and the window is in the process of closing. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -146,7 +149,7 @@ pub struct WindowClosing { /// /// Note that if your application only has a single window, this event may be your last chance to /// persist state before the application terminates. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -176,7 +179,7 @@ pub struct WindowDestroyed { /// you should not use it for non-cursor-like behavior such as 3D camera control. Please see `MouseMotion` instead. /// /// [`WindowEvent::CursorMoved`]: https://docs.rs/winit/latest/winit/event/enum.WindowEvent.html#variant.CursorMoved -#[derive(Event, Debug, Clone, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -201,7 +204,7 @@ pub struct CursorMoved { } /// An event that is sent whenever the user's cursor enters a window. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -218,7 +221,7 @@ pub struct CursorEntered { } /// An event that is sent whenever the user's cursor leaves a window. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -239,7 +242,7 @@ pub struct CursorLeft { /// This event is the translated version of the `WindowEvent::Ime` from the `winit` crate. /// /// It is only sent if IME was enabled on the window with [`Window::ime_enabled`](crate::window::Window::ime_enabled). -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -284,7 +287,7 @@ pub enum Ime { } /// An event that indicates a window has received or lost focus. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -311,7 +314,7 @@ pub struct WindowFocused { /// It is the translated version of [`WindowEvent::Occluded`] from the `winit` crate. /// /// [`WindowEvent::Occluded`]: https://docs.rs/winit/latest/winit/event/enum.WindowEvent.html#variant.Occluded -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -330,7 +333,7 @@ pub struct WindowOccluded { } /// An event that indicates a window's scale factor has changed. -#[derive(Event, Debug, Clone, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -349,7 +352,7 @@ pub struct WindowScaleFactorChanged { } /// An event that indicates a window's OS-reported scale factor has changed. -#[derive(Event, Debug, Clone, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -368,7 +371,7 @@ pub struct WindowBackendScaleFactorChanged { } /// Events related to files being dragged and dropped on a window. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -404,7 +407,7 @@ pub enum FileDragAndDrop { } /// An event that is sent when a window is repositioned in physical pixels. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -426,7 +429,7 @@ pub struct WindowMoved { /// /// This event is only sent when the window is relying on the system theme to control its appearance. /// i.e. It is only sent when [`Window::window_theme`](crate::window::Window::window_theme) is `None` and the system theme changes. -#[derive(Event, Debug, Clone, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -445,7 +448,7 @@ pub struct WindowThemeChanged { } /// Application lifetime events -#[derive(Event, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Event, BufferedEvent, Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), @@ -488,7 +491,7 @@ impl AppLifecycle { /// access window events in the order they were received from the /// operating system. Otherwise, the event types are individually /// readable with `EventReader` (e.g. `EventReader`). -#[derive(Event, Debug, Clone, PartialEq)] +#[derive(Event, BufferedEvent, Debug, Clone, PartialEq)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), diff --git a/crates/bevy_winit/src/cursor.rs b/crates/bevy_winit/src/cursor.rs index 580c7ebee4..c5c5e489a6 100644 --- a/crates/bevy_winit/src/cursor.rs +++ b/crates/bevy_winit/src/cursor.rs @@ -195,7 +195,7 @@ fn update_cursors( fn on_remove_cursor_icon(trigger: On, mut commands: Commands) { // Use `try_insert` to avoid panic if the window is being destroyed. commands - .entity(trigger.target().unwrap()) + .entity(trigger.target()) .try_insert(PendingCursor(Some(CursorSource::System( convert_system_cursor_icon(SystemCursorIcon::Default), )))); diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 4373c4dcfd..6d814d4ac2 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -71,9 +71,9 @@ thread_local! { /// in systems. /// /// When using eg. `MinimalPlugins` you can add this using `WinitPlugin::::default()`, where -/// `WakeUp` is the default `Event` that bevy uses. +/// `WakeUp` is the default event that bevy uses. #[derive(Default)] -pub struct WinitPlugin { +pub struct WinitPlugin { /// Allows the window (and the event loop) to be created on any thread /// instead of only the main thread. /// @@ -87,7 +87,7 @@ pub struct WinitPlugin { marker: PhantomData, } -impl Plugin for WinitPlugin { +impl Plugin for WinitPlugin { fn name(&self) -> &str { "bevy_winit::WinitPlugin" } @@ -155,7 +155,7 @@ impl Plugin for WinitPlugin { /// The default event that can be used to wake the window loop /// Wakes up the loop if in wait state -#[derive(Debug, Default, Clone, Copy, Event, Reflect)] +#[derive(Debug, Default, Clone, Copy, Event, BufferedEvent, Reflect)] #[reflect(Debug, Default, Clone)] pub struct WakeUp; @@ -166,7 +166,7 @@ pub struct WakeUp; /// /// When you receive this event it has already been handled by Bevy's main loop. /// Sending these events will NOT cause them to be processed by Bevy. -#[derive(Debug, Clone, Event)] +#[derive(Debug, Clone, Event, BufferedEvent)] pub struct RawWinitWindowEvent { /// The window for which the event was fired. pub window_id: WindowId, diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index 083341fd2b..934de5dad7 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -58,7 +58,7 @@ use crate::{ /// Persistent state that is used to run the [`App`] according to the current /// [`UpdateMode`]. -struct WinitAppRunnerState { +struct WinitAppRunnerState { /// The running app. app: App, /// Exit value once the loop is finished. @@ -106,7 +106,7 @@ struct WinitAppRunnerState { )>, } -impl WinitAppRunnerState { +impl WinitAppRunnerState { fn new(mut app: App) -> Self { app.add_event::(); #[cfg(feature = "custom_cursor")] @@ -198,7 +198,7 @@ pub enum CursorSource { #[derive(Component, Debug)] pub struct PendingCursor(pub Option); -impl ApplicationHandler for WinitAppRunnerState { +impl ApplicationHandler for WinitAppRunnerState { fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { if event_loop.exiting() { return; @@ -549,7 +549,7 @@ impl ApplicationHandler for WinitAppRunnerState { } } -impl WinitAppRunnerState { +impl WinitAppRunnerState { fn redraw_requested(&mut self, event_loop: &ActiveEventLoop) { let mut redraw_event_reader = EventCursor::::default(); @@ -934,7 +934,7 @@ impl WinitAppRunnerState { /// /// Overriding the app's [runner](bevy_app::App::runner) while using `WinitPlugin` will bypass the /// `EventLoop`. -pub fn winit_runner(mut app: App, event_loop: EventLoop) -> AppExit { +pub fn winit_runner(mut app: App, event_loop: EventLoop) -> AppExit { if app.plugins_state() == PluginsState::Ready { app.finish(); app.cleanup(); diff --git a/examples/3d/edit_material_on_gltf.rs b/examples/3d/edit_material_on_gltf.rs index 11bd35bd1f..97c3c48296 100644 --- a/examples/3d/edit_material_on_gltf.rs +++ b/examples/3d/edit_material_on_gltf.rs @@ -65,12 +65,12 @@ fn change_material( mut asset_materials: ResMut>, ) { // Get the `ColorOverride` of the entity, if it does not have a color override, skip - let Ok(color_override) = color_override.get(trigger.target().unwrap()) else { + let Ok(color_override) = color_override.get(trigger.target()) else { return; }; // Iterate over all children recursively - for descendants in children.iter_descendants(trigger.target().unwrap()) { + for descendants in children.iter_descendants(trigger.target()) { // Get the material of the descendant if let Some(material) = mesh_materials .get(descendants) diff --git a/examples/3d/mixed_lighting.rs b/examples/3d/mixed_lighting.rs index a32efe0a86..ffff8652b4 100644 --- a/examples/3d/mixed_lighting.rs +++ b/examples/3d/mixed_lighting.rs @@ -72,7 +72,7 @@ enum LightingMode { /// An event that's fired whenever the user changes the lighting mode. /// /// This is also fired when the scene loads for the first time. -#[derive(Clone, Copy, Default, Event)] +#[derive(Clone, Copy, Default, Event, BufferedEvent)] struct LightingModeChanged; #[derive(Clone, Copy, Component, Debug)] diff --git a/examples/3d/solari.rs b/examples/3d/solari.rs index 37f78a26ec..389272cbb1 100644 --- a/examples/3d/solari.rs +++ b/examples/3d/solari.rs @@ -74,7 +74,7 @@ fn add_raytracing_meshes_on_scene_load( } } - for descendant in children.iter_descendants(trigger.target().unwrap()) { + for descendant in children.iter_descendants(trigger.target()) { if let Ok(mesh) = mesh.get(descendant) { commands .entity(descendant) diff --git a/examples/animation/animated_mesh.rs b/examples/animation/animated_mesh.rs index c79b9ca79e..06e6c45a58 100644 --- a/examples/animation/animated_mesh.rs +++ b/examples/animation/animated_mesh.rs @@ -70,12 +70,12 @@ fn play_animation_when_ready( ) { // The entity we spawned in `setup_mesh_and_animation` is the trigger's target. // Start by finding the AnimationToPlay component we added to that entity. - if let Ok(animation_to_play) = animations_to_play.get(trigger.target().unwrap()) { + if let Ok(animation_to_play) = animations_to_play.get(trigger.target()) { // The SceneRoot component will have spawned the scene as a hierarchy // of entities parented to our entity. Since the asset contained a skinned // mesh and animations, it will also have spawned an animation player // component. Search our entity's descendants to find the animation player. - for child in children.iter_descendants(trigger.target().unwrap()) { + for child in children.iter_descendants(trigger.target()) { if let Ok(mut player) = players.get_mut(child) { // Tell the animation player to start the animation and keep // repeating it. diff --git a/examples/animation/animated_mesh_events.rs b/examples/animation/animated_mesh_events.rs index f5e6b4dea4..b9ce729bad 100644 --- a/examples/animation/animated_mesh_events.rs +++ b/examples/animation/animated_mesh_events.rs @@ -37,7 +37,7 @@ struct Animations { graph_handle: Handle, } -#[derive(Event, Reflect, Clone)] +#[derive(Event, EntityEvent, Reflect, Clone)] struct OnStep; fn observe_on_step( @@ -47,10 +47,7 @@ fn observe_on_step( transforms: Query<&GlobalTransform>, mut seeded_rng: ResMut, ) { - let translation = transforms - .get(trigger.target().unwrap()) - .unwrap() - .translation(); + let translation = transforms.get(trigger.target()).unwrap().translation(); // Spawn a bunch of particles. for _ in 0..14 { let horizontal = seeded_rng.0.r#gen::() * seeded_rng.0.gen_range(8.0..12.0); diff --git a/examples/animation/animation_events.rs b/examples/animation/animation_events.rs index d31068911b..5c55691070 100644 --- a/examples/animation/animation_events.rs +++ b/examples/animation/animation_events.rs @@ -9,7 +9,6 @@ use bevy::{ fn main() { App::new() .add_plugins(DefaultPlugins) - .add_event::() .add_systems(Startup, setup) .add_systems(Update, animate_text_opacity) .add_observer(edit_message) @@ -19,7 +18,7 @@ fn main() { #[derive(Component)] struct MessageText; -#[derive(Event, Clone)] +#[derive(Event, EntityEvent, Clone)] struct MessageEvent { value: String, color: Color, diff --git a/examples/app/log_layers_ecs.rs b/examples/app/log_layers_ecs.rs index 059ffcbe20..1ee988b7cf 100644 --- a/examples/app/log_layers_ecs.rs +++ b/examples/app/log_layers_ecs.rs @@ -38,7 +38,7 @@ fn main() { } /// A basic message. This is what we will be sending from the [`CaptureLayer`] to [`CapturedLogEvents`] non-send resource. -#[derive(Debug, Event)] +#[derive(Debug, Event, BufferedEvent)] struct LogEvent { message: String, level: Level, diff --git a/examples/async_tasks/external_source_external_thread.rs b/examples/async_tasks/external_source_external_thread.rs index 1b437ed76c..9cd7a57ae4 100644 --- a/examples/async_tasks/external_source_external_thread.rs +++ b/examples/async_tasks/external_source_external_thread.rs @@ -20,7 +20,7 @@ fn main() { #[derive(Resource, Deref)] struct StreamReceiver(Receiver); -#[derive(Event)] +#[derive(Event, BufferedEvent)] struct StreamEvent(u32); fn setup(mut commands: Commands) { diff --git a/examples/audio/pitch.rs b/examples/audio/pitch.rs index 4a49a8885f..6f4108025a 100644 --- a/examples/audio/pitch.rs +++ b/examples/audio/pitch.rs @@ -12,7 +12,7 @@ fn main() { .run(); } -#[derive(Event, Default)] +#[derive(Event, BufferedEvent, Default)] struct PlayPitch; #[derive(Resource)] diff --git a/examples/ecs/component_hooks.rs b/examples/ecs/component_hooks.rs index 4ce6b7b09b..7a97bf454c 100644 --- a/examples/ecs/component_hooks.rs +++ b/examples/ecs/component_hooks.rs @@ -45,7 +45,7 @@ impl Component for MyComponent { #[derive(Resource, Default, Debug, Deref, DerefMut)] struct MyComponentIndex(HashMap); -#[derive(Event)] +#[derive(Event, BufferedEvent)] struct MyEvent; fn main() { diff --git a/examples/ecs/entity_disabling.rs b/examples/ecs/entity_disabling.rs index 66de6c74f4..362b655dfb 100644 --- a/examples/ecs/entity_disabling.rs +++ b/examples/ecs/entity_disabling.rs @@ -40,7 +40,7 @@ fn disable_entities_on_click( valid_query: Query<&DisableOnClick>, mut commands: Commands, ) { - let clicked_entity = trigger.target().unwrap(); + let clicked_entity = trigger.target(); // Windows and text are entities and can be clicked! // We definitely don't want to disable the window itself, // because that would cause the app to close! diff --git a/examples/ecs/event.rs b/examples/ecs/event.rs index e01d810099..33ff7ad60a 100644 --- a/examples/ecs/event.rs +++ b/examples/ecs/event.rs @@ -6,17 +6,17 @@ use bevy::prelude::*; // 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)] +#[derive(Event, BufferedEvent, Debug)] struct DealDamage { pub amount: i32, } // This event should be sent when an entity receives damage. -#[derive(Event, Debug, Default)] +#[derive(Event, BufferedEvent, Debug, Default)] struct DamageReceived; // This event should be sent when an entity blocks damage with armor. -#[derive(Event, Debug, Default)] +#[derive(Event, BufferedEvent, Debug, Default)] struct ArmorBlockedDamage; // This resource represents a timer used to determine when to deal damage diff --git a/examples/ecs/observer_propagation.rs b/examples/ecs/observer_propagation.rs index cea184b3aa..b2da6c9d97 100644 --- a/examples/ecs/observer_propagation.rs +++ b/examples/ecs/observer_propagation.rs @@ -53,8 +53,8 @@ fn setup(mut commands: Commands) { // - **auto_propagate:** // We can also choose whether or not this event will propagate by default when triggered. If this is // false, it will only propagate following a call to `On::propagate(true)`. -#[derive(Clone, Component, Event)] -#[event(traversal = &'static ChildOf, auto_propagate)] +#[derive(Clone, Component, Event, EntityEvent)] +#[entity_event(traversal = &'static ChildOf, auto_propagate)] struct Attack { damage: u16, } @@ -78,14 +78,14 @@ fn attack_armor(entities: Query>, mut commands: Commands) { } fn attack_hits(trigger: On, name: Query<&Name>) { - if let Ok(name) = name.get(trigger.target().unwrap()) { + if let Ok(name) = name.get(trigger.target()) { info!("Attack hit {}", name); } } /// A callback placed on [`Armor`], checking if it absorbed all the [`Attack`] damage. fn block_attack(mut trigger: On, armor: Query<(&Armor, &Name)>) { - let (armor, name) = armor.get(trigger.target().unwrap()).unwrap(); + let (armor, name) = armor.get(trigger.target()).unwrap(); let attack = trigger.event_mut(); let damage = attack.damage.saturating_sub(**armor); if damage > 0 { @@ -110,14 +110,14 @@ fn take_damage( mut app_exit: EventWriter, ) { let attack = trigger.event(); - let (mut hp, name) = hp.get_mut(trigger.target().unwrap()).unwrap(); + let (mut hp, name) = hp.get_mut(trigger.target()).unwrap(); **hp = hp.saturating_sub(attack.damage); if **hp > 0 { info!("{} has {:.1} HP", name, hp.0); } else { warn!("💀 {} has died a gruesome death", name); - commands.entity(trigger.target().unwrap()).despawn(); + commands.entity(trigger.target()).despawn(); app_exit.write(AppExit::Success); } diff --git a/examples/ecs/observers.rs b/examples/ecs/observers.rs index e27a1ad7e2..bf40f08ec3 100644 --- a/examples/ecs/observers.rs +++ b/examples/ecs/observers.rs @@ -60,13 +60,13 @@ impl Mine { } } -#[derive(Event)] +#[derive(Event, EntityEvent)] struct ExplodeMines { pos: Vec2, radius: f32, } -#[derive(Event)] +#[derive(Event, EntityEvent)] struct Explode; fn setup(mut commands: Commands) { @@ -113,33 +113,29 @@ fn setup(mut commands: Commands) { } fn on_add_mine(trigger: On, query: Query<&Mine>, mut index: ResMut) { - let mine = query.get(trigger.target().unwrap()).unwrap(); + let mine = query.get(trigger.target()).unwrap(); let tile = ( (mine.pos.x / CELL_SIZE).floor() as i32, (mine.pos.y / CELL_SIZE).floor() as i32, ); - index - .map - .entry(tile) - .or_default() - .insert(trigger.target().unwrap()); + index.map.entry(tile).or_default().insert(trigger.target()); } // Remove despawned mines from our index fn on_remove_mine(trigger: On, query: Query<&Mine>, mut index: ResMut) { - let mine = query.get(trigger.target().unwrap()).unwrap(); + let mine = query.get(trigger.target()).unwrap(); let tile = ( (mine.pos.x / CELL_SIZE).floor() as i32, (mine.pos.y / CELL_SIZE).floor() as i32, ); index.map.entry(tile).and_modify(|set| { - set.remove(&trigger.target().unwrap()); + set.remove(&trigger.target()); }); } fn explode_mine(trigger: On, query: Query<&Mine>, mut commands: Commands) { // If a triggered event is targeting a specific entity you can access it with `.target()` - let id = trigger.target().unwrap(); + let id = trigger.target(); let Ok(mut entity) = commands.get_entity(id) else { return; }; diff --git a/examples/ecs/removal_detection.rs b/examples/ecs/removal_detection.rs index c247d1bf9b..699bb66e07 100644 --- a/examples/ecs/removal_detection.rs +++ b/examples/ecs/removal_detection.rs @@ -50,7 +50,7 @@ fn remove_component( fn react_on_removal(trigger: On, mut query: Query<&mut Sprite>) { // The `Remove` trigger was automatically called on the `Entity` that had its `MyComponent` removed. - let entity = trigger.target().unwrap(); + let entity = trigger.target(); if let Ok(mut sprite) = query.get_mut(entity) { sprite.color = Color::srgb(0.5, 1., 1.); } diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index c9cb334503..78dd5b240b 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -1,7 +1,7 @@ //! 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. +//! if and only if the [`BufferedEvent`] 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, this would violate Rust's rules against aliased mutability. //! In other words, this would be Undefined Behavior (UB)! @@ -46,10 +46,10 @@ fn main() { app.update(); } -#[derive(Event)] +#[derive(Event, BufferedEvent)] struct A; -#[derive(Event)] +#[derive(Event, BufferedEvent)] struct B; // This works fine, because the types are different, @@ -62,7 +62,7 @@ fn read_and_write_different_event_types(mut a: EventWriter, mut b: EventReade } /// A dummy event type. -#[derive(Debug, Clone, Event)] +#[derive(Debug, Clone, Event, BufferedEvent)] struct DebugEvent { resend_from_param_set: bool, resend_from_local_event_reader: bool, diff --git a/examples/games/breakout.rs b/examples/games/breakout.rs index 0a0b04f7a2..b8d02e8159 100644 --- a/examples/games/breakout.rs +++ b/examples/games/breakout.rs @@ -89,7 +89,7 @@ struct Ball; #[derive(Component, Deref, DerefMut)] struct Velocity(Vec2); -#[derive(Event, Default)] +#[derive(Event, BufferedEvent, Default)] struct CollisionEvent; #[derive(Component)] diff --git a/examples/helpers/widgets.rs b/examples/helpers/widgets.rs index 14873acc5f..02039fed49 100644 --- a/examples/helpers/widgets.rs +++ b/examples/helpers/widgets.rs @@ -6,7 +6,7 @@ use bevy::{ecs::system::EntityCommands, prelude::*}; /// An event that's sent whenever the user changes one of the settings by /// clicking a radio button. -#[derive(Clone, Event, Deref, DerefMut)] +#[derive(Clone, Event, BufferedEvent, Deref, DerefMut)] pub struct WidgetClickEvent(T); /// A marker component that we place on all widgets that send diff --git a/examples/no_std/library/src/lib.rs b/examples/no_std/library/src/lib.rs index e336d8a98e..a193ed0240 100644 --- a/examples/no_std/library/src/lib.rs +++ b/examples/no_std/library/src/lib.rs @@ -106,7 +106,7 @@ struct DelayedComponentTimer(Timer); #[component(immutable)] struct DelayedComponent(B); -#[derive(Event)] +#[derive(Event, EntityEvent)] struct Unwrap; fn tick_timers( @@ -127,10 +127,7 @@ fn tick_timers( } fn unwrap(trigger: On, world: &mut World) { - if let Some(mut target) = trigger - .target() - .and_then(|target| world.get_entity_mut(target).ok()) - { + if let Ok(mut target) = world.get_entity_mut(trigger.target()) { if let Some(DelayedComponent(bundle)) = target.take::>() { target.insert(bundle); } diff --git a/examples/picking/debug_picking.rs b/examples/picking/debug_picking.rs index 7f8c26a4bf..de6fbf0bca 100644 --- a/examples/picking/debug_picking.rs +++ b/examples/picking/debug_picking.rs @@ -48,13 +48,13 @@ fn setup_scene( .observe(on_click_spawn_cube) .observe( |out: On>, mut texts: Query<&mut TextColor>| { - let mut text_color = texts.get_mut(out.target().unwrap()).unwrap(); + let mut text_color = texts.get_mut(out.target()).unwrap(); text_color.0 = Color::WHITE; }, ) .observe( |over: On>, mut texts: Query<&mut TextColor>| { - let mut color = texts.get_mut(over.target().unwrap()).unwrap(); + let mut color = texts.get_mut(over.target()).unwrap(); color.0 = bevy::color::palettes::tailwind::CYAN_400.into(); }, ); @@ -102,7 +102,7 @@ fn on_click_spawn_cube( } fn on_drag_rotate(drag: On>, mut transforms: Query<&mut Transform>) { - if let Ok(mut transform) = transforms.get_mut(drag.target().unwrap()) { + if let Ok(mut transform) = transforms.get_mut(drag.target()) { transform.rotate_y(drag.delta.x * 0.02); transform.rotate_x(drag.delta.y * 0.02); } diff --git a/examples/picking/mesh_picking.rs b/examples/picking/mesh_picking.rs index 60d0cb64ab..d1fa90c93f 100644 --- a/examples/picking/mesh_picking.rs +++ b/examples/picking/mesh_picking.rs @@ -157,14 +157,14 @@ fn setup_scene( } /// Returns an observer that updates the entity's material to the one specified. -fn update_material_on( +fn update_material_on( new_material: Handle, ) -> impl Fn(On, Query<&mut MeshMaterial3d>) { // An observer closure that captures `new_material`. We do this to avoid needing to write four // versions of this observer, each triggered by a different event and with a different hardcoded // material. Instead, the event type is a generic, and the material is passed in. move |trigger, mut query| { - if let Ok(mut material) = query.get_mut(trigger.target().unwrap()) { + if let Ok(mut material) = query.get_mut(trigger.target()) { material.0 = new_material.clone(); } } @@ -191,7 +191,7 @@ fn rotate(mut query: Query<&mut Transform, With>, time: Res