Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions _release-content/migration-guides/observer_event_matching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
title: Moved Observer events `B` Bundle generic into the event type
Comment thread
alice-i-cecile marked this conversation as resolved.
pull_requests: [24013]
---

The `B: Bundle` type parameter has been removed from `On<E, B>` in observer systems.
Lifecycle events (`Add`/`Insert`/`Discard`/`Remove`/`Despawn`) have been updated
to have this `B: Bundle` directly on them.

```rust
// Bevy 0.19
world.add_observer(|on: On<Add, A>| {
// ...
});

// Bevy 0.20
world.add_observer(|on: On<Add<A>>| {
// ...
});
```

For custom event types that previously made use of `B: Bundle`, it's recommended to do the following:

```rust
// Bevy 0.19

#[derive(Event)]
pub struct Foo;

#[derive(Component)]
pub struct Bar;

world.add_observer(|on: On<Foo, Bar>| {
// ...
});

// Bevy 0.20

#[derive(Event)]
pub struct FooEvent;

#[derive(Component)]
pub struct Bar;

pub struct Foo<B: Bundle>(PhantomData<B>);

impl<B: Bundle> EventPattern for Foo<B> {
type Event = FooEvent;
type Components = B;
}

world.add_observer(|on: On<Foo<Bar>>| {
// ...
});
```

For lifecycle observers watching dynamic components, you now need to modify
`On<Add>` to `On<Add<()>>`:
Comment thread
ItsDoot marked this conversation as resolved.

```rust
// Bevy 0.19
world.spawn(
Observer::new(|_: On<Add>| {
// ...
})
.with_component(component_id),
);

// Bevy 0.20
world.spawn(
Observer::new(|_: On<Add<()>>| {
// ...
})
.with_component(component_id),
);
```
2 changes: 1 addition & 1 deletion benches/benches/bevy_ecs/observers/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ pub fn observer_lifecycle(criterion: &mut Criterion) {
#[derive(Component)]
struct A;

fn on_insert(event: On<Insert, A>) {
fn on_insert(event: On<Insert<A>>) {
black_box(event);
}
2 changes: 1 addition & 1 deletion crates/bevy_app/src/hierarchy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub struct ValidateParentHasComponentSystems;
/// An `Insert` observer that when run, will validate that the parent of a given entity contains
/// component `C`. If the parent does not contain `C`, a warning will be logged later in the frame.
fn validate_parent_has_component<C: Component>(
event: On<Insert, C>,
event: On<Insert<C>>,
child: Query<&ChildOf>,
with_component: Query<(), With<C>>,
mut writer: MessageWriter<CheckParentHasComponent<C>>,
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_app/src/propagate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ pub fn on_r_inserted<
F: QueryFilter + 'static,
R: Relationship,
>(
event: On<Insert, R>,
event: On<Insert<R>>,
mut commands: Commands,
query: Query<(&R, Has<Inherited<C>>), (Without<Propagate<C>>, F)>,
relations: Query<&Inherited<C>, Without<PropagateStop<C>>>,
Expand All @@ -207,7 +207,7 @@ pub fn on_r_inserted<

/// Remove [`Inherited::<C>`] when an entity loses its `R` relationship
pub fn on_r_removed<C: Component + Clone + PartialEq, F: QueryFilter + 'static, R: Relationship>(
event: On<Remove, R>,
event: On<Remove<R>>,
mut commands: Commands,
query: Query<(), (With<Inherited<C>>, Without<Propagate<C>>, F)>,
) {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_dev_tools/src/diagnostics_overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ fn rebuild_diagnostics_list(
}

fn build_overlay(
event: On<Add, DiagnosticsOverlay>,
event: On<Add<DiagnosticsOverlay>>,
mut commands: Commands,
diagnostics_overlays: Query<&DiagnosticsOverlay>,
diagnostics_overlay_plane: Single<Entity, With<DiagnosticsOverlayPlane>>,
Expand Down
10 changes: 5 additions & 5 deletions crates/bevy_ecs/src/bundle/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
component::{Components, StorageType},
entity::{Entities, Entity, EntityLocation},
event::EntityComponentsTrigger,
lifecycle::{Add, Discard, Insert, ADD, DISCARD, INSERT},
lifecycle::{AddEvent, DiscardEvent, InsertEvent, ADD, DISCARD, INSERT},
observer::Observers,
query::DebugCheckedUnwrap as _,
relationship::RelationshipHookMode,
Expand Down Expand Up @@ -157,7 +157,7 @@ impl<'w> BundleInserter<'w> {
// SAFETY: the DISCARD event_key corresponds to the Discard event's type
deferred_world.trigger_raw(
DISCARD,
&mut Discard { entity },
&mut DiscardEvent { entity },
&mut EntityComponentsTrigger {
components: archetype_after_insert.existing(),
old_archetype: Some(archetype),
Expand Down Expand Up @@ -412,7 +412,7 @@ impl<'w> BundleInserter<'w> {
// SAFETY: the ADD event_key corresponds to the Add event's type
deferred_world.trigger_raw(
ADD,
&mut Add { entity },
&mut AddEvent { entity },
&mut EntityComponentsTrigger {
components: archetype_after_insert.added(),
old_archetype: Some(old_archetype),
Expand All @@ -435,7 +435,7 @@ impl<'w> BundleInserter<'w> {
// SAFETY: the INSERT event_key corresponds to the Insert event's type
deferred_world.trigger_raw(
INSERT,
&mut Insert { entity },
&mut InsertEvent { entity },
&mut EntityComponentsTrigger {
components: archetype_after_insert.inserted(),
old_archetype: Some(old_archetype),
Expand All @@ -459,7 +459,7 @@ impl<'w> BundleInserter<'w> {
// SAFETY: the INSERT event_key corresponds to the Insert event's type
deferred_world.trigger_raw(
INSERT,
&mut Insert { entity },
&mut InsertEvent { entity },
&mut EntityComponentsTrigger {
components: archetype_after_insert.added(),
old_archetype: Some(old_archetype),
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/bundle/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
component::{ComponentId, Components, StorageType},
entity::{Entity, EntityLocation},
event::EntityComponentsTrigger,
lifecycle::{Discard, Remove, DISCARD, REMOVE},
lifecycle::{DiscardEvent, RemoveEvent, DISCARD, REMOVE},
observer::Observers,
relationship::RelationshipHookMode,
storage::{SparseSets, Storages, Table, TableId},
Expand Down Expand Up @@ -150,7 +150,7 @@ impl<'w> BundleRemover<'w> {
// SAFETY: the DISCARD event_key corresponds to the Discard event's type
deferred_world.trigger_raw(
DISCARD,
&mut Discard { entity },
&mut DiscardEvent { entity },
&mut EntityComponentsTrigger {
components: &components,
old_archetype: Some(self.old_archetype.as_ref()),
Expand All @@ -171,7 +171,7 @@ impl<'w> BundleRemover<'w> {
// SAFETY: the REMOVE event_key corresponds to the Remove event's type
deferred_world.trigger_raw(
REMOVE,
&mut Remove { entity },
&mut RemoveEvent { entity },
&mut EntityComponentsTrigger {
components: &components,
old_archetype: Some(self.old_archetype.as_ref()),
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/bundle/spawner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
change_detection::{MaybeLocation, Tick},
entity::{Entity, EntityAllocator, EntityLocation},
event::EntityComponentsTrigger,
lifecycle::{Add, Insert, ADD, INSERT},
lifecycle::{AddEvent, InsertEvent, ADD, INSERT},
relationship::RelationshipHookMode,
storage::Table,
world::{unsafe_world_cell::UnsafeWorldCell, World},
Expand Down Expand Up @@ -141,7 +141,7 @@ impl<'w> BundleSpawner<'w> {
// SAFETY: the ADD event_key corresponds to the Add event's type
deferred_world.trigger_raw(
ADD,
&mut Add { entity },
&mut AddEvent { entity },
&mut EntityComponentsTrigger {
components: bundle_info.contributed_components(),
old_archetype: None,
Expand All @@ -161,7 +161,7 @@ impl<'w> BundleSpawner<'w> {
// SAFETY: the INSERT event_key corresponds to the Insert event's type
deferred_world.trigger_raw(
INSERT,
&mut Insert { entity },
&mut InsertEvent { entity },
&mut EntityComponentsTrigger {
components: bundle_info.contributed_components(),
old_archetype: None,
Expand Down
42 changes: 42 additions & 0 deletions crates/bevy_ecs/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub use bevy_ecs_macros::{EntityEvent, Event};
pub use trigger::*;

use crate::{
bundle::Bundle,
component::{Component, ComponentId},
entity::Entity,
world::World,
Expand Down Expand Up @@ -90,6 +91,47 @@ pub trait Event: Send + Sync + Sized + 'static {
type Trigger<'a>: Trigger<Self>;
}

/// Trait for types that can be 'matched' on by [`Observer`]s to register additional
Comment thread
ItsDoot marked this conversation as resolved.
/// metadata for an [`Event`] trigger. All [`Event`]s are also implicitly
/// [`EventPattern`]s, but this trait can be manually implemented.
///
/// The following are lifecycle [`EventPattern`]s that register components
/// to watch for via their generic [`Bundle`] type parameter:
///
/// - [`Add`]
/// - [`Insert`]
/// - [`Discard`]
/// - [`Remove`]
/// - [`Despawn`]
///
/// [`Observer`]: crate::observer::Observer
/// [`Add`]: crate::lifecycle::Add
/// [`Insert`]: crate::lifecycle::Insert
/// [`Discard`]: crate::lifecycle::Discard
/// [`Remove`]: crate::lifecycle::Remove
/// [`Despawn`]: crate::lifecycle::Despawn
#[diagnostic::on_unimplemented(
message = "`{Self}` is not an `Event` or `EventPattern`",
label = "invalid `EventPattern`",
note = "consider annotating `{Self}` with `#[derive(Event)]` or implementing `EventPattern` manually"
)]
pub trait EventPattern: Send + Sync + 'static {
/// The event type being observed.
type Event: Event;

/// Components to watch for this event. This is used by [`EntityComponentsTrigger`]
/// to determine which entities to run observers for.
///
/// See [`EntityComponentsTrigger`] for more info.
type Components: Bundle;
}

// All events are implicitly EventPatterns, with no additional components.
impl<E: Event> EventPattern for E {
type Event = Self;
type Components = ();
}

/// An [`EntityEvent`] is an [`Event`] that is triggered for a specific [`EntityEvent::event_target`] entity:
///
/// ```
Expand Down
9 changes: 6 additions & 3 deletions crates/bevy_ecs/src/event/trigger.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::event::SetEntityEventTarget;
use crate::event::{EventPattern, SetEntityEventTarget};
use crate::{
archetype::Archetype,
component::ComponentId,
Expand Down Expand Up @@ -55,6 +55,9 @@ pub unsafe trait Trigger<E: Event> {
);
}

/// Shorthand for accessing an [`EventPattern`]s [`Trigger`] via its [`Event`].
pub type EventPatternTrigger<'a, E> = <<E as EventPattern>::Event as Event>::Trigger<'a>;

/// A [`Trigger`] that runs _every_ "global" [`Observer`](crate::observer::Observer) (ex: registered via [`World::add_observer`](crate::world::World::add_observer))
/// that matches the given [`Event`].
///
Expand Down Expand Up @@ -366,7 +369,7 @@ pub struct EntityComponentsTrigger<'a> {
/// # let mut world = World::new();
/// #
/// fn on_add_disable(
/// on: On<Add, Disabled>,
/// on: On<Add<Disabled>>,
/// mut cache: ResMut<EntitiesWithA>,
/// a_component: ComponentIdFor<A>,
/// ) {
Expand Down Expand Up @@ -405,7 +408,7 @@ pub struct EntityComponentsTrigger<'a> {
/// # let mut world = World::new();
/// #
/// fn on_remove_disable(
/// on: On<Remove, Disabled>,
/// on: On<Remove<Disabled>>,
/// mut cache: ResMut<EntitiesWithA>,
/// a_component: ComponentIdFor<A>,
/// ) {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub mod prelude {
component::Component,
entity::{ContainsEntity, Entity, EntityMapper},
error::{BevyError, Result, ResultSeverityExt, Severity},
event::{EntityEvent, Event},
event::{EntityEvent, Event, EventPattern},
hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children},
lifecycle::{Add, Despawn, Discard, Insert, Remove, RemovedComponents},
message::{
Expand Down
Loading
Loading