Path: blob/main/crates/bevy_ecs/src/change_detection/traits.rs
9395 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// NOTE: if you are changing the following comment also change the [`ContiguousMut::bypass_change_detection`] comment.124/// Manually bypasses change detection, allowing you to mutate the underlying value without updating the change tick.125///126/// # Warning127/// This is a risky operation, that can have unexpected consequences on any system relying on this code.128/// However, it can be an essential escape hatch when, for example,129/// you are trying to synchronize representations using change detection and need to avoid infinite recursion.130fn bypass_change_detection(&mut self) -> &mut Self::Inner;131132/// Overwrites this smart pointer with the given value, if and only if `*self != value`.133/// Returns `true` if the value was overwritten, and returns `false` if it was not.134///135/// This is useful to ensure change detection is only triggered when the underlying value136/// changes, instead of every time it is mutably accessed.137///138/// If you're dealing with non-trivial structs which have multiple fields of non-trivial size,139/// then consider applying a `map_unchanged` beforehand to allow changing only the relevant140/// field and prevent unnecessary copying and cloning.141/// See the docs of [`Mut::map_unchanged`], [`MutUntyped::map_unchanged`],142/// [`ResMut::map_unchanged`] or [`NonSendMut::map_unchanged`] for an example143///144/// If you need the previous value, use [`replace_if_neq`](DetectChangesMut::replace_if_neq).145///146/// # Examples147///148/// ```149/// # use bevy_ecs::{prelude::*, schedule::common_conditions::resource_changed};150/// #[derive(Resource, PartialEq, Eq)]151/// pub struct Score(u32);152///153/// fn reset_score(mut score: ResMut<Score>) {154/// // Set the score to zero, unless it is already zero.155/// score.set_if_neq(Score(0));156/// }157/// # let mut world = World::new();158/// # world.insert_resource(Score(1));159/// # let mut score_changed = IntoSystem::into_system(resource_changed::<Score>);160/// # score_changed.initialize(&mut world);161/// # score_changed.run((), &mut world);162/// #163/// # let mut schedule = Schedule::default();164/// # schedule.add_systems(reset_score);165/// #166/// # // first time `reset_score` runs, the score is changed.167/// # schedule.run(&mut world);168/// # assert!(score_changed.run((), &mut world).unwrap());169/// # // second time `reset_score` runs, the score is not changed.170/// # schedule.run(&mut world);171/// # assert!(!score_changed.run((), &mut world).unwrap());172/// ```173///174/// [`Mut::map_unchanged`]: crate::change_detection::params::Mut::map_unchanged175/// [`MutUntyped::map_unchanged`]: crate::change_detection::params::MutUntyped::map_unchanged176/// [`ResMut::map_unchanged`]: crate::change_detection::params::ResMut::map_unchanged177/// [`NonSendMut::map_unchanged`]: crate::change_detection::params::NonSendMut::map_unchanged178#[inline]179#[track_caller]180fn set_if_neq(&mut self, value: Self::Inner) -> bool181where182Self::Inner: Sized + PartialEq,183{184let old = self.bypass_change_detection();185if *old != value {186*old = value;187self.set_changed();188true189} else {190false191}192}193194/// Overwrites this smart pointer with the given value, if and only if `*self != value`,195/// returning the previous value if this occurs.196///197/// This is useful to ensure change detection is only triggered when the underlying value198/// changes, instead of every time it is mutably accessed.199///200/// If you're dealing with non-trivial structs which have multiple fields of non-trivial size,201/// then consider applying a `map_unchanged` beforehand to allow202/// changing only the relevant field and prevent unnecessary copying and cloning.203/// See the docs of [`Mut::map_unchanged`], [`MutUntyped::map_unchanged`],204/// [`ResMut::map_unchanged`] or [`NonSendMut::map_unchanged`] for an example205///206/// If you don't need the previous value, use [`set_if_neq`](DetectChangesMut::set_if_neq).207///208/// # Examples209///210/// ```211/// # use bevy_ecs::{prelude::*, schedule::common_conditions::{resource_changed, on_message}};212/// #[derive(Resource, PartialEq, Eq)]213/// pub struct Score(u32);214///215/// #[derive(Message, PartialEq, Eq)]216/// pub struct ScoreChanged {217/// current: u32,218/// previous: u32,219/// }220///221/// fn reset_score(mut score: ResMut<Score>, mut score_changed: MessageWriter<ScoreChanged>) {222/// // Set the score to zero, unless it is already zero.223/// let new_score = 0;224/// if let Some(Score(previous_score)) = score.replace_if_neq(Score(new_score)) {225/// // If `score` change, emit a `ScoreChanged` event.226/// score_changed.write(ScoreChanged {227/// current: new_score,228/// previous: previous_score,229/// });230/// }231/// }232/// # let mut world = World::new();233/// # world.insert_resource(Messages::<ScoreChanged>::default());234/// # world.insert_resource(Score(1));235/// # let mut score_changed = IntoSystem::into_system(resource_changed::<Score>);236/// # score_changed.initialize(&mut world);237/// # score_changed.run((), &mut world);238/// #239/// # let mut score_changed_event = IntoSystem::into_system(on_message::<ScoreChanged>);240/// # score_changed_event.initialize(&mut world);241/// # score_changed_event.run((), &mut world);242/// #243/// # let mut schedule = Schedule::default();244/// # schedule.add_systems(reset_score);245/// #246/// # // first time `reset_score` runs, the score is changed.247/// # schedule.run(&mut world);248/// # assert!(score_changed.run((), &mut world).unwrap());249/// # assert!(score_changed_event.run((), &mut world).unwrap());250/// # // second time `reset_score` runs, the score is not changed.251/// # schedule.run(&mut world);252/// # assert!(!score_changed.run((), &mut world).unwrap());253/// # assert!(!score_changed_event.run((), &mut world).unwrap());254/// ```255///256/// [`Mut::map_unchanged`]: crate::change_detection::params::Mut::map_unchanged257/// [`MutUntyped::map_unchanged`]: crate::change_detection::params::MutUntyped::map_unchanged258/// [`ResMut::map_unchanged`]: crate::change_detection::params::ResMut::map_unchanged259/// [`NonSendMut::map_unchanged`]: crate::change_detection::params::NonSendMut::map_unchanged260#[inline]261#[must_use = "If you don't need to handle the previous value, use `set_if_neq` instead."]262fn replace_if_neq(&mut self, value: Self::Inner) -> Option<Self::Inner>263where264Self::Inner: Sized + PartialEq,265{266let old = self.bypass_change_detection();267if *old != value {268let previous = mem::replace(old, value);269self.set_changed();270Some(previous)271} else {272None273}274}275276/// Overwrites this smart pointer with a clone of the given value, if and only if `*self != value`.277/// Returns `true` if the value was overwritten, and returns `false` if it was not.278///279/// This method is useful when the caller only has a borrowed form of `Inner`,280/// e.g. when writing a `&str` into a `Mut<String>`.281///282/// # Examples283/// ```284/// # extern crate alloc;285/// # use alloc::borrow::ToOwned;286/// # use bevy_ecs::{prelude::*, schedule::common_conditions::resource_changed};287/// #[derive(Resource)]288/// pub struct Message(String);289///290/// fn update_message(mut message: ResMut<Message>) {291/// // Set the score to zero, unless it is already zero.292/// ResMut::map_unchanged(message, |Message(msg)| msg).clone_from_if_neq("another string");293/// }294/// # let mut world = World::new();295/// # world.insert_resource(Message("initial string".into()));296/// # let mut message_changed = IntoSystem::into_system(resource_changed::<Message>);297/// # message_changed.initialize(&mut world);298/// # message_changed.run((), &mut world);299/// #300/// # let mut schedule = Schedule::default();301/// # schedule.add_systems(update_message);302/// #303/// # // first time `reset_score` runs, the score is changed.304/// # schedule.run(&mut world);305/// # assert!(message_changed.run((), &mut world).unwrap());306/// # // second time `reset_score` runs, the score is not changed.307/// # schedule.run(&mut world);308/// # assert!(!message_changed.run((), &mut world).unwrap());309/// ```310fn clone_from_if_neq<T>(&mut self, value: &T) -> bool311where312T: ToOwned<Owned = Self::Inner> + ?Sized,313Self::Inner: PartialEq<T>,314{315let old = self.bypass_change_detection();316if old != value {317value.clone_into(old);318self.set_changed();319true320} else {321false322}323}324}325326macro_rules! change_detection_impl {327($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {328impl<$($generics),* : ?Sized $(+ $traits)?> DetectChanges for $name<$($generics),*> {329#[inline]330fn is_added(&self) -> bool {331self.ticks332.added333.is_newer_than(self.ticks.last_run, self.ticks.this_run)334}335336#[inline]337fn is_changed(&self) -> bool {338self.ticks339.changed340.is_newer_than(self.ticks.last_run, self.ticks.this_run)341}342343#[inline]344fn last_changed(&self) -> Tick {345*self.ticks.changed346}347348#[inline]349fn added(&self) -> Tick {350*self.ticks.added351}352353#[inline]354fn changed_by(&self) -> MaybeLocation {355self.ticks.changed_by.copied()356}357}358359impl<$($generics),*: ?Sized $(+ $traits)?> Deref for $name<$($generics),*> {360type Target = $target;361362#[inline]363fn deref(&self) -> &Self::Target {364self.value365}366}367368impl<$($generics),* $(: $traits)?> AsRef<$target> for $name<$($generics),*> {369#[inline]370fn as_ref(&self) -> &$target {371self.deref()372}373}374}375}376377pub(crate) use change_detection_impl;378379macro_rules! change_detection_mut_impl {380($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {381impl<$($generics),* : ?Sized $(+ $traits)?> DetectChangesMut for $name<$($generics),*> {382type Inner = $target;383384#[inline]385#[track_caller]386fn set_changed(&mut self) {387*self.ticks.changed = self.ticks.this_run;388self.ticks.changed_by.assign(MaybeLocation::caller());389}390391#[inline]392#[track_caller]393fn set_added(&mut self) {394*self.ticks.changed = self.ticks.this_run;395*self.ticks.added = self.ticks.this_run;396self.ticks.changed_by.assign(MaybeLocation::caller());397}398399#[inline]400#[track_caller]401fn set_last_changed(&mut self, last_changed: Tick) {402*self.ticks.changed = last_changed;403self.ticks.changed_by.assign(MaybeLocation::caller());404}405406#[inline]407#[track_caller]408fn set_last_added(&mut self, last_added: Tick) {409*self.ticks.added = last_added;410*self.ticks.changed = last_added;411self.ticks.changed_by.assign(MaybeLocation::caller());412}413414#[inline]415fn bypass_change_detection(&mut self) -> &mut Self::Inner {416self.value417}418}419420impl<$($generics),* : ?Sized $(+ $traits)?> DerefMut for $name<$($generics),*> {421#[inline]422#[track_caller]423fn deref_mut(&mut self) -> &mut Self::Target {424self.set_changed();425self.ticks.changed_by.assign(MaybeLocation::caller());426self.value427}428}429430impl<$($generics),* $(: $traits)?> AsMut<$target> for $name<$($generics),*> {431#[inline]432fn as_mut(&mut self) -> &mut $target {433self.deref_mut()434}435}436};437}438439pub(crate) use change_detection_mut_impl;440441macro_rules! impl_methods {442($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {443impl<$($generics),* : ?Sized $(+ $traits)?> $name<$($generics),*> {444/// Consume `self` and return a mutable reference to the445/// contained value while marking `self` as "changed".446#[inline]447pub fn into_inner(mut self) -> &'w mut $target {448self.set_changed();449self.value450}451452/// Returns a `Mut<>` with a smaller lifetime.453/// This is useful if you have `&mut454#[doc = stringify!($name)]455/// <T>`, but you need a `Mut<T>`.456pub fn reborrow(&mut self) -> Mut<'_, $target> {457Mut {458value: self.value,459ticks: ComponentTicksMut {460added: self.ticks.added,461changed: self.ticks.changed,462changed_by: self.ticks.changed_by.as_deref_mut(),463last_run: self.ticks.last_run,464this_run: self.ticks.this_run,465},466}467}468469/// Maps to an inner value by applying a function to the contained reference, without flagging a change.470///471/// You should never modify the argument passed to the closure -- if you want to modify the data472/// without flagging a change, consider using [`DetectChangesMut::bypass_change_detection`] to make your intent explicit.473///474/// ```475/// # use bevy_ecs::prelude::*;476/// # #[derive(PartialEq)] pub struct Vec2;477/// # impl Vec2 { pub const ZERO: Self = Self; }478/// # #[derive(Component)] pub struct Transform { translation: Vec2 }479/// // When run, zeroes the translation of every entity.480/// fn reset_positions(mut transforms: Query<&mut Transform>) {481/// for transform in &mut transforms {482/// // We pinky promise not to modify `t` within the closure.483/// // Breaking this promise will result in logic errors, but will never cause undefined behavior.484/// let mut translation = transform.map_unchanged(|t| &mut t.translation);485/// // Only reset the translation if it isn't already zero;486/// translation.set_if_neq(Vec2::ZERO);487/// }488/// }489/// # bevy_ecs::system::assert_is_system(reset_positions);490/// ```491pub fn map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> &mut U) -> Mut<'w, U> {492Mut {493value: f(self.value),494ticks: self.ticks,495}496}497498/// Optionally maps to an inner value by applying a function to the contained reference.499/// This is useful in a situation where you need to convert a `Mut<T>` to a `Mut<U>`, but only if `T` contains `U`.500///501/// As with `map_unchanged`, you should never modify the argument passed to the closure.502pub fn filter_map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> Option<&mut U>) -> Option<Mut<'w, U>> {503let value = f(self.value);504value.map(|value| Mut {505value,506ticks: self.ticks,507})508}509510/// Optionally maps to an inner value by applying a function to the contained reference, returns an error on failure.511/// This is useful in a situation where you need to convert a `Mut<T>` to a `Mut<U>`, but only if `T` contains `U`.512///513/// As with `map_unchanged`, you should never modify the argument passed to the closure.514pub fn try_map_unchanged<U: ?Sized, E>(self, f: impl FnOnce(&mut $target) -> Result<&mut U, E>) -> Result<Mut<'w, U>, E> {515let value = f(self.value);516value.map(|value| Mut {517value,518ticks: self.ticks,519})520}521522/// Allows you access to the dereferenced value of this pointer without immediately523/// triggering change detection.524pub fn as_deref_mut(&mut self) -> Mut<'_, <$target as Deref>::Target>525where $target: DerefMut526{527self.reborrow().map_unchanged(|v| v.deref_mut())528}529530}531};532}533534pub(crate) use impl_methods;535536macro_rules! impl_debug {537($name:ident < $( $generics:tt ),+ >, $($traits:ident)?) => {538impl<$($generics),* : ?Sized $(+ $traits)?> core::fmt::Debug for $name<$($generics),*>539where T: core::fmt::Debug540{541fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {542f.debug_tuple(stringify!($name))543.field(&self.value)544.finish()545}546}547548};549}550551pub(crate) use impl_debug;552553554