@@ -10,11 +10,13 @@ use core::{
10
10
mem,
11
11
ops:: { Deref , DerefMut } ,
12
12
} ;
13
+ use std:: cell:: RefCell ;
13
14
#[ cfg( feature = "track_change_detection" ) ]
14
15
use {
15
16
bevy_ptr:: ThinSlicePtr ,
16
17
core:: { cell:: UnsafeCell , panic:: Location } ,
17
18
} ;
19
+ use crate :: world:: entity_change:: { EntityChange , EntityChanges } ;
18
20
19
21
/// The (arbitrarily chosen) minimum number of world tick increments between `check_tick` scans.
20
22
///
@@ -370,6 +372,62 @@ macro_rules! change_detection_mut_impl {
370
372
} ;
371
373
}
372
374
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
+
373
431
macro_rules! impl_methods {
374
432
( $name: ident < $( $generics: tt ) ,+ >, $target: ty, $( $traits: ident) ?) => {
375
433
impl <$( $generics) ,* : ?Sized $( + $traits) ?> $name<$( $generics) ,* > {
@@ -387,6 +445,96 @@ macro_rules! impl_methods {
387
445
/// <T>`, but you need a `Mut<T>`.
388
446
pub fn reborrow( & mut self ) -> Mut <' _, $target> {
389
447
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,
390
538
value: self . value,
391
539
ticks: TicksMut {
392
540
added: self . ticks. added,
@@ -423,6 +571,7 @@ macro_rules! impl_methods {
423
571
/// ```
424
572
pub fn map_unchanged<U : ?Sized >( self , f: impl FnOnce ( & mut $target) -> & mut U ) -> Mut <' w, U > {
425
573
Mut {
574
+ on_change: self . on_change,
426
575
value: f( self . value) ,
427
576
ticks: self . ticks,
428
577
#[ cfg( feature = "track_change_detection" ) ]
@@ -437,6 +586,7 @@ macro_rules! impl_methods {
437
586
pub fn filter_map_unchanged<U : ?Sized >( self , f: impl FnOnce ( & mut $target) -> Option <& mut U >) -> Option <Mut <' w, U >> {
438
587
let value = f( self . value) ;
439
588
value. map( |value| Mut {
589
+ on_change: self . on_change,
440
590
value,
441
591
ticks: self . ticks,
442
592
#[ cfg( feature = "track_change_detection" ) ]
@@ -667,6 +817,7 @@ impl<'w, T: Resource> From<ResMut<'w, T>> for Mut<'w, T> {
667
817
/// while losing the specificity of `ResMut` for resources.
668
818
fn from ( other : ResMut < ' w , T > ) -> Mut < ' w , T > {
669
819
Mut {
820
+ on_change : None ,
670
821
value : other. value ,
671
822
ticks : other. ticks ,
672
823
#[ cfg( feature = "track_change_detection" ) ]
@@ -703,6 +854,7 @@ impl<'w, T: 'static> From<NonSendMut<'w, T>> for Mut<'w, T> {
703
854
/// while losing the specificity of `NonSendMut`.
704
855
fn from ( other : NonSendMut < ' w , T > ) -> Mut < ' w , T > {
705
856
Mut {
857
+ on_change : None ,
706
858
value : other. value ,
707
859
ticks : other. ticks ,
708
860
#[ cfg( feature = "track_change_detection" ) ]
@@ -869,6 +1021,7 @@ impl_debug!(Ref<'w, T>,);
869
1021
/// # fn update_player_position(player: &Player, new_position: Position) {}
870
1022
/// ```
871
1023
pub struct Mut < ' w , T : ?Sized > {
1024
+ pub ( crate ) on_change : Option < ( EntityChange , & ' w RefCell < EntityChanges > ) > ,
872
1025
pub ( crate ) value : & ' w mut T ,
873
1026
pub ( crate ) ticks : TicksMut < ' w > ,
874
1027
#[ cfg( feature = "track_change_detection" ) ]
@@ -900,6 +1053,7 @@ impl<'w, T: ?Sized> Mut<'w, T> {
900
1053
#[ cfg( feature = "track_change_detection" ) ] caller : & ' w mut & ' static Location < ' static > ,
901
1054
) -> Self {
902
1055
Self {
1056
+ on_change : None ,
903
1057
value,
904
1058
ticks : TicksMut {
905
1059
added,
@@ -949,9 +1103,10 @@ where
949
1103
}
950
1104
}
951
1105
1106
+
952
1107
change_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 , ) ;
955
1110
impl_debug ! ( Mut <' w, T >, ) ;
956
1111
957
1112
/// Unique mutable borrow of resources or an entity's component.
@@ -963,6 +1118,7 @@ impl_debug!(Mut<'w, T>,);
963
1118
/// [`Mut`], but in situations where the types are not known at compile time
964
1119
/// or are defined outside of rust this can be used.
965
1120
pub struct MutUntyped < ' w > {
1121
+ pub ( crate ) on_change : Option < ( EntityChange , & ' w RefCell < EntityChanges > ) > ,
966
1122
pub ( crate ) value : PtrMut < ' w > ,
967
1123
pub ( crate ) ticks : TicksMut < ' w > ,
968
1124
#[ cfg( feature = "track_change_detection" ) ]
@@ -984,6 +1140,7 @@ impl<'w> MutUntyped<'w> {
984
1140
#[ inline]
985
1141
pub fn reborrow ( & mut self ) -> MutUntyped {
986
1142
MutUntyped {
1143
+ on_change : self . on_change ,
987
1144
value : self . value . reborrow ( ) ,
988
1145
ticks : TicksMut {
989
1146
added : self . ticks . added ,
@@ -1041,6 +1198,7 @@ impl<'w> MutUntyped<'w> {
1041
1198
/// ```
1042
1199
pub fn map_unchanged < T : ?Sized > ( self , f : impl FnOnce ( PtrMut < ' w > ) -> & ' w mut T ) -> Mut < ' w , T > {
1043
1200
Mut {
1201
+ on_change : None , // TODO
1044
1202
value : f ( self . value ) ,
1045
1203
ticks : self . ticks ,
1046
1204
#[ cfg( feature = "track_change_detection" ) ]
@@ -1054,6 +1212,7 @@ impl<'w> MutUntyped<'w> {
1054
1212
/// - `T` must be the erased pointee type for this [`MutUntyped`].
1055
1213
pub unsafe fn with_type < T > ( self ) -> Mut < ' w , T > {
1056
1214
Mut {
1215
+ on_change : None ,
1057
1216
// SAFETY: `value` is `Aligned` and caller ensures the pointee type is `T`.
1058
1217
value : unsafe { self . value . deref_mut ( ) } ,
1059
1218
ticks : self . ticks ,
@@ -1098,6 +1257,9 @@ impl<'w> DetectChangesMut for MutUntyped<'w> {
1098
1257
#[ track_caller]
1099
1258
fn set_changed ( & mut self ) {
1100
1259
* self . ticks . changed = self . ticks . this_run ;
1260
+ if let Some ( ( change, changes) ) = self . on_change {
1261
+ changes. borrow_mut ( ) . push ( change) ;
1262
+ }
1101
1263
#[ cfg( feature = "track_change_detection" ) ]
1102
1264
{
1103
1265
* self . changed_by = Location :: caller ( ) ;
@@ -1132,6 +1294,7 @@ impl core::fmt::Debug for MutUntyped<'_> {
1132
1294
impl < ' w , T > From < Mut < ' w , T > > for MutUntyped < ' w > {
1133
1295
fn from ( value : Mut < ' w , T > ) -> Self {
1134
1296
MutUntyped {
1297
+ on_change : value. on_change ,
1135
1298
value : value. value . into ( ) ,
1136
1299
ticks : value. ticks ,
1137
1300
#[ cfg( feature = "track_change_detection" ) ]
@@ -1423,6 +1586,7 @@ mod tests {
1423
1586
let mut caller = Location :: caller ( ) ;
1424
1587
1425
1588
let ptr = Mut {
1589
+ on_change : None ,
1426
1590
value : & mut outer,
1427
1591
ticks,
1428
1592
#[ cfg( feature = "track_change_detection" ) ]
@@ -1513,6 +1677,7 @@ mod tests {
1513
1677
let mut caller = Location :: caller ( ) ;
1514
1678
1515
1679
let value = MutUntyped {
1680
+ on_change : None ,
1516
1681
value : PtrMut :: from ( & mut value) ,
1517
1682
ticks,
1518
1683
#[ cfg( feature = "track_change_detection" ) ]
@@ -1551,6 +1716,7 @@ mod tests {
1551
1716
let mut caller = Location :: caller ( ) ;
1552
1717
1553
1718
let mut_typed = Mut {
1719
+ on_change : None ,
1554
1720
value : & mut c,
1555
1721
ticks,
1556
1722
#[ cfg( feature = "track_change_detection" ) ]
0 commit comments