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();
TokenStream::from(quote! {
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::Event for #struct_name #type_generics #where_clause {}
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();
TokenStream::from(quote! {
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::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;
}
})
}

View File

@ -95,9 +95,6 @@ use core::{
note = "consider annotating `{Self}` with `#[derive(BroadcastEvent)]` or `#[derive(EntityEvent)]`"
)]
pub trait Event: Send + Sync + 'static {
#[doc(hidden)]
type Kind;
/// Generates the [`EventKey`] for this event type.
///
/// If this type has already been registered,
@ -133,32 +130,7 @@ pub trait Event: Send + Sync + 'static {
}
/// A global [`Event`] without an entity target.
#[diagnostic::on_unimplemented(
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> {}
}
pub trait BroadcastEvent: Event {}
/// An [`Event`] that can be targeted at specific entities.
///
@ -276,7 +248,7 @@ pub(crate) mod sealed_a {
label = "invalid `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.
///
/// [`Entity`]: crate::entity::Entity
@ -291,37 +263,6 @@ pub trait EntityEvent: Event + sealed_b::SealedB {
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.
///
/// Buffered events can be written with [`EventWriter`] and read using the [`EventReader`] system parameter.

View File

@ -11,10 +11,7 @@ mod update;
mod writer;
pub(crate) use base::EventInstance;
pub use base::{
BroadcastEvent, BroadcastEventKind, BufferedEvent, EntityEvent, EntityEventKind, Event,
EventId, EventKey,
};
pub use base::{BroadcastEvent, BufferedEvent, EntityEvent, Event, EventId, EventKey};
pub use bevy_ecs_macros::{BroadcastEvent, BufferedEvent, EntityEvent};
#[expect(deprecated, reason = "`SendBatchIds` was renamed to `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,
/// 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 {
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.
///
/// 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 {
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.
@ -181,11 +185,13 @@ pub struct ObserverTrigger {
pub event_key: EventKey,
/// The [`ComponentId`]s the trigger targeted.
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`].
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,
/// 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_ecs::{
entity::{Entity, EntityHashMap},
event::{EntityEvent, EventCursor, Events},
event::{BroadcastEvent, EntityEvent, EventCursor, Events},
hierarchy::ChildOf,
reflect::AppTypeRegistry,
resource::Resource,
@ -34,6 +34,8 @@ pub struct SceneInstanceReady {
pub instance_id: InstanceId,
}
impl BroadcastEvent for SceneInstanceReady {}
/// Information about a scene instance.
#[derive(Debug)]
pub struct InstanceInfo {
@ -421,8 +423,7 @@ impl SceneSpawner {
.trigger_targets(SceneInstanceReady { instance_id }, parent);
} else {
// 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:
- 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:
@ -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.
- `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
Events without an entity target can be defined, by deriving the `BroadcastEvent` trait.