Skip to content

Deferred doesn't seem to work with observers #14597

@jrobsonchase

Description

@jrobsonchase

Bevy version

0.14.0

What you did

I created an observer with a Deferred<T> parameter, but never see T::apply called.

For context, this comes out of a desire to have component lifecycle observers (OnAdd, OnRemove, etc.) that trigger after an exclusive system/command has released its &mut World access. Previous attempts to use the Commands queue for this were ineffective since observer command queues are flushed immediately after the observer finishes rather than added to the end of the "main" command queue.

Relevant code:

#[derive(Event)]
pub struct Defer<E>(E);

impl From<&'_ OnRemove> for Defer<OnRemove> {
  fn from(_: &'_ OnRemove) -> Self {
    Defer(OnRemove)
  }
}

impl From<&'_ OnInsert> for Defer<OnInsert> {
  fn from(_: &'_ OnInsert) -> Self {
    Defer(OnInsert)
  }
}

impl From<&'_ OnAdd> for Defer<OnAdd> {
  fn from(_: &'_ OnAdd) -> Self {
    Defer(OnAdd)
  }
}

struct EntityComponents(Entity, Vec<ComponentId>);

impl TriggerTargets for EntityComponents {
  fn components(&self) -> impl ExactSizeIterator<Item = ComponentId> {
    self.1.iter().copied()
  }

  fn entities(&self) -> impl ExactSizeIterator<Item = Entity> {
    Some(self.0).into_iter()
  }
}

pub trait DeferEvent {
  fn defer<E, B>(&mut self) -> &mut Self
  where
    E: Event,
    B: Bundle,
    for<'a> Defer<E>: From<&'a E>;

  fn defer_lifecycle<B>(&mut self) -> &mut Self
  where
    B: Bundle,
  {
    self
      .defer::<OnAdd, B>()
      .defer::<OnRemove, B>()
      .defer::<OnInsert, B>()
  }
}

struct TriggerEvent<E, T> {
  inner: Vec<(E, T)>,
}

impl<E, T> FromWorld for TriggerEvent<E, T> {
  fn from_world(world: &mut World) -> Self {
    Self { inner: vec![] }
  }
}

impl<E, T> SystemBuffer for TriggerEvent<E, T>
where
  E: Event,
  T: TriggerTargets,
{
  fn apply(&mut self, system_meta: &bevy::ecs::system::SystemMeta, world: &mut World) {
    debug!("triggering deferred events");
    for (event, targets) in self.inner.drain(..) {
      world.trigger_targets(event, targets);
    }
  }
}

impl DeferEvent for App {
  fn defer<E, B>(&mut self) -> &mut Self
  where
    E: Event,
    B: Bundle,
    for<'a> Defer<E>: From<&'a E>,
  {
    let mut ids = vec![];
    let components = self.world().components();
    B::get_component_ids(components, &mut |id| {
      if let Some(id) = id {
        ids.push(id);
      }
    });
    self.observe(
      move |trigger: Trigger<E, B>,
            mut defer: Deferred<TriggerEvent<Defer<E>, EntityComponents>>| {
        debug!(current_len = defer.inner.len(), "adding event to defer");
        defer.inner.push((
          Defer::<E>::from(trigger.event()),
          EntityComponents(trigger.entity(), ids.clone()),
        ));
      },
    )
  }
}

What went wrong

I see the "adding event to defer" logs with incrementing queue length, but never see the "triggering deferred events" log from the impl SystemBuffer for TriggerEvent.

I expected this to occur at some point, even if it was at the same point as the observer's command buffer application.

Metadata

Metadata

Labels

A-ECSEntities, components, systems, and eventsC-BugAn unexpected or incorrect behaviorC-DocsAn addition or correction to our documentation

Type

No type

Projects

Status

Observer overhaul

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions