Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable Events<T> storage #2073

Closed
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::app_builder::AppBuilder;
use bevy_ecs::{
event::Event,
schedule::{Schedule, Stage},
world::World,
};
Expand Down Expand Up @@ -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;
4 changes: 2 additions & 2 deletions crates/bevy_app/src/app_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -292,7 +292,7 @@ impl AppBuilder {
/// and inserting a `Events::<T>::update_system` system into `CoreStage::First`.
pub fn add_event<T>(&mut self) -> &mut Self
where
T: Component,
T: Event,
{
self.insert_resource(Events::<T>::default())
.add_system_to_stage(CoreStage::First, Events::<T>::update_system.system())
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_asset/src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
};
use bevy_app::{AppBuilder, EventWriter, Events};
use bevy_ecs::{
event::Event,
system::{IntoSystem, ResMut},
world::FromWorld,
};
Expand All @@ -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<T: Asset> {
Created { handle: Handle<T> },
Modified { handle: Handle<T> },
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ecs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ 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"
27 changes: 27 additions & 0 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self>; #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<Self>>;
}
}
};
out.into()
}

fn bevy_ecs_path() -> syn::Path {
fn find_in_manifest(manifest: &mut Manifest, dependencies: Dependencies) -> Option<String> {
manifest.dependencies = dependencies;
Expand Down
104 changes: 81 additions & 23 deletions crates/bevy_ecs/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -45,7 +43,8 @@ impl<T> fmt::Debug for EventId<T> {
}

#[derive(Debug)]
struct EventInstance<T> {
#[doc(hidden)]
pub struct EventInstance<T> {
pub event_id: EventId<T>,
pub event: T,
}
Expand Down Expand Up @@ -79,6 +78,7 @@ enum State {
/// ```
/// use bevy_ecs::event::Events;
///
/// #[derive(Event)]
/// struct MyEvent {
/// value: usize
/// }
Expand Down Expand Up @@ -119,50 +119,104 @@ enum State {
///
/// [`AppBuilder::add_event`]: https://docs.rs/bevy/*/bevy/app/struct.AppBuilder.html#method.add_event
#[derive(Debug)]
pub struct Events<T> {
events_a: Vec<EventInstance<T>>,
events_b: Vec<EventInstance<T>>,
pub struct Events<T: Event> {
events_a: T::Storage,
events_b: T::Storage,
a_start_event_count: usize,
b_start_event_count: usize,
event_count: usize,
state: State,
}

impl<T> Default for Events<T> {
pub trait Event: Sized + Component {
type Storage: for<'a> Storage<'a, Item = EventInstance<Self>>
+ Component
+ std::ops::DerefMut<Target = [EventInstance<Self>]>
+ Default;
}

pub trait Storage<'a> {
type Item: 'a;
type DrainIter: DoubleEndedIterator<Item = Self::Item> + 'a;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth loosening this requirement from just DoubleEndedIterators?
If we're now allowing custom storage types for Events, we should maybe support more than just these types.
For example, if I don't want any duplicate events, I may want to make a Storage type which uses a HashSet internally, but hash_set::Iter does not implement DoubleEndedIter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This trait is a mostly internal trait, which isn't meant to be implemented. HashSet would also fail because it doesn't match the Deref<[_]> criteria.

fn push(&mut self, v: Self::Item);
fn drain<R>(&'a mut self, range: R) -> Self::DrainIter
where
R: std::ops::RangeBounds<usize>;
fn clear(&mut self);
}
impl<'a, T: 'a> Storage<'a> for Vec<T> {
type Item = T;
type DrainIter = std::vec::Drain<'a, T>;

fn push(&mut self, v: T) {
self.push(v);
}

fn drain<R>(&'a mut self, range: R) -> Self::DrainIter
where
R: std::ops::RangeBounds<usize>,
{
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<R>(&'a mut self, range: R) -> Self::DrainIter
where
R: std::ops::RangeBounds<usize>,
{
self.drain(range)
}
fn clear(&mut self) {
self.clear();
}
}

impl<T: Event> Default for Events<T> {
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<T>(event_instance: &EventInstance<T>) -> (&T, EventId<T>) {
fn map_instance_event_with_id<T: 'static>(event_instance: &EventInstance<T>) -> (&T, EventId<T>) {
(&event_instance.event, event_instance.event_id)
}

fn map_instance_event<T>(event_instance: &EventInstance<T>) -> &T {
fn map_instance_event<T: 'static>(event_instance: &EventInstance<T>) -> &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<T>)>,
events: Res<'a, Events<T>>,
}

/// Sends events of type `T`.
#[derive(SystemParam)]
pub struct EventWriter<'a, T: Component> {
pub struct EventWriter<'a, T: Event> {
events: ResMut<'a, Events<T>>,
}

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);
}
Expand All @@ -186,7 +240,7 @@ impl<T> Default for ManualEventReader<T> {
}
}

impl<T> ManualEventReader<T> {
impl<T: Event> ManualEventReader<T> {
/// See [`EventReader::iter`]
pub fn iter<'a>(&mut self, events: &'a Events<T>) -> impl DoubleEndedIterator<Item = &'a T> {
internal_event_reader(&mut self.last_event_count, events).map(|(e, _)| e)
Expand All @@ -203,7 +257,7 @@ impl<T> ManualEventReader<T> {

/// 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<T>,
) -> impl DoubleEndedIterator<Item = (&'a T, EventId<T>)> {
Expand Down Expand Up @@ -252,7 +306,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.
Expand All @@ -269,7 +323,7 @@ impl<'a, T: Component> EventReader<'a, T> {
}
}

impl<T: Component> Events<T> {
impl<T: Event> Events<T> {
/// "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) {
Expand Down Expand Up @@ -311,12 +365,12 @@ impl<T: Component> Events<T> {
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;
}
Expand Down Expand Up @@ -383,6 +437,10 @@ mod tests {
i: usize,
}

impl Event for TestEvent {
type Storage = Vec<EventInstance<Self>>;
}

#[test]
fn test_events() {
let mut events = Events::<TestEvent>::default();
Expand Down
7 changes: 6 additions & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -34,6 +34,11 @@ pub mod prelude {
};
}

#[doc(hidden)]
pub mod __macro_export {
pub use smallvec::SmallVec;
}

#[cfg(test)]
mod tests {
use crate::{
Expand Down
9 changes: 6 additions & 3 deletions crates/bevy_input/src/gamepad.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_input/src/keyboard.rs
Original file line number Diff line number Diff line change
@@ -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<KeyCode>,
Expand Down
11 changes: 7 additions & 4 deletions crates/bevy_input/src/mouse.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -20,7 +23,7 @@ pub enum MouseButton {
}

/// A mouse motion event
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Event)]
pub struct MouseMotion {
pub delta: Vec2,
}
Expand All @@ -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,
Expand Down
Loading