Skip to content

Commit bca4b36

Browse files
change not implemation to custom system struct (#8105)
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
1 parent 52b91ac commit bca4b36

File tree

3 files changed

+171
-12
lines changed

3 files changed

+171
-12
lines changed

crates/bevy_ecs/src/schedule/condition.rs

Lines changed: 146 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
use std::any::TypeId;
12
use std::borrow::Cow;
3+
use std::ops::Not;
24

5+
use crate::component::{self, ComponentId};
6+
use crate::query::Access;
37
use crate::system::{CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, System};
8+
use crate::world::World;
49

510
pub type BoxedCondition = Box<dyn ReadOnlySystem<In = (), Out = bool>>;
611

@@ -131,13 +136,15 @@ mod sealed {
131136
}
132137

133138
pub mod common_conditions {
134-
use super::Condition;
139+
use std::borrow::Cow;
140+
141+
use super::NotSystem;
135142
use crate::{
136143
change_detection::DetectChanges,
137144
event::{Event, EventReader},
138145
prelude::{Component, Query, With},
139146
schedule::{State, States},
140-
system::{In, IntoPipeSystem, Res, Resource},
147+
system::{IntoSystem, Res, Resource, System},
141148
};
142149

143150
/// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
@@ -915,12 +922,103 @@ pub mod common_conditions {
915922
/// app.run(&mut world);
916923
/// assert_eq!(world.resource::<Counter>().0, 0);
917924
/// ```
918-
pub fn not<Marker, T>(condition: T) -> impl Condition<()>
925+
pub fn not<Marker, TOut, T>(condition: T) -> NotSystem<T::System>
919926
where
920-
T: Condition<Marker>,
927+
TOut: std::ops::Not,
928+
T: IntoSystem<(), TOut, Marker>,
921929
{
922-
condition.pipe(|In(val): In<bool>| !val)
930+
let condition = IntoSystem::into_system(condition);
931+
let name = format!("!{}", condition.name());
932+
NotSystem::<T::System> {
933+
condition,
934+
name: Cow::Owned(name),
935+
}
936+
}
937+
}
938+
939+
/// Invokes [`Not`] with the output of another system.
940+
///
941+
/// See [`common_conditions::not`] for examples.
942+
#[derive(Clone)]
943+
pub struct NotSystem<T: System>
944+
where
945+
T::Out: Not,
946+
{
947+
condition: T,
948+
name: Cow<'static, str>,
949+
}
950+
impl<T: System> System for NotSystem<T>
951+
where
952+
T::Out: Not,
953+
{
954+
type In = T::In;
955+
type Out = <T::Out as Not>::Output;
956+
957+
fn name(&self) -> Cow<'static, str> {
958+
self.name.clone()
959+
}
960+
961+
fn type_id(&self) -> TypeId {
962+
TypeId::of::<T>()
963+
}
964+
965+
fn component_access(&self) -> &Access<ComponentId> {
966+
self.condition.component_access()
967+
}
968+
969+
fn archetype_component_access(&self) -> &Access<crate::archetype::ArchetypeComponentId> {
970+
self.condition.archetype_component_access()
971+
}
972+
973+
fn is_send(&self) -> bool {
974+
self.condition.is_send()
975+
}
976+
977+
fn is_exclusive(&self) -> bool {
978+
self.condition.is_exclusive()
979+
}
980+
981+
unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out {
982+
// SAFETY: The inner condition system asserts its own safety.
983+
!self.condition.run_unsafe(input, world)
984+
}
985+
986+
fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
987+
!self.condition.run(input, world)
923988
}
989+
990+
fn apply_buffers(&mut self, world: &mut World) {
991+
self.condition.apply_buffers(world);
992+
}
993+
994+
fn initialize(&mut self, world: &mut World) {
995+
self.condition.initialize(world);
996+
}
997+
998+
fn update_archetype_component_access(&mut self, world: &World) {
999+
self.condition.update_archetype_component_access(world);
1000+
}
1001+
1002+
fn check_change_tick(&mut self, change_tick: component::Tick) {
1003+
self.condition.check_change_tick(change_tick);
1004+
}
1005+
1006+
fn get_last_run(&self) -> component::Tick {
1007+
self.condition.get_last_run()
1008+
}
1009+
1010+
fn set_last_run(&mut self, last_run: component::Tick) {
1011+
self.condition.set_last_run(last_run);
1012+
}
1013+
}
1014+
1015+
// SAFETY: This trait is only implemented when the inner system is read-only.
1016+
// The `Not` condition does not modify access, and thus cannot transform a read-only system into one that is not.
1017+
unsafe impl<T> ReadOnlySystem for NotSystem<T>
1018+
where
1019+
T: ReadOnlySystem,
1020+
T::Out: Not,
1021+
{
9241022
}
9251023

9261024
/// Combines the outputs of two systems using the `&&` operator.
@@ -973,12 +1071,17 @@ where
9731071

9741072
#[cfg(test)]
9751073
mod tests {
976-
use super::Condition;
1074+
use super::{common_conditions::*, Condition};
9771075
use crate as bevy_ecs;
978-
use crate::schedule::common_conditions::not;
979-
use crate::schedule::IntoSystemConfig;
980-
use crate::system::Local;
981-
use crate::{change_detection::ResMut, schedule::Schedule, world::World};
1076+
use crate::{
1077+
change_detection::ResMut,
1078+
component::Component,
1079+
schedule::{
1080+
common_conditions::not, IntoSystemConfig, IntoSystemConfigs, Schedule, State, States,
1081+
},
1082+
system::Local,
1083+
world::World,
1084+
};
9821085
use bevy_ecs_macros::Resource;
9831086

9841087
#[derive(Resource, Default)]
@@ -1074,4 +1177,37 @@ mod tests {
10741177
schedule.run(&mut world);
10751178
assert_eq!(world.resource::<Counter>().0, 0);
10761179
}
1180+
1181+
#[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)]
1182+
enum TestState {
1183+
#[default]
1184+
A,
1185+
B,
1186+
}
1187+
1188+
#[derive(Component)]
1189+
struct TestComponent;
1190+
1191+
fn test_system() {}
1192+
1193+
// Ensure distributive_run_if compiles with the common conditions.
1194+
#[test]
1195+
fn distributive_run_if_compiles() {
1196+
Schedule::default().add_systems(
1197+
(test_system, test_system)
1198+
.distributive_run_if(run_once())
1199+
.distributive_run_if(resource_exists::<State<TestState>>())
1200+
.distributive_run_if(resource_added::<State<TestState>>())
1201+
.distributive_run_if(resource_changed::<State<TestState>>())
1202+
.distributive_run_if(resource_exists_and_changed::<State<TestState>>())
1203+
.distributive_run_if(resource_changed_or_removed::<State<TestState>>())
1204+
.distributive_run_if(resource_removed::<State<TestState>>())
1205+
.distributive_run_if(state_exists::<TestState>())
1206+
.distributive_run_if(in_state(TestState::A))
1207+
.distributive_run_if(state_changed::<TestState>())
1208+
.distributive_run_if(on_event::<u8>())
1209+
.distributive_run_if(any_with_component::<TestComponent>())
1210+
.distributive_run_if(not(run_once())),
1211+
);
1212+
}
10771213
}

crates/bevy_ecs/src/schedule/config.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,8 @@ where
394394

395395
/// Add a run condition to each contained system.
396396
///
397-
/// Each system will receive its own clone of the [`Condition`] and will only run
398-
/// if the `Condition` is true.
397+
/// The [`Condition`] must be [`Clone`]. Each system will receive its own clone
398+
/// of the `Condition` and will only run if the `Condition` is true.
399399
///
400400
/// Each individual condition will be evaluated at most once (per schedule run),
401401
/// right before the corresponding system prepares to run.
@@ -422,6 +422,9 @@ where
422422
/// Use [`run_if`](IntoSystemSetConfig::run_if) on a [`SystemSet`] if you want to make sure
423423
/// that either all or none of the systems are run, or you don't want to evaluate the run
424424
/// condition for each contained system separately.
425+
///
426+
/// The [`Condition`] is cloned for each system.
427+
/// Cloned instances of [`FunctionSystem`](crate::system::FunctionSystem) will be de-initialized.
425428
fn distributive_run_if<M>(self, condition: impl Condition<M> + Clone) -> SystemConfigs {
426429
self.into_configs().distributive_run_if(condition)
427430
}

crates/bevy_ecs/src/system/function_system.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,9 @@ pub struct In<In>(pub In);
367367
/// becomes the functions [`In`] tagged parameter or `()` if no such parameter exists.
368368
///
369369
/// [`FunctionSystem`] must be `.initialized` before they can be run.
370+
///
371+
/// The [`Clone`] implementation for [`FunctionSystem`] returns a new instance which
372+
/// is NOT initialized. The cloned system must also be `.initialized` before it can be run.
370373
pub struct FunctionSystem<Marker, F>
371374
where
372375
F: SystemParamFunction<Marker>,
@@ -380,6 +383,23 @@ where
380383
marker: PhantomData<fn() -> Marker>,
381384
}
382385

386+
// De-initializes the cloned system.
387+
impl<Marker, F> Clone for FunctionSystem<Marker, F>
388+
where
389+
F: SystemParamFunction<Marker> + Clone,
390+
{
391+
fn clone(&self) -> Self {
392+
Self {
393+
func: self.func.clone(),
394+
param_state: None,
395+
system_meta: SystemMeta::new::<F>(),
396+
world_id: None,
397+
archetype_generation: ArchetypeGeneration::initial(),
398+
marker: PhantomData,
399+
}
400+
}
401+
}
402+
383403
pub struct IsFunctionSystem;
384404

385405
impl<Marker, F> IntoSystem<F::In, F::Out, (IsFunctionSystem, Marker)> for F

0 commit comments

Comments
 (0)