Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/change_detection/traits.rs
7219 views
1
use crate::{change_detection::MaybeLocation, change_detection::Tick};
2
use alloc::borrow::ToOwned;
3
use 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
27
pub 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 the change tick recording the time this data was most recently changed.
40
///
41
/// Note that components and resources are also marked as changed upon insertion.
42
///
43
/// For comparison, the previous change tick of a system can be read using the
44
/// [`SystemChangeTick`](crate::system::SystemChangeTick)
45
/// [`SystemParam`](crate::system::SystemParam).
46
fn last_changed(&self) -> Tick;
47
48
/// Returns the change tick recording the time this data was added.
49
fn added(&self) -> Tick;
50
51
/// The location that last caused this to change.
52
fn changed_by(&self) -> MaybeLocation;
53
}
54
55
/// Types that implement reliable change detection.
56
///
57
/// ## Example
58
/// Using types that implement [`DetectChangesMut`], such as [`ResMut`], provide
59
/// a way to query if a value has been mutated in another system.
60
/// Normally change detection is triggered by either [`DerefMut`] or [`AsMut`], however
61
/// it can be manually triggered via [`set_changed`](DetectChangesMut::set_changed).
62
///
63
/// To ensure that changes are only triggered when the value actually differs,
64
/// check if the value would change before assignment, such as by checking that `new != old`.
65
/// You must be *sure* that you are not mutably dereferencing in this process.
66
///
67
/// [`set_if_neq`](DetectChangesMut::set_if_neq) is a helper
68
/// method for this common functionality.
69
///
70
/// ```
71
/// use bevy_ecs::prelude::*;
72
///
73
/// #[derive(Resource)]
74
/// struct MyResource(u32);
75
///
76
/// fn my_system(mut resource: ResMut<MyResource>) {
77
/// if resource.is_changed() {
78
/// println!("My resource was mutated!");
79
/// }
80
///
81
/// resource.0 = 42; // triggers change detection via [`DerefMut`]
82
/// }
83
/// ```
84
///
85
/// [`ResMut`]: crate::change_detection::params::ResMut
86
/// [`DerefMut`]: core::ops::DerefMut
87
pub trait DetectChangesMut: DetectChanges {
88
/// The type contained within this smart pointer
89
///
90
/// For example, for `ResMut<T>` this would be `T`.
91
type Inner: ?Sized;
92
93
/// Flags this value as having been changed.
94
///
95
/// Mutably accessing this smart pointer will automatically flag this value as having been changed.
96
/// However, mutation through interior mutability requires manual reporting.
97
///
98
/// **Note**: This operation cannot be undone.
99
fn set_changed(&mut self);
100
101
/// Flags this value as having been added.
102
///
103
/// It is not normally necessary to call this method.
104
/// The 'added' tick is set when the value is first added,
105
/// and is not normally changed afterwards.
106
///
107
/// **Note**: This operation cannot be undone.
108
fn set_added(&mut self);
109
110
/// Manually sets the change tick recording the time when this data was last mutated.
111
///
112
/// # Warning
113
/// This is a complex and error-prone operation, primarily intended for use with rollback networking strategies.
114
/// If you merely want to flag this data as changed, use [`set_changed`](DetectChangesMut::set_changed) instead.
115
/// If you want to avoid triggering change detection, use [`bypass_change_detection`](DetectChangesMut::bypass_change_detection) instead.
116
fn set_last_changed(&mut self, last_changed: Tick);
117
118
/// Manually sets the added tick recording the time when this data was last added.
119
///
120
/// # Warning
121
/// The caveats of [`set_last_changed`](DetectChangesMut::set_last_changed) apply. This modifies both the added and changed ticks together.
122
fn set_last_added(&mut self, last_added: Tick);
123
124
/// Manually bypasses change detection, allowing you to mutate the underlying value without updating the change tick.
125
///
126
/// # Warning
127
/// 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.
130
fn bypass_change_detection(&mut self) -> &mut Self::Inner;
131
132
/// 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 value
136
/// 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 relevant
140
/// 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 example
143
///
144
/// If you need the previous value, use [`replace_if_neq`](DetectChangesMut::replace_if_neq).
145
///
146
/// # Examples
147
///
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_unchanged
175
/// [`MutUntyped::map_unchanged`]: crate::change_detection::params::MutUntyped::map_unchanged
176
/// [`ResMut::map_unchanged`]: crate::change_detection::params::ResMut::map_unchanged
177
/// [`NonSendMut::map_unchanged`]: crate::change_detection::params::NonSendMut::map_unchanged
178
#[inline]
179
#[track_caller]
180
fn set_if_neq(&mut self, value: Self::Inner) -> bool
181
where
182
Self::Inner: Sized + PartialEq,
183
{
184
let old = self.bypass_change_detection();
185
if *old != value {
186
*old = value;
187
self.set_changed();
188
true
189
} else {
190
false
191
}
192
}
193
194
/// 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 value
198
/// 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 allow
202
/// 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 example
205
///
206
/// If you don't need the previous value, use [`set_if_neq`](DetectChangesMut::set_if_neq).
207
///
208
/// # Examples
209
///
210
/// ```
211
/// # use bevy_ecs::{prelude::*, schedule::common_conditions::{resource_changed, on_event}};
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(Events::<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_event::<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_unchanged
257
/// [`MutUntyped::map_unchanged`]: crate::change_detection::params::MutUntyped::map_unchanged
258
/// [`ResMut::map_unchanged`]: crate::change_detection::params::ResMut::map_unchanged
259
/// [`NonSendMut::map_unchanged`]: crate::change_detection::params::NonSendMut::map_unchanged
260
#[inline]
261
#[must_use = "If you don't need to handle the previous value, use `set_if_neq` instead."]
262
fn replace_if_neq(&mut self, value: Self::Inner) -> Option<Self::Inner>
263
where
264
Self::Inner: Sized + PartialEq,
265
{
266
let old = self.bypass_change_detection();
267
if *old != value {
268
let previous = mem::replace(old, value);
269
self.set_changed();
270
Some(previous)
271
} else {
272
None
273
}
274
}
275
276
/// 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
/// # Examples
283
/// ```
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
/// ```
310
fn clone_from_if_neq<T>(&mut self, value: &T) -> bool
311
where
312
T: ToOwned<Owned = Self::Inner> + ?Sized,
313
Self::Inner: PartialEq<T>,
314
{
315
let old = self.bypass_change_detection();
316
if old != value {
317
value.clone_into(old);
318
self.set_changed();
319
true
320
} else {
321
false
322
}
323
}
324
}
325
326
macro_rules! change_detection_impl {
327
($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
328
impl<$($generics),* : ?Sized $(+ $traits)?> DetectChanges for $name<$($generics),*> {
329
#[inline]
330
fn is_added(&self) -> bool {
331
self.ticks
332
.added
333
.is_newer_than(self.ticks.last_run, self.ticks.this_run)
334
}
335
336
#[inline]
337
fn is_changed(&self) -> bool {
338
self.ticks
339
.changed
340
.is_newer_than(self.ticks.last_run, self.ticks.this_run)
341
}
342
343
#[inline]
344
fn last_changed(&self) -> Tick {
345
*self.ticks.changed
346
}
347
348
#[inline]
349
fn added(&self) -> Tick {
350
*self.ticks.added
351
}
352
353
#[inline]
354
fn changed_by(&self) -> MaybeLocation {
355
self.ticks.changed_by.copied()
356
}
357
}
358
359
impl<$($generics),*: ?Sized $(+ $traits)?> Deref for $name<$($generics),*> {
360
type Target = $target;
361
362
#[inline]
363
fn deref(&self) -> &Self::Target {
364
self.value
365
}
366
}
367
368
impl<$($generics),* $(: $traits)?> AsRef<$target> for $name<$($generics),*> {
369
#[inline]
370
fn as_ref(&self) -> &$target {
371
self.deref()
372
}
373
}
374
}
375
}
376
377
pub(crate) use change_detection_impl;
378
379
macro_rules! change_detection_mut_impl {
380
($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
381
impl<$($generics),* : ?Sized $(+ $traits)?> DetectChangesMut for $name<$($generics),*> {
382
type Inner = $target;
383
384
#[inline]
385
#[track_caller]
386
fn set_changed(&mut self) {
387
*self.ticks.changed = self.ticks.this_run;
388
self.ticks.changed_by.assign(MaybeLocation::caller());
389
}
390
391
#[inline]
392
#[track_caller]
393
fn set_added(&mut self) {
394
*self.ticks.changed = self.ticks.this_run;
395
*self.ticks.added = self.ticks.this_run;
396
self.ticks.changed_by.assign(MaybeLocation::caller());
397
}
398
399
#[inline]
400
#[track_caller]
401
fn set_last_changed(&mut self, last_changed: Tick) {
402
*self.ticks.changed = last_changed;
403
self.ticks.changed_by.assign(MaybeLocation::caller());
404
}
405
406
#[inline]
407
#[track_caller]
408
fn set_last_added(&mut self, last_added: Tick) {
409
*self.ticks.added = last_added;
410
*self.ticks.changed = last_added;
411
self.ticks.changed_by.assign(MaybeLocation::caller());
412
}
413
414
#[inline]
415
fn bypass_change_detection(&mut self) -> &mut Self::Inner {
416
self.value
417
}
418
}
419
420
impl<$($generics),* : ?Sized $(+ $traits)?> DerefMut for $name<$($generics),*> {
421
#[inline]
422
#[track_caller]
423
fn deref_mut(&mut self) -> &mut Self::Target {
424
self.set_changed();
425
self.ticks.changed_by.assign(MaybeLocation::caller());
426
self.value
427
}
428
}
429
430
impl<$($generics),* $(: $traits)?> AsMut<$target> for $name<$($generics),*> {
431
#[inline]
432
fn as_mut(&mut self) -> &mut $target {
433
self.deref_mut()
434
}
435
}
436
};
437
}
438
439
pub(crate) use change_detection_mut_impl;
440
441
macro_rules! impl_methods {
442
($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
443
impl<$($generics),* : ?Sized $(+ $traits)?> $name<$($generics),*> {
444
/// Consume `self` and return a mutable reference to the
445
/// contained value while marking `self` as "changed".
446
#[inline]
447
pub fn into_inner(mut self) -> &'w mut $target {
448
self.set_changed();
449
self.value
450
}
451
452
/// Returns a `Mut<>` with a smaller lifetime.
453
/// This is useful if you have `&mut
454
#[doc = stringify!($name)]
455
/// <T>`, but you need a `Mut<T>`.
456
pub fn reborrow(&mut self) -> Mut<'_, $target> {
457
Mut {
458
value: self.value,
459
ticks: ComponentTicksMut {
460
added: self.ticks.added,
461
changed: self.ticks.changed,
462
changed_by: self.ticks.changed_by.as_deref_mut(),
463
last_run: self.ticks.last_run,
464
this_run: self.ticks.this_run,
465
},
466
}
467
}
468
469
/// 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 data
472
/// 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
/// ```
491
pub fn map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> &mut U) -> Mut<'w, U> {
492
Mut {
493
value: f(self.value),
494
ticks: self.ticks,
495
}
496
}
497
498
/// 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.
502
pub fn filter_map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> Option<&mut U>) -> Option<Mut<'w, U>> {
503
let value = f(self.value);
504
value.map(|value| Mut {
505
value,
506
ticks: self.ticks,
507
})
508
}
509
510
/// 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.
514
pub fn try_map_unchanged<U: ?Sized, E>(self, f: impl FnOnce(&mut $target) -> Result<&mut U, E>) -> Result<Mut<'w, U>, E> {
515
let value = f(self.value);
516
value.map(|value| Mut {
517
value,
518
ticks: self.ticks,
519
})
520
}
521
522
/// Allows you access to the dereferenced value of this pointer without immediately
523
/// triggering change detection.
524
pub fn as_deref_mut(&mut self) -> Mut<'_, <$target as Deref>::Target>
525
where $target: DerefMut
526
{
527
self.reborrow().map_unchanged(|v| v.deref_mut())
528
}
529
530
}
531
};
532
}
533
534
pub(crate) use impl_methods;
535
536
macro_rules! impl_debug {
537
($name:ident < $( $generics:tt ),+ >, $($traits:ident)?) => {
538
impl<$($generics),* : ?Sized $(+ $traits)?> core::fmt::Debug for $name<$($generics),*>
539
where T: core::fmt::Debug
540
{
541
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
542
f.debug_tuple(stringify!($name))
543
.field(&self.value)
544
.finish()
545
}
546
}
547
548
};
549
}
550
551
pub(crate) use impl_debug;
552
553