-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Allow users to register their own disabling components / default query filters #17768
Allow users to register their own disabling components / default query filters #17768
Conversation
I also need this, but wasn't there concerns in the previous PR that forces us to abandon letting users define their own disabled components? For example using a sparse component as a disabling component was incorrect, or something to that effect |
Does this automatically work with scenes? |
Can you say more about what you mean? Scenes aren't hooked up to any prefab system yet for example. |
On sparse set filters: #13120 (comment) and #13120 (comment). However, the soundness issues appear to be resolved: #13120 (comment) That said, I'm happy to just forbid them: I think they're a serious performance footgun.
A Vec is likely faster / more sensible, or even a SmallVec. I don't expect more than a handful of these to exist. I'll also clean up the description. We can discuss the ecosystem concerns further: I share some of them, but I also think they're broadly hypothetical. Crates can already effectively have "private entities" by making their component types public or nesting them in a subworld or something: that hasn't been a serious problem AFAIK. |
I think the problem is largely a documentation and method naming issue fwiw. There are cases where the concerns would apply, like if someone creates dozens of alternatives to |
Iirc the original PR to add |
Looking at #17514, this PR actually opts |
Alright, I've added more documentation, cleaned up the PR description, swapped to a SmallVec and made a nice little Default -> FromWorld change. I think this is in a good spot to merge, but y'all are the reviewers. @SkiFire13, could I get your review here as well? You had concerns about the original DQF, and I want to be sure that you're okay with the tradeoffs we're making here. |
/// Semantically, this component is used to mark entities that are temporarily disabled (typically for gameplay reasons), | ||
/// but will likely be re-enabled at some point. | ||
/// | ||
/// Like all disabling components, this only disables the entity itself, |
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.
For all lot of these comments here, I think you can just reference what you wrote at the top of the module.
Might be good to build the expectation for users that they should look to the top of the module for most of the comments. That's where most of the info is likely to be I suspect, so that way users can get all the info rather than bits and pieces. Also it prevents us from having to maintain all these comments if they go out of date.
I also think this would be a nice thing to get new contributors to do too. We have a lot of docs and centralizing them could be a good way to learn about the engine. IDK food for thought
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.
I think that's a good strategy in general! In this particular case, there are enough concerns that I want to go overkill on the warnings. I'd really like to avoid having to take this toy away in the future 😅
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.
I like it!
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.
I'm a bit out of loop and recently got a new job so I don't have lot of time to fully review the code. I gave a quick glance and left a couple of comments, from what I can see nothing much has changed in terms of tradeoffs, so I think I have the same concerns as before, but if you're ok with them you could give this a try.
//! For example, `Query<&Position>` will not include entities with the [`Disabled`] component, | ||
//! even if they have a `Position` component, | ||
//! but `Query<&Position, With<Disabled>>` or `Query<(&Position, Has<Disabled>)>` will see them. |
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.
IMO this should include an example of a Query
that matches entities that are both enabled and disabled. The ones shown match only entities that are disabled.
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.
Has matches both!
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.
Ah right. It still feels kinda hacky though
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.
Yeah, it's awkward to have an extra bool
that isn't used, and it will count the component as archetypal access, which is somewhat misleading.
Would it make sense to have a QueryFilter
with the meaning "include entities whether or not they have T
, even though it's normally excluded by default query filters"? (That wouldn't be part of this PR, of course.)
//! Currently only queries for which the cache is built after enabling a filter will have entities | ||
//! Currently, only queries for which the cache is built after enabling a default query filter will have entities | ||
//! with those components filtered. As a result, they should generally only be modified before the | ||
//! app starts. |
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.
My concern with this still stands, but I don't see an easy way to solve it. Maybe there could be a warning in the console if someone attempts this? Or maybe this could be exposed mainly on App
.
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.
We can add a check for this and an internal bool. Lemme toss that behind a debug asserts flag...
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.
Ugh, I'm not happy with the design I had in mind there. Doing this will be really fragile due to how plugin initialization works, and I think the false positive rate will be too high.
I would like this feature, crate owners can disable entities "for their own reasons" that cannot be accidentally enabled by other parts of the code. |
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.
Great docs!
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))] | ||
pub struct DefaultQueryFilters { | ||
disabled: Option<ComponentId>, | ||
// We only expect a few components per application to act as disabling components, so we use a SmallVec here | ||
// to avoid heap allocation in most cases. |
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.
As there is only one DefaultQueryFilters
value per world, it seems like overkill to worry about one heap allocation, but it's harmless at worst. :)
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.
Won't iterating over the vector be slower than if we use a smallvec? I'm less concerned about the initial allocation than I am about maximizing iteration speed at tiny n.
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.
Won't iterating over the vector be slower than if we use a smallvec?
Oh, I don't know! I think they both iterate by Deref
ing to a slice, so I'd expect it to be similar. I think smallvec
has an extra branch to check whether it's inline or heap-allocated, but on the other hand the data is closer by so there might be better cache locality. I don't expect it to be a big deal either way.
Thanks for the last minute nits @chescock, and thanks for the reviews to everyone! |
Objective
Currently, default query filters, as added in #13120 / #17514 are hardcoded to only use a single query filter.
This is limiting, as multiple distinct disabling components can serve important distinct roles. I ran into this limitation when experimenting with a workflow for prefabs, which don't represent the same state as "an entity which is temporarily nonfunctional".
Solution
DefaultQueryFilters
to store a SmallVec of ComponentId, rather than an Option.DefaultQueryFilters
,World
andApp
to actually configure this.Follow-up
I'm not convinced that supporting sparse set disabling components is useful, given the hit to iteration performance and runtime checks incurred. That's disjoint from this PR though, so I'm not doing it here. The existing warnings are fine for now.
Testing
I've added both a doc test and an mid-level unit test to verify that this works!