@@ -10,11 +10,13 @@ use core::{
1010 mem,
1111 ops:: { Deref , DerefMut } ,
1212} ;
13+ use std:: cell:: RefCell ;
1314#[ cfg( feature = "track_change_detection" ) ]
1415use {
1516 bevy_ptr:: ThinSlicePtr ,
1617 core:: { cell:: UnsafeCell , panic:: Location } ,
1718} ;
19+ use crate :: storage:: { EntityChange , EntityChanges } ;
1820
1921/// The (arbitrarily chosen) minimum number of world tick increments between `check_tick` scans.
2022///
@@ -370,6 +372,62 @@ macro_rules! change_detection_mut_impl {
370372 } ;
371373}
372374
375+ macro_rules! change_detection_mut_with_onchange_impl {
376+ ( $name: ident < $( $generics: tt ) ,+ >, $target: ty, $( $traits: ident) ?) => {
377+ impl <$( $generics) ,* : ?Sized $( + $traits) ?> DetectChangesMut for $name<$( $generics) ,* > {
378+ type Inner = $target;
379+
380+ #[ inline]
381+ #[ track_caller]
382+ fn set_changed( & mut self ) {
383+ * self . ticks. changed = self . ticks. this_run;
384+ if let Some ( ( change, changes) ) = self . on_change {
385+ changes. borrow_mut( ) . push( change) ;
386+ }
387+ #[ cfg( feature = "track_change_detection" ) ]
388+ {
389+ * self . changed_by = Location :: caller( ) ;
390+ }
391+ }
392+
393+ #[ inline]
394+ #[ track_caller]
395+ fn set_last_changed( & mut self , last_changed: Tick ) {
396+ * self . ticks. changed = last_changed;
397+ #[ cfg( feature = "track_change_detection" ) ]
398+ {
399+ * self . changed_by = Location :: caller( ) ;
400+ }
401+ }
402+
403+ #[ inline]
404+ fn bypass_change_detection( & mut self ) -> & mut Self :: Inner {
405+ self . value
406+ }
407+ }
408+
409+ impl <$( $generics) ,* : ?Sized $( + $traits) ?> DerefMut for $name<$( $generics) ,* > {
410+ #[ inline]
411+ #[ track_caller]
412+ fn deref_mut( & mut self ) -> & mut Self :: Target {
413+ self . set_changed( ) ;
414+ #[ cfg( feature = "track_change_detection" ) ]
415+ {
416+ * self . changed_by = Location :: caller( ) ;
417+ }
418+ self . value
419+ }
420+ }
421+
422+ impl <$( $generics) ,* $( : $traits) ?> AsMut <$target> for $name<$( $generics) ,* > {
423+ #[ inline]
424+ fn as_mut( & mut self ) -> & mut $target {
425+ self . deref_mut( )
426+ }
427+ }
428+ } ;
429+ }
430+
373431macro_rules! impl_methods {
374432 ( $name: ident < $( $generics: tt ) ,+ >, $target: ty, $( $traits: ident) ?) => {
375433 impl <$( $generics) ,* : ?Sized $( + $traits) ?> $name<$( $generics) ,* > {
@@ -387,6 +445,96 @@ macro_rules! impl_methods {
387445 /// <T>`, but you need a `Mut<T>`.
388446 pub fn reborrow( & mut self ) -> Mut <' _, $target> {
389447 Mut {
448+ on_change: None ,
449+ value: self . value,
450+ ticks: TicksMut {
451+ added: self . ticks. added,
452+ changed: self . ticks. changed,
453+ last_run: self . ticks. last_run,
454+ this_run: self . ticks. this_run,
455+ } ,
456+ #[ cfg( feature = "track_change_detection" ) ]
457+ changed_by: self . changed_by,
458+ }
459+ }
460+
461+ /// Maps to an inner value by applying a function to the contained reference, without flagging a change.
462+ ///
463+ /// You should never modify the argument passed to the closure -- if you want to modify the data
464+ /// without flagging a change, consider using [`DetectChangesMut::bypass_change_detection`] to make your intent explicit.
465+ ///
466+ /// ```
467+ /// # use bevy_ecs::prelude::*;
468+ /// # #[derive(PartialEq)] pub struct Vec2;
469+ /// # impl Vec2 { pub const ZERO: Self = Self; }
470+ /// # #[derive(Component)] pub struct Transform { translation: Vec2 }
471+ /// // When run, zeroes the translation of every entity.
472+ /// fn reset_positions(mut transforms: Query<&mut Transform>) {
473+ /// for transform in &mut transforms {
474+ /// // We pinky promise not to modify `t` within the closure.
475+ /// // Breaking this promise will result in logic errors, but will never cause undefined behavior.
476+ /// let mut translation = transform.map_unchanged(|t| &mut t.translation);
477+ /// // Only reset the translation if it isn't already zero;
478+ /// translation.set_if_neq(Vec2::ZERO);
479+ /// }
480+ /// }
481+ /// # bevy_ecs::system::assert_is_system(reset_positions);
482+ /// ```
483+ pub fn map_unchanged<U : ?Sized >( self , f: impl FnOnce ( & mut $target) -> & mut U ) -> Mut <' w, U > {
484+ Mut {
485+ on_change: None ,
486+ value: f( self . value) ,
487+ ticks: self . ticks,
488+ #[ cfg( feature = "track_change_detection" ) ]
489+ changed_by: self . changed_by,
490+ }
491+ }
492+
493+ /// Optionally maps to an inner value by applying a function to the contained reference.
494+ /// This is useful in a situation where you need to convert a `Mut<T>` to a `Mut<U>`, but only if `T` contains `U`.
495+ ///
496+ /// As with `map_unchanged`, you should never modify the argument passed to the closure.
497+ pub fn filter_map_unchanged<U : ?Sized >( self , f: impl FnOnce ( & mut $target) -> Option <& mut U >) -> Option <Mut <' w, U >> {
498+ let value = f( self . value) ;
499+ value. map( |value| Mut {
500+ on_change: None ,
501+ value,
502+ ticks: self . ticks,
503+ #[ cfg( feature = "track_change_detection" ) ]
504+ changed_by: self . changed_by,
505+ } )
506+ }
507+
508+ /// Allows you access to the dereferenced value of this pointer without immediately
509+ /// triggering change detection.
510+ pub fn as_deref_mut( & mut self ) -> Mut <' _, <$target as Deref >:: Target >
511+ where $target: DerefMut
512+ {
513+ self . reborrow( ) . map_unchanged( |v| v. deref_mut( ) )
514+ }
515+
516+ }
517+ } ;
518+ }
519+
520+ macro_rules! impl_methods_with_onchange {
521+ ( $name: ident < $( $generics: tt ) ,+ >, $target: ty, $( $traits: ident) ?) => {
522+ impl <$( $generics) ,* : ?Sized $( + $traits) ?> $name<$( $generics) ,* > {
523+ /// Consume `self` and return a mutable reference to the
524+ /// contained value while marking `self` as "changed".
525+ #[ inline]
526+ pub fn into_inner( mut self ) -> & ' w mut $target {
527+ self . set_changed( ) ;
528+ self . value
529+ }
530+
531+ /// Returns a `Mut<>` with a smaller lifetime.
532+ /// This is useful if you have `&mut
533+ #[ doc = stringify!( $name) ]
534+ /// <T>`, but you need a `Mut<T>`.
535+ pub fn reborrow( & mut self ) -> Mut <' _, $target> {
536+ Mut {
537+ on_change: self . on_change,
390538 value: self . value,
391539 ticks: TicksMut {
392540 added: self . ticks. added,
@@ -423,6 +571,7 @@ macro_rules! impl_methods {
423571 /// ```
424572 pub fn map_unchanged<U : ?Sized >( self , f: impl FnOnce ( & mut $target) -> & mut U ) -> Mut <' w, U > {
425573 Mut {
574+ on_change: self . on_change,
426575 value: f( self . value) ,
427576 ticks: self . ticks,
428577 #[ cfg( feature = "track_change_detection" ) ]
@@ -437,6 +586,7 @@ macro_rules! impl_methods {
437586 pub fn filter_map_unchanged<U : ?Sized >( self , f: impl FnOnce ( & mut $target) -> Option <& mut U >) -> Option <Mut <' w, U >> {
438587 let value = f( self . value) ;
439588 value. map( |value| Mut {
589+ on_change: self . on_change,
440590 value,
441591 ticks: self . ticks,
442592 #[ cfg( feature = "track_change_detection" ) ]
@@ -667,6 +817,7 @@ impl<'w, T: Resource> From<ResMut<'w, T>> for Mut<'w, T> {
667817 /// while losing the specificity of `ResMut` for resources.
668818 fn from ( other : ResMut < ' w , T > ) -> Mut < ' w , T > {
669819 Mut {
820+ on_change : None ,
670821 value : other. value ,
671822 ticks : other. ticks ,
672823 #[ cfg( feature = "track_change_detection" ) ]
@@ -703,6 +854,7 @@ impl<'w, T: 'static> From<NonSendMut<'w, T>> for Mut<'w, T> {
703854 /// while losing the specificity of `NonSendMut`.
704855 fn from ( other : NonSendMut < ' w , T > ) -> Mut < ' w , T > {
705856 Mut {
857+ on_change : None ,
706858 value : other. value ,
707859 ticks : other. ticks ,
708860 #[ cfg( feature = "track_change_detection" ) ]
@@ -869,6 +1021,7 @@ impl_debug!(Ref<'w, T>,);
8691021/// # fn update_player_position(player: &Player, new_position: Position) {}
8701022/// ```
8711023pub struct Mut < ' w , T : ?Sized > {
1024+ pub ( crate ) on_change : Option < ( EntityChange , & ' w RefCell < EntityChanges > ) > ,
8721025 pub ( crate ) value : & ' w mut T ,
8731026 pub ( crate ) ticks : TicksMut < ' w > ,
8741027 #[ cfg( feature = "track_change_detection" ) ]
@@ -900,6 +1053,7 @@ impl<'w, T: ?Sized> Mut<'w, T> {
9001053 #[ cfg( feature = "track_change_detection" ) ] caller : & ' w mut & ' static Location < ' static > ,
9011054 ) -> Self {
9021055 Self {
1056+ on_change : None ,
9031057 value,
9041058 ticks : TicksMut {
9051059 added,
@@ -949,9 +1103,10 @@ where
9491103 }
9501104}
9511105
1106+
9521107change_detection_impl ! ( Mut <' w, T >, T , ) ;
953- change_detection_mut_impl ! ( Mut <' w, T >, T , ) ;
954- impl_methods ! ( Mut <' w, T >, T , ) ;
1108+ change_detection_mut_with_onchange_impl ! ( Mut <' w, T >, T , ) ;
1109+ impl_methods_with_onchange ! ( Mut <' w, T >, T , ) ;
9551110impl_debug ! ( Mut <' w, T >, ) ;
9561111
9571112/// Unique mutable borrow of resources or an entity's component.
@@ -963,6 +1118,7 @@ impl_debug!(Mut<'w, T>,);
9631118/// [`Mut`], but in situations where the types are not known at compile time
9641119/// or are defined outside of rust this can be used.
9651120pub struct MutUntyped < ' w > {
1121+ pub ( crate ) on_change : Option < ( EntityChange , & ' w RefCell < EntityChanges > ) > ,
9661122 pub ( crate ) value : PtrMut < ' w > ,
9671123 pub ( crate ) ticks : TicksMut < ' w > ,
9681124 #[ cfg( feature = "track_change_detection" ) ]
@@ -984,6 +1140,7 @@ impl<'w> MutUntyped<'w> {
9841140 #[ inline]
9851141 pub fn reborrow ( & mut self ) -> MutUntyped {
9861142 MutUntyped {
1143+ on_change : self . on_change ,
9871144 value : self . value . reborrow ( ) ,
9881145 ticks : TicksMut {
9891146 added : self . ticks . added ,
@@ -1041,6 +1198,7 @@ impl<'w> MutUntyped<'w> {
10411198 /// ```
10421199 pub fn map_unchanged < T : ?Sized > ( self , f : impl FnOnce ( PtrMut < ' w > ) -> & ' w mut T ) -> Mut < ' w , T > {
10431200 Mut {
1201+ on_change : None , // TODO
10441202 value : f ( self . value ) ,
10451203 ticks : self . ticks ,
10461204 #[ cfg( feature = "track_change_detection" ) ]
@@ -1054,6 +1212,7 @@ impl<'w> MutUntyped<'w> {
10541212 /// - `T` must be the erased pointee type for this [`MutUntyped`].
10551213 pub unsafe fn with_type < T > ( self ) -> Mut < ' w , T > {
10561214 Mut {
1215+ on_change : None ,
10571216 // SAFETY: `value` is `Aligned` and caller ensures the pointee type is `T`.
10581217 value : unsafe { self . value . deref_mut ( ) } ,
10591218 ticks : self . ticks ,
@@ -1098,6 +1257,9 @@ impl<'w> DetectChangesMut for MutUntyped<'w> {
10981257 #[ track_caller]
10991258 fn set_changed ( & mut self ) {
11001259 * self . ticks . changed = self . ticks . this_run ;
1260+ if let Some ( ( change, changes) ) = self . on_change {
1261+ changes. borrow_mut ( ) . push ( change) ;
1262+ }
11011263 #[ cfg( feature = "track_change_detection" ) ]
11021264 {
11031265 * self . changed_by = Location :: caller ( ) ;
@@ -1132,6 +1294,7 @@ impl core::fmt::Debug for MutUntyped<'_> {
11321294impl < ' w , T > From < Mut < ' w , T > > for MutUntyped < ' w > {
11331295 fn from ( value : Mut < ' w , T > ) -> Self {
11341296 MutUntyped {
1297+ on_change : value. on_change ,
11351298 value : value. value . into ( ) ,
11361299 ticks : value. ticks ,
11371300 #[ cfg( feature = "track_change_detection" ) ]
@@ -1423,6 +1586,7 @@ mod tests {
14231586 let mut caller = Location :: caller ( ) ;
14241587
14251588 let ptr = Mut {
1589+ on_change : None ,
14261590 value : & mut outer,
14271591 ticks,
14281592 #[ cfg( feature = "track_change_detection" ) ]
@@ -1513,6 +1677,7 @@ mod tests {
15131677 let mut caller = Location :: caller ( ) ;
15141678
15151679 let value = MutUntyped {
1680+ on_change : None ,
15161681 value : PtrMut :: from ( & mut value) ,
15171682 ticks,
15181683 #[ cfg( feature = "track_change_detection" ) ]
@@ -1551,6 +1716,7 @@ mod tests {
15511716 let mut caller = Location :: caller ( ) ;
15521717
15531718 let mut_typed = Mut {
1719+ on_change : None ,
15541720 value : & mut c,
15551721 ticks,
15561722 #[ cfg( feature = "track_change_detection" ) ]
0 commit comments