bevy_event_chain 0.1.0

Easy trait-like behavior using observers and relations in Bevy
Documentation
use bevy::ecs::component::ComponentId;
use bevy::ecs::event::{Trigger, trigger_entity_internal};
use bevy::ecs::observer::{CachedObservers, TriggerContext};
use bevy::ecs::world::DeferredWorld;
use bevy::prelude::*;
use bevy::ptr::PtrMut;

/// [`EntityComponentsTrigger`](bevy::ecs::event::EntityComponentsTrigger) but one component and it's not a slice.
pub struct EntityComponentTrigger {
    pub component: ComponentId,
}

// SAFETY:
// - `E`'s [`Event::Trigger`] is constrained to [`EntityComponentTrigger`]
unsafe impl<'a, E: EntityEvent + Event<Trigger<'a> = EntityComponentTrigger>> Trigger<E>
    for EntityComponentTrigger
{
    unsafe fn trigger(
        &mut self,
        world: DeferredWorld,
        observers: &CachedObservers,
        trigger_context: &TriggerContext,
        event: &mut E,
    ) {
        let entity = event.event_target();
        // SAFETY:
        // - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
        // - the passed in event pointer comes from `event`, which is an `Event`
        // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger`
        unsafe {
            self.trigger_internal(world, observers, event.into(), entity, trigger_context);
        }
    }
}

impl EntityComponentTrigger {
    /// # Safety
    /// - `observers` must come from the `world` [`DeferredWorld`]
    /// - `event` must point to an [`Event`] whose [`Event::Trigger`] is [`EntityComponentTrigger`]
    /// - `trigger_context`'s [`TriggerContext::event_key`] must correspond to the `event` type.
    #[inline(never)]
    unsafe fn trigger_internal(
        &mut self,
        mut world: DeferredWorld,
        observers: &CachedObservers,
        mut event: PtrMut,
        entity: Entity,
        trigger_context: &TriggerContext,
    ) {
        // SAFETY:
        // - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
        // - the passed in event pointer comes from `event`, which is an `Event`
        // - `trigger` is a matching trigger type, as it comes from `self`, which is the Trigger for `E`
        // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger`
        unsafe {
            trigger_entity_internal(
                world.reborrow(),
                observers,
                event.reborrow(),
                self.into(),
                entity,
                trigger_context,
            );
        }

        // Trigger observers watching for a specific component
        if let Some(component_observers) = observers.component_observers().get(&self.component) {
            for (observer, runner) in component_observers.global_observers() {
                // SAFETY:
                // - `observers` come from `world` and match the `event` type, enforced by the call to `trigger_internal`
                // - the passed in event pointer is an `Event`, enforced by the call to `trigger_internal`
                // - `trigger` is a matching trigger type, enforced by the call to `trigger_internal`
                // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger_internal`
                unsafe {
                    (runner)(
                        world.reborrow(),
                        *observer,
                        trigger_context,
                        event.reborrow(),
                        self.into(),
                    );
                }
            }

            if let Some(map) = component_observers
                .entity_component_observers()
                .get(&entity)
            {
                for (observer, runner) in map {
                    // SAFETY:
                    // - `observers` come from `world` and match the `event` type, enforced by the call to `trigger_internal`
                    // - the passed in event pointer is an `Event`, enforced by the call to `trigger_internal`
                    // - `trigger` is a matching trigger type, enforced by the call to `trigger_internal`
                    // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger_internal`
                    unsafe {
                        (runner)(
                            world.reborrow(),
                            *observer,
                            trigger_context,
                            event.reborrow(),
                            self.into(),
                        );
                    }
                }
            }
        }
    }
}