Skip to main content

bevy_ecs/change_detection/
traits.rs

1use crate::{change_detection::MaybeLocation, change_detection::Tick};
2use alloc::borrow::ToOwned;
3use core::mem;
4
5/// Types that can read change detection information.
6/// This change detection is controlled by [`DetectChangesMut`] types such as [`ResMut`].
7///
8/// ## Example
9/// Using types that implement [`DetectChanges`], such as [`Res`], provide
10/// a way to query if a value has been mutated in another system.
11///
12/// ```
13/// use bevy_ecs::prelude::*;
14///
15/// #[derive(Resource)]
16/// struct MyResource(u32);
17///
18/// fn my_system(mut resource: Res<MyResource>) {
19///     if resource.is_changed() {
20///         println!("My component was mutated!");
21///     }
22/// }
23/// ```
24///
25/// [`Res`]: crate::change_detection::params::Res
26/// [`ResMut`]: crate::change_detection::params::ResMut
27pub trait DetectChanges {
28    /// Returns `true` if this value was added after the system last ran.
29    fn is_added(&self) -> bool;
30
31    /// Returns `true` if this value was added or mutably dereferenced
32    /// either since the last time the system ran or, if the system never ran,
33    /// since the beginning of the program.
34    ///
35    /// To check if the value was mutably dereferenced only,
36    /// use `this.is_changed() && !this.is_added()`.
37    fn is_changed(&self) -> bool;
38
39    /// Returns `true` if this value was added after the `other` tick.
40    fn is_added_after(&self, other: Tick) -> bool;
41
42    /// Returns `true` if this value was added or mutably dereferenced
43    /// after the `other` tick.
44    ///
45    /// # Example
46    ///
47    /// ```
48    /// # use bevy_ecs::prelude::*;
49    /// # #[derive(Component)]
50    /// # struct Source;
51    /// # #[derive(Component)]
52    /// # struct Target;
53    /// #
54    /// # impl Target {
55    /// #     fn from_source(_: &Source) -> Self {
56    /// #         Target
57    /// #     }
58    /// # }
59    /// #
60    /// fn system(query: Query<(Ref<Source>, &mut Target)>) {
61    ///     for (source, mut target) in query {
62    ///         // Only convert the source to the target if the source is newer
63    ///         if source.is_changed_after(target.last_changed()) {
64    ///             *target = Target::from_source(&source);
65    ///         }
66    ///     }
67    /// }
68    /// #
69    /// # bevy_ecs::system::assert_is_system(system);
70    /// ```
71    fn is_changed_after(&self, other: Tick) -> bool;
72
73    /// Returns the change tick recording the time this data was most recently changed.
74    ///
75    /// Note that components and resources are also marked as changed upon insertion.
76    ///
77    /// For comparison, the previous change tick of a system can be read using the
78    /// [`SystemChangeTick`](crate::system::SystemChangeTick)
79    /// [`SystemParam`](crate::system::SystemParam).
80    fn last_changed(&self) -> Tick;
81
82    /// Returns the change tick recording the time this data was added.
83    fn added(&self) -> Tick;
84
85    /// Returns the change tick of the current run of this system.
86    fn this_run(&self) -> Tick;
87
88    /// Returns the change tick of the last run of this system.
89    fn last_run(&self) -> Tick;
90
91    /// The location that last caused this to change.
92    fn changed_by(&self) -> MaybeLocation;
93}
94
95/// Types that implement reliable change detection.
96///
97/// ## Example
98/// Using types that implement [`DetectChangesMut`], such as [`ResMut`], provide
99/// a way to query if a value has been mutated in another system.
100/// Normally change detection is triggered by either [`DerefMut`] or [`AsMut`], however
101/// it can be manually triggered via [`set_changed`](DetectChangesMut::set_changed).
102///
103/// To ensure that changes are only triggered when the value actually differs,
104/// check if the value would change before assignment, such as by checking that `new != old`.
105/// You must be *sure* that you are not mutably dereferencing in this process.
106///
107/// [`set_if_neq`](DetectChangesMut::set_if_neq) is a helper
108/// method for this common functionality.
109///
110/// ```
111/// use bevy_ecs::prelude::*;
112///
113/// #[derive(Resource)]
114/// struct MyResource(u32);
115///
116/// fn my_system(mut resource: ResMut<MyResource>) {
117///     if resource.is_changed() {
118///         println!("My resource was mutated!");
119///     }
120///
121///    resource.0 = 42; // triggers change detection via [`DerefMut`]
122/// }
123/// ```
124///
125/// [`ResMut`]: crate::change_detection::params::ResMut
126/// [`DerefMut`]: core::ops::DerefMut
127pub trait DetectChangesMut: DetectChanges {
128    /// The type contained within this smart pointer
129    ///
130    /// For example, for `ResMut<T>` this would be `T`.
131    type Inner: ?Sized;
132
133    /// Flags this value as having been changed.
134    ///
135    /// Mutably accessing this smart pointer will automatically flag this value as having been changed.
136    /// However, mutation through interior mutability requires manual reporting.
137    ///
138    /// **Note**: This operation cannot be undone.
139    fn set_changed(&mut self);
140
141    /// Flags this value as having been added.
142    ///
143    /// It is not normally necessary to call this method.
144    /// The 'added' tick is set when the value is first added,
145    /// and is not normally changed afterwards.
146    ///
147    /// **Note**: This operation cannot be undone.
148    fn set_added(&mut self);
149
150    /// Manually sets the change tick recording the time when this data was last mutated.
151    ///
152    /// # Warning
153    /// This is a complex and error-prone operation, primarily intended for use with rollback networking strategies.
154    /// If you merely want to flag this data as changed, use [`set_changed`](DetectChangesMut::set_changed) instead.
155    /// If you want to avoid triggering change detection, use [`bypass_change_detection`](DetectChangesMut::bypass_change_detection) instead.
156    fn set_last_changed(&mut self, last_changed: Tick);
157
158    /// Manually sets the added tick recording the time when this data was last added.
159    ///
160    /// # Warning
161    /// The caveats of [`set_last_changed`](DetectChangesMut::set_last_changed) apply. This modifies both the added and changed ticks together.
162    fn set_last_added(&mut self, last_added: Tick);
163
164    // NOTE: if you are changing the following comment also change the [`ContiguousMut::bypass_change_detection`] comment.
165    /// Manually bypasses change detection, allowing you to mutate the underlying value without updating the change tick.
166    ///
167    /// # Warning
168    /// This is a risky operation, that can have unexpected consequences on any system relying on this code.
169    /// However, it can be an essential escape hatch when, for example,
170    /// you are trying to synchronize representations using change detection and need to avoid infinite recursion.
171    fn bypass_change_detection(&mut self) -> &mut Self::Inner;
172
173    /// Overwrites this smart pointer with the given value, if and only if `*self != value`.
174    /// Returns `true` if the value was overwritten, and returns `false` if it was not.
175    ///
176    /// This is useful to ensure change detection is only triggered when the underlying value
177    /// changes, instead of every time it is mutably accessed.
178    ///
179    /// If you're dealing with non-trivial structs which have multiple fields of non-trivial size,
180    /// then consider applying a `map_unchanged` beforehand to allow changing only the relevant
181    /// field and prevent unnecessary copying and cloning.
182    /// See the docs of [`Mut::map_unchanged`], [`MutUntyped::map_unchanged`],
183    /// [`ResMut::map_unchanged`] or [`NonSendMut::map_unchanged`] for an example
184    ///
185    /// If you need the previous value, use [`replace_if_neq`](DetectChangesMut::replace_if_neq).
186    ///
187    /// # Examples
188    ///
189    /// ```
190    /// # use bevy_ecs::{prelude::*, schedule::common_conditions::resource_changed};
191    /// #[derive(Resource, PartialEq, Eq)]
192    /// pub struct Score(u32);
193    ///
194    /// fn reset_score(mut score: ResMut<Score>) {
195    ///     // Set the score to zero, unless it is already zero.
196    ///     score.set_if_neq(Score(0));
197    /// }
198    /// # let mut world = World::new();
199    /// # world.insert_resource(Score(1));
200    /// # let mut score_changed = IntoSystem::into_system(resource_changed::<Score>);
201    /// # score_changed.initialize(&mut world);
202    /// # score_changed.run((), &mut world);
203    /// #
204    /// # let mut schedule = Schedule::default();
205    /// # schedule.add_systems(reset_score);
206    /// #
207    /// # // first time `reset_score` runs, the score is changed.
208    /// # schedule.run(&mut world);
209    /// # assert!(score_changed.run((), &mut world).unwrap());
210    /// # // second time `reset_score` runs, the score is not changed.
211    /// # schedule.run(&mut world);
212    /// # assert!(!score_changed.run((), &mut world).unwrap());
213    /// ```
214    ///
215    /// [`Mut::map_unchanged`]: crate::change_detection::params::Mut::map_unchanged
216    /// [`MutUntyped::map_unchanged`]: crate::change_detection::params::MutUntyped::map_unchanged
217    /// [`ResMut::map_unchanged`]: crate::change_detection::params::ResMut::map_unchanged
218    /// [`NonSendMut::map_unchanged`]: crate::change_detection::params::NonSendMut::map_unchanged
219    #[inline]
220    #[track_caller]
221    fn set_if_neq(&mut self, value: Self::Inner) -> bool
222    where
223        Self::Inner: Sized + PartialEq,
224    {
225        let old = self.bypass_change_detection();
226        if *old != value {
227            *old = value;
228            self.set_changed();
229            true
230        } else {
231            false
232        }
233    }
234
235    /// Overwrites this smart pointer with the given value, if and only if `*self != value`,
236    /// returning the previous value if this occurs.
237    ///
238    /// This is useful to ensure change detection is only triggered when the underlying value
239    /// changes, instead of every time it is mutably accessed.
240    ///
241    /// If you're dealing with non-trivial structs which have multiple fields of non-trivial size,
242    /// then consider applying a `map_unchanged` beforehand to allow
243    /// changing only the relevant field and prevent unnecessary copying and cloning.
244    /// See the docs of [`Mut::map_unchanged`], [`MutUntyped::map_unchanged`],
245    /// [`ResMut::map_unchanged`] or [`NonSendMut::map_unchanged`] for an example
246    ///
247    /// If you don't need the previous value, use [`set_if_neq`](DetectChangesMut::set_if_neq).
248    ///
249    /// # Examples
250    ///
251    /// ```
252    /// # use bevy_ecs::{prelude::*, schedule::common_conditions::{resource_changed, on_message}};
253    /// #[derive(Resource, PartialEq, Eq)]
254    /// pub struct Score(u32);
255    ///
256    /// #[derive(Message, PartialEq, Eq)]
257    /// pub struct ScoreChanged {
258    ///     current: u32,
259    ///     previous: u32,
260    /// }
261    ///
262    /// fn reset_score(mut score: ResMut<Score>, mut score_changed: MessageWriter<ScoreChanged>) {
263    ///     // Set the score to zero, unless it is already zero.
264    ///     let new_score = 0;
265    ///     if let Some(Score(previous_score)) = score.replace_if_neq(Score(new_score)) {
266    ///         // If `score` change, emit a `ScoreChanged` event.
267    ///         score_changed.write(ScoreChanged {
268    ///             current: new_score,
269    ///             previous: previous_score,
270    ///         });
271    ///     }
272    /// }
273    /// # let mut world = World::new();
274    /// # world.insert_resource(Messages::<ScoreChanged>::default());
275    /// # world.insert_resource(Score(1));
276    /// # let mut score_changed = IntoSystem::into_system(resource_changed::<Score>);
277    /// # score_changed.initialize(&mut world);
278    /// # score_changed.run((), &mut world);
279    /// #
280    /// # let mut score_changed_event = IntoSystem::into_system(on_message::<ScoreChanged>);
281    /// # score_changed_event.initialize(&mut world);
282    /// # score_changed_event.run((), &mut world);
283    /// #
284    /// # let mut schedule = Schedule::default();
285    /// # schedule.add_systems(reset_score);
286    /// #
287    /// # // first time `reset_score` runs, the score is changed.
288    /// # schedule.run(&mut world);
289    /// # assert!(score_changed.run((), &mut world).unwrap());
290    /// # assert!(score_changed_event.run((), &mut world).unwrap());
291    /// # // second time `reset_score` runs, the score is not changed.
292    /// # schedule.run(&mut world);
293    /// # assert!(!score_changed.run((), &mut world).unwrap());
294    /// # assert!(!score_changed_event.run((), &mut world).unwrap());
295    /// ```
296    ///
297    /// [`Mut::map_unchanged`]: crate::change_detection::params::Mut::map_unchanged
298    /// [`MutUntyped::map_unchanged`]: crate::change_detection::params::MutUntyped::map_unchanged
299    /// [`ResMut::map_unchanged`]: crate::change_detection::params::ResMut::map_unchanged
300    /// [`NonSendMut::map_unchanged`]: crate::change_detection::params::NonSendMut::map_unchanged
301    #[inline]
302    #[must_use = "If you don't need to handle the previous value, use `set_if_neq` instead."]
303    fn replace_if_neq(&mut self, value: Self::Inner) -> Option<Self::Inner>
304    where
305        Self::Inner: Sized + PartialEq,
306    {
307        let old = self.bypass_change_detection();
308        if *old != value {
309            let previous = mem::replace(old, value);
310            self.set_changed();
311            Some(previous)
312        } else {
313            None
314        }
315    }
316
317    /// Overwrites this smart pointer with a clone of the given value, if and only if `*self != value`.
318    /// Returns `true` if the value was overwritten, and returns `false` if it was not.
319    ///
320    /// This method is useful when the caller only has a borrowed form of `Inner`,
321    /// e.g. when writing a `&str` into a `Mut<String>`.
322    ///
323    /// # Examples
324    /// ```
325    /// # extern crate alloc;
326    /// # use alloc::borrow::ToOwned;
327    /// # use bevy_ecs::{prelude::*, schedule::common_conditions::resource_changed};
328    /// #[derive(Resource)]
329    /// pub struct Message(String);
330    ///
331    /// fn update_message(mut message: ResMut<Message>) {
332    ///     // Set the score to zero, unless it is already zero.
333    ///     ResMut::map_unchanged(message, |Message(msg)| msg).clone_from_if_neq("another string");
334    /// }
335    /// # let mut world = World::new();
336    /// # world.insert_resource(Message("initial string".into()));
337    /// # let mut message_changed = IntoSystem::into_system(resource_changed::<Message>);
338    /// # message_changed.initialize(&mut world);
339    /// # message_changed.run((), &mut world);
340    /// #
341    /// # let mut schedule = Schedule::default();
342    /// # schedule.add_systems(update_message);
343    /// #
344    /// # // first time `reset_score` runs, the score is changed.
345    /// # schedule.run(&mut world);
346    /// # assert!(message_changed.run((), &mut world).unwrap());
347    /// # // second time `reset_score` runs, the score is not changed.
348    /// # schedule.run(&mut world);
349    /// # assert!(!message_changed.run((), &mut world).unwrap());
350    /// ```
351    fn clone_from_if_neq<T>(&mut self, value: &T) -> bool
352    where
353        T: ToOwned<Owned = Self::Inner> + ?Sized,
354        Self::Inner: PartialEq<T>,
355    {
356        let old = self.bypass_change_detection();
357        if old != value {
358            value.clone_into(old);
359            self.set_changed();
360            true
361        } else {
362            false
363        }
364    }
365}
366
367macro_rules! change_detection_impl {
368    ($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:path)?) => {
369        impl<$($generics),* : ?Sized $(+ $traits)?> DetectChanges for $name<$($generics),*> {
370            #[inline]
371            fn is_added(&self) -> bool {
372                self.is_added_after(self.ticks.last_run)
373            }
374
375            #[inline]
376            fn is_changed(&self) -> bool {
377                self.is_changed_after(self.ticks.last_run)
378            }
379
380            #[inline]
381             fn is_added_after(&self, other: Tick) -> bool {
382                self.ticks
383                    .added
384                    .is_newer_than(other, self.ticks.this_run)
385            }
386
387            #[inline]
388            fn is_changed_after(&self, other: Tick) -> bool {
389                self.ticks
390                    .changed
391                    .is_newer_than(other, self.ticks.this_run)
392            }
393
394            #[inline]
395            fn last_changed(&self) -> Tick {
396                *self.ticks.changed
397            }
398
399            #[inline]
400            fn added(&self) -> Tick {
401                *self.ticks.added
402            }
403
404            #[inline]
405            fn this_run(&self) -> Tick {
406                self.ticks.this_run
407            }
408
409            #[inline]
410            fn last_run(&self) -> Tick {
411                self.ticks.last_run
412            }
413
414            #[inline]
415            fn changed_by(&self) -> MaybeLocation {
416                self.ticks.changed_by.copied()
417            }
418        }
419
420        impl<$($generics),*: ?Sized $(+ $traits)?> Deref for $name<$($generics),*> {
421            type Target = $target;
422
423            #[inline]
424            fn deref(&self) -> &Self::Target {
425                self.value
426            }
427        }
428
429        impl<$($generics),* $(: $traits)?> AsRef<$target> for $name<$($generics),*> {
430            #[inline]
431            fn as_ref(&self) -> &$target {
432                self.deref()
433            }
434        }
435    }
436}
437
438pub(crate) use change_detection_impl;
439
440macro_rules! change_detection_mut_impl {
441    ($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:path)?) => {
442        impl<$($generics),* : ?Sized $(+ $traits)?> DetectChangesMut for $name<$($generics),*> {
443            type Inner = $target;
444
445            #[inline]
446            #[track_caller]
447            fn set_changed(&mut self) {
448                *self.ticks.changed = self.ticks.this_run;
449                self.ticks.changed_by.assign(MaybeLocation::caller());
450                if let Some(summary_tick) = self.ticks.summary_tick {
451                    summary_tick.set(self.ticks.this_run);
452                }
453            }
454
455            #[inline]
456            #[track_caller]
457            fn set_added(&mut self) {
458                *self.ticks.changed = self.ticks.this_run;
459                *self.ticks.added = self.ticks.this_run;
460                self.ticks.changed_by.assign(MaybeLocation::caller());
461                if let Some(summary_tick) = self.ticks.summary_tick {
462                    summary_tick.set(self.ticks.this_run);
463                }
464            }
465
466            #[inline]
467            #[track_caller]
468            fn set_last_changed(&mut self, last_changed: Tick) {
469                *self.ticks.changed = last_changed;
470                self.ticks.changed_by.assign(MaybeLocation::caller());
471                if let Some(summary_tick) = self.ticks.summary_tick {
472                    summary_tick.set(self.ticks.this_run);
473                }
474            }
475
476            #[inline]
477            #[track_caller]
478            fn set_last_added(&mut self, last_added: Tick) {
479                *self.ticks.added = last_added;
480                *self.ticks.changed = last_added;
481                self.ticks.changed_by.assign(MaybeLocation::caller());
482                if let Some(summary_tick) = self.ticks.summary_tick {
483                    summary_tick.set(self.ticks.this_run);
484                }
485            }
486
487            #[inline]
488            fn bypass_change_detection(&mut self) -> &mut Self::Inner {
489                self.value
490            }
491        }
492
493        impl<$($generics),* : ?Sized $(+ $traits)?> DerefMut for $name<$($generics),*> {
494            #[inline]
495            #[track_caller]
496            fn deref_mut(&mut self) -> &mut Self::Target {
497                self.set_changed();
498                self.ticks.changed_by.assign(MaybeLocation::caller());
499                self.value
500            }
501        }
502
503        impl<$($generics),* $(: $traits)?> AsMut<$target> for $name<$($generics),*> {
504            #[inline]
505            fn as_mut(&mut self) -> &mut $target {
506                self.deref_mut()
507            }
508        }
509    };
510}
511
512pub(crate) use change_detection_mut_impl;
513
514macro_rules! impl_methods {
515    ($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:path)?) => {
516        impl<$($generics),* : ?Sized $(+ $traits)?> $name<$($generics),*> {
517            /// Consume `self` and return a mutable reference to the
518            /// contained value while marking `self` as "changed".
519            #[inline]
520            pub fn into_inner(mut self) -> &'w mut $target {
521                self.set_changed();
522                self.value
523            }
524
525            /// Returns a `Mut<>` with a smaller lifetime.
526            /// This is useful if you have `&mut
527            #[doc = stringify!($name)]
528            /// <T>`, but you need a `Mut<T>`.
529            pub fn reborrow(&mut self) -> Mut<'_, $target> {
530                Mut {
531                    value: self.value,
532                    ticks: ComponentTicksMut {
533                        added: self.ticks.added,
534                        changed: self.ticks.changed,
535                        changed_by: self.ticks.changed_by.as_deref_mut(),
536                        last_run: self.ticks.last_run,
537                        this_run: self.ticks.this_run,
538                        summary_tick: self.ticks.summary_tick,
539                    },
540                }
541            }
542
543            /// Maps to an inner value by applying a function to the contained reference, without flagging a change.
544            ///
545            /// You should never modify the argument passed to the closure -- if you want to modify the data
546            /// without flagging a change, consider using [`DetectChangesMut::bypass_change_detection`] to make your intent explicit.
547            ///
548            /// ```
549            /// # use bevy_ecs::prelude::*;
550            /// # #[derive(PartialEq)] pub struct Vec2;
551            /// # impl Vec2 { pub const ZERO: Self = Self; }
552            /// # #[derive(Component)] pub struct Transform { translation: Vec2 }
553            /// // When run, zeroes the translation of every entity.
554            /// fn reset_positions(mut transforms: Query<&mut Transform>) {
555            ///     for transform in &mut transforms {
556            ///         // We pinky promise not to modify `t` within the closure.
557            ///         // Breaking this promise will result in logic errors, but will never cause undefined behavior.
558            ///         let mut translation = transform.map_unchanged(|t| &mut t.translation);
559            ///         // Only reset the translation if it isn't already zero;
560            ///         translation.set_if_neq(Vec2::ZERO);
561            ///     }
562            /// }
563            /// # bevy_ecs::system::assert_is_system(reset_positions);
564            /// ```
565            pub fn map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> &mut U) -> Mut<'w, U> {
566                Mut {
567                    value: f(self.value),
568                    ticks: self.ticks,
569                }
570            }
571
572            /// Optionally maps to an inner value by applying a function to the contained reference.
573            /// This is useful in a situation where you need to convert a `Mut<T>` to a `Mut<U>`, but only if `T` contains `U`.
574            ///
575            /// As with `map_unchanged`, you should never modify the argument passed to the closure.
576            pub fn filter_map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> Option<&mut U>) -> Option<Mut<'w, U>> {
577                let value = f(self.value);
578                value.map(|value| Mut {
579                    value,
580                    ticks: self.ticks,
581                })
582            }
583
584            /// Optionally maps to an inner value by applying a function to the contained reference, returns an error on failure.
585            /// This is useful in a situation where you need to convert a `Mut<T>` to a `Mut<U>`, but only if `T` contains `U`.
586            ///
587            /// As with `map_unchanged`, you should never modify the argument passed to the closure.
588            pub fn try_map_unchanged<U: ?Sized, E>(self, f: impl FnOnce(&mut $target) -> Result<&mut U, E>) -> Result<Mut<'w, U>, E> {
589                let value = f(self.value);
590                value.map(|value| Mut {
591                    value,
592                    ticks: self.ticks,
593                })
594            }
595
596            /// Allows you access to the dereferenced value of this pointer without immediately
597            /// triggering change detection.
598            pub fn as_deref_mut(&mut self) -> Mut<'_, <$target as Deref>::Target>
599                where $target: DerefMut
600            {
601                self.reborrow().map_unchanged(|v| v.deref_mut())
602            }
603
604        }
605    };
606}
607
608pub(crate) use impl_methods;
609
610macro_rules! impl_debug {
611    ($name:ident < $( $generics:tt ),+ >, $($traits:path)?) => {
612        impl<$($generics),* : ?Sized $(+ $traits)?> core::fmt::Debug for $name<$($generics),*>
613            where T: core::fmt::Debug
614        {
615            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
616                f.debug_tuple(stringify!($name))
617                    .field(&self.value)
618                    .finish()
619            }
620        }
621
622    };
623}
624
625pub(crate) use impl_debug;