bevyengine / bevyengine/bevy

`Deferred` doesn't seem to work with observers

Open
#14,597 5 comments 0 reactions 1 assignee Claimed by @alice-i-cecile View on GitHub
A-ECS C-Bug C-Docs
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 22h
Merged PRs (30d)
161

Description

## Bevy version

`0.14.0`

## What you did

I created an observer with a `Deferred` 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:

```rust
#[derive(Event)]
pub struct Defer(E);

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

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

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

struct EntityComponents(Entity, Vec);

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

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

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

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

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

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

impl SystemBuffer for TriggerEvent
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(&mut self) -> &mut Self
where
E: Event,
B: Bundle,
for<'a> Defer: 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,
mut defer: Deferred, EntityComponents>>| {
debug!(current_len = defer.inner.len(), "adding event to defer");
defer.inner.push((
Defer::::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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.