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