From 738791e1f8c16fd0aa2e61db79002850561db76f Mon Sep 17 00:00:00 2001 From: TheRawMeatball Date: Sat, 1 May 2021 23:17:07 +0300 Subject: [PATCH 1/4] Configurable Events storage --- crates/bevy_app/src/app.rs | 3 +- crates/bevy_app/src/app_builder.rs | 4 +- crates/bevy_asset/src/assets.rs | 2 + crates/bevy_ecs/Cargo.toml | 1 + crates/bevy_ecs/macros/src/lib.rs | 27 ++++++++ crates/bevy_ecs/src/event.rs | 103 ++++++++++++++++++++++------- crates/bevy_ecs/src/lib.rs | 7 +- crates/bevy_input/src/gamepad.rs | 9 ++- crates/bevy_input/src/keyboard.rs | 4 +- crates/bevy_input/src/mouse.rs | 11 +-- crates/bevy_input/src/touch.rs | 4 +- crates/bevy_window/src/event.rs | 32 ++++----- examples/ecs/event.rs | 2 + 13 files changed, 155 insertions(+), 54 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index d30303a4ac9bd..836a6baf7625a 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1,5 +1,6 @@ use crate::app_builder::AppBuilder; use bevy_ecs::{ + event::Event, schedule::{Schedule, Stage}, world::World, }; @@ -74,5 +75,5 @@ impl 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_app/src/app_builder.rs b/crates/bevy_app/src/app_builder.rs index c32f380607d2b..4c5a302dead23 100644 --- a/crates/bevy_app/src/app_builder.rs +++ b/crates/bevy_app/src/app_builder.rs @@ -5,7 +5,7 @@ use crate::{ }; use bevy_ecs::{ component::{Component, ComponentDescriptor}, - event::Events, + event::{Event, Events}, schedule::{ RunOnce, Schedule, Stage, StageLabel, State, SystemDescriptor, SystemSet, SystemStage, }, @@ -230,7 +230,7 @@ impl AppBuilder { /// 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.system()) diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index dfc14417f91ce..243ef37edc5e7 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -4,6 +4,7 @@ use crate::{ }; use bevy_app::{AppBuilder, EventWriter, Events}; use bevy_ecs::{ + event::Event, system::{IntoSystem, ResMut}, world::FromWorld, }; @@ -12,6 +13,7 @@ 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 ff86bb109c1b4..eac9e35785373 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -30,5 +30,6 @@ fxhash = "0.2" thiserror = "1.0" downcast-rs = "1.2" parking_lot = "0.11" +smallvec = { version = "1.4.2", features = ["const_generics"] } rand = "0.8" serde = "1" diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index b8ed1d882bbfb..bc6b6a91e1856 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -470,6 +470,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 { fn find_in_manifest(manifest: &mut Manifest, dependencies: Dependencies) -> Option { manifest.dependencies = dependencies; diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 889d9d22e8b4c..1aa37fe514705 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, } @@ -119,50 +118,104 @@ enum State { /// /// [`AppBuilder::add_event`]: https://docs.rs/bevy/*/bevy/app/struct.AppBuilder.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]> + + 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<'a, T: Component> { +pub struct EventReader<'a, T: Event> { last_event_count: Local<'a, (usize, PhantomData)>, events: Res<'a, Events>, } /// Sends events of type `T`. #[derive(SystemParam)] -pub struct EventWriter<'a, T: Component> { +pub struct EventWriter<'a, T: Event> { events: ResMut<'a, Events>, } -impl<'a, T: Component> EventWriter<'a, T> { +impl<'a, T: Event> EventWriter<'a, T> { pub fn send(&mut self, event: T) { self.events.send(event); } @@ -186,7 +239,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) @@ -203,7 +256,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)> { @@ -252,7 +305,7 @@ fn internal_event_reader<'a, T>( } } -impl<'a, T: Component> EventReader<'a, T> { +impl<'a, T: Event> EventReader<'a, 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. @@ -269,7 +322,7 @@ impl<'a, T: Component> EventReader<'a, 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) { @@ -311,12 +364,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,6 +436,10 @@ mod tests { i: usize, } + impl Event for TestEvent { + type Storage = Vec>; + } + #[test] fn test_events() { let mut events = Events::::default(); diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index f45c6790a5780..1fd0dc4afffdf 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -19,7 +19,7 @@ pub mod prelude { pub use crate::{ bundle::Bundle, entity::Entity, - event::{EventReader, EventWriter}, + event::{Event, EventReader, EventWriter}, query::{Added, ChangeTrackers, Changed, Or, QueryState, With, WithBundle, Without}, schedule::{ AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, @@ -34,6 +34,11 @@ pub mod prelude { }; } +#[doc(hidden)] +pub mod __macro_export { + pub use smallvec::SmallVec; +} + #[cfg(test)] mod tests { use crate::{ 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 0ae4d028c0ba9..64162050776cd 100644 --- a/examples/ecs/event.rs +++ b/examples/ecs/event.rs @@ -12,6 +12,8 @@ fn main() { .run(); } +#[derive(Event)] +#[store(3)] struct MyEvent { pub message: String, } From 357e9687c3092a7e5daede2c4789725478d2134d Mon Sep 17 00:00:00 2001 From: TheRawMeatball Date: Sat, 1 May 2021 23:57:53 +0300 Subject: [PATCH 2/4] Fix missing derive in doc comment --- crates/bevy_ecs/src/event.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 1aa37fe514705..d38aa95857b9c 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -78,6 +78,7 @@ enum State { /// ``` /// use bevy_ecs::event::Events; /// +/// #[derive(Event)] /// struct MyEvent { /// value: usize /// } From 9e1b925301c93b823f67b4b91fa4d80f30f7bcca Mon Sep 17 00:00:00 2001 From: TheRawMeatball Date: Thu, 6 May 2021 11:19:46 +0300 Subject: [PATCH 3/4] Update smallvec dep --- crates/bevy_ecs/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index eac9e35785373..647f9b07341b2 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -30,6 +30,6 @@ fxhash = "0.2" thiserror = "1.0" downcast-rs = "1.2" parking_lot = "0.11" -smallvec = { version = "1.4.2", features = ["const_generics"] } +smallvec = { version = "1.6", features = ["union", "const_generics"] } rand = "0.8" serde = "1" From b7a645d94250ca5c202a48341ef89b72e289203e Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 1 Sep 2021 13:33:52 +0300 Subject: [PATCH 4/4] tiny fixes --- crates/bevy_ecs/examples/events.rs | 2 +- examples/ecs/event.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) 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/examples/ecs/event.rs b/examples/ecs/event.rs index e4e33ff4380da..163c499456938 100644 --- a/examples/ecs/event.rs +++ b/examples/ecs/event.rs @@ -13,7 +13,6 @@ fn main() { } #[derive(Event)] -#[store(3)] struct MyEvent { pub message: String, }