Merge c15e5a85fd
into 877d278785
This commit is contained in:
commit
c6383356ff
@ -318,7 +318,7 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
prelude::{Component, In, IntoSystem, Resource, Schedule},
|
prelude::{Component, In, IntoSystem, Resource, Schedule},
|
||||||
schedule::ExecutorKind,
|
schedule::ExecutorKind,
|
||||||
system::{Populated, Res, ResMut, Single},
|
system::{Populated, Res, ResMut, Single, When},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -341,12 +341,12 @@ mod tests {
|
|||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
struct Counter(u8);
|
struct Counter(u8);
|
||||||
|
|
||||||
fn set_single_state(mut _single: Single<&TestComponent>, mut state: ResMut<TestState>) {
|
fn set_single_state(mut _single: When<Single<&TestComponent>>, mut state: ResMut<TestState>) {
|
||||||
state.single_ran = true;
|
state.single_ran = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_populated_state(
|
fn set_populated_state(
|
||||||
mut _populated: Populated<&TestComponent>,
|
mut _populated: When<Populated<&TestComponent>>,
|
||||||
mut state: ResMut<TestState>,
|
mut state: ResMut<TestState>,
|
||||||
) {
|
) {
|
||||||
state.populated_ran = true;
|
state.populated_ran = true;
|
||||||
@ -418,7 +418,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn piped_systems_first_system_skipped() {
|
fn piped_systems_first_system_skipped() {
|
||||||
// This system should be skipped when run due to no matching entity
|
// This system should be skipped when run due to no matching entity
|
||||||
fn pipe_out(_single: Single<&TestComponent>) -> u8 {
|
fn pipe_out(_single: When<Single<&TestComponent>>) -> u8 {
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +446,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This system should be skipped when run due to no matching entity
|
// This system should be skipped when run due to no matching entity
|
||||||
fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
|
fn pipe_in(
|
||||||
|
_input: In<u8>,
|
||||||
|
_single: When<Single<&TestComponent>>,
|
||||||
|
mut counter: ResMut<Counter>,
|
||||||
|
) {
|
||||||
counter.0 += 1;
|
counter.0 += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,7 +503,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn piped_system_skip_and_panic() {
|
fn piped_system_skip_and_panic() {
|
||||||
// This system should be skipped when run due to no matching entity
|
// This system should be skipped when run due to no matching entity
|
||||||
fn pipe_out(_single: Single<&TestComponent>) -> u8 {
|
fn pipe_out(_single: When<Single<&TestComponent>>) -> u8 {
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,7 +527,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This system should be skipped when run due to no matching entity
|
// This system should be skipped when run due to no matching entity
|
||||||
fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>) {}
|
fn pipe_in(_input: In<u8>, _single: When<Single<&TestComponent>>) {}
|
||||||
|
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
let mut schedule = Schedule::default();
|
let mut schedule = Schedule::default();
|
||||||
@ -555,13 +559,17 @@ mod tests {
|
|||||||
fn piped_system_skip_and_skip() {
|
fn piped_system_skip_and_skip() {
|
||||||
// This system should be skipped when run due to no matching entity
|
// This system should be skipped when run due to no matching entity
|
||||||
|
|
||||||
fn pipe_out(_single: Single<&TestComponent>, mut counter: ResMut<Counter>) -> u8 {
|
fn pipe_out(_single: When<Single<&TestComponent>>, mut counter: ResMut<Counter>) -> u8 {
|
||||||
counter.0 += 1;
|
counter.0 += 1;
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
|
|
||||||
// This system should be skipped when run due to no matching entity
|
// This system should be skipped when run due to no matching entity
|
||||||
fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
|
fn pipe_in(
|
||||||
|
_input: In<u8>,
|
||||||
|
_single: When<Single<&TestComponent>>,
|
||||||
|
mut counter: ResMut<Counter>,
|
||||||
|
) {
|
||||||
counter.0 += 1;
|
counter.0 += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2581,9 +2581,11 @@ impl<'w, 'q, Q: QueryData, F: QueryFilter> From<&'q mut Query<'w, '_, Q, F>>
|
|||||||
/// [System parameter] that provides access to single entity's components, much like [`Query::single`]/[`Query::single_mut`].
|
/// [System parameter] that provides access to single entity's components, much like [`Query::single`]/[`Query::single_mut`].
|
||||||
///
|
///
|
||||||
/// This [`SystemParam`](crate::system::SystemParam) fails validation if zero or more than one matching entity exists.
|
/// This [`SystemParam`](crate::system::SystemParam) fails validation if zero or more than one matching entity exists.
|
||||||
/// This will cause the system to be skipped, according to the rules laid out in [`SystemParamValidationError`](crate::system::SystemParamValidationError).
|
/// This will cause the system to fail, according to the rules laid out in [`SystemParamValidationError`](crate::system::SystemParamValidationError).
|
||||||
///
|
///
|
||||||
/// Use [`Option<Single<D, F>>`] instead if zero or one matching entities can exist.
|
/// Use [`Option<Single<D, F>>`] instead to run the system but get `None` when there are zero or multiple matching entities.
|
||||||
|
///
|
||||||
|
/// Use [`When<Single<D, F>>`](crate::system::When) instead to skip the system when there are zero or multiple matching entities.
|
||||||
///
|
///
|
||||||
/// See [`Query`] for more details.
|
/// See [`Query`] for more details.
|
||||||
///
|
///
|
||||||
@ -2632,7 +2634,9 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Single<'w, 's, D, F> {
|
|||||||
/// [System parameter] that works very much like [`Query`] except it always contains at least one matching entity.
|
/// [System parameter] that works very much like [`Query`] except it always contains at least one matching entity.
|
||||||
///
|
///
|
||||||
/// This [`SystemParam`](crate::system::SystemParam) fails validation if no matching entities exist.
|
/// This [`SystemParam`](crate::system::SystemParam) fails validation if no matching entities exist.
|
||||||
/// This will cause the system to be skipped, according to the rules laid out in [`SystemParamValidationError`](crate::system::SystemParamValidationError).
|
/// This will cause the system to fail, according to the rules laid out in [`SystemParamValidationError`](crate::system::SystemParamValidationError).
|
||||||
|
///
|
||||||
|
/// Use [`When<Populated<D, F>>`](crate::system::When) instead to skip the system when there are zero matching entities.
|
||||||
///
|
///
|
||||||
/// Much like [`Query::is_empty`] the worst case runtime will be `O(n)` where `n` is the number of *potential* matches.
|
/// Much like [`Query::is_empty`] the worst case runtime will be `O(n)` where `n` is the number of *potential* matches.
|
||||||
/// This can be notably expensive for queries that rely on non-archetypal filters such as [`Added`](crate::query::Added),
|
/// This can be notably expensive for queries that rely on non-archetypal filters such as [`Added`](crate::query::Added),
|
||||||
|
@ -448,10 +448,10 @@ unsafe impl<'a, 'b, D: QueryData + 'static, F: QueryFilter + 'static> SystemPara
|
|||||||
match query.single_inner() {
|
match query.single_inner() {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(QuerySingleError::NoEntities(_)) => Err(
|
Err(QuerySingleError::NoEntities(_)) => Err(
|
||||||
SystemParamValidationError::skipped::<Self>("No matching entities"),
|
SystemParamValidationError::invalid::<Self>("No matching entities"),
|
||||||
),
|
),
|
||||||
Err(QuerySingleError::MultipleEntities(_)) => Err(
|
Err(QuerySingleError::MultipleEntities(_)) => Err(
|
||||||
SystemParamValidationError::skipped::<Self>("Multiple matching entities"),
|
SystemParamValidationError::invalid::<Self>("Multiple matching entities"),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -509,7 +509,7 @@ unsafe impl<D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
|
|||||||
state.query_unchecked_with_ticks(world, system_meta.last_run, world.change_tick())
|
state.query_unchecked_with_ticks(world, system_meta.last_run, world.change_tick())
|
||||||
};
|
};
|
||||||
if query.is_empty() {
|
if query.is_empty() {
|
||||||
Err(SystemParamValidationError::skipped::<Self>(
|
Err(SystemParamValidationError::invalid::<Self>(
|
||||||
"No matching entities",
|
"No matching entities",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
@ -1836,6 +1836,8 @@ unsafe impl<T: ReadOnlySystemParam> ReadOnlySystemParam for Result<T, SystemPara
|
|||||||
|
|
||||||
/// A [`SystemParam`] that wraps another parameter and causes its system to skip instead of failing when the parameter is invalid.
|
/// A [`SystemParam`] that wraps another parameter and causes its system to skip instead of failing when the parameter is invalid.
|
||||||
///
|
///
|
||||||
|
/// This is especially useful with [`Single`] and [`Populated`].
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -2803,12 +2805,6 @@ pub struct SystemParamValidationError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SystemParamValidationError {
|
impl SystemParamValidationError {
|
||||||
/// Constructs a `SystemParamValidationError` that skips the system.
|
|
||||||
/// The parameter name is initialized to the type name of `T`, so a `SystemParam` should usually pass `Self`.
|
|
||||||
pub fn skipped<T>(message: impl Into<Cow<'static, str>>) -> Self {
|
|
||||||
Self::new::<T>(true, message, Cow::Borrowed(""))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a `SystemParamValidationError` for an invalid parameter that should be treated as an error.
|
/// Constructs a `SystemParamValidationError` for an invalid parameter that should be treated as an error.
|
||||||
/// The parameter name is initialized to the type name of `T`, so a `SystemParam` should usually pass `Self`.
|
/// The parameter name is initialized to the type name of `T`, so a `SystemParam` should usually pass `Self`.
|
||||||
pub fn invalid<T>(message: impl Into<Cow<'static, str>>) -> Self {
|
pub fn invalid<T>(message: impl Into<Cow<'static, str>>) -> Self {
|
||||||
|
@ -243,10 +243,10 @@ pub type InputFocusSet = InputFocusSystems;
|
|||||||
/// If no entity is focused, sets the focus to the primary window, if any.
|
/// If no entity is focused, sets the focus to the primary window, if any.
|
||||||
pub fn set_initial_focus(
|
pub fn set_initial_focus(
|
||||||
mut input_focus: ResMut<InputFocus>,
|
mut input_focus: ResMut<InputFocus>,
|
||||||
window: Single<Entity, With<PrimaryWindow>>,
|
window: When<Single<Entity, With<PrimaryWindow>>>,
|
||||||
) {
|
) {
|
||||||
if input_focus.0.is_none() {
|
if input_focus.0.is_none() {
|
||||||
input_focus.0 = Some(*window);
|
input_focus.0 = Some(**window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,8 +119,8 @@ fn user_input(
|
|||||||
|
|
||||||
// System that moves the enemies in a circle.
|
// System that moves the enemies in a circle.
|
||||||
// Only runs if there are enemies, due to the `Populated` parameter.
|
// Only runs if there are enemies, due to the `Populated` parameter.
|
||||||
fn move_targets(mut enemies: Populated<(&mut Transform, &mut Enemy)>, time: Res<Time>) {
|
fn move_targets(mut enemies: When<Populated<(&mut Transform, &mut Enemy)>>, time: Res<Time>) {
|
||||||
for (mut transform, mut target) in &mut *enemies {
|
for (mut transform, mut target) in &mut **enemies {
|
||||||
target.rotation += target.rotation_speed * time.delta_secs();
|
target.rotation += target.rotation_speed * time.delta_secs();
|
||||||
transform.rotation = Quat::from_rotation_z(target.rotation);
|
transform.rotation = Quat::from_rotation_z(target.rotation);
|
||||||
let offset = transform.right() * target.radius;
|
let offset = transform.right() * target.radius;
|
||||||
@ -160,4 +160,4 @@ fn track_targets(
|
|||||||
|
|
||||||
/// This system always fails param validation, because we never
|
/// This system always fails param validation, because we never
|
||||||
/// create an entity with both [`Player`] and [`Enemy`] components.
|
/// create an entity with both [`Player`] and [`Enemy`] components.
|
||||||
fn do_nothing_fail_validation(_: Single<(), (With<Player>, With<Enemy>)>) {}
|
fn do_nothing_fail_validation(_: When<Single<(), (With<Player>, With<Enemy>)>>) {}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: "`Single` and `Populated` now fail instead of skipping"
|
||||||
|
pull_requests: [19489, 18765]
|
||||||
|
---
|
||||||
|
|
||||||
|
`Single<D, F>` and `Populated<D, F>` now cause systems to fail instead of skip.
|
||||||
|
|
||||||
|
The introduction of `When` makes it possible to skip systems for any invalid parameter, such as `When<Res<R>>`.
|
||||||
|
The change to the behavior of `Single` and `Populated` keeps them consistent with other parameters,
|
||||||
|
and makes it possible to use them as assertions instead of only as run conditions.
|
||||||
|
|
||||||
|
Replace `Single<D, F>` with `When<Single<D, F>>`
|
||||||
|
and `Populated<D, F>` with `When<Populated<D, F>>`
|
||||||
|
to restore the old behavior.
|
Loading…
Reference in New Issue
Block a user