Skip to content

Commit

Permalink
Add bypass_change_detection method
Browse files Browse the repository at this point in the history
  • Loading branch information
alice-i-cecile committed Aug 9, 2022
1 parent 171a92b commit 9dca3d1
Showing 1 changed file with 28 additions and 1 deletion.
29 changes: 28 additions & 1 deletion crates/bevy_ecs/src/change_detection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ pub const MAX_CHANGE_AGE: u32 = u32::MAX - (2 * CHECK_TICK_THRESHOLD - 1);
/// ```
///
pub trait DetectChanges {
/// The type contained within this smart pointer
///
/// For example, for `Res<T>` this would be `T`.
type Inner;

/// Returns `true` if this value was added after the system last ran.
fn is_added(&self) -> bool;

Expand Down Expand Up @@ -73,11 +78,21 @@ pub trait DetectChanges {
/// If you merely want to flag this data as changed, use [`set_changed`](DetectChanges::set_changed) instead.
/// If you want to avoid triggering change detection, use [`bypass_change_detection`](DetectChanges::bypass_change_detection) instead.
fn set_last_changed(&mut self, last_change_tick: u32);

/// Manually bypasses change detection, allowing you to mutate the underlying value without updating the change tick.
///
/// This is a risky operation, that can have unexpected consequences on any system relying on this code.
/// However, it can be an essential escape hatch when, for example,
/// you are trying to synchronize representations using change detection and need to avoid infinite recursion.
fn bypass_change_detection(&mut self) -> &mut Self::Inner;
}

macro_rules! change_detection_impl {
($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
impl<$($generics),* $(: $traits)?> DetectChanges for $name<$($generics),*> {
// FIXME: this is requesting *all* generics, I just want the first generic type
type Inner = $generics;

#[inline]
fn is_added(&self) -> bool {
self.ticks
Expand Down Expand Up @@ -108,6 +123,11 @@ macro_rules! change_detection_impl {
fn set_last_changed(&mut self, last_change_tick: u32) {
self.ticks.last_change_tick = last_change_tick
}

#[inline]
fn bypass_change_detection(&mut self) -> &mut Self::Inner {
self.value
}
}

impl<$($generics),* $(: $traits)?> Deref for $name<$($generics),*> {
Expand Down Expand Up @@ -268,12 +288,15 @@ impl<'a> MutUntyped<'a> {
/// Returns the pointer to the value, without marking it as changed.
///
/// In order to mark the value as changed, you need to call [`set_changed`](DetectChanges::set_changed) manually.
#[inline]
pub fn into_inner(self) -> PtrMut<'a> {
self.value
}
}

impl DetectChanges for MutUntyped<'_> {
impl<'a> DetectChanges for MutUntyped<'a> {
type Inner = PtrMut<'a>;

fn is_added(&self) -> bool {
self.ticks
.component_ticks
Expand All @@ -300,6 +323,10 @@ impl DetectChanges for MutUntyped<'_> {
fn set_last_changed(&mut self, last_change_tick: u32) {
self.ticks.last_change_tick = last_change_tick
}

fn bypass_change_detection(&mut self) -> &mut Self::Inner {
&mut self.into_inner()
}
}

impl std::fmt::Debug for MutUntyped<'_> {
Expand Down

0 comments on commit 9dca3d1

Please sign in to comment.