//! Types that detect when their internal data mutate. use crate::{component::ComponentTicks, ptr::PtrMut, system::Resource}; #[cfg(feature = "bevy_reflect")] use std::ops::{Deref, DerefMut}; /// The (arbitrarily chosen) minimum number of world tick increments between `check_tick` scans. /// /// Change ticks can only be scanned when systems aren't running. Thus, if the threshold is `N`, /// the maximum is `2 * N - 1` (i.e. the world ticks `N - 1` times, then `N` times). /// /// If no change is older than `u32::MAX - (2 * N - 1)` following a scan, none of their ages can /// overflow and cause false positives. // (518,400,000 = 1000 ticks per frame * 144 frames per second * 3600 seconds per hour) pub const CHECK_TICK_THRESHOLD: u32 = 518_400_000; /// The maximum change tick difference that won't overflow before the next `check_tick` scan. /// /// Changes stop being detected once they become this old. pub const MAX_CHANGE_AGE: u32 = u32::MAX - (2 * CHECK_TICK_THRESHOLD - 1); /// Types that implement reliable change detection. /// /// ## Example /// Using types that implement [`DetectChanges`], such as [`ResMut`], provide /// a way to query if a value has been mutated in another system. /// Normally change detecting is triggered by either [`DerefMut`] or [`AsMut`], however /// it can be manually triggered via [`DetectChanges::set_changed`]. /// /// ``` /// use bevy_ecs::prelude::*; /// /// #[derive(Resource)] /// struct MyResource(u32); /// /// fn my_system(mut resource: ResMut) { /// if resource.is_changed() { /// println!("My resource was mutated!"); /// } /// /// resource.0 = 42; // triggers change detection via [`DerefMut`] /// } /// ``` /// pub trait DetectChanges { /// Returns `true` if this value was added after the system last ran. fn is_added(&self) -> bool; /// Returns `true` if this value was added or mutably dereferenced after the system last ran. fn is_changed(&self) -> bool; /// Flags this value as having been changed. /// /// Mutably accessing this smart pointer will automatically flag this value as having been changed. /// However, mutation through interior mutability requires manual reporting. /// /// **Note**: This operation cannot be undone. fn set_changed(&mut self); /// Returns the change tick recording the previous time this component (or resource) was changed. /// /// Note that components and resources are also marked as changed upon insertion. /// /// For comparison, the previous change tick of a system can be read using the /// [`SystemChangeTick`](crate::system::SystemChangeTick) /// [`SystemParam`](crate::system::SystemParam). fn last_changed(&self) -> u32; } macro_rules! change_detection_impl { ($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => { impl<$($generics),* $(: $traits)?> DetectChanges for $name<$($generics),*> { #[inline] fn is_added(&self) -> bool { self.ticks .component_ticks .is_added(self.ticks.last_change_tick, self.ticks.change_tick) } #[inline] fn is_changed(&self) -> bool { self.ticks .component_ticks .is_changed(self.ticks.last_change_tick, self.ticks.change_tick) } #[inline] fn set_changed(&mut self) { self.ticks .component_ticks .set_changed(self.ticks.change_tick); } #[inline] fn last_changed(&self) -> u32 { self.ticks.last_change_tick } } impl<$($generics),* $(: $traits)?> Deref for $name<$($generics),*> { type Target = $target; #[inline] fn deref(&self) -> &Self::Target { self.value } } impl<$($generics),* $(: $traits)?> DerefMut for $name<$($generics),*> { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { self.set_changed(); self.value } } impl<$($generics),* $(: $traits)?> AsRef<$target> for $name<$($generics),*> { #[inline] fn as_ref(&self) -> &$target { self.deref() } } impl<$($generics),* $(: $traits)?> AsMut<$target> for $name<$($generics),*> { #[inline] fn as_mut(&mut self) -> &mut $target { self.deref_mut() } } }; } macro_rules! impl_into_inner { ($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => { impl<$($generics),* $(: $traits)?> $name<$($generics),*> { /// Consume `self` and return a mutable reference to the /// contained value while marking `self` as "changed". #[inline] pub fn into_inner(mut self) -> &'a mut $target { self.set_changed(); self.value } } }; } macro_rules! impl_debug { ($name:ident < $( $generics:tt ),+ >, $($traits:ident)?) => { impl<$($generics),* $(: $traits)?> std::fmt::Debug for $name<$($generics),*> where T: std::fmt::Debug { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple(stringify!($name)) .field(self.value) .finish() } } }; } pub(crate) struct Ticks<'a> { pub(crate) component_ticks: &'a mut ComponentTicks, pub(crate) last_change_tick: u32, pub(crate) change_tick: u32, } /// Unique mutable borrow of a [`Resource`]. /// /// See the [`Resource`] documentation for usage. /// /// If you need a shared borrow, use [`Res`](crate::system::Res) instead. /// /// # Panics /// /// Panics when used as a [`SystemParam`](crate::system::SystemParam) if the resource does not exist. /// /// Use `Option>` instead if the resource might not always exist. pub struct ResMut<'a, T: Resource> { pub(crate) value: &'a mut T, pub(crate) ticks: Ticks<'a>, } change_detection_impl!(ResMut<'a, T>, T, Resource); impl_into_inner!(ResMut<'a, T>, T, Resource); impl_debug!(ResMut<'a, T>, Resource); impl<'a, T: Resource> From> for Mut<'a, T> { /// Convert this `ResMut` into a `Mut`. This allows keeping the change-detection feature of `Mut` /// while losing the specificity of `ResMut` for resources. fn from(other: ResMut<'a, T>) -> Mut<'a, T> { Mut { value: other.value, ticks: other.ticks, } } } /// Unique borrow of a non-[`Send`] resource. /// /// Only [`Send`] resources may be accessed with the [`ResMut`] [`SystemParam`](crate::system::SystemParam). In case that the /// resource does not implement `Send`, this `SystemParam` wrapper can be used. This will instruct /// the scheduler to instead run the system on the main thread so that it doesn't send the resource /// over to another thread. /// /// # Panics /// /// Panics when used as a `SystemParameter` if the resource does not exist. /// /// Use `Option>` instead if the resource might not always exist. pub struct NonSendMut<'a, T: 'static> { pub(crate) value: &'a mut T, pub(crate) ticks: Ticks<'a>, } change_detection_impl!(NonSendMut<'a, T>, T,); impl_into_inner!(NonSendMut<'a, T>, T,); impl_debug!(NonSendMut<'a, T>,); impl<'a, T: 'static> From> for Mut<'a, T> { /// Convert this `NonSendMut` into a `Mut`. This allows keeping the change-detection feature of `Mut` /// while losing the specificity of `NonSendMut`. fn from(other: NonSendMut<'a, T>) -> Mut<'a, T> { Mut { value: other.value, ticks: other.ticks, } } } /// Unique mutable borrow of an entity's component pub struct Mut<'a, T: ?Sized> { pub(crate) value: &'a mut T, pub(crate) ticks: Ticks<'a>, } change_detection_impl!(Mut<'a, T>, T,); impl_into_inner!(Mut<'a, T>, T,); impl_debug!(Mut<'a, T>,); /// Unique mutable borrow of resources or an entity's component. /// /// Similar to [`Mut`], but not generic over the component type, instead /// exposing the raw pointer as a `*mut ()`. /// /// Usually you don't need to use this and can instead use the APIs returning a /// [`Mut`], but in situations where the types are not known at compile time /// or are defined outside of rust this can be used. pub struct MutUntyped<'a> { pub(crate) value: PtrMut<'a>, pub(crate) ticks: Ticks<'a>, } impl<'a> MutUntyped<'a> { /// Returns the pointer to the value, without marking it as changed. /// /// In order to mark the value as changed, you need to call [`set_changed`](DetectChanges::set_changed) manually. pub fn into_inner(self) -> PtrMut<'a> { self.value } } impl DetectChanges for MutUntyped<'_> { fn is_added(&self) -> bool { self.ticks .component_ticks .is_added(self.ticks.last_change_tick, self.ticks.change_tick) } fn is_changed(&self) -> bool { self.ticks .component_ticks .is_changed(self.ticks.last_change_tick, self.ticks.change_tick) } fn set_changed(&mut self) { self.ticks .component_ticks .set_changed(self.ticks.change_tick); } fn last_changed(&self) -> u32 { self.ticks.last_change_tick } } impl std::fmt::Debug for MutUntyped<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("MutUntyped") .field(&self.value.as_ptr()) .finish() } } #[cfg(test)] mod tests { use bevy_ecs_macros::Resource; use crate::{ self as bevy_ecs, change_detection::{ ComponentTicks, Mut, NonSendMut, ResMut, Ticks, CHECK_TICK_THRESHOLD, MAX_CHANGE_AGE, }, component::Component, query::ChangeTrackers, system::{IntoSystem, Query, System}, world::World, }; #[derive(Component)] struct C; #[derive(Resource)] struct R; #[test] fn change_expiration() { fn change_detected(query: Query>) -> bool { query.single().is_changed() } fn change_expired(query: Query>) -> bool { query.single().is_changed() } let mut world = World::new(); // component added: 1, changed: 1 world.spawn().insert(C); let mut change_detected_system = IntoSystem::into_system(change_detected); let mut change_expired_system = IntoSystem::into_system(change_expired); change_detected_system.initialize(&mut world); change_expired_system.initialize(&mut world); // world: 1, system last ran: 0, component changed: 1 // The spawn will be detected since it happened after the system "last ran". assert!(change_detected_system.run((), &mut world)); // world: 1 + MAX_CHANGE_AGE let change_tick = world.change_tick.get_mut(); *change_tick = change_tick.wrapping_add(MAX_CHANGE_AGE); // Both the system and component appeared `MAX_CHANGE_AGE` ticks ago. // Since we clamp things to `MAX_CHANGE_AGE` for determinism, // `ComponentTicks::is_changed` will now see `MAX_CHANGE_AGE > MAX_CHANGE_AGE` // and return `false`. assert!(!change_expired_system.run((), &mut world)); } #[test] fn change_tick_wraparound() { fn change_detected(query: Query>) -> bool { query.single().is_changed() } let mut world = World::new(); world.last_change_tick = u32::MAX; *world.change_tick.get_mut() = 0; // component added: 0, changed: 0 world.spawn().insert(C); // system last ran: u32::MAX let mut change_detected_system = IntoSystem::into_system(change_detected); change_detected_system.initialize(&mut world); // Since the world is always ahead, as long as changes can't get older than `u32::MAX` (which we ensure), // the wrapping difference will always be positive, so wraparound doesn't matter. assert!(change_detected_system.run((), &mut world)); } #[test] fn change_tick_scan() { let mut world = World::new(); // component added: 1, changed: 1 world.spawn().insert(C); // a bunch of stuff happens, the component is now older than `MAX_CHANGE_AGE` *world.change_tick.get_mut() += MAX_CHANGE_AGE + CHECK_TICK_THRESHOLD; let change_tick = world.change_tick(); let mut query = world.query::>(); for tracker in query.iter(&world) { let ticks_since_insert = change_tick.wrapping_sub(tracker.component_ticks.added); let ticks_since_change = change_tick.wrapping_sub(tracker.component_ticks.changed); assert!(ticks_since_insert > MAX_CHANGE_AGE); assert!(ticks_since_change > MAX_CHANGE_AGE); } // scan change ticks and clamp those at risk of overflow world.check_change_ticks(); for tracker in query.iter(&world) { let ticks_since_insert = change_tick.wrapping_sub(tracker.component_ticks.added); let ticks_since_change = change_tick.wrapping_sub(tracker.component_ticks.changed); assert!(ticks_since_insert == MAX_CHANGE_AGE); assert!(ticks_since_change == MAX_CHANGE_AGE); } } #[test] fn mut_from_res_mut() { let mut component_ticks = ComponentTicks { added: 1, changed: 2, }; let ticks = Ticks { component_ticks: &mut component_ticks, last_change_tick: 3, change_tick: 4, }; let mut res = R {}; let res_mut = ResMut { value: &mut res, ticks, }; let into_mut: Mut = res_mut.into(); assert_eq!(1, into_mut.ticks.component_ticks.added); assert_eq!(2, into_mut.ticks.component_ticks.changed); assert_eq!(3, into_mut.ticks.last_change_tick); assert_eq!(4, into_mut.ticks.change_tick); } #[test] fn mut_from_non_send_mut() { let mut component_ticks = ComponentTicks { added: 1, changed: 2, }; let ticks = Ticks { component_ticks: &mut component_ticks, last_change_tick: 3, change_tick: 4, }; let mut res = R {}; let non_send_mut = NonSendMut { value: &mut res, ticks, }; let into_mut: Mut = non_send_mut.into(); assert_eq!(1, into_mut.ticks.component_ticks.added); assert_eq!(2, into_mut.ticks.component_ticks.changed); assert_eq!(3, into_mut.ticks.last_change_tick); assert_eq!(4, into_mut.ticks.change_tick); } }