Skip to content

Tracking Issue: Entity API Deduplication #18837

Open
@ItsDoot

Description

@ItsDoot

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 an UnsafeEntityCell.
  • Make EntityWorldMut into an alias for EntityMut and add the Global access scope. EntityWorldRef will also be added at this point.
  • Make World::entity and World::get_entity return EntityWorldRef.
  • Add EntityRefOnly, EntityMutOnly and the Only access scope, as well as the split 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 components
  • FilteredEntityRef: read access to some components, determined at runtime
  • EntityRefExcept: read access to some components, determined at compile-time
  • EntityMut: write access to all components
  • FilteredEntityMut: write access to some components, determined at runtime
  • EntityMutExcept: write access to some components, determined at compile-time
  • EntityWorldMut: 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 components
    • FilteredEntityRef: some components, determined at runtime
    • EntityRefExcept: some components, determined at compile-time
  • write access
    • EntityMut: all components
    • FilteredEntityMut: some components, determined at runtime
    • EntityMutExcept: some components, determined at compile-time
    • EntityWorldMut: 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

Additional context

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ECSEntities, components, systems, and eventsC-Code-QualityA section of code that is hard to understand or changeC-FeatureA new feature, making something new possibleC-Tracking-IssueAn issue that collects information about a broad development initiative

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions