use std::any::TypeId; use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::sync::atomic::{AtomicUsize, Ordering}; pub use bevy_ecs_macros::{ScheduleLabel, SystemSet}; use bevy_utils::define_boxed_label; use bevy_utils::label::DynHash; use crate::system::{ ExclusiveSystemParamFunction, IsExclusiveFunctionSystem, IsFunctionSystem, SystemParamFunction, }; define_boxed_label!(ScheduleLabel); /// A shorthand for `Box`. pub type BoxedSystemSet = Box; /// A shorthand for `Box`. pub type BoxedScheduleLabel = Box; /// Types that identify logical groups of systems. pub trait SystemSet: DynHash + Debug + Send + Sync + 'static { /// Returns `Some` if this system set is a [`SystemTypeSet`]. fn system_type(&self) -> Option { None } /// Returns `true` if this system set is an [`AnonymousSet`]. fn is_anonymous(&self) -> bool { false } /// Creates a boxed clone of the label corresponding to this system set. fn dyn_clone(&self) -> Box; } impl PartialEq for dyn SystemSet { fn eq(&self, other: &Self) -> bool { self.dyn_eq(other.as_dyn_eq()) } } impl Eq for dyn SystemSet {} impl Hash for dyn SystemSet { fn hash(&self, state: &mut H) { self.dyn_hash(state); } } impl Clone for Box { fn clone(&self) -> Self { self.dyn_clone() } } /// A [`SystemSet`] grouping instances of the same function. /// /// This kind of set is automatically populated and thus has some special rules: /// - You cannot manually add members. /// - You cannot configure them. /// - You cannot order something relative to one if it has more than one member. pub struct SystemTypeSet(PhantomData T>); impl SystemTypeSet { pub(crate) fn new() -> Self { Self(PhantomData) } } impl Debug for SystemTypeSet { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("SystemTypeSet") .field(&format_args!("fn {}()", &std::any::type_name::())) .finish() } } impl Hash for SystemTypeSet { fn hash(&self, _state: &mut H) { // all systems of a given type are the same } } impl Clone for SystemTypeSet { fn clone(&self) -> Self { *self } } impl Copy for SystemTypeSet {} impl PartialEq for SystemTypeSet { #[inline] fn eq(&self, _other: &Self) -> bool { // all systems of a given type are the same true } } impl Eq for SystemTypeSet {} impl SystemSet for SystemTypeSet { fn system_type(&self) -> Option { Some(TypeId::of::()) } fn dyn_clone(&self) -> Box { Box::new(*self) } } /// A [`SystemSet`] implicitly created when using /// [`Schedule::add_systems`](super::Schedule::add_systems). #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub struct AnonymousSet(usize); static NEXT_ANONYMOUS_SET_ID: AtomicUsize = AtomicUsize::new(0); impl AnonymousSet { pub(crate) fn new() -> Self { Self(NEXT_ANONYMOUS_SET_ID.fetch_add(1, Ordering::Relaxed)) } } impl SystemSet for AnonymousSet { fn is_anonymous(&self) -> bool { true } fn dyn_clone(&self) -> Box { Box::new(*self) } } /// Types that can be converted into a [`SystemSet`]. pub trait IntoSystemSet: Sized { /// The type of [`SystemSet`] this instance converts into. type Set: SystemSet; /// Converts this instance to its associated [`SystemSet`] type. fn into_system_set(self) -> Self::Set; } // systems sets impl IntoSystemSet<()> for S { type Set = Self; #[inline] fn into_system_set(self) -> Self::Set { self } } // systems impl IntoSystemSet<(IsFunctionSystem, Marker)> for F where F: SystemParamFunction, { type Set = SystemTypeSet; #[inline] fn into_system_set(self) -> Self::Set { SystemTypeSet::new() } } // exclusive systems impl IntoSystemSet<(IsExclusiveFunctionSystem, Marker)> for F where F: ExclusiveSystemParamFunction, { type Set = SystemTypeSet; #[inline] fn into_system_set(self) -> Self::Set { SystemTypeSet::new() } } #[cfg(test)] mod tests { use crate::{ schedule::{tests::ResMut, Schedule}, system::Resource, }; use super::*; #[test] fn test_boxed_label() { use crate::{self as bevy_ecs, world::World}; #[derive(Resource)] struct Flag(bool); #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] struct A; let mut world = World::new(); let mut schedule = Schedule::new(A); schedule.add_systems(|mut flag: ResMut| flag.0 = true); world.add_schedule(schedule); let boxed: Box = Box::new(A); world.insert_resource(Flag(false)); world.run_schedule(&boxed); assert!(world.resource::().0); world.insert_resource(Flag(false)); world.run_schedule(boxed); assert!(world.resource::().0); } }