Revert changes related mutual exclusivity + minor docs and release note changes

This commit is contained in:
Tim Blackbird 2025-07-17 14:02:26 +02:00
parent 779e4535d9
commit 1161216938
6 changed files with 24 additions and 80 deletions

View File

@ -30,9 +30,8 @@ pub fn derive_broadcast_event(input: TokenStream) -> TokenStream {
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
TokenStream::from(quote! { 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::Event for #struct_name #type_generics #where_clause {}
type Kind = #bevy_ecs_path::event::BroadcastEventKind; impl #impl_generics #bevy_ecs_path::event::BroadcastEvent for #struct_name #type_generics #where_clause {}
}
}) })
} }
@ -75,8 +74,10 @@ pub fn derive_entity_event(input: TokenStream) -> TokenStream {
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
TokenStream::from(quote! { 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::Event for #struct_name #type_generics #where_clause {}
type Kind = #bevy_ecs_path::event::EntityEventKind<Self, #traversal, #auto_propagate>; impl #impl_generics #bevy_ecs_path::event::EntityEvent for #struct_name #type_generics #where_clause {
type Traversal = #traversal;
const AUTO_PROPAGATE: bool = #auto_propagate;
} }
}) })
} }

View File

@ -95,9 +95,6 @@ use core::{
note = "consider annotating `{Self}` with `#[derive(BroadcastEvent)]` or `#[derive(EntityEvent)]`" note = "consider annotating `{Self}` with `#[derive(BroadcastEvent)]` or `#[derive(EntityEvent)]`"
)] )]
pub trait Event: Send + Sync + 'static { pub trait Event: Send + Sync + 'static {
#[doc(hidden)]
type Kind;
/// Generates the [`EventKey`] for this event type. /// Generates the [`EventKey`] for this event type.
/// ///
/// If this type has already been registered, /// If this type has already been registered,
@ -133,32 +130,7 @@ pub trait Event: Send + Sync + 'static {
} }
/// A global [`Event`] without an entity target. /// A global [`Event`] without an entity target.
#[diagnostic::on_unimplemented( pub trait BroadcastEvent: Event {}
message = "`{Self}` is not an `BroadcastEvent`",
label = "invalid `BroadcastEvent`",
note = "consider annotating `{Self}` with `#[derive(BroadcastEvent)]`"
)]
pub trait BroadcastEvent: Event + sealed_a::SealedA {}
#[doc(hidden)]
pub struct BroadcastEventKind;
#[diagnostic::do_not_recommend]
impl<T> BroadcastEvent for T where T: Event<Kind = BroadcastEventKind> {}
pub(crate) mod sealed_a {
use super::*;
/// Seal for the [`BroadcastEvent`] trait.
#[diagnostic::on_unimplemented(
message = "manual implementations of `BroadcastEvent` are disallowed",
note = "consider annotating `{Self}` with `#[derive(BroadcastEvent)]` instead"
)]
pub trait SealedA {}
#[diagnostic::do_not_recommend]
impl<T> SealedA for T where T: Event<Kind = BroadcastEventKind> {}
}
/// An [`Event`] that can be targeted at specific entities. /// An [`Event`] that can be targeted at specific entities.
/// ///
@ -276,7 +248,7 @@ pub(crate) mod sealed_a {
label = "invalid `EntityEvent`", label = "invalid `EntityEvent`",
note = "consider annotating `{Self}` with `#[derive(EntityEvent)]`" note = "consider annotating `{Self}` with `#[derive(EntityEvent)]`"
)] )]
pub trait EntityEvent: Event + sealed_b::SealedB { pub trait EntityEvent: Event {
/// The component that describes which [`Entity`] to propagate this event to next, when [propagation] is enabled. /// The component that describes which [`Entity`] to propagate this event to next, when [propagation] is enabled.
/// ///
/// [`Entity`]: crate::entity::Entity /// [`Entity`]: crate::entity::Entity
@ -291,37 +263,6 @@ pub trait EntityEvent: Event + sealed_b::SealedB {
const AUTO_PROPAGATE: bool = false; const AUTO_PROPAGATE: bool = false;
} }
#[doc(hidden)]
pub struct EntityEventKind<T: ?Sized, R: Traversal<T>, const A: bool> {
_t: PhantomData<T>,
_r: PhantomData<R>,
}
// Blanket impl for EntityEvent
#[diagnostic::do_not_recommend]
impl<T, R: Traversal<T>, const A: bool> EntityEvent for T
where
T: Event<Kind = EntityEventKind<T, R, A>>,
{
type Traversal = R;
const AUTO_PROPAGATE: bool = A;
}
pub(crate) mod sealed_b {
use super::*;
/// Seal for the [`EntityEvent`] trait.
#[diagnostic::on_unimplemented(
message = "manual implementations of `EntityEvent` are disallowed",
note = "consider annotating `{Self}` with `#[derive(EntityEvent)]` instead"
)]
pub trait SealedB {}
#[diagnostic::do_not_recommend]
impl<T, R: Traversal<T>, const A: bool> SealedB for T where T: Event<Kind = EntityEventKind<T, R, A>>
{}
}
/// A buffered event for pull-based event handling. /// A buffered event for pull-based event handling.
/// ///
/// Buffered events can be written with [`EventWriter`] and read using the [`EventReader`] system parameter. /// Buffered events can be written with [`EventWriter`] and read using the [`EventReader`] system parameter.

View File

@ -11,10 +11,7 @@ mod update;
mod writer; mod writer;
pub(crate) use base::EventInstance; pub(crate) use base::EventInstance;
pub use base::{ pub use base::{BroadcastEvent, BufferedEvent, EntityEvent, Event, EventId, EventKey};
BroadcastEvent, BroadcastEventKind, BufferedEvent, EntityEvent, EntityEventKind, Event,
EventId, EventKey,
};
pub use bevy_ecs_macros::{BroadcastEvent, BufferedEvent, EntityEvent}; pub use bevy_ecs_macros::{BroadcastEvent, BufferedEvent, EntityEvent};
#[expect(deprecated, reason = "`SendBatchIds` was renamed to `WriteBatchIds`.")] #[expect(deprecated, reason = "`SendBatchIds` was renamed to `WriteBatchIds`.")]
pub use collections::{Events, SendBatchIds, WriteBatchIds}; pub use collections::{Events, SendBatchIds, WriteBatchIds};

View File

@ -110,15 +110,19 @@ impl<'w, E: EntityEvent, B: Bundle> On<'w, E, B> {
/// ///
/// Note that if event propagation is enabled, this may not be the same as the original target of the event, /// Note that if event propagation is enabled, this may not be the same as the original target of the event,
/// which can be accessed via [`On::original_target`]. /// which can be accessed via [`On::original_target`].
///
/// If the event is also a [`BroadcastEvent`] sent with [`trigger`](World::trigger), this will return [`Entity::PLACEHOLDER`].
pub fn target(&self) -> Entity { pub fn target(&self) -> Entity {
self.trigger.current_target.unwrap() self.trigger.current_target.unwrap_or(Entity::PLACEHOLDER)
} }
/// Returns the original [`Entity`] that the [`EntityEvent`] was targeted at when it was first triggered. /// Returns the original [`Entity`] that the [`EntityEvent`] was targeted at when it was first triggered.
/// ///
/// If event propagation is not enabled, this will always return the same value as [`On::target`]. /// If event propagation is not enabled, this will always return the same value as [`On::target`].
///
/// If the event is also a [`BroadcastEvent`] sent with [`trigger`](World::trigger), this will return [`Entity::PLACEHOLDER`].
pub fn original_target(&self) -> Entity { pub fn original_target(&self) -> Entity {
self.trigger.original_target.unwrap() self.trigger.original_target.unwrap_or(Entity::PLACEHOLDER)
} }
/// Enables or disables event propagation, allowing the same event to trigger observers on a chain of different entities. /// Enables or disables event propagation, allowing the same event to trigger observers on a chain of different entities.
@ -181,11 +185,13 @@ pub struct ObserverTrigger {
pub event_key: EventKey, pub event_key: EventKey,
/// The [`ComponentId`]s the trigger targeted. /// The [`ComponentId`]s the trigger targeted.
pub components: SmallVec<[ComponentId; 2]>, pub components: SmallVec<[ComponentId; 2]>,
/// For [`EntityEvent`]s this is the entity that the event targeted. Always `None` for [`BroadcastEvent`]. /// For [`EntityEvent`]s used with `trigger_targets` this is the entity that the event targeted.
/// Can only be `None` for [`BroadcastEvent`]s used with `trigger`.
/// ///
/// Note that if event propagation is enabled, this may not be the same as [`ObserverTrigger::original_target`]. /// Note that if event propagation is enabled, this may not be the same as [`ObserverTrigger::original_target`].
pub current_target: Option<Entity>, pub current_target: Option<Entity>,
/// For [`EntityEvent`]s this is the entity that the event was originally targeted at. Always `None` for [`BroadcastEvent`]. /// For [`EntityEvent`]s used with `trigger_targets` this is the entity that the event was originally targeted at.
/// Can only be `None` for [`BroadcastEvent`]s used with `trigger`.
/// ///
/// If event propagation is enabled, this will be the first entity that the event was targeted at, /// If event propagation is enabled, this will be the first entity that the event was targeted at,
/// even if the event was propagated to other entities. /// even if the event was propagated to other entities.

View File

@ -2,7 +2,7 @@ use crate::{DynamicScene, Scene};
use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; use bevy_asset::{AssetEvent, AssetId, Assets, Handle};
use bevy_ecs::{ use bevy_ecs::{
entity::{Entity, EntityHashMap}, entity::{Entity, EntityHashMap},
event::{EntityEvent, EventCursor, Events}, event::{BroadcastEvent, EntityEvent, EventCursor, Events},
hierarchy::ChildOf, hierarchy::ChildOf,
reflect::AppTypeRegistry, reflect::AppTypeRegistry,
resource::Resource, resource::Resource,
@ -34,6 +34,8 @@ pub struct SceneInstanceReady {
pub instance_id: InstanceId, pub instance_id: InstanceId,
} }
impl BroadcastEvent for SceneInstanceReady {}
/// Information about a scene instance. /// Information about a scene instance.
#[derive(Debug)] #[derive(Debug)]
pub struct InstanceInfo { pub struct InstanceInfo {
@ -421,8 +423,7 @@ impl SceneSpawner {
.trigger_targets(SceneInstanceReady { instance_id }, parent); .trigger_targets(SceneInstanceReady { instance_id }, parent);
} else { } else {
// Defer via commands otherwise SceneSpawner is not available in the observer. // Defer via commands otherwise SceneSpawner is not available in the observer.
// TODO: Thinkies world.commands().trigger(SceneInstanceReady { instance_id });
// world.commands().trigger(SceneInstanceReady { instance_id });
} }
} }
} }

View File

@ -25,7 +25,7 @@ APIs are typically built to support only one of them.
This has led to a lot of confusion and frustration for users. Common footguns include: This has led to a lot of confusion and frustration for users. Common footguns include:
- Using a "buffered event" with an observer, or an observer event with `EventReader`, leaving the user wondering why the event is not being detected. - Using a "buffered event" with an observer, or an observer event with `EventReader`, leaving the user wondering why the event is not being detected.
- `On`(formerly `Trigger`) has a `target` getter which would return `Entity::PLACEHOLDER` for events sent via `trigger` rather than `trigger_targets`. - `On`(formerly `Trigger`) has a `target` getter which would cause confusion for events only mean to be used with `trigger` where it returns `Entity::PLACEHOLDER`.
**Bevy 0.17** aims to solve this ambiguity by splitting the different kinds of events into multiple traits: **Bevy 0.17** aims to solve this ambiguity by splitting the different kinds of events into multiple traits:
@ -34,8 +34,6 @@ This has led to a lot of confusion and frustration for users. Common footguns in
- `EntityEvent`: An observer event that targets specific entities and can propagate the event from one entity to another across relationships. - `EntityEvent`: An observer event that targets specific entities and can propagate the event from one entity to another across relationships.
- `BufferedEvent`: An event used with `EventReader` and `EventWriter` for pull-based event handling. - `BufferedEvent`: An event used with `EventReader` and `EventWriter` for pull-based event handling.
Note: To fully prevent the footgun of `On::target` returning `Entity::PLACEHOLDER` the `BroadcastEvent` and `EntityEvent` traits were made mutually exclusive.
## Using Events ## Using Events
Events without an entity target can be defined, by deriving the `BroadcastEvent` trait. Events without an entity target can be defined, by deriving the `BroadcastEvent` trait.