Skip to content

Granular event privacy #5356

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
79 changes: 75 additions & 4 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{CoreStage, Plugin, PluginGroup, PluginGroupBuilder, StartupSchedule, StartupStage};
pub use bevy_derive::AppLabel;
use bevy_ecs::{
event::{Event, Events},
event::{self, Event, Events},
prelude::{FromWorld, IntoExclusiveSystem},
schedule::{
IntoSystemDescriptor, Schedule, ShouldRun, Stage, StageLabel, State, StateData, SystemSet,
Expand Down Expand Up @@ -620,18 +620,89 @@ impl App {
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Event)]
/// # struct MyEvent;
/// # let mut app = App::new();
/// #
/// app.add_event::<MyEvent>();
/// ```
pub fn add_event<T>(&mut self) -> &mut Self
where
T: Event,
T: Event + event::Read + event::Write,
{
self.add_event_with_vis::<T, _, _>()
}

/// Setup the application to manage read-only events of type `T`.
/// See [`add_event`](#method.add_event) for more info.
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// pub struct MyEvent;
///
/// // Anyone can read `MyEvent`
/// impl bevy_ecs::event::Read for MyEvent {}
///
/// // You can only write `MyEvent` if you have access `PrivateKey`
/// struct PrivateKey;
/// impl bevy_ecs::event::Write<PrivateKey> for MyEvent {}
///
/// let mut app = App::new();
/// app.add_read_only_event::<MyEvent, PrivateKey>();
/// // this also works:
/// app.add_read_only_event::<MyEvent, _>();
/// ```
pub fn add_read_only_event<T, Vis: 'static>(&mut self) -> &mut Self
where
T: Event + event::Read + event::Write<Vis>,
{
self.add_event_with_vis::<T, _, _>()
}

/// Setup the application to manage write-only events of type `T`.
/// See [`add_event`](#method.add_event) for more info.
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// pub struct MyEvent;
///
/// // Anyone can write `MyEvent`
/// impl bevy_ecs::event::Write for MyEvent {}
///
/// // You can only read `MyEvent` if you have access `PrivateKey`
/// struct PrivateKey;
/// impl bevy_ecs::event::Read<PrivateKey> for MyEvent {}
///
/// let mut app = App::new();
/// app.add_write_only_event::<MyEvent, PrivateKey>();
/// // this also works:
/// app.add_write_only_event::<MyEvent, _>();
/// ```
pub fn add_write_only_event<T, Vis: 'static>(&mut self) -> &mut Self
where
T: Event + event::Read<Vis> + event::Write,
{
self.add_event_with_vis::<T, _, _>()
}

/// Like [`add_event`](#method.add_event), but supports events with restricted privacy.
/// When not in a generic context, consider [`add_read_only_event`](#method.add_read_only_event)
/// or [`add_write_only_event`](#method.add_write_only_event).
pub fn add_event_with_vis<T, VisA: 'static, VisB: 'static>(&mut self) -> &mut Self
where
T: Event + event::Read<VisA> + event::Write<VisB>,
{
if !self.world.contains_resource::<Events<T>>() {
self.init_resource::<Events<T>>()
.add_system_to_stage(CoreStage::First, Events::<T>::update_system);
.add_system_to_stage(CoreStage::First, Events::<T>::update_system::<VisA, VisB>);
}
self
}
Expand Down Expand Up @@ -987,5 +1058,5 @@ fn run_once(mut app: App) {
/// You can also use this event to detect that an exit was requested. In order to receive it, systems
/// subscribing to this event should run after it was emitted and before the schedule of the same
/// frame is over.
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, Event)]
pub struct AppExit;
3 changes: 2 additions & 1 deletion crates/bevy_asset/src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
};
use bevy_app::App;
use bevy_ecs::{
event::{EventWriter, Events},
event::{Event, EventWriter, Events},
system::ResMut,
world::FromWorld,
};
Expand All @@ -16,6 +16,7 @@ use std::fmt::Debug;
///
/// Events sent via the [`Assets`] struct will always be sent with a _Weak_ handle, because the
/// asset may not exist by the time the event is handled.
#[derive(Event)]
pub enum AssetEvent<T: Asset> {
#[allow(missing_docs)]
Created { handle: Handle<T> },
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ Events offer a communication channel between one or more systems. Events can be
```rust
use bevy_ecs::prelude::*;

#[derive(Event)]
struct MyEvent {
message: String,
}
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ecs/examples/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ fn main() {
}

// This is our event that we will send and receive in systems
#[derive(Event)]
struct MyEvent {
pub message: String,
pub random_value: f32,
Expand Down
19 changes: 19 additions & 0 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,25 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
})
}

#[proc_macro_derive(Event, attributes(event))]
pub fn derive_event(input: TokenStream) -> TokenStream {
let path = bevy_ecs_path();

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 where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
where_token: Default::default(),
predicates: Default::default(),
});

quote! {
impl #impl_generics #path::event::Read for #ident #ty_generics #where_clause { }
impl #impl_generics #path::event::Write for #ident #ty_generics #where_clause { }
}
.into()
}

fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<Ident> {
(0..count)
.map(|i| Ident::new(&fmt_string(i), Span::call_site()))
Expand Down
Loading