Implement SystemParam::queue() method for blanket implementation of ParamSet (#15599)

# Objective

The `queue()` method is an optional trait method which is necessary for
deferred operations (such as command queues) to work properly in the
context of an observer.

This method was omitted from the proc_macro blanket implementation of
`ParamSet` for tuples; as a result, SystemParams with deferred
application (such as Commands) would not work in observers if they were
part of a ParamSet.

This appears to have been a simple omission, as `queue()` was already
implemented for the separate blanket implementation of `ParamSet` for
`Vec<T>`. In both cases, it is a simple pass-through to the component
SystemParams.

## Solution

Add the `queue()` method implementation to the `impl_param_set` proco
macro.

## Testing

Added a unit test which clearly demonstrates the issue. It fails before
the fix, and passes afterwards.

---
This commit is contained in:
Matt Tracy 2024-10-02 12:46:50 -07:00 committed by GitHub
parent 587a508ef9
commit 8fb55dbf59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 27 additions and 0 deletions

View File

@ -376,6 +376,10 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
<(#(#param,)*) as SystemParam>::apply(state, system_meta, world);
}
fn queue(state: &mut Self::State, system_meta: &SystemMeta, mut world: DeferredWorld) {
<(#(#param,)*) as SystemParam>::queue(state, system_meta, world.reborrow());
}
#[inline]
unsafe fn validate_param<'w, 's>(
state: &'s Self::State,

View File

@ -1211,4 +1211,27 @@ mod tests {
assert!(world.get_resource::<ResB>().is_none());
}
#[test]
fn observer_apply_deferred_from_param_set() {
#[derive(Event)]
struct EventA;
#[derive(Resource)]
struct ResA;
let mut world = World::new();
world.observe(
|_: Trigger<EventA>, mut params: ParamSet<(Query<Entity>, Commands)>| {
params.p1().insert_resource(ResA);
},
);
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
// and therefore does not automatically flush.
world.flush();
world.trigger(EventA);
world.flush();
assert!(world.get_resource::<ResA>().is_some());
}
}