Add methods to work with dynamic immutable components (#18532)
# Objective - Fixes #16861 ## Solution - Added: - `UnsafeEntityCell::get_mut_assume_mutable_by_id` - `EntityMut::get_mut_assume_mutable_by_id` - `EntityMut::get_mut_assume_mutable_by_id_unchecked` - `EntityWorldMut::into_mut_assume_mutable_by_id` - `EntityWorldMut::into_mut_assume_mutable` - `EntityWorldMut::get_mut_assume_mutable_by_id` - `EntityWorldMut::into_mut_assume_mutable_by_id` - `EntityWorldMut::modify_component_by_id` - `World::modify_component_by_id` - `DeferredWorld::modify_component_by_id` - Added `fetch_mut_assume_mutable` to `DynamicComponentFetch` trait (this is a breaking change) ## Testing - CI --- ## Migration Guide If you had previously implemented `DynamicComponentFetch` you must now include a definition for `fetch_mut_assume_mutable`. In general this will be identical to `fetch_mut` using the relevant alternatives for actually getting a component. --- ## Notes All of the added methods are minor variations on existing functions and should therefore be of low risk for inclusion during the RC process.
This commit is contained in:
parent
d6d9f63101
commit
921ff6701f
@ -103,9 +103,38 @@ impl<'w> DeferredWorld<'w> {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.modify_component_by_id(entity, component_id, move |component| {
|
||||||
|
// SAFETY: component matches the component_id collected in the above line
|
||||||
|
let mut component = unsafe { component.with_type::<T>() };
|
||||||
|
|
||||||
|
f(&mut component)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Temporarily removes a [`Component`] identified by the provided
|
||||||
|
/// [`ComponentId`] from the provided [`Entity`] and runs the provided
|
||||||
|
/// closure on it, returning the result if the component was available.
|
||||||
|
/// This will trigger the `OnRemove` and `OnReplace` component hooks without
|
||||||
|
/// causing an archetype move.
|
||||||
|
///
|
||||||
|
/// This is most useful with immutable components, where removal and reinsertion
|
||||||
|
/// is the only way to modify a value.
|
||||||
|
///
|
||||||
|
/// If you do not need to ensure the above hooks are triggered, and your component
|
||||||
|
/// is mutable, prefer using [`get_mut_by_id`](DeferredWorld::get_mut_by_id).
|
||||||
|
///
|
||||||
|
/// You should prefer the typed [`modify_component`](DeferredWorld::modify_component)
|
||||||
|
/// whenever possible.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn modify_component_by_id<R>(
|
||||||
|
&mut self,
|
||||||
|
entity: Entity,
|
||||||
|
component_id: ComponentId,
|
||||||
|
f: impl for<'a> FnOnce(MutUntyped<'a>) -> R,
|
||||||
|
) -> Result<Option<R>, EntityMutableFetchError> {
|
||||||
let entity_cell = self.get_entity_mut(entity)?;
|
let entity_cell = self.get_entity_mut(entity)?;
|
||||||
|
|
||||||
if !entity_cell.contains::<T>() {
|
if !entity_cell.contains_id(component_id) {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,11 +171,11 @@ impl<'w> DeferredWorld<'w> {
|
|||||||
// SAFETY: we will run the required hooks to simulate removal/replacement.
|
// SAFETY: we will run the required hooks to simulate removal/replacement.
|
||||||
let mut component = unsafe {
|
let mut component = unsafe {
|
||||||
entity_cell
|
entity_cell
|
||||||
.get_mut_assume_mutable::<T>()
|
.get_mut_assume_mutable_by_id(component_id)
|
||||||
.expect("component access confirmed above")
|
.expect("component access confirmed above")
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = f(&mut component);
|
let result = f(component.reborrow());
|
||||||
|
|
||||||
// Simulate adding this component by updating the relevant ticks
|
// Simulate adding this component by updating the relevant ticks
|
||||||
*component.ticks.added = *component.ticks.changed;
|
*component.ticks.added = *component.ticks.changed;
|
||||||
|
@ -824,6 +824,39 @@ impl<'w> EntityMut<'w> {
|
|||||||
unsafe { component_ids.fetch_mut(self.cell) }
|
unsafe { component_ids.fetch_mut(self.cell) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns [untyped mutable reference(s)](MutUntyped) to component(s) for
|
||||||
|
/// the current entity, based on the given [`ComponentId`]s.
|
||||||
|
/// Assumes the given [`ComponentId`]s refer to mutable components.
|
||||||
|
///
|
||||||
|
/// **You should prefer to use the typed API [`EntityMut::get_mut_assume_mutable`] where
|
||||||
|
/// possible and only use this in cases where the actual component types
|
||||||
|
/// are not known at compile time.**
|
||||||
|
///
|
||||||
|
/// Unlike [`EntityMut::get_mut_assume_mutable`], this returns untyped reference(s) to
|
||||||
|
/// component(s), and it's the job of the caller to ensure the correct
|
||||||
|
/// type(s) are dereferenced (if necessary).
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// - Returns [`EntityComponentError::MissingComponent`] if the entity does
|
||||||
|
/// not have a component.
|
||||||
|
/// - Returns [`EntityComponentError::AliasedMutability`] if a component
|
||||||
|
/// is requested multiple times.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// It is the callers responsibility to ensure that
|
||||||
|
/// - the provided [`ComponentId`]s must refer to mutable components.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn get_mut_assume_mutable_by_id<F: DynamicComponentFetch>(
|
||||||
|
&mut self,
|
||||||
|
component_ids: F,
|
||||||
|
) -> Result<F::Mut<'_>, EntityComponentError> {
|
||||||
|
// SAFETY:
|
||||||
|
// - `&mut self` ensures that no references exist to this entity's components.
|
||||||
|
// - We have exclusive access to all components of this entity.
|
||||||
|
unsafe { component_ids.fetch_mut_assume_mutable(self.cell) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns [untyped mutable reference](MutUntyped) to component for
|
/// Returns [untyped mutable reference](MutUntyped) to component for
|
||||||
/// the current entity, based on the given [`ComponentId`].
|
/// the current entity, based on the given [`ComponentId`].
|
||||||
///
|
///
|
||||||
@ -852,6 +885,36 @@ impl<'w> EntityMut<'w> {
|
|||||||
unsafe { component_ids.fetch_mut(self.cell) }
|
unsafe { component_ids.fetch_mut(self.cell) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns [untyped mutable reference](MutUntyped) to component for
|
||||||
|
/// the current entity, based on the given [`ComponentId`].
|
||||||
|
/// Assumes the given [`ComponentId`]s refer to mutable components.
|
||||||
|
///
|
||||||
|
/// Unlike [`EntityMut::get_mut_assume_mutable_by_id`], this method borrows &self instead of
|
||||||
|
/// &mut self, allowing the caller to access multiple components simultaneously.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// - Returns [`EntityComponentError::MissingComponent`] if the entity does
|
||||||
|
/// not have a component.
|
||||||
|
/// - Returns [`EntityComponentError::AliasedMutability`] if a component
|
||||||
|
/// is requested multiple times.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// It is the callers responsibility to ensure that
|
||||||
|
/// - the [`UnsafeEntityCell`] has permission to access the component mutably
|
||||||
|
/// - no other references to the component exist at the same time
|
||||||
|
/// - the provided [`ComponentId`]s must refer to mutable components.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn get_mut_assume_mutable_by_id_unchecked<F: DynamicComponentFetch>(
|
||||||
|
&self,
|
||||||
|
component_ids: F,
|
||||||
|
) -> Result<F::Mut<'_>, EntityComponentError> {
|
||||||
|
// SAFETY:
|
||||||
|
// - The caller must ensure simultaneous access is limited
|
||||||
|
// - to components that are mutually independent.
|
||||||
|
unsafe { component_ids.fetch_mut_assume_mutable(self.cell) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Consumes `self` and returns [untyped mutable reference(s)](MutUntyped)
|
/// Consumes `self` and returns [untyped mutable reference(s)](MutUntyped)
|
||||||
/// to component(s) with lifetime `'w` for the current entity, based on the
|
/// to component(s) with lifetime `'w` for the current entity, based on the
|
||||||
/// given [`ComponentId`]s.
|
/// given [`ComponentId`]s.
|
||||||
@ -885,6 +948,40 @@ impl<'w> EntityMut<'w> {
|
|||||||
unsafe { component_ids.fetch_mut(self.cell) }
|
unsafe { component_ids.fetch_mut(self.cell) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consumes `self` and returns [untyped mutable reference(s)](MutUntyped)
|
||||||
|
/// to component(s) with lifetime `'w` for the current entity, based on the
|
||||||
|
/// given [`ComponentId`]s.
|
||||||
|
/// Assumes the given [`ComponentId`]s refer to mutable components.
|
||||||
|
///
|
||||||
|
/// **You should prefer to use the typed API [`EntityMut::into_mut_assume_mutable`] where
|
||||||
|
/// possible and only use this in cases where the actual component types
|
||||||
|
/// are not known at compile time.**
|
||||||
|
///
|
||||||
|
/// Unlike [`EntityMut::into_mut_assume_mutable`], this returns untyped reference(s) to
|
||||||
|
/// component(s), and it's the job of the caller to ensure the correct
|
||||||
|
/// type(s) are dereferenced (if necessary).
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// - Returns [`EntityComponentError::MissingComponent`] if the entity does
|
||||||
|
/// not have a component.
|
||||||
|
/// - Returns [`EntityComponentError::AliasedMutability`] if a component
|
||||||
|
/// is requested multiple times.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// It is the callers responsibility to ensure that
|
||||||
|
/// - the provided [`ComponentId`]s must refer to mutable components.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn into_mut_assume_mutable_by_id<F: DynamicComponentFetch>(
|
||||||
|
self,
|
||||||
|
component_ids: F,
|
||||||
|
) -> Result<F::Mut<'w>, EntityComponentError> {
|
||||||
|
// SAFETY:
|
||||||
|
// - consuming `self` ensures that no references exist to this entity's components.
|
||||||
|
// - We have exclusive access to all components of this entity.
|
||||||
|
unsafe { component_ids.fetch_mut_assume_mutable(self.cell) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the source code location from which this entity has been spawned.
|
/// Returns the source code location from which this entity has been spawned.
|
||||||
pub fn spawned_by(&self) -> MaybeLocation {
|
pub fn spawned_by(&self) -> MaybeLocation {
|
||||||
self.cell.spawned_by()
|
self.cell.spawned_by()
|
||||||
@ -1302,6 +1399,38 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
Some(result)
|
Some(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Temporarily removes a [`Component`] `T` from this [`Entity`] and runs the
|
||||||
|
/// provided closure on it, returning the result if `T` was available.
|
||||||
|
/// This will trigger the `OnRemove` and `OnReplace` component hooks without
|
||||||
|
/// causing an archetype move.
|
||||||
|
///
|
||||||
|
/// This is most useful with immutable components, where removal and reinsertion
|
||||||
|
/// is the only way to modify a value.
|
||||||
|
///
|
||||||
|
/// If you do not need to ensure the above hooks are triggered, and your component
|
||||||
|
/// is mutable, prefer using [`get_mut`](EntityWorldMut::get_mut).
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
||||||
|
#[inline]
|
||||||
|
pub fn modify_component_by_id<R>(
|
||||||
|
&mut self,
|
||||||
|
component_id: ComponentId,
|
||||||
|
f: impl for<'a> FnOnce(MutUntyped<'a>) -> R,
|
||||||
|
) -> Option<R> {
|
||||||
|
self.assert_not_despawned();
|
||||||
|
|
||||||
|
let result = self
|
||||||
|
.world
|
||||||
|
.modify_component_by_id(self.entity, component_id, f)
|
||||||
|
.expect("entity access must be valid")?;
|
||||||
|
|
||||||
|
self.update_location();
|
||||||
|
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets mutable access to the component of type `T` for the current entity.
|
/// Gets mutable access to the component of type `T` for the current entity.
|
||||||
/// Returns `None` if the entity does not have a component of type `T`.
|
/// Returns `None` if the entity does not have a component of type `T`.
|
||||||
///
|
///
|
||||||
@ -1326,6 +1455,23 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
unsafe { self.into_unsafe_entity_cell().get_mut() }
|
unsafe { self.into_unsafe_entity_cell().get_mut() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consumes `self` and gets mutable access to the component of type `T`
|
||||||
|
/// with the world `'w` lifetime for the current entity.
|
||||||
|
/// Returns `None` if the entity does not have a component of type `T`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// - `T` must be a mutable component
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn into_mut_assume_mutable<T: Component>(self) -> Option<Mut<'w, T>> {
|
||||||
|
// SAFETY: consuming `self` implies exclusive access
|
||||||
|
unsafe { self.into_unsafe_entity_cell().get_mut_assume_mutable() }
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets a reference to the resource of the given type
|
/// Gets a reference to the resource of the given type
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -1487,6 +1633,41 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
self.as_mutable().into_mut_by_id(component_ids)
|
self.as_mutable().into_mut_by_id(component_ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns [untyped mutable reference(s)](MutUntyped) to component(s) for
|
||||||
|
/// the current entity, based on the given [`ComponentId`]s.
|
||||||
|
/// Assumes the given [`ComponentId`]s refer to mutable components.
|
||||||
|
///
|
||||||
|
/// **You should prefer to use the typed API [`EntityWorldMut::get_mut_assume_mutable`] where
|
||||||
|
/// possible and only use this in cases where the actual component types
|
||||||
|
/// are not known at compile time.**
|
||||||
|
///
|
||||||
|
/// Unlike [`EntityWorldMut::get_mut_assume_mutable`], this returns untyped reference(s) to
|
||||||
|
/// component(s), and it's the job of the caller to ensure the correct
|
||||||
|
/// type(s) are dereferenced (if necessary).
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// - Returns [`EntityComponentError::MissingComponent`] if the entity does
|
||||||
|
/// not have a component.
|
||||||
|
/// - Returns [`EntityComponentError::AliasedMutability`] if a component
|
||||||
|
/// is requested multiple times.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// It is the callers responsibility to ensure that
|
||||||
|
/// - the provided [`ComponentId`]s must refer to mutable components.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn get_mut_assume_mutable_by_id<F: DynamicComponentFetch>(
|
||||||
|
&mut self,
|
||||||
|
component_ids: F,
|
||||||
|
) -> Result<F::Mut<'_>, EntityComponentError> {
|
||||||
|
self.as_mutable()
|
||||||
|
.into_mut_assume_mutable_by_id(component_ids)
|
||||||
|
}
|
||||||
|
|
||||||
/// Consumes `self` and returns [untyped mutable reference(s)](MutUntyped)
|
/// Consumes `self` and returns [untyped mutable reference(s)](MutUntyped)
|
||||||
/// to component(s) with lifetime `'w` for the current entity, based on the
|
/// to component(s) with lifetime `'w` for the current entity, based on the
|
||||||
/// given [`ComponentId`]s.
|
/// given [`ComponentId`]s.
|
||||||
@ -1521,6 +1702,42 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
self.into_mutable().into_mut_by_id(component_ids)
|
self.into_mutable().into_mut_by_id(component_ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consumes `self` and returns [untyped mutable reference(s)](MutUntyped)
|
||||||
|
/// to component(s) with lifetime `'w` for the current entity, based on the
|
||||||
|
/// given [`ComponentId`]s.
|
||||||
|
/// Assumes the given [`ComponentId`]s refer to mutable components.
|
||||||
|
///
|
||||||
|
/// **You should prefer to use the typed API [`EntityWorldMut::into_mut_assume_mutable`] where
|
||||||
|
/// possible and only use this in cases where the actual component types
|
||||||
|
/// are not known at compile time.**
|
||||||
|
///
|
||||||
|
/// Unlike [`EntityWorldMut::into_mut_assume_mutable`], this returns untyped reference(s) to
|
||||||
|
/// component(s), and it's the job of the caller to ensure the correct
|
||||||
|
/// type(s) are dereferenced (if necessary).
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// - Returns [`EntityComponentError::MissingComponent`] if the entity does
|
||||||
|
/// not have a component.
|
||||||
|
/// - Returns [`EntityComponentError::AliasedMutability`] if a component
|
||||||
|
/// is requested multiple times.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// It is the callers responsibility to ensure that
|
||||||
|
/// - the provided [`ComponentId`]s must refer to mutable components.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn into_mut_assume_mutable_by_id<F: DynamicComponentFetch>(
|
||||||
|
self,
|
||||||
|
component_ids: F,
|
||||||
|
) -> Result<F::Mut<'w>, EntityComponentError> {
|
||||||
|
self.into_mutable()
|
||||||
|
.into_mut_assume_mutable_by_id(component_ids)
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a [`Bundle`] of components to the entity.
|
/// Adds a [`Bundle`] of components to the entity.
|
||||||
///
|
///
|
||||||
/// This will overwrite any previous value(s) of the same component type.
|
/// This will overwrite any previous value(s) of the same component type.
|
||||||
@ -4396,6 +4613,26 @@ pub unsafe trait DynamicComponentFetch {
|
|||||||
self,
|
self,
|
||||||
cell: UnsafeEntityCell<'_>,
|
cell: UnsafeEntityCell<'_>,
|
||||||
) -> Result<Self::Mut<'_>, EntityComponentError>;
|
) -> Result<Self::Mut<'_>, EntityComponentError>;
|
||||||
|
|
||||||
|
/// Returns untyped mutable reference(s) to the component(s) with the
|
||||||
|
/// given [`ComponentId`]s, as determined by `self`.
|
||||||
|
/// Assumes all [`ComponentId`]s refer to mutable components.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// It is the caller's responsibility to ensure that:
|
||||||
|
/// - The given [`UnsafeEntityCell`] has mutable access to the fetched components.
|
||||||
|
/// - No other references to the fetched components exist at the same time.
|
||||||
|
/// - The requested components are all mutable.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// - Returns [`EntityComponentError::MissingComponent`] if a component is missing from the entity.
|
||||||
|
/// - Returns [`EntityComponentError::AliasedMutability`] if a component is requested multiple times.
|
||||||
|
unsafe fn fetch_mut_assume_mutable(
|
||||||
|
self,
|
||||||
|
cell: UnsafeEntityCell<'_>,
|
||||||
|
) -> Result<Self::Mut<'_>, EntityComponentError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
@ -4421,6 +4658,15 @@ unsafe impl DynamicComponentFetch for ComponentId {
|
|||||||
unsafe { cell.get_mut_by_id(self) }
|
unsafe { cell.get_mut_by_id(self) }
|
||||||
.map_err(|_| EntityComponentError::MissingComponent(self))
|
.map_err(|_| EntityComponentError::MissingComponent(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch_mut_assume_mutable(
|
||||||
|
self,
|
||||||
|
cell: UnsafeEntityCell<'_>,
|
||||||
|
) -> Result<Self::Mut<'_>, EntityComponentError> {
|
||||||
|
// SAFETY: caller ensures that the cell has mutable access to the component.
|
||||||
|
unsafe { cell.get_mut_assume_mutable_by_id(self) }
|
||||||
|
.map_err(|_| EntityComponentError::MissingComponent(self))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
@ -4443,6 +4689,13 @@ unsafe impl<const N: usize> DynamicComponentFetch for [ComponentId; N] {
|
|||||||
) -> Result<Self::Mut<'_>, EntityComponentError> {
|
) -> Result<Self::Mut<'_>, EntityComponentError> {
|
||||||
<&Self>::fetch_mut(&self, cell)
|
<&Self>::fetch_mut(&self, cell)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch_mut_assume_mutable(
|
||||||
|
self,
|
||||||
|
cell: UnsafeEntityCell<'_>,
|
||||||
|
) -> Result<Self::Mut<'_>, EntityComponentError> {
|
||||||
|
<&Self>::fetch_mut_assume_mutable(&self, cell)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
@ -4497,6 +4750,34 @@ unsafe impl<const N: usize> DynamicComponentFetch for &'_ [ComponentId; N] {
|
|||||||
|
|
||||||
Ok(ptrs)
|
Ok(ptrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch_mut_assume_mutable(
|
||||||
|
self,
|
||||||
|
cell: UnsafeEntityCell<'_>,
|
||||||
|
) -> Result<Self::Mut<'_>, EntityComponentError> {
|
||||||
|
// Check for duplicate component IDs.
|
||||||
|
for i in 0..self.len() {
|
||||||
|
for j in 0..i {
|
||||||
|
if self[i] == self[j] {
|
||||||
|
return Err(EntityComponentError::AliasedMutability(self[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ptrs = [const { MaybeUninit::uninit() }; N];
|
||||||
|
for (ptr, &id) in core::iter::zip(&mut ptrs, self) {
|
||||||
|
*ptr = MaybeUninit::new(
|
||||||
|
// SAFETY: caller ensures that the cell has mutable access to the component.
|
||||||
|
unsafe { cell.get_mut_assume_mutable_by_id(id) }
|
||||||
|
.map_err(|_| EntityComponentError::MissingComponent(id))?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Each ptr was initialized in the loop above.
|
||||||
|
let ptrs = ptrs.map(|ptr| unsafe { MaybeUninit::assume_init(ptr) });
|
||||||
|
|
||||||
|
Ok(ptrs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
@ -4543,6 +4824,30 @@ unsafe impl DynamicComponentFetch for &'_ [ComponentId] {
|
|||||||
}
|
}
|
||||||
Ok(ptrs)
|
Ok(ptrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch_mut_assume_mutable(
|
||||||
|
self,
|
||||||
|
cell: UnsafeEntityCell<'_>,
|
||||||
|
) -> Result<Self::Mut<'_>, EntityComponentError> {
|
||||||
|
// Check for duplicate component IDs.
|
||||||
|
for i in 0..self.len() {
|
||||||
|
for j in 0..i {
|
||||||
|
if self[i] == self[j] {
|
||||||
|
return Err(EntityComponentError::AliasedMutability(self[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ptrs = Vec::with_capacity(self.len());
|
||||||
|
for &id in self {
|
||||||
|
ptrs.push(
|
||||||
|
// SAFETY: caller ensures that the cell has mutable access to the component.
|
||||||
|
unsafe { cell.get_mut_assume_mutable_by_id(id) }
|
||||||
|
.map_err(|_| EntityComponentError::MissingComponent(id))?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(ptrs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
@ -4582,6 +4887,22 @@ unsafe impl DynamicComponentFetch for &'_ HashSet<ComponentId> {
|
|||||||
}
|
}
|
||||||
Ok(ptrs)
|
Ok(ptrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch_mut_assume_mutable(
|
||||||
|
self,
|
||||||
|
cell: UnsafeEntityCell<'_>,
|
||||||
|
) -> Result<Self::Mut<'_>, EntityComponentError> {
|
||||||
|
let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default());
|
||||||
|
for &id in self {
|
||||||
|
ptrs.insert(
|
||||||
|
id,
|
||||||
|
// SAFETY: caller ensures that the cell has mutable access to the component.
|
||||||
|
unsafe { cell.get_mut_assume_mutable_by_id(id) }
|
||||||
|
.map_err(|_| EntityComponentError::MissingComponent(id))?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(ptrs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1332,6 +1332,35 @@ impl World {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Temporarily removes a [`Component`] identified by the provided
|
||||||
|
/// [`ComponentId`] from the provided [`Entity`] and runs the provided
|
||||||
|
/// closure on it, returning the result if the component was available.
|
||||||
|
/// This will trigger the `OnRemove` and `OnReplace` component hooks without
|
||||||
|
/// causing an archetype move.
|
||||||
|
///
|
||||||
|
/// This is most useful with immutable components, where removal and reinsertion
|
||||||
|
/// is the only way to modify a value.
|
||||||
|
///
|
||||||
|
/// If you do not need to ensure the above hooks are triggered, and your component
|
||||||
|
/// is mutable, prefer using [`get_mut_by_id`](World::get_mut_by_id).
|
||||||
|
///
|
||||||
|
/// You should prefer the typed [`modify_component`](World::modify_component)
|
||||||
|
/// whenever possible.
|
||||||
|
#[inline]
|
||||||
|
pub fn modify_component_by_id<R>(
|
||||||
|
&mut self,
|
||||||
|
entity: Entity,
|
||||||
|
component_id: ComponentId,
|
||||||
|
f: impl for<'a> FnOnce(MutUntyped<'a>) -> R,
|
||||||
|
) -> Result<Option<R>, EntityMutableFetchError> {
|
||||||
|
let mut world = DeferredWorld::from(&mut *self);
|
||||||
|
|
||||||
|
let result = world.modify_component_by_id(entity, component_id, f)?;
|
||||||
|
|
||||||
|
self.flush();
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
/// [`Components`](Component).
|
/// [`Components`](Component).
|
||||||
///
|
///
|
||||||
|
@ -1081,6 +1081,54 @@ impl<'w> UnsafeEntityCell<'w> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves a mutable untyped reference to the given `entity`'s [`Component`] of the given [`ComponentId`].
|
||||||
|
/// Returns `None` if the `entity` does not have a [`Component`] of the given type.
|
||||||
|
/// This method assumes the [`Component`] is mutable, skipping that check.
|
||||||
|
///
|
||||||
|
/// **You should prefer to use the typed API [`UnsafeEntityCell::get_mut_assume_mutable`] where possible and only
|
||||||
|
/// use this in cases where the actual types are not known at compile time.**
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// It is the callers responsibility to ensure that
|
||||||
|
/// - the [`UnsafeEntityCell`] has permission to access the component mutably
|
||||||
|
/// - no other references to the component exist at the same time
|
||||||
|
/// - the component `T` is mutable
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn get_mut_assume_mutable_by_id(
|
||||||
|
self,
|
||||||
|
component_id: ComponentId,
|
||||||
|
) -> Result<MutUntyped<'w>, GetEntityMutByIdError> {
|
||||||
|
self.world.assert_allows_mutable_access();
|
||||||
|
|
||||||
|
let info = self
|
||||||
|
.world
|
||||||
|
.components()
|
||||||
|
.get_info(component_id)
|
||||||
|
.ok_or(GetEntityMutByIdError::InfoNotFound)?;
|
||||||
|
|
||||||
|
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
||||||
|
unsafe {
|
||||||
|
get_component_and_ticks(
|
||||||
|
self.world,
|
||||||
|
component_id,
|
||||||
|
info.storage_type(),
|
||||||
|
self.entity,
|
||||||
|
self.location,
|
||||||
|
)
|
||||||
|
.map(|(value, cells, caller)| MutUntyped {
|
||||||
|
// SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime
|
||||||
|
value: value.assert_unique(),
|
||||||
|
ticks: TicksMut::from_tick_cells(
|
||||||
|
cells,
|
||||||
|
self.world.last_change_tick(),
|
||||||
|
self.world.change_tick(),
|
||||||
|
),
|
||||||
|
changed_by: caller.map(|caller| caller.deref_mut()),
|
||||||
|
})
|
||||||
|
.ok_or(GetEntityMutByIdError::ComponentNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the source code location from which this entity has been spawned.
|
/// Returns the source code location from which this entity has been spawned.
|
||||||
pub fn spawned_by(self) -> MaybeLocation {
|
pub fn spawned_by(self) -> MaybeLocation {
|
||||||
self.world()
|
self.world()
|
||||||
|
Loading…
Reference in New Issue
Block a user