diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 4b9a71812f083..aab2b63e39459 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1,6 +1,7 @@ use crate::{CoreStage, Events, Plugin, PluginGroup, PluginGroupBuilder, StartupStage}; use bevy_ecs::{ component::{Component, ComponentDescriptor}, + event::Event, prelude::{FromWorld, IntoExclusiveSystem}, schedule::{ IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, SystemSet, SystemStage, @@ -332,7 +333,7 @@ impl App { /// and inserting a `Events::::update_system` system into `CoreStage::First`. pub fn add_event(&mut self) -> &mut Self where - T: Component, + T: Event, { self.insert_resource(Events::::default()) .add_system_to_stage(CoreStage::First, Events::::update_system) @@ -583,5 +584,5 @@ fn run_once(mut app: App) { } /// An event that indicates the app should exit. This will fully exit the app process. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct AppExit; diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index d444cdee2d159..512579e5a0499 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -2,13 +2,18 @@ use crate::{ update_asset_storage_system, Asset, AssetLoader, AssetServer, AssetStage, Handle, HandleId, RefChange, }; -use bevy_app::{App, EventWriter, Events}; -use bevy_ecs::{system::ResMut, world::FromWorld}; +use bevy_app::{App, Events}; +use bevy_ecs::{ + event::{Event, EventWriter}, + system::{IntoSystem, ResMut}, + world::FromWorld, +}; use bevy_utils::HashMap; use crossbeam_channel::Sender; use std::fmt::Debug; /// Events that happen on assets of type `T` +#[derive(Event)] pub enum AssetEvent { Created { handle: Handle }, Modified { handle: Handle }, diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 00010a15252e0..5e1cff04e250a 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -24,6 +24,8 @@ fixedbitset = "0.4" fxhash = "0.2" thiserror = "1.0" downcast-rs = "1.2" +parking_lot = "0.11" +smallvec = { version = "1.6", features = ["union", "const_generics"] } rand = "0.8" serde = "1" diff --git a/crates/bevy_ecs/examples/events.rs b/crates/bevy_ecs/examples/events.rs index a90510526f002..f5049082fe6df 100644 --- a/crates/bevy_ecs/examples/events.rs +++ b/crates/bevy_ecs/examples/events.rs @@ -41,7 +41,7 @@ enum EventSystem { } // This is our event that we will send and receive in systems -#[derive(Debug)] +#[derive(Event, Debug)] struct MyEvent { pub message: String, pub random_value: f32, diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 8371de3e4b160..123170ef607fc 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -468,6 +468,33 @@ fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 { } } +#[proc_macro_derive(Event, attributes(store))] +pub fn derive_event(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let ident = input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let ecs_path = bevy_ecs_path(); + let store_attr = input + .attrs + .into_iter() + .find(|v| *v.path.get_ident().as_ref().unwrap() == "store") + .map(|v| v.tokens); + let out = if let Some(num) = store_attr { + quote! { + impl #impl_generics #ecs_path::event::Event for #ident #ty_generics #where_clause { + type Storage = #ecs_path::__macro_export::SmallVec<[#ecs_path::event::EventInstance; #num]>; + } + } + } else { + quote! { + impl #impl_generics #ecs_path::event::Event for #ident #ty_generics #where_clause { + type Storage = ::std::vec::Vec<#ecs_path::event::EventInstance>; + } + } + }; + out.into() +} + fn bevy_ecs_path() -> syn::Path { BevyManifest::default().get_path("bevy_ecs") } diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index f088b27f1060a..59e040ee91229 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -3,12 +3,10 @@ use crate::{ component::Component, system::{Local, Res, ResMut, SystemParam}, }; +pub use bevy_ecs_macros::Event; use bevy_utils::tracing::trace; -use std::{ - fmt::{self}, - hash::Hash, - marker::PhantomData, -}; +use smallvec::SmallVec; +use std::{fmt, hash::Hash, marker::PhantomData}; /// An `EventId` uniquely identifies an event. /// @@ -45,7 +43,8 @@ impl fmt::Debug for EventId { } #[derive(Debug)] -struct EventInstance { +#[doc(hidden)] +pub struct EventInstance { pub event_id: EventId, pub event: T, } @@ -79,6 +78,7 @@ enum State { /// ``` /// use bevy_ecs::event::Events; /// +/// #[derive(Event)] /// struct MyEvent { /// value: usize /// } @@ -119,52 +119,107 @@ enum State { /// /// [`App::add_event`]: https://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event #[derive(Debug)] -pub struct Events { - events_a: Vec>, - events_b: Vec>, +pub struct Events { + events_a: T::Storage, + events_b: T::Storage, a_start_event_count: usize, b_start_event_count: usize, event_count: usize, state: State, } -impl Default for Events { +pub trait Event: Sized + Component { + type Storage: for<'a> Storage<'a, Item = EventInstance> + + Component + + std::ops::DerefMut]> + + Extend> + + Default; +} + +pub trait Storage<'a> { + type Item: 'a; + type DrainIter: DoubleEndedIterator + 'a; + fn push(&mut self, v: Self::Item); + fn drain(&'a mut self, range: R) -> Self::DrainIter + where + R: std::ops::RangeBounds; + fn clear(&mut self); +} +impl<'a, T: 'a> Storage<'a> for Vec { + type Item = T; + type DrainIter = std::vec::Drain<'a, T>; + + fn push(&mut self, v: T) { + self.push(v); + } + + fn drain(&'a mut self, range: R) -> Self::DrainIter + where + R: std::ops::RangeBounds, + { + self.drain(range) + } + + fn clear(&mut self) { + self.clear(); + } +} +impl<'a, T: 'a, const N: usize> Storage<'a> for SmallVec<[T; N]> { + type Item = T; + type DrainIter = smallvec::Drain<'a, [T; N]>; + + fn push(&mut self, v: T) { + self.push(v); + } + + fn drain(&'a mut self, range: R) -> Self::DrainIter + where + R: std::ops::RangeBounds, + { + self.drain(range) + } + fn clear(&mut self) { + self.clear(); + } +} + +impl Default for Events { fn default() -> Self { Events { a_start_event_count: 0, b_start_event_count: 0, event_count: 0, - events_a: Vec::new(), - events_b: Vec::new(), + events_a: T::Storage::default(), + events_b: T::Storage::default(), state: State::A, } } } -fn map_instance_event_with_id(event_instance: &EventInstance) -> (&T, EventId) { +fn map_instance_event_with_id(event_instance: &EventInstance) -> (&T, EventId) { (&event_instance.event, event_instance.event_id) } -fn map_instance_event(event_instance: &EventInstance) -> &T { +fn map_instance_event(event_instance: &EventInstance) -> &T { &event_instance.event } /// Reads events of type `T` in order and tracks which events have already been read. #[derive(SystemParam)] -pub struct EventReader<'w, 's, T: Component> { +pub struct EventReader<'w, 's, T: Event> { last_event_count: Local<'s, (usize, PhantomData)>, events: Res<'w, Events>, } /// Sends events of type `T`. #[derive(SystemParam)] -pub struct EventWriter<'w, 's, T: Component> { +pub struct EventWriter<'w, 's, T: Event> { events: ResMut<'w, Events>, #[system_param(ignore)] marker: PhantomData<&'s usize>, } -impl<'w, 's, T: Component> EventWriter<'w, 's, T> { +impl<'w, 's, T: Event> EventWriter<'w, 's, T> { pub fn send(&mut self, event: T) { self.events.send(event); } @@ -188,7 +243,7 @@ impl Default for ManualEventReader { } } -impl ManualEventReader { +impl ManualEventReader { /// See [`EventReader::iter`] pub fn iter<'a>(&mut self, events: &'a Events) -> impl DoubleEndedIterator { internal_event_reader(&mut self.last_event_count, events).map(|(e, _)| e) @@ -205,7 +260,7 @@ impl ManualEventReader { /// Like [`iter_with_id`](EventReader::iter_with_id) except not emitting any traces for read /// messages. -fn internal_event_reader<'a, T>( +fn internal_event_reader<'a, T: Event>( last_event_count: &mut usize, events: &'a Events, ) -> impl DoubleEndedIterator)> { @@ -254,7 +309,7 @@ fn internal_event_reader<'a, T>( } } -impl<'w, 's, T: Component> EventReader<'w, 's, T> { +impl<'w, 's, T: Event> EventReader<'w, 's, T> { /// 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. @@ -271,7 +326,7 @@ impl<'w, 's, T: Component> EventReader<'w, 's, T> { } } -impl Events { +impl Events { /// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read /// the event. pub fn send(&mut self, event: T) { @@ -313,12 +368,12 @@ impl Events { pub fn update(&mut self) { match self.state { State::A => { - self.events_b = Vec::new(); + self.events_b.clear(); self.state = State::B; self.b_start_event_count = self.event_count; } State::B => { - self.events_a = Vec::new(); + self.events_a.clear(); self.state = State::A; self.a_start_event_count = self.event_count; } @@ -383,7 +438,7 @@ impl Events { } } -impl std::iter::Extend for Events { +impl std::iter::Extend for Events { fn extend(&mut self, iter: I) where I: IntoIterator, @@ -421,6 +476,10 @@ mod tests { i: usize, } + impl Event for TestEvent { + type Storage = Vec>; + } + #[test] fn test_events() { let mut events = Events::::default(); @@ -519,7 +578,7 @@ mod tests { reader.iter(events).cloned().collect::>() } - #[derive(PartialEq, Eq, Debug)] + #[derive(Event, PartialEq, Eq, Debug)] struct E(usize); fn events_clear_and_read_impl(clear_func: impl FnOnce(&mut Events)) { diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index c51925b710569..4d23aeb498284 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -21,7 +21,7 @@ pub mod prelude { bundle::Bundle, change_detection::DetectChanges, entity::Entity, - event::{EventReader, EventWriter}, + event::{Event, EventReader, EventWriter}, query::{Added, ChangeTrackers, Changed, Or, QueryState, With, Without}, schedule::{ AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, @@ -36,6 +36,11 @@ pub mod prelude { }; } +#[doc(hidden)] +pub mod __macro_export { + pub use smallvec::SmallVec; +} + #[cfg(test)] mod tests { use crate as bevy_ecs; diff --git a/crates/bevy_input/src/gamepad.rs b/crates/bevy_input/src/gamepad.rs index 9ba8e018e27bc..d2390ae2b1ee0 100644 --- a/crates/bevy_input/src/gamepad.rs +++ b/crates/bevy_input/src/gamepad.rs @@ -1,6 +1,9 @@ use crate::{Axis, Input}; use bevy_app::{EventReader, EventWriter}; -use bevy_ecs::system::{Res, ResMut}; +use bevy_ecs::{ + event::Event, + system::{Res, ResMut}, +}; use bevy_utils::HashMap; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -16,11 +19,11 @@ pub enum GamepadEventType { AxisChanged(GamepadAxisType, f32), } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Event)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct GamepadEvent(pub Gamepad, pub GamepadEventType); -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Event)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub struct GamepadEventRaw(pub Gamepad, pub GamepadEventType); diff --git a/crates/bevy_input/src/keyboard.rs b/crates/bevy_input/src/keyboard.rs index 0d5bdb89f3903..02db2efc9eeb3 100644 --- a/crates/bevy_input/src/keyboard.rs +++ b/crates/bevy_input/src/keyboard.rs @@ -1,9 +1,9 @@ use crate::{ElementState, Input}; use bevy_app::EventReader; -use bevy_ecs::system::ResMut; +use bevy_ecs::{event::Event, system::ResMut}; /// A key input event from a keyboard device -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct KeyboardInput { pub scan_code: u32, pub key_code: Option, diff --git a/crates/bevy_input/src/mouse.rs b/crates/bevy_input/src/mouse.rs index 71899ca95ca63..da3c7b8a16ab0 100644 --- a/crates/bevy_input/src/mouse.rs +++ b/crates/bevy_input/src/mouse.rs @@ -1,9 +1,12 @@ use crate::{ElementState, Input}; -use bevy_ecs::{event::EventReader, system::ResMut}; +use bevy_ecs::{ + event::{Event, EventReader}, + system::ResMut, +}; use bevy_math::Vec2; /// A mouse button input event -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct MouseButtonInput { pub button: MouseButton, pub state: ElementState, @@ -20,7 +23,7 @@ pub enum MouseButton { } /// A mouse motion event -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct MouseMotion { pub delta: Vec2, } @@ -34,7 +37,7 @@ pub enum MouseScrollUnit { /// A mouse scroll wheel event, where x represents horizontal scroll and y represents vertical /// scroll. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct MouseWheel { pub unit: MouseScrollUnit, pub x: f32, diff --git a/crates/bevy_input/src/touch.rs b/crates/bevy_input/src/touch.rs index 9961a084eefe6..cd9830dd47754 100644 --- a/crates/bevy_input/src/touch.rs +++ b/crates/bevy_input/src/touch.rs @@ -1,4 +1,4 @@ -use bevy_app::EventReader; +use bevy_app::{Event, EventReader}; use bevy_ecs::system::ResMut; use bevy_math::Vec2; use bevy_utils::HashMap; @@ -19,7 +19,7 @@ use bevy_utils::HashMap; /// A `Cancelled` event is emitted when the system has canceled tracking this /// touch, such as when the window loses focus, or on iOS if the user moves the /// device against their face. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Event)] pub struct TouchInput { pub phase: TouchPhase, pub position: Vec2, diff --git a/crates/bevy_window/src/event.rs b/crates/bevy_window/src/event.rs index 9feb54c4ed66e..efc99eee48e09 100644 --- a/crates/bevy_window/src/event.rs +++ b/crates/bevy_window/src/event.rs @@ -1,10 +1,10 @@ -use std::path::PathBuf; - use super::{WindowDescriptor, WindowId}; +use bevy_ecs::event::Event; use bevy_math::{IVec2, Vec2}; +use std::path::PathBuf; /// A window event that is sent whenever a window has been resized. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct WindowResized { pub id: WindowId, pub width: f32, @@ -12,76 +12,76 @@ pub struct WindowResized { } /// An event that indicates that a new window should be created. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct CreateWindow { pub id: WindowId, pub descriptor: WindowDescriptor, } /// An event that indicates a window should be closed. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct CloseWindow { pub id: WindowId, } /// An event that is sent whenever a new window is created. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct WindowCreated { pub id: WindowId, } /// An event that is sent whenever a close was requested for a window. For example: when the "close" /// button is pressed on a window. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct WindowCloseRequested { pub id: WindowId, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct CursorMoved { pub id: WindowId, pub position: Vec2, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct CursorEntered { pub id: WindowId, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct CursorLeft { pub id: WindowId, } /// An event that is sent whenever a window receives a character from the OS or underlying system. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct ReceivedCharacter { pub id: WindowId, pub char: char, } /// An event that indicates a window has received or lost focus. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct WindowFocused { pub id: WindowId, pub focused: bool, } /// An event that indicates a window's scale factor has changed. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct WindowScaleFactorChanged { pub id: WindowId, pub scale_factor: f64, } /// An event that indicates a window's OS-reported scale factor has changed. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct WindowBackendScaleFactorChanged { pub id: WindowId, pub scale_factor: f64, } /// Events related to files being dragged and dropped on a window. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub enum FileDragAndDrop { DroppedFile { id: WindowId, path_buf: PathBuf }, @@ -91,7 +91,7 @@ pub enum FileDragAndDrop { } /// An event that is sent when a window is repositioned in physical pixels. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Event)] pub struct WindowMoved { pub id: WindowId, pub position: IVec2, diff --git a/examples/ecs/event.rs b/examples/ecs/event.rs index cc913f45f2bb7..163c499456938 100644 --- a/examples/ecs/event.rs +++ b/examples/ecs/event.rs @@ -12,6 +12,7 @@ fn main() { .run(); } +#[derive(Event)] struct MyEvent { pub message: String, }