diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index ae1427928c..f5d3a306a2 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -30,8 +30,9 @@ 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 {} - impl #impl_generics #bevy_ecs_path::event::BroadcastEvent 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; + } }) } @@ -74,10 +75,8 @@ 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 {} - impl #impl_generics #bevy_ecs_path::event::EntityEvent for #struct_name #type_generics #where_clause { - type Traversal = #traversal; - const AUTO_PROPAGATE: bool = #auto_propagate; + impl #impl_generics #bevy_ecs_path::event::Event for #struct_name #type_generics #where_clause { + type Kind = #bevy_ecs_path::event::EntityEventKind; } }) } diff --git a/crates/bevy_ecs/src/event/base.rs b/crates/bevy_ecs/src/event/base.rs index 06de097f26..5089ae3511 100644 --- a/crates/bevy_ecs/src/event/base.rs +++ b/crates/bevy_ecs/src/event/base.rs @@ -11,7 +11,7 @@ use core::{ marker::PhantomData, }; -// TODO: Adjust these docs +// TODO: These docs need to be moved around and adjusted /// Something that "happens" and can be processed by app logic. /// /// Events can be triggered on a [`World`] using a method like [`trigger`](World::trigger), @@ -36,7 +36,7 @@ use core::{ /// ``` /// # use bevy_ecs::prelude::*; /// # -/// #[derive(Event)] +/// #[derive(BroadcastEvent)] /// struct Speak { /// message: String, /// } @@ -47,7 +47,7 @@ use core::{ /// ``` /// # use bevy_ecs::prelude::*; /// # -/// # #[derive(Event)] +/// # #[derive(BroadcastEvent)] /// # struct Speak { /// # message: String, /// # } @@ -64,7 +64,7 @@ use core::{ /// ``` /// # use bevy_ecs::prelude::*; /// # -/// # #[derive(Event)] +/// # #[derive(BroadcastEvent)] /// # struct Speak { /// # message: String, /// # } @@ -90,11 +90,14 @@ use core::{ /// [`EventReader`]: super::EventReader /// [`EventWriter`]: super::EventWriter #[diagnostic::on_unimplemented( - message = "`{Self}` is not an `ObserverEvent`", - label = "invalid `ObserverEvent`", + message = "`{Self}` is not an `Event`", + label = "invalid `Event`", note = "consider annotating `{Self}` with `#[derive(BroadcastEvent)]` or `#[derive(EntityEvent)]`" )] pub trait Event: Send + Sync + 'static { + /// + type Kind; + /// Generates the [`EventKey`] for this event type. /// /// If this type has already been registered, @@ -130,7 +133,32 @@ pub trait Event: Send + Sync + 'static { } /// A global [`Event`] without an entity target. -pub trait BroadcastEvent: Event {} +#[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 BroadcastEvent for T where T: Event {} + +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 SealedA for T where T: Event {} +} /// An [`Event`] that can be targeted at specific entities. /// @@ -248,7 +276,7 @@ pub trait BroadcastEvent: Event {} label = "invalid `EntityEvent`", note = "consider annotating `{Self}` with `#[derive(EntityEvent)]`" )] -pub trait EntityEvent: Event { +pub trait EntityEvent: Event + sealed_b::SealedB { /// The component that describes which [`Entity`] to propagate this event to next, when [propagation] is enabled. /// /// [`Entity`]: crate::entity::Entity @@ -263,6 +291,37 @@ pub trait EntityEvent: Event { const AUTO_PROPAGATE: bool = false; } +#[doc(hidden)] +pub struct EntityEventKind, const A: bool> { + _t: PhantomData, + _r: PhantomData, +} + +// Blanket impl for EntityEvent +#[diagnostic::do_not_recommend] +impl, const A: bool> EntityEvent for T +where + T: Event>, +{ + 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, const A: bool> SealedB for T where T: Event> + {} +} + /// A buffered event for pull-based event handling. /// /// Buffered events can be written with [`EventWriter`] and read using the [`EventReader`] system parameter. diff --git a/crates/bevy_ecs/src/event/mod.rs b/crates/bevy_ecs/src/event/mod.rs index 3a5f1de8d6..6acde7003d 100644 --- a/crates/bevy_ecs/src/event/mod.rs +++ b/crates/bevy_ecs/src/event/mod.rs @@ -11,7 +11,10 @@ mod update; mod writer; pub(crate) use base::EventInstance; -pub use base::{BroadcastEvent, BufferedEvent, EntityEvent, Event, EventId, EventKey}; +pub use base::{ + BroadcastEvent, BroadcastEventKind, BufferedEvent, EntityEvent, EntityEventKind, 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};