mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
refactor: move internals from entity_ref
to World
, add SAFETY
comments (#6402)
# Objective There are some utility functions for actually working with `Storages` inside `entity_ref.rs` that are used both for `EntityRef/EntityMut` and `World`, with a `// TODO: move to Storages`. This PR moves them to private methods on `World`, because that's the safest API boundary. On `Storages` you would need to ensure that you pass `Components` from the same world. ## Solution - move get_component[_with_type], get_ticks[_with_type], get_component_and_ticks[_with_type] to `World` (still pub(crate)) - replace `pub use entity_ref::*;` with `pub use entity_ref::{EntityRef, EntityMut}` and qualified `entity_ref::get_mut[_by_id]` in `world.rs` - add safety comments to a bunch of methods
This commit is contained in:
parent
feac2c206c
commit
008c156991
2 changed files with 323 additions and 312 deletions
|
@ -4,10 +4,9 @@ use crate::{
|
|||
change_detection::{MutUntyped, TicksMut},
|
||||
component::{
|
||||
Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType,
|
||||
TickCells,
|
||||
},
|
||||
entity::{Entities, Entity, EntityLocation},
|
||||
storage::{Column, ComponentSparseSet, SparseSet, Storages},
|
||||
storage::{SparseSet, Storages},
|
||||
world::{Mut, World},
|
||||
};
|
||||
use bevy_ptr::{OwningPtr, Ptr};
|
||||
|
@ -72,17 +71,18 @@ impl<'w> EntityRef<'w> {
|
|||
pub fn get<T: Component>(&self) -> Option<&'w T> {
|
||||
// SAFETY:
|
||||
// - entity location and entity is valid
|
||||
// - returned component is of type T
|
||||
// - the storage type provided is correct for T
|
||||
// - world access is immutable, lifetime tied to `&self`
|
||||
unsafe {
|
||||
get_component_with_type(
|
||||
self.world,
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
)
|
||||
.map(|value| value.deref::<T>())
|
||||
self.world
|
||||
.get_component_with_type(
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
)
|
||||
// SAFETY: returned component is of type T
|
||||
.map(|value| value.deref::<T>())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,10 +92,10 @@ impl<'w> EntityRef<'w> {
|
|||
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
|
||||
// SAFETY:
|
||||
// - entity location and entity is valid
|
||||
// - world access is immutable, lifetime tied to `&self`
|
||||
// - the storage type provided is correct for T
|
||||
unsafe {
|
||||
get_ticks_with_type(
|
||||
self.world,
|
||||
self.world.get_ticks_with_type(
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
self.entity,
|
||||
|
@ -112,15 +112,13 @@ impl<'w> EntityRef<'w> {
|
|||
/// compile time.**
|
||||
#[inline]
|
||||
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> {
|
||||
if !self.contains_id(component_id) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let info = self.world.components().get_info(component_id)?;
|
||||
// SAFETY: Entity location is valid and component_id exists.
|
||||
// SAFETY:
|
||||
// - entity location and entity is valid
|
||||
// - world access is immutable, lifetime tied to `&self`
|
||||
// - the storage type provided is correct for T
|
||||
unsafe {
|
||||
get_ticks(
|
||||
self.world,
|
||||
self.world.get_ticks(
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
self.entity,
|
||||
|
@ -149,20 +147,20 @@ impl<'w> EntityRef<'w> {
|
|||
// - entity location and entity is valid
|
||||
// - returned component is of type T
|
||||
// - the storage type provided is correct for T
|
||||
get_component_and_ticks_with_type(
|
||||
self.world,
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
)
|
||||
.map(|(value, ticks)| Mut {
|
||||
// SAFETY:
|
||||
// - returned component is of type T
|
||||
// - Caller guarantees that this reference will not alias.
|
||||
value: value.assert_unique().deref_mut::<T>(),
|
||||
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
|
||||
})
|
||||
self.world
|
||||
.get_component_and_ticks_with_type(
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
)
|
||||
.map(|(value, ticks)| Mut {
|
||||
// SAFETY:
|
||||
// - returned component is of type T
|
||||
// - Caller guarantees that this reference will not alias.
|
||||
value: value.assert_unique().deref_mut::<T>(),
|
||||
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,12 +177,11 @@ impl<'w> EntityRef<'w> {
|
|||
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
|
||||
let info = self.world.components().get_info(component_id)?;
|
||||
// SAFETY:
|
||||
// - entity_location is valid,
|
||||
// - component_id is valid as checked by the line above
|
||||
// - entity_location and entity are valid
|
||||
// . component_id is valid as checked by the line above
|
||||
// - the storage type is accurate as checked by the fetched ComponentInfo
|
||||
unsafe {
|
||||
get_component(
|
||||
self.world,
|
||||
self.world.get_component(
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
self.entity,
|
||||
|
@ -257,19 +254,19 @@ impl<'w> EntityMut<'w> {
|
|||
#[inline]
|
||||
pub fn get<T: Component>(&self) -> Option<&'_ T> {
|
||||
// SAFETY:
|
||||
// - lifetimes enforce correct usage of returned borrow
|
||||
// - entity location and entity is valid
|
||||
// - returned component is of type T
|
||||
// - entity location is valid
|
||||
// - world access is immutable, lifetime tied to `&self`
|
||||
// - the storage type provided is correct for T
|
||||
unsafe {
|
||||
get_component_with_type(
|
||||
self.world,
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
)
|
||||
.map(|value| value.deref::<T>())
|
||||
self.world
|
||||
.get_component_with_type(
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
)
|
||||
// SAFETY: returned component is of type T
|
||||
.map(|value| value.deref::<T>())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,11 +281,11 @@ impl<'w> EntityMut<'w> {
|
|||
#[inline]
|
||||
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
|
||||
// SAFETY:
|
||||
// - entity location and entity is valid
|
||||
// - entity location is valid
|
||||
// - world access is immutable, lifetime tied to `&self`
|
||||
// - the storage type provided is correct for T
|
||||
unsafe {
|
||||
get_ticks_with_type(
|
||||
self.world,
|
||||
self.world.get_ticks_with_type(
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
self.entity,
|
||||
|
@ -305,15 +302,13 @@ impl<'w> EntityMut<'w> {
|
|||
/// compile time.**
|
||||
#[inline]
|
||||
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> {
|
||||
if !self.contains_id(component_id) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let info = self.world.components().get_info(component_id)?;
|
||||
// SAFETY: Entity location is valid and component_id exists.
|
||||
// SAFETY:
|
||||
// - entity location is valid
|
||||
// - world access is immutable, lifetime tied to `&self`
|
||||
// - the storage type provided is correct for T
|
||||
unsafe {
|
||||
get_ticks(
|
||||
self.world,
|
||||
self.world.get_ticks(
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
self.entity,
|
||||
|
@ -338,21 +333,21 @@ impl<'w> EntityMut<'w> {
|
|||
// - entity location and entity is valid
|
||||
// - returned component is of type T
|
||||
// - the storage type provided is correct for T
|
||||
get_component_and_ticks_with_type(
|
||||
self.world,
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
)
|
||||
.map(|(value, ticks)| Mut {
|
||||
value: value.assert_unique().deref_mut::<T>(),
|
||||
ticks: TicksMut::from_tick_cells(
|
||||
ticks,
|
||||
self.world.last_change_tick(),
|
||||
self.world.read_change_tick(),
|
||||
),
|
||||
})
|
||||
self.world
|
||||
.get_component_and_ticks_with_type(
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
)
|
||||
.map(|(value, ticks)| Mut {
|
||||
value: value.assert_unique().deref_mut::<T>(),
|
||||
ticks: TicksMut::from_tick_cells(
|
||||
ticks,
|
||||
self.world.last_change_tick(),
|
||||
self.world.read_change_tick(),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
/// Adds a [`Bundle`] of components to the entity.
|
||||
|
@ -417,10 +412,13 @@ impl<'w> EntityMut<'w> {
|
|||
let result = unsafe {
|
||||
T::from_components(storages, &mut |storages| {
|
||||
let component_id = bundle_components.next().unwrap();
|
||||
// SAFETY: entity location is valid and table row is removed below
|
||||
// SAFETY:
|
||||
// - entity location is valid
|
||||
// - table row is removed below, without dropping the contents
|
||||
// - `components` comes from the same world as `storages`
|
||||
take_component(
|
||||
components,
|
||||
storages,
|
||||
components,
|
||||
removed_components,
|
||||
component_id,
|
||||
entity,
|
||||
|
@ -671,8 +669,7 @@ impl<'w> EntityMut<'w> {
|
|||
// - component_id is valid as checked by the line above
|
||||
// - the storage type is accurate as checked by the fetched ComponentInfo
|
||||
unsafe {
|
||||
get_component(
|
||||
self.world,
|
||||
self.world.get_component(
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
self.entity,
|
||||
|
@ -697,213 +694,6 @@ impl<'w> EntityMut<'w> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fetch_table(
|
||||
world: &World,
|
||||
location: EntityLocation,
|
||||
component_id: ComponentId,
|
||||
) -> Option<&Column> {
|
||||
world.storages.tables[location.table_id].get_column(component_id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fetch_sparse_set(world: &World, component_id: ComponentId) -> Option<&ComponentSparseSet> {
|
||||
world.storages.sparse_sets.get(component_id)
|
||||
}
|
||||
|
||||
// TODO: move to Storages?
|
||||
/// Get a raw pointer to a particular [`Component`] on a particular [`Entity`] in the provided [`World`].
|
||||
///
|
||||
/// # Safety
|
||||
/// - `location` must be within bounds of the given archetype and table and `entity` must exist inside
|
||||
/// the archetype and table
|
||||
/// - `component_id` must be valid
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_component(
|
||||
world: &World,
|
||||
component_id: ComponentId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<Ptr<'_>> {
|
||||
match storage_type {
|
||||
StorageType::Table => {
|
||||
let components = fetch_table(world, location, component_id)?;
|
||||
// SAFETY: archetypes only store valid table_rows and the stored component type is T
|
||||
Some(components.get_data_unchecked(location.table_row))
|
||||
}
|
||||
StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get(entity),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move to Storages?
|
||||
/// Get a raw pointer to the [`ComponentTicks`] of a particular [`Component`] on a particular [`Entity`] in the provided [World].
|
||||
///
|
||||
/// # Safety
|
||||
/// - Caller must ensure that `component_id` is valid
|
||||
/// - `location` must be within bounds of the given archetype and `entity` must exist inside
|
||||
/// the archetype
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
#[inline]
|
||||
unsafe fn get_component_and_ticks(
|
||||
world: &World,
|
||||
component_id: ComponentId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<(Ptr<'_>, TickCells<'_>)> {
|
||||
match storage_type {
|
||||
StorageType::Table => {
|
||||
let components = fetch_table(world, location, component_id)?;
|
||||
// SAFETY: archetypes only store valid table_rows and the stored component type is T
|
||||
Some((
|
||||
components.get_data_unchecked(location.table_row),
|
||||
TickCells {
|
||||
added: components.get_added_ticks_unchecked(location.table_row),
|
||||
changed: components.get_changed_ticks_unchecked(location.table_row),
|
||||
},
|
||||
))
|
||||
}
|
||||
StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get_with_ticks(entity),
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside
|
||||
/// the archetype
|
||||
/// - `component_id` must be valid
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
unsafe fn get_ticks(
|
||||
world: &World,
|
||||
component_id: ComponentId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<ComponentTicks> {
|
||||
match storage_type {
|
||||
StorageType::Table => {
|
||||
let components = fetch_table(world, location, component_id)?;
|
||||
// SAFETY: archetypes only store valid table_rows and the stored component type is T
|
||||
Some(components.get_ticks_unchecked(location.table_row))
|
||||
}
|
||||
StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get_ticks(entity),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move to Storages?
|
||||
/// Moves component data out of storage.
|
||||
///
|
||||
/// This function leaves the underlying memory unchanged, but the component behind
|
||||
/// returned pointer is semantically owned by the caller and will not be dropped in its original location.
|
||||
/// Caller is responsible to drop component data behind returned pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `location` must be within bounds of the given archetype and table and `entity` must exist inside the archetype
|
||||
/// and table.
|
||||
/// - `component_id` must be valid
|
||||
/// - The relevant table row **must be removed** by the caller once all components are taken
|
||||
#[inline]
|
||||
unsafe fn take_component<'a>(
|
||||
components: &Components,
|
||||
storages: &'a mut Storages,
|
||||
removed_components: &mut SparseSet<ComponentId, Vec<Entity>>,
|
||||
component_id: ComponentId,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> OwningPtr<'a> {
|
||||
let component_info = components.get_info_unchecked(component_id);
|
||||
let removed_components = removed_components.get_or_insert_with(component_id, Vec::new);
|
||||
removed_components.push(entity);
|
||||
match component_info.storage_type() {
|
||||
StorageType::Table => {
|
||||
let table = &mut storages.tables[location.table_id];
|
||||
// SAFETY: archetypes will always point to valid columns
|
||||
let components = table.get_column_mut(component_id).unwrap();
|
||||
// SAFETY: archetypes only store valid table_rows and the stored component type is T
|
||||
components
|
||||
.get_data_unchecked_mut(location.table_row)
|
||||
.promote()
|
||||
}
|
||||
StorageType::SparseSet => storages
|
||||
.sparse_sets
|
||||
.get_mut(component_id)
|
||||
.unwrap()
|
||||
.remove_and_forget(entity)
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a raw pointer to a particular [`Component`] by [`TypeId`] on a particular [`Entity`] in the provided [`World`].
|
||||
///
|
||||
/// # Safety
|
||||
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside
|
||||
/// the archetype
|
||||
/// - `type_id` must be correspond to a type that implements [`Component`]
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
#[inline]
|
||||
unsafe fn get_component_with_type(
|
||||
world: &World,
|
||||
type_id: TypeId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<Ptr<'_>> {
|
||||
get_component(
|
||||
world,
|
||||
world.components.get_id(type_id)?,
|
||||
storage_type,
|
||||
entity,
|
||||
location,
|
||||
)
|
||||
}
|
||||
|
||||
/// Get a raw pointer to the [`ComponentTicks`] of a particular [`Component`] by [`TypeId`] on a particular [`Entity`] in the provided [`World`].
|
||||
///
|
||||
/// # Safety
|
||||
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside
|
||||
/// the archetype
|
||||
/// - `type_id` must be correspond to a type that implements [`Component`]
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
#[inline]
|
||||
unsafe fn get_component_and_ticks_with_type(
|
||||
world: &World,
|
||||
type_id: TypeId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<(Ptr<'_>, TickCells<'_>)> {
|
||||
get_component_and_ticks(
|
||||
world,
|
||||
world.components.get_id(type_id)?,
|
||||
storage_type,
|
||||
entity,
|
||||
location,
|
||||
)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside
|
||||
/// the archetype
|
||||
/// - `type_id` must be correspond to a type that implements [`Component`]
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
#[inline]
|
||||
unsafe fn get_ticks_with_type(
|
||||
world: &World,
|
||||
type_id: TypeId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<ComponentTicks> {
|
||||
get_ticks(
|
||||
world,
|
||||
world.components.get_id(type_id)?,
|
||||
storage_type,
|
||||
entity,
|
||||
location,
|
||||
)
|
||||
}
|
||||
|
||||
fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool {
|
||||
if let Some(component_id) = world.components.get_id(type_id) {
|
||||
contains_component_with_id(world, component_id, location)
|
||||
|
@ -1044,21 +834,26 @@ pub(crate) unsafe fn get_mut<T: Component>(
|
|||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<Mut<'_, T>> {
|
||||
// SAFETY: world access is unique, entity location is valid, and returned component is of type
|
||||
// T
|
||||
let change_tick = world.change_tick();
|
||||
let last_change_tick = world.last_change_tick();
|
||||
get_component_and_ticks_with_type(
|
||||
world,
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
entity,
|
||||
location,
|
||||
)
|
||||
.map(|(value, ticks)| Mut {
|
||||
value: value.assert_unique().deref_mut::<T>(),
|
||||
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
|
||||
})
|
||||
// SAFETY:
|
||||
// - world access is unique
|
||||
// - entity location is valid
|
||||
// - and returned component is of type T
|
||||
world
|
||||
.get_component_and_ticks_with_type(
|
||||
TypeId::of::<T>(),
|
||||
T::Storage::STORAGE_TYPE,
|
||||
entity,
|
||||
location,
|
||||
)
|
||||
.map(|(value, ticks)| Mut {
|
||||
// SAFETY:
|
||||
// - world access is unique and ties world lifetime to `Mut` lifetime
|
||||
// - `value` is of type `T`
|
||||
value: value.assert_unique().deref_mut::<T>(),
|
||||
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
|
||||
})
|
||||
}
|
||||
|
||||
// SAFETY: EntityLocation must be valid, component_id must be valid
|
||||
|
@ -1068,16 +863,66 @@ pub(crate) unsafe fn get_mut_by_id(
|
|||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
component_id: ComponentId,
|
||||
) -> Option<MutUntyped> {
|
||||
) -> Option<MutUntyped<'_>> {
|
||||
let change_tick = world.change_tick();
|
||||
// SAFETY: component_id is valid
|
||||
let info = world.components.get_info_unchecked(component_id);
|
||||
// SAFETY: world access is unique, entity location and component_id required to be valid
|
||||
get_component_and_ticks(world, component_id, info.storage_type(), entity, location).map(
|
||||
|(value, ticks)| MutUntyped {
|
||||
// SAFETY:
|
||||
// - world access is unique
|
||||
// - entity location is valid
|
||||
// - and returned component is of type T
|
||||
world
|
||||
.get_component_and_ticks(component_id, info.storage_type(), entity, location)
|
||||
.map(|(value, ticks)| MutUntyped {
|
||||
// SAFETY: world access is unique and ties world lifetime to `MutUntyped` lifetime
|
||||
value: value.assert_unique(),
|
||||
ticks: TicksMut::from_tick_cells(ticks, world.last_change_tick(), change_tick),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Moves component data out of storage.
|
||||
///
|
||||
/// This function leaves the underlying memory unchanged, but the component behind
|
||||
/// returned pointer is semantically owned by the caller and will not be dropped in its original location.
|
||||
/// Caller is responsible to drop component data behind returned pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `location.table_row` must be in bounds of column of component id `component_id`
|
||||
/// - `component_id` must be valid
|
||||
/// - `components` must come from the same world as `self`
|
||||
/// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the value
|
||||
#[inline]
|
||||
pub(crate) unsafe fn take_component<'a>(
|
||||
storages: &'a mut Storages,
|
||||
components: &Components,
|
||||
removed_components: &mut SparseSet<ComponentId, Vec<Entity>>,
|
||||
component_id: ComponentId,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> OwningPtr<'a> {
|
||||
// SAFETY: caller promises component_id to be valid
|
||||
let component_info = components.get_info_unchecked(component_id);
|
||||
let removed_components = removed_components.get_or_insert_with(component_id, Vec::new);
|
||||
removed_components.push(entity);
|
||||
match component_info.storage_type() {
|
||||
StorageType::Table => {
|
||||
let table = &mut storages.tables[location.table_id];
|
||||
let components = table.get_column_mut(component_id).unwrap();
|
||||
// SAFETY:
|
||||
// - archetypes only store valid table_rows
|
||||
// - index is in bounds as promised by caller
|
||||
// - promote is safe because the caller promises to remove the table row without dropping it immediately afterwards
|
||||
components
|
||||
.get_data_unchecked_mut(location.table_row)
|
||||
.promote()
|
||||
}
|
||||
StorageType::SparseSet => storages
|
||||
.sparse_sets
|
||||
.get_mut(component_id)
|
||||
.unwrap()
|
||||
.remove_and_forget(entity)
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -3,7 +3,7 @@ mod spawn_batch;
|
|||
mod world_cell;
|
||||
|
||||
pub use crate::change_detection::{Mut, Ref};
|
||||
pub use entity_ref::*;
|
||||
pub use entity_ref::{EntityMut, EntityRef};
|
||||
pub use spawn_batch::*;
|
||||
pub use world_cell::*;
|
||||
|
||||
|
@ -12,13 +12,14 @@ use crate::{
|
|||
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
|
||||
change_detection::{MutUntyped, TicksMut},
|
||||
component::{
|
||||
Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, TickCells,
|
||||
Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components,
|
||||
StorageType, TickCells,
|
||||
},
|
||||
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
|
||||
event::{Event, Events},
|
||||
ptr::UnsafeCellDeref,
|
||||
query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery},
|
||||
storage::{ResourceData, SparseSet, Storages},
|
||||
storage::{Column, ComponentSparseSet, ResourceData, SparseSet, Storages, TableRow},
|
||||
system::Resource,
|
||||
};
|
||||
use bevy_ptr::{OwningPtr, Ptr};
|
||||
|
@ -564,8 +565,10 @@ impl World {
|
|||
/// ```
|
||||
#[inline]
|
||||
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
|
||||
// SAFETY: lifetimes enforce correct usage of returned borrow
|
||||
unsafe { get_mut(self, entity, self.get_entity(entity)?.location()) }
|
||||
// SAFETY:
|
||||
// - lifetimes enforce correct usage of returned borrow
|
||||
// - entity location is checked in `get_entity`
|
||||
unsafe { entity_ref::get_mut(self, entity, self.get_entity(entity)?.location()) }
|
||||
}
|
||||
|
||||
/// Despawns the given `entity`, if it exists. This will also remove all of the entity's
|
||||
|
@ -1727,8 +1730,7 @@ impl World {
|
|||
// - component_id is valid as checked by the line above
|
||||
// - the storage type is accurate as checked by the fetched ComponentInfo
|
||||
unsafe {
|
||||
get_component(
|
||||
self,
|
||||
self.get_component(
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
entity,
|
||||
|
@ -1751,7 +1753,7 @@ impl World {
|
|||
self.components().get_info(component_id)?;
|
||||
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
||||
unsafe {
|
||||
get_mut_by_id(
|
||||
entity_ref::get_mut_by_id(
|
||||
self,
|
||||
entity,
|
||||
self.get_entity(entity)?.location(),
|
||||
|
@ -1761,6 +1763,170 @@ impl World {
|
|||
}
|
||||
}
|
||||
|
||||
impl World {
|
||||
/// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] identified by their [`TypeId`]
|
||||
///
|
||||
/// # Safety
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
/// - `location` must refer to an archetype that contains `entity`
|
||||
/// - the caller must ensure that no aliasing rules are violated
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_component_and_ticks_with_type(
|
||||
&self,
|
||||
type_id: TypeId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<(Ptr<'_>, TickCells<'_>)> {
|
||||
let component_id = self.components.get_id(type_id)?;
|
||||
// SAFETY: component_id is valid, the rest is deferred to caller
|
||||
self.get_component_and_ticks(component_id, storage_type, entity, location)
|
||||
}
|
||||
|
||||
/// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`]
|
||||
///
|
||||
/// # Safety
|
||||
/// - `location` must refer to an archetype that contains `entity`
|
||||
/// - `component_id` must be valid
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
/// - the caller must ensure that no aliasing rules are violated
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_component_and_ticks(
|
||||
&self,
|
||||
component_id: ComponentId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<(Ptr<'_>, TickCells<'_>)> {
|
||||
match storage_type {
|
||||
StorageType::Table => {
|
||||
let (components, table_row) = self.fetch_table(location, component_id)?;
|
||||
|
||||
// SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules
|
||||
Some((
|
||||
components.get_data_unchecked(table_row),
|
||||
TickCells {
|
||||
added: components.get_added_ticks_unchecked(table_row),
|
||||
changed: components.get_changed_ticks_unchecked(table_row),
|
||||
},
|
||||
))
|
||||
}
|
||||
StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get_with_ticks(entity),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`], identified by the component's type
|
||||
///
|
||||
/// # Safety
|
||||
/// - `location` must refer to an archetype that contains `entity`
|
||||
/// the archetype
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
/// - the caller must ensure that no aliasing rules are violated
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_component_with_type(
|
||||
&self,
|
||||
type_id: TypeId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<Ptr<'_>> {
|
||||
let component_id = self.components.get_id(type_id)?;
|
||||
// SAFETY: component_id is valid, the rest is deferred to caller
|
||||
self.get_component(component_id, storage_type, entity, location)
|
||||
}
|
||||
|
||||
/// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`] in the provided [`World`](crate::world::World).
|
||||
///
|
||||
/// # Safety
|
||||
/// - `location` must refer to an archetype that contains `entity`
|
||||
/// the archetype
|
||||
/// - `component_id` must be valid
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
/// - the caller must ensure that no aliasing rules are violated
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_component(
|
||||
&self,
|
||||
component_id: ComponentId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<Ptr<'_>> {
|
||||
// SAFETY: component_id exists and is therefore valid
|
||||
match storage_type {
|
||||
StorageType::Table => {
|
||||
let (components, table_row) = self.fetch_table(location, component_id)?;
|
||||
// SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules
|
||||
Some(components.get_data_unchecked(table_row))
|
||||
}
|
||||
StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get(entity),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`], identified by the component's [`TypeId`]
|
||||
///
|
||||
/// # Safety
|
||||
/// - `location` must refer to an archetype that contains `entity`
|
||||
/// the archetype
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
/// - the caller must ensure that no aliasing rules are violated
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_ticks_with_type(
|
||||
&self,
|
||||
type_id: TypeId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<ComponentTicks> {
|
||||
let component_id = self.components.get_id(type_id)?;
|
||||
// SAFETY: component_id is valid, the rest is deferred to caller
|
||||
self.get_ticks(component_id, storage_type, entity, location)
|
||||
}
|
||||
|
||||
/// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`]
|
||||
///
|
||||
/// # Safety
|
||||
/// - `location` must refer to an archetype that contains `entity`
|
||||
/// the archetype
|
||||
/// - `component_id` must be valid
|
||||
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
|
||||
/// - the caller must ensure that no aliasing rules are violated
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_ticks(
|
||||
&self,
|
||||
component_id: ComponentId,
|
||||
storage_type: StorageType,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
) -> Option<ComponentTicks> {
|
||||
match storage_type {
|
||||
StorageType::Table => {
|
||||
let (components, table_row) = self.fetch_table(location, component_id)?;
|
||||
// SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules
|
||||
Some(components.get_ticks_unchecked(table_row))
|
||||
}
|
||||
StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get_ticks(entity),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fetch_table(
|
||||
&self,
|
||||
location: EntityLocation,
|
||||
component_id: ComponentId,
|
||||
) -> Option<(&Column, TableRow)> {
|
||||
let archetype = &self.archetypes[location.archetype_id];
|
||||
let table = &self.storages.tables[archetype.table_id()];
|
||||
let components = table.get_column(component_id)?;
|
||||
let table_row = archetype.entity_table_row(location.archetype_row);
|
||||
Some((components, table_row))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fetch_sparse_set(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> {
|
||||
self.storages.sparse_sets.get(component_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for World {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("World")
|
||||
|
|
Loading…
Reference in a new issue