bevy/crates/bevy_ecs/src/observer/trigger_event.rs
Martín Maita eca8220761
Generalised ECS reactivity with Observers (#10839) (#13873)
# Objective

- Fixes #13825 

## Solution

- Cherry picked and fixed non-trivial conflicts to be able to merge
#10839 into the 0.14 release branch.

Link to PR: https://github.com/bevyengine/bevy/pull/10839

Co-authored-by: James O'Brien <james.obrien@drafly.net>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: MiniaczQ <xnetroidpl@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-06-16 11:44:08 -04:00

166 lines
4.8 KiB
Rust

use crate::{
component::ComponentId,
entity::Entity,
event::Event,
world::{Command, DeferredWorld, World},
};
/// A [`Command`] that emits a given trigger for a given set of targets.
pub struct TriggerEvent<E, Targets: TriggerTargets = ()> {
/// The event to trigger.
pub event: E,
/// The targets to trigger the event for.
pub targets: Targets,
}
impl<E: Event, Targets: TriggerTargets> Command for TriggerEvent<E, Targets> {
fn apply(mut self, world: &mut World) {
let event_type = world.init_component::<E>();
trigger_event(world, event_type, &mut self.event, self.targets);
}
}
/// Emit a trigger for a dynamic component id. This is unsafe and must be verified manually.
pub struct EmitDynamicTrigger<T, Targets: TriggerTargets = ()> {
event_type: ComponentId,
event_data: T,
targets: Targets,
}
impl<E, Targets: TriggerTargets> EmitDynamicTrigger<E, Targets> {
/// Sets the event type of the resulting trigger, used for dynamic triggers
/// # Safety
/// Caller must ensure that the component associated with `event_type` is accessible as E
pub unsafe fn new_with_id(event_type: ComponentId, event_data: E, targets: Targets) -> Self {
Self {
event_type,
event_data,
targets,
}
}
}
impl<E: Event, Targets: TriggerTargets> Command for EmitDynamicTrigger<E, Targets> {
fn apply(mut self, world: &mut World) {
trigger_event(world, self.event_type, &mut self.event_data, self.targets);
}
}
#[inline]
fn trigger_event<E, Targets: TriggerTargets>(
world: &mut World,
event_type: ComponentId,
event_data: &mut E,
targets: Targets,
) {
let mut world = DeferredWorld::from(world);
if targets.entities().len() == 0 {
// SAFETY: T is accessible as the type represented by self.trigger, ensured in `Self::new`
unsafe {
world.trigger_observers_with_data(
event_type,
Entity::PLACEHOLDER,
targets.components(),
event_data,
);
};
} else {
for target in targets.entities() {
// SAFETY: T is accessible as the type represented by self.trigger, ensured in `Self::new`
unsafe {
world.trigger_observers_with_data(
event_type,
target,
targets.components(),
event_data,
);
};
}
}
}
/// Represents a collection of targets for a specific [`Trigger`] of an [`Event`]. Targets can be of type [`Entity`] or [`ComponentId`].
/// When a trigger occurs for a given event and [`TriggerTargets`], any [`Observer`] that watches for that specific event-target combination
/// will run.
///
/// [`Trigger`]: crate::observer::Trigger
/// [`Observer`]: crate::observer::Observer
pub trait TriggerTargets: Send + Sync + 'static {
/// The components the trigger should target.
fn components(&self) -> impl ExactSizeIterator<Item = ComponentId>;
/// The entities the trigger should target.
fn entities(&self) -> impl ExactSizeIterator<Item = Entity>;
}
impl TriggerTargets for () {
fn components(&self) -> impl ExactSizeIterator<Item = ComponentId> {
[].into_iter()
}
fn entities(&self) -> impl ExactSizeIterator<Item = Entity> {
[].into_iter()
}
}
impl TriggerTargets for Entity {
fn components(&self) -> impl ExactSizeIterator<Item = ComponentId> {
[].into_iter()
}
fn entities(&self) -> impl ExactSizeIterator<Item = Entity> {
std::iter::once(*self)
}
}
impl TriggerTargets for Vec<Entity> {
fn components(&self) -> impl ExactSizeIterator<Item = ComponentId> {
[].into_iter()
}
fn entities(&self) -> impl ExactSizeIterator<Item = Entity> {
self.iter().copied()
}
}
impl<const N: usize> TriggerTargets for [Entity; N] {
fn components(&self) -> impl ExactSizeIterator<Item = ComponentId> {
[].into_iter()
}
fn entities(&self) -> impl ExactSizeIterator<Item = Entity> {
self.iter().copied()
}
}
impl TriggerTargets for ComponentId {
fn components(&self) -> impl ExactSizeIterator<Item = ComponentId> {
std::iter::once(*self)
}
fn entities(&self) -> impl ExactSizeIterator<Item = Entity> {
[].into_iter()
}
}
impl TriggerTargets for Vec<ComponentId> {
fn components(&self) -> impl ExactSizeIterator<Item = ComponentId> {
self.iter().copied()
}
fn entities(&self) -> impl ExactSizeIterator<Item = Entity> {
[].into_iter()
}
}
impl<const N: usize> TriggerTargets for [ComponentId; N] {
fn components(&self) -> impl ExactSizeIterator<Item = ComponentId> {
self.iter().copied()
}
fn entities(&self) -> impl ExactSizeIterator<Item = Entity> {
[].into_iter()
}
}