Skip to content

Commit 967a011

Browse files
add InteriorMutableEntityRef
1 parent 3d6d454 commit 967a011

File tree

2 files changed

+211
-4
lines changed

2 files changed

+211
-4
lines changed

crates/bevy_ecs/src/world/entity_ref.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -673,15 +673,19 @@ impl<'w> EntityMut<'w> {
673673
}
674674
}
675675

676-
fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool {
676+
pub(crate) fn contains_component_with_type(
677+
world: &World,
678+
type_id: TypeId,
679+
location: EntityLocation,
680+
) -> bool {
677681
if let Some(component_id) = world.components.get_id(type_id) {
678682
contains_component_with_id(world, component_id, location)
679683
} else {
680684
false
681685
}
682686
}
683687

684-
fn contains_component_with_id(
688+
pub(crate) fn contains_component_with_id(
685689
world: &World,
686690
component_id: ComponentId,
687691
location: EntityLocation,

crates/bevy_ecs/src/world/interior_mutable_world.rs

Lines changed: 205 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
#![warn(unsafe_op_in_unsafe_fn)]
22

3-
use super::{Mut, World};
3+
use super::{entity_ref, Mut, World};
44
use crate::{
5+
archetype::Archetype,
56
change_detection::{MutUntyped, Ticks},
6-
component::ComponentId,
7+
component::{ComponentId, ComponentTicks},
8+
entity::{Entity, EntityLocation},
9+
prelude::Component,
710
system::Resource,
811
};
912
use bevy_ptr::Ptr;
@@ -81,6 +84,17 @@ impl<'w> InteriorMutableWorld<'w> {
8184
self.0
8285
}
8386

87+
/// Retrieves an [`InteriorMutableEntityRef`] that exposes read and write operations for the given `entity`.
88+
/// Similar to the [`InteriorMutableWorld`], you are in charge of making sure that no aliasing rules are violated.
89+
pub fn get_entity(&self, entity: Entity) -> Option<InteriorMutableEntityRef<'w>> {
90+
let location = self.0.entities.get(entity)?;
91+
Some(InteriorMutableEntityRef {
92+
world: InteriorMutableWorld(self.0),
93+
entity,
94+
location,
95+
})
96+
}
97+
8498
/// Gets a reference to the resource of the given type if it exists
8599
#[inline]
86100
pub unsafe fn get_resource<R: Resource>(&self) -> Option<&'w R> {
@@ -192,3 +206,192 @@ impl<'w> InteriorMutableWorld<'w> {
192206
unsafe { self.get_resource_mut_with_id(component_id) }
193207
}
194208
}
209+
210+
/// A interior-mutable reference to a particular [`Entity`] and all of its components
211+
#[derive(Copy, Clone)]
212+
pub struct InteriorMutableEntityRef<'w> {
213+
world: InteriorMutableWorld<'w>,
214+
entity: Entity,
215+
location: EntityLocation,
216+
}
217+
218+
impl<'w> InteriorMutableEntityRef<'w> {
219+
#[inline]
220+
#[must_use = "Omit the .id() call if you do not need to store the `Entity` identifier."]
221+
pub fn id(&self) -> Entity {
222+
self.entity
223+
}
224+
225+
#[inline]
226+
pub fn location(&self) -> EntityLocation {
227+
self.location
228+
}
229+
230+
#[inline]
231+
pub fn archetype(&self) -> &Archetype {
232+
&self.world.0.archetypes[self.location.archetype_id]
233+
}
234+
235+
#[inline]
236+
pub fn world(&self) -> InteriorMutableWorld<'w> {
237+
self.world
238+
}
239+
240+
#[inline]
241+
pub fn contains<T: Component>(&self) -> bool {
242+
self.contains_type_id(TypeId::of::<T>())
243+
}
244+
245+
#[inline]
246+
pub fn contains_id(&self, component_id: ComponentId) -> bool {
247+
entity_ref::contains_component_with_id(self.world.0, component_id, self.location)
248+
}
249+
250+
#[inline]
251+
pub fn contains_type_id(&self, type_id: TypeId) -> bool {
252+
entity_ref::contains_component_with_type(self.world.0, type_id, self.location)
253+
}
254+
255+
#[inline]
256+
pub unsafe fn get<T: Component>(&self) -> Option<&'w T> {
257+
// SAFETY:
258+
// - entity location is valid
259+
// - archetypes and components come from the same world
260+
// - world access is immutable, lifetime tied to `&self`
261+
unsafe {
262+
self.world
263+
.0
264+
.storages
265+
.get_component_with_type(
266+
&self.world.0.archetypes,
267+
&self.world.0.components,
268+
TypeId::of::<T>(),
269+
self.entity,
270+
self.location,
271+
)
272+
// SAFETY: returned component is of type T
273+
.map(|value| value.deref::<T>())
274+
}
275+
}
276+
277+
/// Retrieves the change ticks for the given component. This can be useful for implementing change
278+
/// detection in custom runtimes.
279+
#[inline]
280+
pub unsafe fn get_change_ticks<T: Component>(&self) -> Option<&'w ComponentTicks> {
281+
// SAFETY:
282+
// - entity location is valid
283+
// - archetypes and components come from the same world
284+
// - world access is immutable, lifetime tied to `&self`
285+
unsafe {
286+
self.world
287+
.0
288+
.storages
289+
.get_ticks_with_type(
290+
&self.world.0.archetypes,
291+
&self.world.0.components,
292+
TypeId::of::<T>(),
293+
self.entity,
294+
self.location,
295+
)
296+
// SAFETY: we have read access, lifetime tied to `&self`
297+
.map(|ticks| ticks.deref())
298+
}
299+
}
300+
301+
#[inline]
302+
pub unsafe fn get_mut<T: Component>(&self) -> Option<Mut<'w, T>> {
303+
// SAFETY: same safety requirements
304+
unsafe {
305+
self.get_mut_using_ticks(
306+
self.world.0.last_change_tick(),
307+
self.world.0.read_change_tick(),
308+
)
309+
}
310+
}
311+
312+
#[inline]
313+
pub unsafe fn get_mut_using_ticks<T: Component>(
314+
&self,
315+
last_change_tick: u32,
316+
change_tick: u32,
317+
) -> Option<Mut<'w, T>> {
318+
unsafe {
319+
self.world
320+
.0
321+
.storages
322+
.get_component_and_ticks_with_type(
323+
&self.world.0.archetypes,
324+
&self.world.0.components,
325+
TypeId::of::<T>(),
326+
self.entity,
327+
self.location,
328+
)
329+
.map(|(value, ticks)| Mut {
330+
value: value.assert_unique().deref_mut::<T>(),
331+
ticks: Ticks {
332+
component_ticks: ticks.deref_mut(),
333+
last_change_tick,
334+
change_tick,
335+
},
336+
})
337+
}
338+
}
339+
}
340+
341+
impl<'w> InteriorMutableEntityRef<'w> {
342+
/// Gets the component of the given [`ComponentId`] from the entity.
343+
///
344+
/// **You should prefer to use the typed API where possible and only
345+
/// use this in cases where the actual component types are not known at
346+
/// compile time.**
347+
///
348+
/// Unlike [`InteriorMutableEntityRef::get`], this returns a raw pointer to the component,
349+
/// which is only valid while the `'w` borrow of the lifetime is active.
350+
#[inline]
351+
pub unsafe fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
352+
self.world.0.components.get_info(component_id)?;
353+
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
354+
unsafe {
355+
self.world.0.storages.get_component(
356+
&self.world.0.archetypes,
357+
&self.world.0.components,
358+
component_id,
359+
self.entity,
360+
self.location,
361+
)
362+
}
363+
}
364+
365+
/// Retrieves a mutable untyped reference to the given `entity`'s [Component] of the given [`ComponentId`].
366+
/// Returns [None] if the `entity` does not have a [Component] of the given type.
367+
///
368+
/// **You should prefer to use the typed API [`InteriorMutableEntityRef::get_mut`] where possible and only
369+
/// use this in cases where the actual types are not known at compile time.**
370+
#[inline]
371+
pub unsafe fn get_mut_by_id(&self, component_id: ComponentId) -> Option<MutUntyped<'w>> {
372+
self.world.0.components.get_info(component_id)?;
373+
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
374+
unsafe {
375+
self.world
376+
.0
377+
.storages
378+
.get_component_and_ticks(
379+
&self.world.0.archetypes,
380+
&self.world.0.components,
381+
component_id,
382+
self.entity,
383+
self.location,
384+
)
385+
.map(|(value, ticks)| MutUntyped {
386+
// SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime
387+
value: value.assert_unique(),
388+
ticks: Ticks {
389+
// SAFETY: world access is validated by caller and ties world lifetime to `Mut` lifetime
390+
component_ticks: ticks.deref_mut(),
391+
last_change_tick: self.world.0.last_change_tick(),
392+
change_tick: self.world.0.read_change_tick(),
393+
},
394+
})
395+
}
396+
}
397+
}

0 commit comments

Comments
 (0)