Description
This is a tracking issue for the Entity API Deduplication Working Group.
Progress
- Scoped EntityRef/Mut 🔬 #18820
- Cleanup
EntityWorldMut
in preparation for changing its layout. - Swap
EntityWorldMut
to storing anUnsafeEntityCell
. - Make
EntityWorldMut
into an alias forEntityMut
and add theGlobal
access scope.EntityWorldRef
will also be added at this point. - Make
World::entity
andWorld::get_entity
returnEntityWorldRef
. - Add
EntityRefOnly
,EntityMutOnly
and theOnly
access scope, as well as thesplit
functions detailed in part 2.
Plan (Part 1)
This plan is based on this design document.
We have a lot of types for accessing individual entities:
EntityRef
: read access to all componentsFilteredEntityRef
: read access to some components, determined at runtimeEntityRefExcept
: read access to some components, determined at compile-timeEntityMut
: write access to all componentsFilteredEntityMut
: write access to some components, determined at runtimeEntityMutExcept
: write access to some components, determined at compile-timeEntityWorldMut
: write access to all components, and to the entire world including structural changes
We can observe two sets of types here: those with read access, and those with read/write access. This pairs nicely with how normal Rust references work, so lets group them up.
- read access
EntityRef
: all componentsFilteredEntityRef
: some components, determined at runtimeEntityRefExcept
: some components, determined at compile-time
- write access
EntityMut
: all componentsFilteredEntityMut
: some components, determined at runtimeEntityMutExcept
: some components, determined at compile-timeEntityWorldMut
: all components and the entire world, including structural changes
Now within each group, we see that they all have the same kind of access (reading or writing), but differ in what they can access. Therefore, we can collapse each group down into a single generic type, EntityRef
and EntityMut
which use a "scope" type parameter to determine what the reference has access to:
pub struct EntityRef<'w, S: AccessScope = Full> { /* ... */ }
pub type FilteredEntityRef<'w> = EntityRef<'w, Filtered>;
pub type EntityRefExcept<'w, B> = EntityRef<'w, Except<B>>;
pub struct EntityMut<'w, S: AccessScope = Full> { /* ... */ }
pub type FilteredEntityMut<'w> = EntityMut<'w, Filtered>;
pub type EntityMutExcept<'w, B> = EntityMut<'w, Except<B>>;
pub type EntityWorldMut<'w> = EntityMut<'w, Global>;
pub unsafe trait AccessScope {
fn can_read(&self, components: &Components, id: ComponentId) -> bool;
fn can_write(&self, components: &Components, id: ComponentId) -> bool;
}
At this point, we've reduced our need for duplicating functions across all of the entity reference types from 7 down to 2!
Plan (Part 2)
At this stage, we can add some new features.
EntityWorldRef
This is an EntityRef
counterpart to EntityWorldMut
. We can return this type from World::entity
and World::get_entity
(non-mut) to provide simultaneous immutable access to the whole world.
EntityRefOnly
and EntityMutOnly
These are the inverse of EntityRefExcept
and EntityMutExcept
: they provide access to only the components contained in the Bundle
. We can make use of the XOnly
and XExcept
reference types together to provide a low-overhead alternative to a proposed EntityMut::get_components_mut
:
impl<'w> EntityMut<'w, Full> {
pub fn split<B: Bundle>(&mut self) -> (EntityMutOnly<'_, B>, EntityMutExcept<'_, B>);
pub fn split_by(&mut self, access: Access<ComponentId>) -> (FilteredEntityMut<'_>, FilteredEntityMut<'_>);
pub fn into_split<B: Bundle>(self) -> (EntityMutOnly<'w, B>, EntityMutExcept<'w, B>);
pub fn into_split_by(self, access: Access<ComponentId>) -> (FilteredEntityMut<'w>, FilteredEntityMut<'w>);
}
Plan (Part 3)
WIP.
Design documents
- Original: https://hackmd.io/@bevy/Hyj8Kjy1ye
- Deref-ladder based alternative: https://hackmd.io/@bevy/B1vgzGdf1g
- Original v2 (what we've gone with): https://hackmd.io/@bevy/By4NcOYzkg