Path: blob/main/crates/bevy_ecs/src/change_detection/traits.rs
7219 views
use crate::{change_detection::MaybeLocation, change_detection::Tick};1use alloc::borrow::ToOwned;2use core::mem;34/// Types that can read change detection information.5/// This change detection is controlled by [`DetectChangesMut`] types such as [`ResMut`].6///7/// ## Example8/// Using types that implement [`DetectChanges`], such as [`Res`], provide9/// a way to query if a value has been mutated in another system.10///11/// ```12/// use bevy_ecs::prelude::*;13///14/// #[derive(Resource)]15/// struct MyResource(u32);16///17/// fn my_system(mut resource: Res<MyResource>) {18/// if resource.is_changed() {19/// println!("My component was mutated!");20/// }21/// }22/// ```23///24/// [`Res`]: crate::change_detection::params::Res25/// [`ResMut`]: crate::change_detection::params::ResMut26pub trait DetectChanges {27/// Returns `true` if this value was added after the system last ran.28fn is_added(&self) -> bool;2930/// Returns `true` if this value was added or mutably dereferenced31/// either since the last time the system ran or, if the system never ran,32/// since the beginning of the program.33///34/// To check if the value was mutably dereferenced only,35/// use `this.is_changed() && !this.is_added()`.36fn is_changed(&self) -> bool;3738/// Returns the change tick recording the time this data was most recently changed.39///40/// Note that components and resources are also marked as changed upon insertion.41///42/// For comparison, the previous change tick of a system can be read using the43/// [`SystemChangeTick`](crate::system::SystemChangeTick)44/// [`SystemParam`](crate::system::SystemParam).45fn last_changed(&self) -> Tick;4647/// Returns the change tick recording the time this data was added.48fn added(&self) -> Tick;4950/// The location that last caused this to change.51fn changed_by(&self) -> MaybeLocation;52}5354/// Types that implement reliable change detection.55///56/// ## Example57/// Using types that implement [`DetectChangesMut`], such as [`ResMut`], provide58/// a way to query if a value has been mutated in another system.59/// Normally change detection is triggered by either [`DerefMut`] or [`AsMut`], however60/// it can be manually triggered via [`set_changed`](DetectChangesMut::set_changed).61///62/// To ensure that changes are only triggered when the value actually differs,63/// check if the value would change before assignment, such as by checking that `new != old`.64/// You must be *sure* that you are not mutably dereferencing in this process.65///66/// [`set_if_neq`](DetectChangesMut::set_if_neq) is a helper67/// method for this common functionality.68///69/// ```70/// use bevy_ecs::prelude::*;71///72/// #[derive(Resource)]73/// struct MyResource(u32);74///75/// fn my_system(mut resource: ResMut<MyResource>) {76/// if resource.is_changed() {77/// println!("My resource was mutated!");78/// }79///80/// resource.0 = 42; // triggers change detection via [`DerefMut`]81/// }82/// ```83///84/// [`ResMut`]: crate::change_detection::params::ResMut85/// [`DerefMut`]: core::ops::DerefMut86pub trait DetectChangesMut: DetectChanges {87/// The type contained within this smart pointer88///89/// For example, for `ResMut<T>` this would be `T`.90type Inner: ?Sized;9192/// Flags this value as having been changed.93///94/// Mutably accessing this smart pointer will automatically flag this value as having been changed.95/// However, mutation through interior mutability requires manual reporting.96///97/// **Note**: This operation cannot be undone.98fn set_changed(&mut self);99100/// Flags this value as having been added.101///102/// It is not normally necessary to call this method.103/// The 'added' tick is set when the value is first added,104/// and is not normally changed afterwards.105///106/// **Note**: This operation cannot be undone.107fn set_added(&mut self);108109/// Manually sets the change tick recording the time when this data was last mutated.110///111/// # Warning112/// This is a complex and error-prone operation, primarily intended for use with rollback networking strategies.113/// If you merely want to flag this data as changed, use [`set_changed`](DetectChangesMut::set_changed) instead.114/// If you want to avoid triggering change detection, use [`bypass_change_detection`](DetectChangesMut::bypass_change_detection) instead.115fn set_last_changed(&mut self, last_changed: Tick);116117/// Manually sets the added tick recording the time when this data was last added.118///119/// # Warning120/// The caveats of [`set_last_changed`](DetectChangesMut::set_last_changed) apply. This modifies both the added and changed ticks together.121fn set_last_added(&mut self, last_added: Tick);122123/// Manually bypasses change detection, allowing you to mutate the underlying value without updating the change tick.124///125/// # Warning126/// This is a risky operation, that can have unexpected consequences on any system relying on this code.127/// However, it can be an essential escape hatch when, for example,128/// you are trying to synchronize representations using change detection and need to avoid infinite recursion.129fn bypass_change_detection(&mut self) -> &mut Self::Inner;130131/// Overwrites this smart pointer with the given value, if and only if `*self != value`.132/// Returns `true` if the value was overwritten, and returns `false` if it was not.133///134/// This is useful to ensure change detection is only triggered when the underlying value135/// changes, instead of every time it is mutably accessed.136///137/// If you're dealing with non-trivial structs which have multiple fields of non-trivial size,138/// then consider applying a `map_unchanged` beforehand to allow changing only the relevant139/// field and prevent unnecessary copying and cloning.140/// See the docs of [`Mut::map_unchanged`], [`MutUntyped::map_unchanged`],141/// [`ResMut::map_unchanged`] or [`NonSendMut::map_unchanged`] for an example142///143/// If you need the previous value, use [`replace_if_neq`](DetectChangesMut::replace_if_neq).144///145/// # Examples146///147/// ```148/// # use bevy_ecs::{prelude::*, schedule::common_conditions::resource_changed};149/// #[derive(Resource, PartialEq, Eq)]150/// pub struct Score(u32);151///152/// fn reset_score(mut score: ResMut<Score>) {153/// // Set the score to zero, unless it is already zero.154/// score.set_if_neq(Score(0));155/// }156/// # let mut world = World::new();157/// # world.insert_resource(Score(1));158/// # let mut score_changed = IntoSystem::into_system(resource_changed::<Score>);159/// # score_changed.initialize(&mut world);160/// # score_changed.run((), &mut world);161/// #162/// # let mut schedule = Schedule::default();163/// # schedule.add_systems(reset_score);164/// #165/// # // first time `reset_score` runs, the score is changed.166/// # schedule.run(&mut world);167/// # assert!(score_changed.run((), &mut world).unwrap());168/// # // second time `reset_score` runs, the score is not changed.169/// # schedule.run(&mut world);170/// # assert!(!score_changed.run((), &mut world).unwrap());171/// ```172///173/// [`Mut::map_unchanged`]: crate::change_detection::params::Mut::map_unchanged174/// [`MutUntyped::map_unchanged`]: crate::change_detection::params::MutUntyped::map_unchanged175/// [`ResMut::map_unchanged`]: crate::change_detection::params::ResMut::map_unchanged176/// [`NonSendMut::map_unchanged`]: crate::change_detection::params::NonSendMut::map_unchanged177#[inline]178#[track_caller]179fn set_if_neq(&mut self, value: Self::Inner) -> bool180where181Self::Inner: Sized + PartialEq,182{183let old = self.bypass_change_detection();184if *old != value {185*old = value;186self.set_changed();187true188} else {189false190}191}192193/// Overwrites this smart pointer with the given value, if and only if `*self != value`,194/// returning the previous value if this occurs.195///196/// This is useful to ensure change detection is only triggered when the underlying value197/// changes, instead of every time it is mutably accessed.198///199/// If you're dealing with non-trivial structs which have multiple fields of non-trivial size,200/// then consider applying a `map_unchanged` beforehand to allow201/// changing only the relevant field and prevent unnecessary copying and cloning.202/// See the docs of [`Mut::map_unchanged`], [`MutUntyped::map_unchanged`],203/// [`ResMut::map_unchanged`] or [`NonSendMut::map_unchanged`] for an example204///205/// If you don't need the previous value, use [`set_if_neq`](DetectChangesMut::set_if_neq).206///207/// # Examples208///209/// ```210/// # use bevy_ecs::{prelude::*, schedule::common_conditions::{resource_changed, on_event}};211/// #[derive(Resource, PartialEq, Eq)]212/// pub struct Score(u32);213///214/// #[derive(Message, PartialEq, Eq)]215/// pub struct ScoreChanged {216/// current: u32,217/// previous: u32,218/// }219///220/// fn reset_score(mut score: ResMut<Score>, mut score_changed: MessageWriter<ScoreChanged>) {221/// // Set the score to zero, unless it is already zero.222/// let new_score = 0;223/// if let Some(Score(previous_score)) = score.replace_if_neq(Score(new_score)) {224/// // If `score` change, emit a `ScoreChanged` event.225/// score_changed.write(ScoreChanged {226/// current: new_score,227/// previous: previous_score,228/// });229/// }230/// }231/// # let mut world = World::new();232/// # world.insert_resource(Events::<ScoreChanged>::default());233/// # world.insert_resource(Score(1));234/// # let mut score_changed = IntoSystem::into_system(resource_changed::<Score>);235/// # score_changed.initialize(&mut world);236/// # score_changed.run((), &mut world);237/// #238/// # let mut score_changed_event = IntoSystem::into_system(on_event::<ScoreChanged>);239/// # score_changed_event.initialize(&mut world);240/// # score_changed_event.run((), &mut world);241/// #242/// # let mut schedule = Schedule::default();243/// # schedule.add_systems(reset_score);244/// #245/// # // first time `reset_score` runs, the score is changed.246/// # schedule.run(&mut world);247/// # assert!(score_changed.run((), &mut world).unwrap());248/// # assert!(score_changed_event.run((), &mut world).unwrap());249/// # // second time `reset_score` runs, the score is not changed.250/// # schedule.run(&mut world);251/// # assert!(!score_changed.run((), &mut world).unwrap());252/// # assert!(!score_changed_event.run((), &mut world).unwrap());253/// ```254///255/// [`Mut::map_unchanged`]: crate::change_detection::params::Mut::map_unchanged256/// [`MutUntyped::map_unchanged`]: crate::change_detection::params::MutUntyped::map_unchanged257/// [`ResMut::map_unchanged`]: crate::change_detection::params::ResMut::map_unchanged258/// [`NonSendMut::map_unchanged`]: crate::change_detection::params::NonSendMut::map_unchanged259#[inline]260#[must_use = "If you don't need to handle the previous value, use `set_if_neq` instead."]261fn replace_if_neq(&mut self, value: Self::Inner) -> Option<Self::Inner>262where263Self::Inner: Sized + PartialEq,264{265let old = self.bypass_change_detection();266if *old != value {267let previous = mem::replace(old, value);268self.set_changed();269Some(previous)270} else {271None272}273}274275/// Overwrites this smart pointer with a clone of the given value, if and only if `*self != value`.276/// Returns `true` if the value was overwritten, and returns `false` if it was not.277///278/// This method is useful when the caller only has a borrowed form of `Inner`,279/// e.g. when writing a `&str` into a `Mut<String>`.280///281/// # Examples282/// ```283/// # extern crate alloc;284/// # use alloc::borrow::ToOwned;285/// # use bevy_ecs::{prelude::*, schedule::common_conditions::resource_changed};286/// #[derive(Resource)]287/// pub struct Message(String);288///289/// fn update_message(mut message: ResMut<Message>) {290/// // Set the score to zero, unless it is already zero.291/// ResMut::map_unchanged(message, |Message(msg)| msg).clone_from_if_neq("another string");292/// }293/// # let mut world = World::new();294/// # world.insert_resource(Message("initial string".into()));295/// # let mut message_changed = IntoSystem::into_system(resource_changed::<Message>);296/// # message_changed.initialize(&mut world);297/// # message_changed.run((), &mut world);298/// #299/// # let mut schedule = Schedule::default();300/// # schedule.add_systems(update_message);301/// #302/// # // first time `reset_score` runs, the score is changed.303/// # schedule.run(&mut world);304/// # assert!(message_changed.run((), &mut world).unwrap());305/// # // second time `reset_score` runs, the score is not changed.306/// # schedule.run(&mut world);307/// # assert!(!message_changed.run((), &mut world).unwrap());308/// ```309fn clone_from_if_neq<T>(&mut self, value: &T) -> bool310where311T: ToOwned<Owned = Self::Inner> + ?Sized,312Self::Inner: PartialEq<T>,313{314let old = self.bypass_change_detection();315if old != value {316value.clone_into(old);317self.set_changed();318true319} else {320false321}322}323}324325macro_rules! change_detection_impl {326($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {327impl<$($generics),* : ?Sized $(+ $traits)?> DetectChanges for $name<$($generics),*> {328#[inline]329fn is_added(&self) -> bool {330self.ticks331.added332.is_newer_than(self.ticks.last_run, self.ticks.this_run)333}334335#[inline]336fn is_changed(&self) -> bool {337self.ticks338.changed339.is_newer_than(self.ticks.last_run, self.ticks.this_run)340}341342#[inline]343fn last_changed(&self) -> Tick {344*self.ticks.changed345}346347#[inline]348fn added(&self) -> Tick {349*self.ticks.added350}351352#[inline]353fn changed_by(&self) -> MaybeLocation {354self.ticks.changed_by.copied()355}356}357358impl<$($generics),*: ?Sized $(+ $traits)?> Deref for $name<$($generics),*> {359type Target = $target;360361#[inline]362fn deref(&self) -> &Self::Target {363self.value364}365}366367impl<$($generics),* $(: $traits)?> AsRef<$target> for $name<$($generics),*> {368#[inline]369fn as_ref(&self) -> &$target {370self.deref()371}372}373}374}375376pub(crate) use change_detection_impl;377378macro_rules! change_detection_mut_impl {379($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {380impl<$($generics),* : ?Sized $(+ $traits)?> DetectChangesMut for $name<$($generics),*> {381type Inner = $target;382383#[inline]384#[track_caller]385fn set_changed(&mut self) {386*self.ticks.changed = self.ticks.this_run;387self.ticks.changed_by.assign(MaybeLocation::caller());388}389390#[inline]391#[track_caller]392fn set_added(&mut self) {393*self.ticks.changed = self.ticks.this_run;394*self.ticks.added = self.ticks.this_run;395self.ticks.changed_by.assign(MaybeLocation::caller());396}397398#[inline]399#[track_caller]400fn set_last_changed(&mut self, last_changed: Tick) {401*self.ticks.changed = last_changed;402self.ticks.changed_by.assign(MaybeLocation::caller());403}404405#[inline]406#[track_caller]407fn set_last_added(&mut self, last_added: Tick) {408*self.ticks.added = last_added;409*self.ticks.changed = last_added;410self.ticks.changed_by.assign(MaybeLocation::caller());411}412413#[inline]414fn bypass_change_detection(&mut self) -> &mut Self::Inner {415self.value416}417}418419impl<$($generics),* : ?Sized $(+ $traits)?> DerefMut for $name<$($generics),*> {420#[inline]421#[track_caller]422fn deref_mut(&mut self) -> &mut Self::Target {423self.set_changed();424self.ticks.changed_by.assign(MaybeLocation::caller());425self.value426}427}428429impl<$($generics),* $(: $traits)?> AsMut<$target> for $name<$($generics),*> {430#[inline]431fn as_mut(&mut self) -> &mut $target {432self.deref_mut()433}434}435};436}437438pub(crate) use change_detection_mut_impl;439440macro_rules! impl_methods {441($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {442impl<$($generics),* : ?Sized $(+ $traits)?> $name<$($generics),*> {443/// Consume `self` and return a mutable reference to the444/// contained value while marking `self` as "changed".445#[inline]446pub fn into_inner(mut self) -> &'w mut $target {447self.set_changed();448self.value449}450451/// Returns a `Mut<>` with a smaller lifetime.452/// This is useful if you have `&mut453#[doc = stringify!($name)]454/// <T>`, but you need a `Mut<T>`.455pub fn reborrow(&mut self) -> Mut<'_, $target> {456Mut {457value: self.value,458ticks: ComponentTicksMut {459added: self.ticks.added,460changed: self.ticks.changed,461changed_by: self.ticks.changed_by.as_deref_mut(),462last_run: self.ticks.last_run,463this_run: self.ticks.this_run,464},465}466}467468/// Maps to an inner value by applying a function to the contained reference, without flagging a change.469///470/// You should never modify the argument passed to the closure -- if you want to modify the data471/// without flagging a change, consider using [`DetectChangesMut::bypass_change_detection`] to make your intent explicit.472///473/// ```474/// # use bevy_ecs::prelude::*;475/// # #[derive(PartialEq)] pub struct Vec2;476/// # impl Vec2 { pub const ZERO: Self = Self; }477/// # #[derive(Component)] pub struct Transform { translation: Vec2 }478/// // When run, zeroes the translation of every entity.479/// fn reset_positions(mut transforms: Query<&mut Transform>) {480/// for transform in &mut transforms {481/// // We pinky promise not to modify `t` within the closure.482/// // Breaking this promise will result in logic errors, but will never cause undefined behavior.483/// let mut translation = transform.map_unchanged(|t| &mut t.translation);484/// // Only reset the translation if it isn't already zero;485/// translation.set_if_neq(Vec2::ZERO);486/// }487/// }488/// # bevy_ecs::system::assert_is_system(reset_positions);489/// ```490pub fn map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> &mut U) -> Mut<'w, U> {491Mut {492value: f(self.value),493ticks: self.ticks,494}495}496497/// Optionally maps to an inner value by applying a function to the contained reference.498/// This is useful in a situation where you need to convert a `Mut<T>` to a `Mut<U>`, but only if `T` contains `U`.499///500/// As with `map_unchanged`, you should never modify the argument passed to the closure.501pub fn filter_map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> Option<&mut U>) -> Option<Mut<'w, U>> {502let value = f(self.value);503value.map(|value| Mut {504value,505ticks: self.ticks,506})507}508509/// Optionally maps to an inner value by applying a function to the contained reference, returns an error on failure.510/// This is useful in a situation where you need to convert a `Mut<T>` to a `Mut<U>`, but only if `T` contains `U`.511///512/// As with `map_unchanged`, you should never modify the argument passed to the closure.513pub fn try_map_unchanged<U: ?Sized, E>(self, f: impl FnOnce(&mut $target) -> Result<&mut U, E>) -> Result<Mut<'w, U>, E> {514let value = f(self.value);515value.map(|value| Mut {516value,517ticks: self.ticks,518})519}520521/// Allows you access to the dereferenced value of this pointer without immediately522/// triggering change detection.523pub fn as_deref_mut(&mut self) -> Mut<'_, <$target as Deref>::Target>524where $target: DerefMut525{526self.reborrow().map_unchanged(|v| v.deref_mut())527}528529}530};531}532533pub(crate) use impl_methods;534535macro_rules! impl_debug {536($name:ident < $( $generics:tt ),+ >, $($traits:ident)?) => {537impl<$($generics),* : ?Sized $(+ $traits)?> core::fmt::Debug for $name<$($generics),*>538where T: core::fmt::Debug539{540fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {541f.debug_tuple(stringify!($name))542.field(&self.value)543.finish()544}545}546547};548}549550pub(crate) use impl_debug;551552553