Skip to content

Commit 9705eae

Browse files
bushrat011899mockersf
authored andcommitted
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.
1 parent 8dc1a7c commit 9705eae

File tree

4 files changed

+430
-3
lines changed

4 files changed

+430
-3
lines changed

crates/bevy_ecs/src/world/deferred_world.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,38 @@ impl<'w> DeferredWorld<'w> {
101101
return Ok(None);
102102
};
103103

104+
self.modify_component_by_id(entity, component_id, move |component| {
105+
// SAFETY: component matches the component_id collected in the above line
106+
let mut component = unsafe { component.with_type::<T>() };
107+
108+
f(&mut component)
109+
})
110+
}
111+
112+
/// Temporarily removes a [`Component`] identified by the provided
113+
/// [`ComponentId`] from the provided [`Entity`] and runs the provided
114+
/// closure on it, returning the result if the component was available.
115+
/// This will trigger the `OnRemove` and `OnReplace` component hooks without
116+
/// causing an archetype move.
117+
///
118+
/// This is most useful with immutable components, where removal and reinsertion
119+
/// is the only way to modify a value.
120+
///
121+
/// If you do not need to ensure the above hooks are triggered, and your component
122+
/// is mutable, prefer using [`get_mut_by_id`](DeferredWorld::get_mut_by_id).
123+
///
124+
/// You should prefer the typed [`modify_component`](DeferredWorld::modify_component)
125+
/// whenever possible.
126+
#[inline]
127+
pub(crate) fn modify_component_by_id<R>(
128+
&mut self,
129+
entity: Entity,
130+
component_id: ComponentId,
131+
f: impl for<'a> FnOnce(MutUntyped<'a>) -> R,
132+
) -> Result<Option<R>, EntityMutableFetchError> {
104133
let entity_cell = self.get_entity_mut(entity)?;
105134

106-
if !entity_cell.contains::<T>() {
135+
if !entity_cell.contains_id(component_id) {
107136
return Ok(None);
108137
}
109138

@@ -140,11 +169,11 @@ impl<'w> DeferredWorld<'w> {
140169
// SAFETY: we will run the required hooks to simulate removal/replacement.
141170
let mut component = unsafe {
142171
entity_cell
143-
.get_mut_assume_mutable::<T>()
172+
.get_mut_assume_mutable_by_id(component_id)
144173
.expect("component access confirmed above")
145174
};
146175

147-
let result = f(&mut component);
176+
let result = f(component.reborrow());
148177

149178
// Simulate adding this component by updating the relevant ticks
150179
*component.ticks.added = *component.ticks.changed;

0 commit comments

Comments
 (0)