use crate::Scene;
use crate::context::{GameContext, Res, ResMut, Resource};
use crate::nodes::Node;
use crate::platform::SendSync;
use crate::scene::{NodeHandle, NodeId};
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
pub trait EventLabel: Any {}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct Ready;
impl EventLabel for Ready {}
#[derive(Clone, Copy, Debug)]
pub struct Update {
pub dt: f32,
}
impl EventLabel for Update {}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct FixedUpdate;
impl EventLabel for FixedUpdate {}
pub struct EventCtx<'a, E, N: Node> {
pub node: NodeHandle<'a, N>,
pub game: &'a GameContext,
pub event: &'a E,
}
impl<'a, E, N: Node> EventCtx<'a, E, N> {
pub fn get_resource<T: Resource>(&self) -> Res<'a, T> {
self.game.get_resource()
}
pub fn get_resource_mut<T: Resource>(&self) -> ResMut<'a, T> {
self.game.get_resource_mut()
}
pub fn scene(&self) -> &Scene {
&self.game.scene
}
}
#[cfg(not(target_arch = "wasm32"))]
type ErasedEventCallback = Box<dyn FnMut(&Scene, NodeId, &GameContext, &dyn Any) + Send + Sync>;
#[cfg(target_arch = "wasm32")]
type ErasedEventCallback = Box<dyn FnMut(&Scene, NodeId, &GameContext, &dyn Any)>;
#[derive(Default)]
pub struct EventReceiver {
callbacks: HashMap<TypeId, Vec<Arc<Mutex<ErasedEventCallback>>>>,
}
impl Clone for EventReceiver {
fn clone(&self) -> Self {
let callbacks = self
.callbacks
.iter()
.map(|(id, cbs)| (*id, cbs.iter().map(Arc::clone).collect()))
.collect();
Self { callbacks }
}
}
impl EventReceiver {
pub fn new() -> Self {
Self {
callbacks: HashMap::new(),
}
}
pub fn on<E, N, F>(&mut self, mut f: F)
where
E: EventLabel + 'static,
N: Node + 'static,
F: for<'a> FnMut(EventCtx<'a, E, N>) + SendSync + 'static,
{
let event_id = TypeId::of::<E>();
let callback: ErasedEventCallback = Box::new(
move |scene, node_id, game: &GameContext, event_data: &dyn Any| {
let event = match event_data.downcast_ref::<E>() {
Some(e) => e,
None => return,
};
let Some(handle) = scene.get::<N>(node_id) else {
return;
};
let ctx = EventCtx {
node: handle,
game,
event,
};
f(ctx);
},
);
self.callbacks
.entry(event_id)
.or_default()
.push(Arc::new(Mutex::new(callback)));
}
pub fn trigger<E: EventLabel>(
&self,
event: &E,
scene: &Scene,
node_id: NodeId,
game: &GameContext,
) {
let event_id = TypeId::of::<E>();
if let Some(callbacks) = self.callbacks.get(&event_id) {
for callback in callbacks {
if let Ok(mut callback) = callback.lock() {
callback(scene, node_id, game, event as &dyn Any);
}
}
}
}
}