|
| 1 | +use std::any::TypeId; |
1 | 2 | use std::borrow::Cow;
|
| 3 | +use std::ops::Not; |
2 | 4 |
|
| 5 | +use crate::component::{self, ComponentId}; |
| 6 | +use crate::query::Access; |
3 | 7 | use crate::system::{CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, System};
|
| 8 | +use crate::world::World; |
4 | 9 |
|
5 | 10 | pub type BoxedCondition = Box<dyn ReadOnlySystem<In = (), Out = bool>>;
|
6 | 11 |
|
@@ -131,13 +136,15 @@ mod sealed {
|
131 | 136 | }
|
132 | 137 |
|
133 | 138 | pub mod common_conditions {
|
134 |
| - use super::Condition; |
| 139 | + use std::borrow::Cow; |
| 140 | + |
| 141 | + use super::NotSystem; |
135 | 142 | use crate::{
|
136 | 143 | change_detection::DetectChanges,
|
137 | 144 | event::{Event, EventReader},
|
138 | 145 | prelude::{Component, Query, With},
|
139 | 146 | schedule::{State, States},
|
140 |
| - system::{In, IntoPipeSystem, Res, Resource}, |
| 147 | + system::{IntoSystem, Res, Resource, System}, |
141 | 148 | };
|
142 | 149 |
|
143 | 150 | /// Generates a [`Condition`](super::Condition)-satisfying closure that returns `true`
|
@@ -915,12 +922,103 @@ pub mod common_conditions {
|
915 | 922 | /// app.run(&mut world);
|
916 | 923 | /// assert_eq!(world.resource::<Counter>().0, 0);
|
917 | 924 | /// ```
|
918 |
| - pub fn not<Marker, T>(condition: T) -> impl Condition<()> |
| 925 | + pub fn not<Marker, TOut, T>(condition: T) -> NotSystem<T::System> |
919 | 926 | where
|
920 |
| - T: Condition<Marker>, |
| 927 | + TOut: std::ops::Not, |
| 928 | + T: IntoSystem<(), TOut, Marker>, |
921 | 929 | {
|
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) |
923 | 988 | }
|
| 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 | +{ |
924 | 1022 | }
|
925 | 1023 |
|
926 | 1024 | /// Combines the outputs of two systems using the `&&` operator.
|
@@ -973,12 +1071,17 @@ where
|
973 | 1071 |
|
974 | 1072 | #[cfg(test)]
|
975 | 1073 | mod tests {
|
976 |
| - use super::Condition; |
| 1074 | + use super::{common_conditions::*, Condition}; |
977 | 1075 | 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 | + }; |
982 | 1085 | use bevy_ecs_macros::Resource;
|
983 | 1086 |
|
984 | 1087 | #[derive(Resource, Default)]
|
@@ -1074,4 +1177,37 @@ mod tests {
|
1074 | 1177 | schedule.run(&mut world);
|
1075 | 1178 | assert_eq!(world.resource::<Counter>().0, 0);
|
1076 | 1179 | }
|
| 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 | + } |
1077 | 1213 | }
|
0 commit comments