Add missing unsafe to entity_command::insert_by_id and make it more configurable (#18052)
## Objective `insert_by_id` is unsafe, but I forgot to add that to the manually-queueable version in `entity_command`. It also can only insert using `InsertMode::Replace`, when it could easily be configurable by threading an `InsertMode` parameter to the final `BundleInserter::insert` call. ## Solution - Add `unsafe` and safety comment. - Add `InsertMode` parameter to `entity_command::insert_by_id`, `EntityWorldMut::insert_by_id_with_caller`, and `EntityWorldMut::insert_dynamic_bundle`. - Add `InsertMode` parameter to `entity_command::insert` and remove `entity_command::insert_if_new`, for consistency with the other manually-queued insertion commands.
This commit is contained in:
parent
ad016cb850
commit
67146bdef7
@ -151,36 +151,34 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity,
|
/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity.
|
||||||
/// replacing any that were already present.
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn insert(bundle: impl Bundle) -> impl EntityCommand {
|
pub fn insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {
|
||||||
let caller = MaybeLocation::caller();
|
let caller = MaybeLocation::caller();
|
||||||
move |mut entity: EntityWorldMut| {
|
move |mut entity: EntityWorldMut| {
|
||||||
entity.insert_with_caller(bundle, InsertMode::Replace, caller);
|
entity.insert_with_caller(bundle, mode, caller);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity,
|
|
||||||
/// except for any that were already present.
|
|
||||||
#[track_caller]
|
|
||||||
pub fn insert_if_new(bundle: impl Bundle) -> impl EntityCommand {
|
|
||||||
let caller = MaybeLocation::caller();
|
|
||||||
move |mut entity: EntityWorldMut| {
|
|
||||||
entity.insert_with_caller(bundle, InsertMode::Keep, caller);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An [`EntityCommand`] that adds a dynamic component to an entity.
|
/// An [`EntityCommand`] that adds a dynamic component to an entity.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// - [`ComponentId`] must be from the same world as the target entity.
|
||||||
|
/// - `T` must have the same layout as the one passed during `component_id` creation.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn insert_by_id<T: Send + 'static>(component_id: ComponentId, value: T) -> impl EntityCommand {
|
pub unsafe fn insert_by_id<T: Send + 'static>(
|
||||||
|
component_id: ComponentId,
|
||||||
|
value: T,
|
||||||
|
mode: InsertMode,
|
||||||
|
) -> impl EntityCommand {
|
||||||
let caller = MaybeLocation::caller();
|
let caller = MaybeLocation::caller();
|
||||||
move |mut entity: EntityWorldMut| {
|
move |mut entity: EntityWorldMut| {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - `component_id` safety is ensured by the caller
|
// - `component_id` safety is ensured by the caller
|
||||||
// - `ptr` is valid within the `make` block
|
// - `ptr` is valid within the `make` block
|
||||||
OwningPtr::make(value, |ptr| unsafe {
|
OwningPtr::make(value, |ptr| unsafe {
|
||||||
entity.insert_by_id_with_caller(component_id, ptr, caller);
|
entity.insert_by_id_with_caller(component_id, ptr, mode, caller);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1290,7 +1290,7 @@ impl<'a> EntityCommands<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn insert(&mut self, bundle: impl Bundle) -> &mut Self {
|
pub fn insert(&mut self, bundle: impl Bundle) -> &mut Self {
|
||||||
self.queue(entity_command::insert(bundle))
|
self.queue(entity_command::insert(bundle, InsertMode::Replace))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to [`Self::insert`] but will only insert if the predicate returns true.
|
/// Similar to [`Self::insert`] but will only insert if the predicate returns true.
|
||||||
@ -1350,7 +1350,7 @@ impl<'a> EntityCommands<'a> {
|
|||||||
/// To avoid a panic in this case, use the command [`Self::try_insert_if_new`] instead.
|
/// To avoid a panic in this case, use the command [`Self::try_insert_if_new`] instead.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn insert_if_new(&mut self, bundle: impl Bundle) -> &mut Self {
|
pub fn insert_if_new(&mut self, bundle: impl Bundle) -> &mut Self {
|
||||||
self.queue(entity_command::insert_if_new(bundle))
|
self.queue(entity_command::insert(bundle, InsertMode::Keep))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a [`Bundle`] of components to the entity without overwriting if the
|
/// Adds a [`Bundle`] of components to the entity without overwriting if the
|
||||||
@ -1399,7 +1399,12 @@ impl<'a> EntityCommands<'a> {
|
|||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
value: T,
|
value: T,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.queue(entity_command::insert_by_id(component_id, value))
|
self.queue(
|
||||||
|
// SAFETY:
|
||||||
|
// - `ComponentId` safety is ensured by the caller.
|
||||||
|
// - `T` safety is ensured by the caller.
|
||||||
|
unsafe { entity_command::insert_by_id(component_id, value, InsertMode::Replace) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to add a dynamic component to an entity.
|
/// Attempts to add a dynamic component to an entity.
|
||||||
@ -1417,7 +1422,10 @@ impl<'a> EntityCommands<'a> {
|
|||||||
value: T,
|
value: T,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.queue_handled(
|
self.queue_handled(
|
||||||
entity_command::insert_by_id(component_id, value),
|
// SAFETY:
|
||||||
|
// - `ComponentId` safety is ensured by the caller.
|
||||||
|
// - `T` safety is ensured by the caller.
|
||||||
|
unsafe { entity_command::insert_by_id(component_id, value, InsertMode::Replace) },
|
||||||
error_handler::silent(),
|
error_handler::silent(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1472,7 +1480,10 @@ impl<'a> EntityCommands<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn try_insert(&mut self, bundle: impl Bundle) -> &mut Self {
|
pub fn try_insert(&mut self, bundle: impl Bundle) -> &mut Self {
|
||||||
self.queue_handled(entity_command::insert(bundle), error_handler::silent())
|
self.queue_handled(
|
||||||
|
entity_command::insert(bundle, InsertMode::Replace),
|
||||||
|
error_handler::silent(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to [`Self::try_insert`] but will only try to insert if the predicate returns true.
|
/// Similar to [`Self::try_insert`] but will only try to insert if the predicate returns true.
|
||||||
@ -1572,7 +1583,7 @@ impl<'a> EntityCommands<'a> {
|
|||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn try_insert_if_new(&mut self, bundle: impl Bundle) -> &mut Self {
|
pub fn try_insert_if_new(&mut self, bundle: impl Bundle) -> &mut Self {
|
||||||
self.queue_handled(
|
self.queue_handled(
|
||||||
entity_command::insert_if_new(bundle),
|
entity_command::insert(bundle, InsertMode::Keep),
|
||||||
error_handler::silent(),
|
error_handler::silent(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1585,16 +1585,24 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
component: OwningPtr<'_>,
|
component: OwningPtr<'_>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.insert_by_id_with_caller(component_id, component, MaybeLocation::caller())
|
self.insert_by_id_with_caller(
|
||||||
|
component_id,
|
||||||
|
component,
|
||||||
|
InsertMode::Replace,
|
||||||
|
MaybeLocation::caller(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// See [`EntityWorldMut::insert_by_id`]
|
///
|
||||||
|
/// - [`ComponentId`] must be from the same world as [`EntityWorldMut`]
|
||||||
|
/// - [`OwningPtr`] must be a valid reference to the type represented by [`ComponentId`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn insert_by_id_with_caller(
|
pub(crate) unsafe fn insert_by_id_with_caller(
|
||||||
&mut self,
|
&mut self,
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
component: OwningPtr<'_>,
|
component: OwningPtr<'_>,
|
||||||
|
mode: InsertMode,
|
||||||
caller: MaybeLocation,
|
caller: MaybeLocation,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.assert_not_despawned();
|
self.assert_not_despawned();
|
||||||
@ -1619,6 +1627,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
self.location,
|
self.location,
|
||||||
Some(component).into_iter(),
|
Some(component).into_iter(),
|
||||||
Some(storage_type).iter().cloned(),
|
Some(storage_type).iter().cloned(),
|
||||||
|
mode,
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
@ -1670,6 +1679,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
self.location,
|
self.location,
|
||||||
iter_components,
|
iter_components,
|
||||||
(*storage_types).iter().cloned(),
|
(*storage_types).iter().cloned(),
|
||||||
|
InsertMode::Replace,
|
||||||
MaybeLocation::caller(),
|
MaybeLocation::caller(),
|
||||||
);
|
);
|
||||||
*self.world.bundles.get_storages_unchecked(bundle_id) = core::mem::take(&mut storage_types);
|
*self.world.bundles.get_storages_unchecked(bundle_id) = core::mem::take(&mut storage_types);
|
||||||
@ -4153,6 +4163,7 @@ unsafe fn insert_dynamic_bundle<
|
|||||||
location: EntityLocation,
|
location: EntityLocation,
|
||||||
components: I,
|
components: I,
|
||||||
storage_types: S,
|
storage_types: S,
|
||||||
|
mode: InsertMode,
|
||||||
caller: MaybeLocation,
|
caller: MaybeLocation,
|
||||||
) -> EntityLocation {
|
) -> EntityLocation {
|
||||||
struct DynamicInsertBundle<'a, I: Iterator<Item = (StorageType, OwningPtr<'a>)>> {
|
struct DynamicInsertBundle<'a, I: Iterator<Item = (StorageType, OwningPtr<'a>)>> {
|
||||||
@ -4175,7 +4186,7 @@ unsafe fn insert_dynamic_bundle<
|
|||||||
// SAFETY: location matches current entity.
|
// SAFETY: location matches current entity.
|
||||||
unsafe {
|
unsafe {
|
||||||
bundle_inserter
|
bundle_inserter
|
||||||
.insert(entity, location, bundle, InsertMode::Replace, caller)
|
.insert(entity, location, bundle, mode, caller)
|
||||||
.0
|
.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user