-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement opt-in change detection bypass #2363
Conversation
@@ -41,6 +41,9 @@ pub trait DetectChanges { | |||
fn set_changed(&mut self); | |||
} | |||
|
|||
// TODO: Add some nice docs here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If someone would contribute to this point, it'd be highly appreciated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// When implemented for a component or resource type, this trait allows end users to manually bypass change detection within the ECS using the [`get_untracked`] method on `Query` and `World`.
I think that's where those methods live?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was a "please add these methods too" comment for @TheRawMeatball ;)
This looks delightfully overengineered. I would be on board, but my sentiment from linked issue remains: do we actually need it? The two examples so far can be (and best be, if you ask me) solved by splitting resources/components not just by the data they store, but also by the mutation events that happen to them, if that makes sense. I think that's a universal solution, be it inside a specific plugin or between several plugins. |
@Ratysz Would you mind clarifying exactly what you mean by this? |
Yes, pretty much. E.g., we could split |
While this can sometimes be an option, I don't think it can be applied globally as it requires changing the external API to do something that only bothers the internal impl. For the asset case for example, the external API shouldn't need to care about how the |
Ran into a user with a serious need for this earlier today. Effectively, they wanted to make a change detecting that system that responded to all changes other than its own. I spent a while helping them and couldn't come up with a clean workaround. This now has my 👍🏽 in terms of use case. Lets take a look at the specific design now... |
Gave you some docs ;) The impl looks reasonable to me. Two outstanding concerns:
|
I haven't used bevy/crates/bevy_ecs/src/change_detection.rs Line 216 in 6591742
|
Closing in favor of the upcoming permissions work |
I ran into a very compelling use case of this today. I have two components: However, this creates an infinite ping-pong, as doing so causes the other to be changed, thus triggering another update. The workaround is to check whether the new value is equal to the old value before setting it. |
Reopening this: permissions has serious open questions, and this is at least an important stop-gap. |
…evyengine#5635) # Objective - Our existing change detection API is not flexible enough for advanced users: particularly those attempting to do rollback networking. - This is an important use case, and with adequate warnings we can make mucking about with change ticks scary enough that users generally won't do it. - Fixes bevyengine#5633. - Closes bevyengine#2363. ## Changelog - added `ChangeDetection::set_last_changed` to manually mutate the `last_change_ticks` field" - the `ChangeDetection` trait now requires an `Inner` associated type, which contains the value being wrapped. - added `ChangeDetection::bypass_change_detection`, which hands out a raw `&mut Inner` ## Migration Guide Add the `Inner` associated type and new methods to any type that you've implemented `DetectChanges` for.
…evyengine#5635) # Objective - Our existing change detection API is not flexible enough for advanced users: particularly those attempting to do rollback networking. - This is an important use case, and with adequate warnings we can make mucking about with change ticks scary enough that users generally won't do it. - Fixes bevyengine#5633. - Closes bevyengine#2363. ## Changelog - added `ChangeDetection::set_last_changed` to manually mutate the `last_change_ticks` field" - the `ChangeDetection` trait now requires an `Inner` associated type, which contains the value being wrapped. - added `ChangeDetection::bypass_change_detection`, which hands out a raw `&mut Inner` ## Migration Guide Add the `Inner` associated type and new methods to any type that you've implemented `DetectChanges` for.
# Objective Change detection can be spuriously triggered by setting a field to the same value as before. As a result, a common pattern is to write: ```rust if *foo != value { *foo = value; } ``` This is confusing to read, and heavy on boilerplate. ## Solution 1. Add a method to the `DetectChanges` trait that implements this boilerplate when the appropriate trait bounds are met. 2. Document this minor footgun, and point users to it. ## Changelog - added the `set_if_neq` method to avoid triggering change detection when the new and previous values are equal. This will work on both components and resources. ## Migration Guide If you are manually checking if a component or resource's value is equal to its new value before setting it to avoid triggering change detection, migrate to the clearer and more convenient `set_if_neq` method. ## Context Related to #2363 as it avoids triggering change detection, but not a complete solution (as it still requires triggering it when real changes are made).
# Objective Change detection can be spuriously triggered by setting a field to the same value as before. As a result, a common pattern is to write: ```rust if *foo != value { *foo = value; } ``` This is confusing to read, and heavy on boilerplate. Adopted from #5373, but untangled and rebased to current `bevy/main`. ## Solution 1. Add a method to the `DetectChanges` trait that implements this boilerplate when the appropriate trait bounds are met. 2. Document this minor footgun, and point users to it. ## Changelog * added the `set_if_neq` method to avoid triggering change detection when the new and previous values are equal. This will work on both components and resources. ## Migration Guide If you are manually checking if a component or resource's value is equal to its new value before setting it to avoid triggering change detection, migrate to the clearer and more convenient `set_if_neq` method. ## Context Related to #2363 as it avoids triggering change detection, but not a complete solution (as it still requires triggering it when real changes are made). Co-authored-by: Zoey <Dessix@Dessix.net>
…e#6853) # Objective Change detection can be spuriously triggered by setting a field to the same value as before. As a result, a common pattern is to write: ```rust if *foo != value { *foo = value; } ``` This is confusing to read, and heavy on boilerplate. Adopted from bevyengine#5373, but untangled and rebased to current `bevy/main`. ## Solution 1. Add a method to the `DetectChanges` trait that implements this boilerplate when the appropriate trait bounds are met. 2. Document this minor footgun, and point users to it. ## Changelog * added the `set_if_neq` method to avoid triggering change detection when the new and previous values are equal. This will work on both components and resources. ## Migration Guide If you are manually checking if a component or resource's value is equal to its new value before setting it to avoid triggering change detection, migrate to the clearer and more convenient `set_if_neq` method. ## Context Related to bevyengine#2363 as it avoids triggering change detection, but not a complete solution (as it still requires triggering it when real changes are made). Co-authored-by: Zoey <Dessix@Dessix.net>
…evyengine#5635) # Objective - Our existing change detection API is not flexible enough for advanced users: particularly those attempting to do rollback networking. - This is an important use case, and with adequate warnings we can make mucking about with change ticks scary enough that users generally won't do it. - Fixes bevyengine#5633. - Closes bevyengine#2363. ## Changelog - added `ChangeDetection::set_last_changed` to manually mutate the `last_change_ticks` field" - the `ChangeDetection` trait now requires an `Inner` associated type, which contains the value being wrapped. - added `ChangeDetection::bypass_change_detection`, which hands out a raw `&mut Inner` ## Migration Guide Add the `Inner` associated type and new methods to any type that you've implemented `DetectChanges` for.
…e#6853) # Objective Change detection can be spuriously triggered by setting a field to the same value as before. As a result, a common pattern is to write: ```rust if *foo != value { *foo = value; } ``` This is confusing to read, and heavy on boilerplate. Adopted from bevyengine#5373, but untangled and rebased to current `bevy/main`. ## Solution 1. Add a method to the `DetectChanges` trait that implements this boilerplate when the appropriate trait bounds are met. 2. Document this minor footgun, and point users to it. ## Changelog * added the `set_if_neq` method to avoid triggering change detection when the new and previous values are equal. This will work on both components and resources. ## Migration Guide If you are manually checking if a component or resource's value is equal to its new value before setting it to avoid triggering change detection, migrate to the clearer and more convenient `set_if_neq` method. ## Context Related to bevyengine#2363 as it avoids triggering change detection, but not a complete solution (as it still requires triggering it when real changes are made). Co-authored-by: Zoey <Dessix@Dessix.net>
Objective
Implements #2348
Solution
Implements the solution outlined here