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

Added multiple callback logic to event_listener #22

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
105 changes: 105 additions & 0 deletions examples/multiple_listeners.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use bevy::{input::{keyboard::KeyboardInput, mouse::{MouseButton, MouseButtonInput}}, prelude::*};
use bevy_eventlistener::prelude::*;

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(EventListenerPlugin::<MyEvent<1>>::default())
.add_systems(Startup, setup)
.add_systems(Update, keyboard_events)
.insert_resource(TargetEntity(Entity::PLACEHOLDER))
.run();
}

#[derive(Clone, Event, EntityEvent)]
#[can_bubble]
struct MyEvent<const N: usize> {
target: Entity,
}

#[derive(Resource)]
struct TargetEntity(Entity);

fn setup(mut commands: Commands, mut target_entity: ResMut<TargetEntity>, my_query: Query<On>) {

// create empty listener
let listener = commands.spawn((Name::new("Foo"), On::<MyEvent<1>>::default()));

// Option 2 is to create a listener with callbacks ahead of time, and insert that
// Create an empty listener
let mut bar_listener = On::<MyEvent<1>>::default();
// Add callbacks to it via `Commands`, and return the id of the callback you just added
let callback_1 = commands.add_callback(bar_listener, || info!("hello from a callback 1"));
// Repeat as many times as you'd like
let callback_2 = commands.add_callback(bar_listener, || info!("hello from a callback 2"));
// Add the listener, which now has two callbacks, to a new entity:
commands.spawn((Name::new("Bar"), bar_listener));

// Option 3 is to add to an existing listener which you got from a query or similar
// Create an empty listener
let mut baz_listener = my_query.single_mut();
// Same API as above: we have mutable access to the listener, so we can add callbacks to it
let callback_3 = commands.add_callback(baz_listener, || info!("hello from a callback 3"));

// Note that commands.add_callback() could be provided in a few ways:
// same as above: Commands::add_callback(&mut self, listener: &mut On<E>) -> ListenerId
// on the listener: On::<E>::add_callback(&mut self, &mut World) -> ListenerId

// Because `add_callback` returns the id, you can then use it to remove or replace callbacks
let callback_id = commands.add_callback(listener, || info!("hello world"));
commands.remove_callback(listener, callback_id); // also despawns the callback entity
commands.replace_callback(listener, callback_id, || info!("a new callback"));

}

fn some_simple_system(time: Res<Time>) {
info!("Hello from a bevy system! {:?} has elapsed", time.elapsed());
}

#[allow(clippy::too_many_arguments)]
fn keyboard_events(
target: Res<TargetEntity>,
mut inputs: EventReader<KeyboardInput>,
mut ev1: EventWriter<MyEvent<1>>,
) {
let target = target.0;
for input in inputs
.read()
.filter(|input| !input.state.is_pressed())
.map(|input| input.key_code)
{ev1.send(MyEvent {
target
});}
}

/*
fn setup(mut commands: Commands, mut listener: ResMut<Click>, ) {
// In the simplest case, you can add a listener with no callbacks in a bundle
commands.spawn((Name::new("Foo"), On::<Click>::default()));

// Option 2 is to create a listener with callbacks ahead of time, and insert that
// Create an empty listener
let mut bar_listener = On::<Click>::default();
// Add callbacks to it via `Commands`, and return the id of the callback you just added
let callback_1 = commands.add_callback(bar_listener, || info!("hello from a callback 1"));
// Repeat as many times as you'd like
let callback_2 = commands.add_callback(bar_listener, || info!("hello from a callback 2"));
// Add the listener, which now has two callbacks, to a new entity:
commands.spawn((Name::new("Bar"), bar_listener));

// Option 3 is to add to an existing listener which you got from a query or similar
// Create an empty listener
let mut baz_listener = my_query.single_mut();
// Same API as above: we have mutable access to the listener, so we can add callbacks to it
let callback_3 = commands.add_callback(baz_listener, || info!("hello from a callback 3"));

// Note that commands.add_callback() could be provided in a few ways:
// same as above: Commands::add_callback(&mut self, listener: &mut On<E>) -> ListenerId
// on the listener: On::<E>::add_callback(&mut self, &mut World) -> ListenerId

// Because `add_callback` returns the id, you can then use it to remove or replace callbacks
let callback_id = commands.add_callback(listener, || info!("hello world"));
commands.remove_callback(listener, callback_id); // also despawns the callback entity
commands.replace_callback(listener, callback_id, || info!("a new callback"));
}
*/
115 changes: 83 additions & 32 deletions src/event_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//! [`bevy_eventlistener`](crate).

use std::marker::PhantomData;
use std::sync::atomic::{AtomicUsize, Ordering};

use crate::callbacks::{CallbackSystem, ListenerInput};
use bevy_ecs::{
Expand All @@ -22,6 +23,29 @@ pub trait EntityEvent: Event + Clone {
}
}

/// `ListenerHandle` is a struct that generates unique identifiers for event listeners.

Choose a reason for hiding this comment

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

ListenerHandle no longer exists :)

/// It uses an atomic counter to ensure that each ID is unique and that IDs can be generated safely in a multi-threaded context.
#[derive(Default)]
pub struct ListenerId {
/// An atomic counter used to generate unique IDs for event listeners.
id_counter: AtomicUsize,
}

impl ListenerId {
/// Creates a new `ListenerHandle` with its internal counter set to 0.
pub fn new() -> Self {
Self {
id_counter: AtomicUsize::new(0),
}
}

/// Generates a new unique ID by incrementing the internal counter and returning its value.
/// The `fetch_add` method ensures that this operation is atomic, so it's safe to call in a multi-threaded context.
pub fn next_id(&self) -> usize {
self.id_counter.fetch_add(1, Ordering::SeqCst)
}
}

/// An event listener with a callback that is triggered when an [`EntityEvent`] bubbles past or
/// targets this entity.
///
Expand All @@ -32,8 +56,10 @@ pub trait EntityEvent: Event + Clone {
#[derive(Component, Default)]
pub struct On<E: EntityEvent> {
phantom: PhantomData<E>,
/// A function that is called when the event listener is triggered.
pub(crate) callback: CallbackSystem,
/// store an atomic counter for generating unique IDs for event listeners
listener_id: ListenerId,
/// use tuple as we want to keep the order of the callbacks with hashmap pattern
pub(crate) callbacks: Vec<(usize, CallbackSystem)>,
}

impl<E: EntityEvent> On<E> {
Expand All @@ -42,26 +68,48 @@ impl<E: EntityEvent> On<E> {
/// systems is that the callback system can access a resource with event data,
/// [`ListenerInput`]. You can more easily access this with the system params
/// [`Listener`](crate::callbacks::Listener) and [`ListenerMut`](crate::callbacks::ListenerMut).
pub fn run<Marker>(callback: impl IntoSystem<(), (), Marker>) -> Self {
pub fn run<Marker>(&mut self, callback: impl IntoSystem<(), (), Marker>) -> usize {
let id = self.listener_id.next_id(); // creates new id and ties to callback, returns id
self.callbacks.push((id, CallbackSystem::New(Box::new(IntoSystem::into_system(callback)))));
id
}

/// constructor for empty event listener
pub fn new() -> Self {
Self {
phantom: PhantomData,
callback: CallbackSystem::New(Box::new(IntoSystem::into_system(callback))),
// initializes a new listenerId for each EntityEvent, assumes that each EntityEvent will have its own listenerId
listener_id: ListenerId::new(),
callbacks: Vec::new(),
}
}

/// Remove a callback from this event listener.
pub fn remove(&mut self, handle: usize) {
self.callbacks.retain(|(h, _)| *h != handle);
}

/// Replace a callback with a new one. This is useful for hot-reloading systems.
pub fn replace<Marker>(&mut self, id: usize, callback: impl IntoSystem<(), (), Marker>) {
if let Some((_, cb)) = self.callbacks.iter_mut().find(|(h, _)| *h == id) {
*cb = CallbackSystem::New(Box::new(IntoSystem::into_system(callback)));
}
}

/// Add a single [`Command`] any time this event listener is triggered. The command must
/// implement `From<E>`.
pub fn add_command<C: From<ListenerInput<E>> + Command + Send + Sync + 'static>() -> Self {
Self::run(|event: Res<ListenerInput<E>>, mut commands: Commands| {
pub fn add_command<C: From<ListenerInput<E>> + Command + Send + Sync + 'static>(&mut self) -> usize {
self.run(|event: Res<ListenerInput<E>>, mut commands: Commands| {
commands.add(C::from(event.to_owned()));
})
}

/// Get mutable access to [`Commands`] any time this event listener is triggered.
pub fn commands_mut(
&mut self,
mut func: impl 'static + Send + Sync + FnMut(&mut ListenerInput<E>, &mut Commands),
) -> Self {
Self::run(
) -> usize {
self.run(
move |mut event: ResMut<ListenerInput<E>>, mut commands: Commands| {
func(&mut event, &mut commands);
},
Expand All @@ -71,9 +119,10 @@ impl<E: EntityEvent> On<E> {
/// Get mutable access to the target entity's [`EntityCommands`] using a closure any time this
/// event listener is triggered.
pub fn target_commands_mut(
&mut self,
mut func: impl 'static + Send + Sync + FnMut(&mut ListenerInput<E>, &mut EntityCommands),
) -> Self {
Self::run(
) -> usize {
self.run(
move |mut event: ResMut<ListenerInput<E>>, mut commands: Commands| {
let target = event.target();
func(&mut event, &mut commands.entity(target));
Expand All @@ -82,8 +131,8 @@ impl<E: EntityEvent> On<E> {
}

/// Insert a bundle on the target entity any time this event listener is triggered.
pub fn target_insert(bundle: impl Bundle + Clone) -> Self {
Self::run(
pub fn target_insert(&mut self, bundle: impl Bundle + Clone) -> usize {
self.run(
move |event: Res<ListenerInput<E>>, mut commands: Commands| {
let bundle = bundle.clone();
commands.entity(event.target()).insert(bundle);
Expand All @@ -92,18 +141,19 @@ impl<E: EntityEvent> On<E> {
}

/// Remove a bundle from the target entity any time this event listener is triggered.
pub fn target_remove<B: Bundle>() -> Self {
Self::run(|event: Res<ListenerInput<E>>, mut commands: Commands| {
pub fn target_remove<B: Bundle>(&mut self) -> usize {
self.run(|event: Res<ListenerInput<E>>, mut commands: Commands| {
commands.entity(event.target()).remove::<B>();
})
}

/// Get mutable access to a specific component on the target entity using a closure any time
/// this event listener is triggered. If the component does not exist, an error will be logged.
pub fn target_component_mut<C: Component>(
&mut self,
mut func: impl 'static + Send + Sync + FnMut(&mut ListenerInput<E>, &mut C),
) -> Self {
Self::run(
) -> usize {
self.run(
move |mut event: ResMut<ListenerInput<E>>, mut query: Query<&mut C>| {
if let Ok(mut component) = query.get_mut(event.target()) {
func(&mut event, &mut component);
Expand All @@ -123,9 +173,10 @@ impl<E: EntityEvent> On<E> {
/// Get mutable access to the listener entity's [`EntityCommands`] using a closure any time this
/// event listener is triggered.
pub fn listener_commands_mut(
&mut self,
mut func: impl 'static + Send + Sync + FnMut(&mut ListenerInput<E>, &mut EntityCommands),
) -> Self {
Self::run(
) -> usize {
self.run(
move |mut event: ResMut<ListenerInput<E>>, mut commands: Commands| {
let listener = event.listener();
func(&mut event, &mut commands.entity(listener));
Expand All @@ -134,8 +185,8 @@ impl<E: EntityEvent> On<E> {
}

/// Insert a bundle on the listener entity any time this event listener is triggered.
pub fn listener_insert(bundle: impl Bundle + Clone) -> Self {
Self::run(
pub fn listener_insert(&mut self, bundle: impl Bundle + Clone) -> usize {
self.run(
move |event: Res<ListenerInput<E>>, mut commands: Commands| {
let bundle = bundle.clone();
commands.entity(event.listener()).insert(bundle);
Expand All @@ -144,18 +195,19 @@ impl<E: EntityEvent> On<E> {
}

/// Remove a bundle from the listener entity any time this event listener is triggered.
pub fn listener_remove<B: Bundle>() -> Self {
Self::run(|event: Res<ListenerInput<E>>, mut commands: Commands| {
pub fn listener_remove<B: Bundle>(&mut self) -> usize {
self.run(|event: Res<ListenerInput<E>>, mut commands: Commands| {
commands.entity(event.listener()).remove::<B>();
})
}

/// Get mutable access to a specific component on the listener entity using a closure any time
/// this event listener is triggered. If the component does not exist, an error will be logged.
pub fn listener_component_mut<C: Component>(
&mut self,
mut func: impl 'static + Send + Sync + FnMut(&mut ListenerInput<E>, &mut C),
) -> Self {
Self::run(
) -> usize {
self.run(
move |mut event: ResMut<ListenerInput<E>>, mut query: Query<&mut C>| {
if let Ok(mut component) = query.get_mut(event.listener()) {
func(&mut event, &mut component);
Expand All @@ -172,19 +224,18 @@ impl<E: EntityEvent> On<E> {
)
}

/// Send an event `F` any time this event listener is triggered.
pub fn send_event<F: Event + From<ListenerInput<E>>>() -> Self {
Self::run(
/// Send an event `F` any time this event listener is triggered.
pub fn send_event<F: Event + From<ListenerInput<E>>>(&mut self) -> usize {
self.run(
move |event: Res<ListenerInput<E>>, mut ev: EventWriter<F>| {
ev.send(F::from(event.to_owned()));
},
)
}

/// Take the boxed system callback out of this listener, leaving an empty one behind.
pub(crate) fn take(&mut self) -> CallbackSystem {
let mut temp = CallbackSystem::Empty;
std::mem::swap(&mut self.callback, &mut temp);
temp
/// Remove all callbacks from this event listener and return them.
pub(crate) fn take(&mut self) -> Vec<(usize, CallbackSystem)> {
std::mem::take(&mut self.callbacks)
}

}