maple_engine 0.3.0

Engine implementation of maple engine
Documentation
//! the EventReceiver handles systems that are ran on different schedules.
//!
//! for example the you may want move an object if the player presses a key you can define a
//! callback on the Event::Update that checks if that key is pressed then executes a callback the
//! offsets the position.

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 {
    /// Create a new event receiver
    pub fn new() -> Self {
        Self {
            callbacks: HashMap::new(),
        }
    }

    /// Register a callback for event `E` on node type `N`
    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| {
                // Downcast event
                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)));
    }

    /// Trigger an event for a specific node
    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);
                }
            }
        }
    }
}

// helpers
// pub fn none<F, E, N>(mut f: F) -> impl for<'a> FnMut(EventCtx<'a, E, N>) + SendSync
// where
//     F: FnMut() + SendSync + 'static,
// {
//     move |_ctx| f()
// }
//
// pub fn node<F, E, N>(mut f: F) -> impl for<'a> FnMut(EventCtx<'a, E, N>) + SendSync
// where
//     F: FnMut(&mut N) + SendSync + 'static,
// {
//     move |ctx| f(ctx.node)
// }
//
// pub fn event<F, E, N>(mut f: F) -> impl for<'a> FnMut(EventCtx<'a, E, N>) + SendSync
// where
//     F: FnMut(&E) + SendSync + 'static,
// {
//     move |ctx| f(ctx.event)
// }
//
// pub fn game<F, E, N>(mut f: F) -> impl for<'a> FnMut(EventCtx<'a, E, N>) + SendSync
// where
//     F: FnMut(&GameContext) + SendSync + 'static,
// {
//     move |ctx| f(ctx.game)
// }
//
// pub fn node_event<F, E, N>(mut f: F) -> impl for<'a> FnMut(EventCtx<'a, E, N>) + SendSync
// where
//     F: FnMut(&mut N, &E) + SendSync + 'static,
// {
//     move |ctx| f(ctx.node, ctx.event)
// }
//
// pub fn node_game<F, E, N>(mut f: F) -> impl for<'a> FnMut(EventCtx<'a, E, N>) + SendSync
// where
//     F: FnMut(&mut N, &GameContext) + SendSync + 'static,
// {
//     move |ctx| f(ctx.node, ctx.game)
// }
//
// pub fn event_game<F, E, N>(mut f: F) -> impl for<'a> FnMut(EventCtx<'a, E, N>) + SendSync
// where
//     F: FnMut(&E, &GameContext) + SendSync + 'static,
// {
//     move |ctx| f(ctx.event, ctx.game)
// }
//
// pub fn all<F, E, N>(mut f: F) -> impl for<'a> FnMut(EventCtx<'a, E, N>) + SendSync
// where
//     F: FnMut(&mut N, &E, &GameContext) + SendSync + 'static,
// {
//     move |ctx| f(ctx.node, ctx.event, ctx.game)
// }