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
@ -4,10 +4,9 @@ use crate::{
|
|||||||
change_detection::{MutUntyped, TicksMut},
|
change_detection::{MutUntyped, TicksMut},
|
||||||
component::{
|
component::{
|
||||||
Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType,
|
Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType,
|
||||||
TickCells,
|
|
||||||
},
|
},
|
||||||
entity::{Entities, Entity, EntityLocation},
|
entity::{Entities, Entity, EntityLocation},
|
||||||
storage::{Column, ComponentSparseSet, SparseSet, Storages},
|
storage::{SparseSet, Storages},
|
||||||
world::{Mut, World},
|
world::{Mut, World},
|
||||||
};
|
};
|
||||||
use bevy_ptr::{OwningPtr, Ptr};
|
use bevy_ptr::{OwningPtr, Ptr};
|
||||||
@ -72,16 +71,17 @@ impl<'w> EntityRef<'w> {
|
|||||||
pub fn get<T: Component>(&self) -> Option<&'w T> {
|
pub fn get<T: Component>(&self) -> Option<&'w T> {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - entity location and entity is valid
|
// - entity location and entity is valid
|
||||||
// - returned component is of type T
|
|
||||||
// - the storage type provided is correct for T
|
// - the storage type provided is correct for T
|
||||||
|
// - world access is immutable, lifetime tied to `&self`
|
||||||
unsafe {
|
unsafe {
|
||||||
get_component_with_type(
|
self.world
|
||||||
self.world,
|
.get_component_with_type(
|
||||||
TypeId::of::<T>(),
|
TypeId::of::<T>(),
|
||||||
T::Storage::STORAGE_TYPE,
|
T::Storage::STORAGE_TYPE,
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
self.location,
|
||||||
)
|
)
|
||||||
|
// SAFETY: returned component is of type T
|
||||||
.map(|value| value.deref::<T>())
|
.map(|value| value.deref::<T>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,10 +92,10 @@ impl<'w> EntityRef<'w> {
|
|||||||
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
|
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - entity location and entity is valid
|
// - entity location and entity is valid
|
||||||
|
// - world access is immutable, lifetime tied to `&self`
|
||||||
// - the storage type provided is correct for T
|
// - the storage type provided is correct for T
|
||||||
unsafe {
|
unsafe {
|
||||||
get_ticks_with_type(
|
self.world.get_ticks_with_type(
|
||||||
self.world,
|
|
||||||
TypeId::of::<T>(),
|
TypeId::of::<T>(),
|
||||||
T::Storage::STORAGE_TYPE,
|
T::Storage::STORAGE_TYPE,
|
||||||
self.entity,
|
self.entity,
|
||||||
@ -112,15 +112,13 @@ impl<'w> EntityRef<'w> {
|
|||||||
/// compile time.**
|
/// compile time.**
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> {
|
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)?;
|
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 {
|
unsafe {
|
||||||
get_ticks(
|
self.world.get_ticks(
|
||||||
self.world,
|
|
||||||
component_id,
|
component_id,
|
||||||
info.storage_type(),
|
info.storage_type(),
|
||||||
self.entity,
|
self.entity,
|
||||||
@ -149,8 +147,8 @@ impl<'w> EntityRef<'w> {
|
|||||||
// - entity location and entity is valid
|
// - entity location and entity is valid
|
||||||
// - returned component is of type T
|
// - returned component is of type T
|
||||||
// - the storage type provided is correct for T
|
// - the storage type provided is correct for T
|
||||||
get_component_and_ticks_with_type(
|
self.world
|
||||||
self.world,
|
.get_component_and_ticks_with_type(
|
||||||
TypeId::of::<T>(),
|
TypeId::of::<T>(),
|
||||||
T::Storage::STORAGE_TYPE,
|
T::Storage::STORAGE_TYPE,
|
||||||
self.entity,
|
self.entity,
|
||||||
@ -179,12 +177,11 @@ impl<'w> EntityRef<'w> {
|
|||||||
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
|
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
|
||||||
let info = self.world.components().get_info(component_id)?;
|
let info = self.world.components().get_info(component_id)?;
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - entity_location is valid,
|
// - entity_location and entity are valid
|
||||||
// - component_id is valid as checked by the line above
|
// . component_id is valid as checked by the line above
|
||||||
// - the storage type is accurate as checked by the fetched ComponentInfo
|
// - the storage type is accurate as checked by the fetched ComponentInfo
|
||||||
unsafe {
|
unsafe {
|
||||||
get_component(
|
self.world.get_component(
|
||||||
self.world,
|
|
||||||
component_id,
|
component_id,
|
||||||
info.storage_type(),
|
info.storage_type(),
|
||||||
self.entity,
|
self.entity,
|
||||||
@ -257,18 +254,18 @@ impl<'w> EntityMut<'w> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get<T: Component>(&self) -> Option<&'_ T> {
|
pub fn get<T: Component>(&self) -> Option<&'_ T> {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - lifetimes enforce correct usage of returned borrow
|
// - entity location is valid
|
||||||
// - entity location and entity is valid
|
// - world access is immutable, lifetime tied to `&self`
|
||||||
// - returned component is of type T
|
|
||||||
// - the storage type provided is correct for T
|
// - the storage type provided is correct for T
|
||||||
unsafe {
|
unsafe {
|
||||||
get_component_with_type(
|
self.world
|
||||||
self.world,
|
.get_component_with_type(
|
||||||
TypeId::of::<T>(),
|
TypeId::of::<T>(),
|
||||||
T::Storage::STORAGE_TYPE,
|
T::Storage::STORAGE_TYPE,
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
self.location,
|
||||||
)
|
)
|
||||||
|
// SAFETY: returned component is of type T
|
||||||
.map(|value| value.deref::<T>())
|
.map(|value| value.deref::<T>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,11 +281,11 @@ impl<'w> EntityMut<'w> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
|
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
|
||||||
// SAFETY:
|
// 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
|
// - the storage type provided is correct for T
|
||||||
unsafe {
|
unsafe {
|
||||||
get_ticks_with_type(
|
self.world.get_ticks_with_type(
|
||||||
self.world,
|
|
||||||
TypeId::of::<T>(),
|
TypeId::of::<T>(),
|
||||||
T::Storage::STORAGE_TYPE,
|
T::Storage::STORAGE_TYPE,
|
||||||
self.entity,
|
self.entity,
|
||||||
@ -305,15 +302,13 @@ impl<'w> EntityMut<'w> {
|
|||||||
/// compile time.**
|
/// compile time.**
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> {
|
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)?;
|
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 {
|
unsafe {
|
||||||
get_ticks(
|
self.world.get_ticks(
|
||||||
self.world,
|
|
||||||
component_id,
|
component_id,
|
||||||
info.storage_type(),
|
info.storage_type(),
|
||||||
self.entity,
|
self.entity,
|
||||||
@ -338,8 +333,8 @@ impl<'w> EntityMut<'w> {
|
|||||||
// - entity location and entity is valid
|
// - entity location and entity is valid
|
||||||
// - returned component is of type T
|
// - returned component is of type T
|
||||||
// - the storage type provided is correct for T
|
// - the storage type provided is correct for T
|
||||||
get_component_and_ticks_with_type(
|
self.world
|
||||||
self.world,
|
.get_component_and_ticks_with_type(
|
||||||
TypeId::of::<T>(),
|
TypeId::of::<T>(),
|
||||||
T::Storage::STORAGE_TYPE,
|
T::Storage::STORAGE_TYPE,
|
||||||
self.entity,
|
self.entity,
|
||||||
@ -417,10 +412,13 @@ impl<'w> EntityMut<'w> {
|
|||||||
let result = unsafe {
|
let result = unsafe {
|
||||||
T::from_components(storages, &mut |storages| {
|
T::from_components(storages, &mut |storages| {
|
||||||
let component_id = bundle_components.next().unwrap();
|
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(
|
take_component(
|
||||||
components,
|
|
||||||
storages,
|
storages,
|
||||||
|
components,
|
||||||
removed_components,
|
removed_components,
|
||||||
component_id,
|
component_id,
|
||||||
entity,
|
entity,
|
||||||
@ -671,8 +669,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
// - component_id is valid as checked by the line above
|
// - component_id is valid as checked by the line above
|
||||||
// - the storage type is accurate as checked by the fetched ComponentInfo
|
// - the storage type is accurate as checked by the fetched ComponentInfo
|
||||||
unsafe {
|
unsafe {
|
||||||
get_component(
|
self.world.get_component(
|
||||||
self.world,
|
|
||||||
component_id,
|
component_id,
|
||||||
info.storage_type(),
|
info.storage_type(),
|
||||||
self.entity,
|
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 {
|
fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool {
|
||||||
if let Some(component_id) = world.components.get_id(type_id) {
|
if let Some(component_id) = world.components.get_id(type_id) {
|
||||||
contains_component_with_id(world, component_id, location)
|
contains_component_with_id(world, component_id, location)
|
||||||
@ -1044,18 +834,23 @@ pub(crate) unsafe fn get_mut<T: Component>(
|
|||||||
entity: Entity,
|
entity: Entity,
|
||||||
location: EntityLocation,
|
location: EntityLocation,
|
||||||
) -> Option<Mut<'_, T>> {
|
) -> 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 change_tick = world.change_tick();
|
||||||
let last_change_tick = world.last_change_tick();
|
let last_change_tick = world.last_change_tick();
|
||||||
get_component_and_ticks_with_type(
|
// SAFETY:
|
||||||
world,
|
// - 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>(),
|
TypeId::of::<T>(),
|
||||||
T::Storage::STORAGE_TYPE,
|
T::Storage::STORAGE_TYPE,
|
||||||
entity,
|
entity,
|
||||||
location,
|
location,
|
||||||
)
|
)
|
||||||
.map(|(value, ticks)| Mut {
|
.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>(),
|
value: value.assert_unique().deref_mut::<T>(),
|
||||||
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
|
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
|
||||||
})
|
})
|
||||||
@ -1068,16 +863,66 @@ pub(crate) unsafe fn get_mut_by_id(
|
|||||||
entity: Entity,
|
entity: Entity,
|
||||||
location: EntityLocation,
|
location: EntityLocation,
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
) -> Option<MutUntyped> {
|
) -> Option<MutUntyped<'_>> {
|
||||||
let change_tick = world.change_tick();
|
let change_tick = world.change_tick();
|
||||||
|
// SAFETY: component_id is valid
|
||||||
let info = world.components.get_info_unchecked(component_id);
|
let info = world.components.get_info_unchecked(component_id);
|
||||||
// SAFETY: world access is unique, entity location and component_id required to be valid
|
// SAFETY:
|
||||||
get_component_and_ticks(world, component_id, info.storage_type(), entity, location).map(
|
// - world access is unique
|
||||||
|(value, ticks)| MutUntyped {
|
// - 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(),
|
value: value.assert_unique(),
|
||||||
ticks: TicksMut::from_tick_cells(ticks, world.last_change_tick(), change_tick),
|
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)]
|
#[cfg(test)]
|
||||||
|
|||||||
@ -3,7 +3,7 @@ mod spawn_batch;
|
|||||||
mod world_cell;
|
mod world_cell;
|
||||||
|
|
||||||
pub use crate::change_detection::{Mut, Ref};
|
pub use crate::change_detection::{Mut, Ref};
|
||||||
pub use entity_ref::*;
|
pub use entity_ref::{EntityMut, EntityRef};
|
||||||
pub use spawn_batch::*;
|
pub use spawn_batch::*;
|
||||||
pub use world_cell::*;
|
pub use world_cell::*;
|
||||||
|
|
||||||
@ -12,13 +12,14 @@ use crate::{
|
|||||||
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
|
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
|
||||||
change_detection::{MutUntyped, TicksMut},
|
change_detection::{MutUntyped, TicksMut},
|
||||||
component::{
|
component::{
|
||||||
Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, TickCells,
|
Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components,
|
||||||
|
StorageType, TickCells,
|
||||||
},
|
},
|
||||||
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
|
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
|
||||||
event::{Event, Events},
|
event::{Event, Events},
|
||||||
ptr::UnsafeCellDeref,
|
ptr::UnsafeCellDeref,
|
||||||
query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery},
|
query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery},
|
||||||
storage::{ResourceData, SparseSet, Storages},
|
storage::{Column, ComponentSparseSet, ResourceData, SparseSet, Storages, TableRow},
|
||||||
system::Resource,
|
system::Resource,
|
||||||
};
|
};
|
||||||
use bevy_ptr::{OwningPtr, Ptr};
|
use bevy_ptr::{OwningPtr, Ptr};
|
||||||
@ -564,8 +565,10 @@ impl World {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
|
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
|
||||||
// SAFETY: lifetimes enforce correct usage of returned borrow
|
// SAFETY:
|
||||||
unsafe { get_mut(self, entity, self.get_entity(entity)?.location()) }
|
// - 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
|
/// 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
|
// - component_id is valid as checked by the line above
|
||||||
// - the storage type is accurate as checked by the fetched ComponentInfo
|
// - the storage type is accurate as checked by the fetched ComponentInfo
|
||||||
unsafe {
|
unsafe {
|
||||||
get_component(
|
self.get_component(
|
||||||
self,
|
|
||||||
component_id,
|
component_id,
|
||||||
info.storage_type(),
|
info.storage_type(),
|
||||||
entity,
|
entity,
|
||||||
@ -1751,7 +1753,7 @@ impl World {
|
|||||||
self.components().get_info(component_id)?;
|
self.components().get_info(component_id)?;
|
||||||
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
||||||
unsafe {
|
unsafe {
|
||||||
get_mut_by_id(
|
entity_ref::get_mut_by_id(
|
||||||
self,
|
self,
|
||||||
entity,
|
entity,
|
||||||
self.get_entity(entity)?.location(),
|
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 {
|
impl fmt::Debug for World {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("World")
|
f.debug_struct("World")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user