-
-
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
EntityRef/Mut get_components #13375
EntityRef/Mut get_components #13375
Conversation
In light of #12660, I would vote against adding more panicking shorthands like |
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.
Would this also allow for
let (mut a, mut b) = world.entity_mut(SOME_ENTITY).components_mut::<(&mut A, &mut B)>();
?
Maybe we can add a unit test for that case as well
Co-authored-by: Periwink <[email protected]>
Co-authored-by: Periwink <[email protected]>
I think discouraging unwrap/panicking variants entirely (as in, not providing impls) would have pretty nasty ergonomics implications for people, especially with the current Option vs Result compatibility problem for |
I agree somewhat with this. It's frustrating when something like |
Left some replies in #12660 |
); | ||
assert_eq!(None, world.entity(e2).get_components::<(&X, &Y)>()); | ||
assert_eq!(None, world.entity(e3).get_components::<(&X, &Y)>()); | ||
} |
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.
can you add a test to make sure it returns none or panics if you do world.entity_mut(e).get_components_mut::<&mut X, &mut X>()
?
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.
also should have one for (&mut X, &X)
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.
Haha I'm so glad you asked this question. The Access part of the Query infrastructure was what enforced this elsewhere, so this was allowed. We can't merge this as-is, and fixing it will almost certainly incur a significant perf cost.
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.
Wouldn't creating an empty access and calling update_component_access(state, &mut access)
on it work?
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 would. It would just be dissatisfying from a perf perspective because Access allocates.
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.
FYI I have a PR that removes the allocation from an empty Access
: https://github.com/bevyengine/bevy/pull/14026/files which might unblock this?
Or maybe not.. calling update_component_access
will itself allocate by updating the bitsets
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.
Could the allocation not just be removed in the meantime by using a smallvec that has 8 items or something?
} | ||
|
||
/// Returns read-only components for the current entity that match the query `Q`. | ||
pub fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'w>> { |
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.
Should add some docs to explain why it might return None
. Ditto for the other methods that return an option.
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 find this kind of funny. Thinking about this historically, this feels like we've inverted the relationship between Query::get_component
and EntityMut
/EntityRef
with this change.
I think discouraging unwrap/panicking variants entirely (as in, not providing impls) would have pretty nasty ergonomics implications for people, especially with the current Option vs Result compatibility problem for ?. I would be very uncomfortable removing those apis right now.
I agree that we probably shouldn't remove them until try
-blocks or some equivalent are stabilized.
.get(location.archetype_id) | ||
.debug_checked_unwrap() | ||
}; | ||
if Q::matches_component_set(&state, &|id| archetype.contains(id)) { |
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.
Could use bool::then
instead of the if/else here.
Since this uses
"components" sorta makes sense for Option/Has... Is Entity a component though? What if QueryData gets implemented with more Component-adjacent stuff in the future? I'm guessing the next steps item 2 would be renaming these to the |
Haha yeah thats an interesting comparison. Although I think this is significantly more defensible / necessary than
Yup!
Yup that is something discussed in the Next Steps section above. Haven't thought of a better interim name. And |
Welp this PR is sadly stalled until we sort out this problem: Not sure there will be a solution that doesn't involve doing access checks on each call. |
I think the best we can hope for is "allocation free check every entry against every other entry" (similar to |
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 in favor of this existing! I agree with the request for more thorough docs (shock). Nonblocking, but I also think we should be returning Result
, not Option
here (as with most of our ECS APIs). QueryEntityError looks like the closest fit, but would need to be tweaked to better fit the actual problems encountered (and API used).
Obviously the UB is a blocker here on this getting merged. Given the discovered complexity, I'm removing this from the 0.14 milestone <3 I still want this, and if it makes it it makes it, but it's not correct to try and wait for this to be resolved.
I think we should be able to reuse the access mechanisms from queries to validate soundness here. Would need to sit down with the code to figure out exactly how. I much prefer that over creating another mechanism though, especially since I'd like to be able to add untyped variants to this in the future. |
Would it be possible to merge a version of this PR with just the immutable |
I agree the an immutable form would be useful still. Feel free to spin off a PR that does that please. |
Closing in favor of #15089; further attempts to implement the mutable form will be easier in a separate PR. |
# Objective Smaller scoped version of #13375 without the `_mut` variants which currently have unsoundness issues. ## Solution Same as #13375, but without the `_mut` variants. ## Testing - The same test from #13375 is reused. --- ## Migration Guide - Renamed `FilteredEntityRef::components` to `FilteredEntityRef::accessed_components` and `FilteredEntityMut::components` to `FilteredEntityMut::accessed_components`. --------- Co-authored-by: Carter Anderson <[email protected]> Co-authored-by: Periwink <[email protected]>
Objective
Implements #13127
EntityRef
/EntityMut
usage is currently unnecessarily verbose and borrow-checker-constrained. It is currently impossible to useEntityMut
to get mutable references to more than one component at the same time:This makes
EntityMut
significantly less useful than it could be. Querying necessary components on demand is a pattern many prefer to use over traditional top-level queries.Solution
Implement new
get_components
andcomponents
methods forEntityRef
andEntityMut
(and mutable variants forEntityMut
) to provide multi-component access:This is implemented using our existing Query implementation to provide multi-component access. It checks access without actually computing QueryState / access using the very convenient
Q::matches_component_set
.Next Steps
FilteredEntityRef
andFilteredEntityMut
variants, as checking that access is actually more challenging (and with the current methods, likely prohibitively expensive because I think we need to computeAccess<ComponentId>
)components
toget
andget_components
totry_get
renaming / unification I proposed in AddEntityMut::components
andEntityRef
andEntityWorldMut
equivalent #13127 as I suspect that will be more controversial, and it also touches a lot of code.Changelog
Added
EntityRef::components
,EntityRef::get_components
,EntityMut::components
,EntityMut::get_components
,EntityMut::components_mut
, andEntityMut::get_components_mut
.Changed
FilteredEntityRef::components
toFilteredEntityRef::accessed_components
andFilteredEntityMut::components
toFilteredEntityMut::accessed_components
to avoid future name clashesMigration Guide
FilteredEntityRef::components
toFilteredEntityRef::accessed_components
andFilteredEntityMut::components
toFilteredEntityMut::accessed_components