use super::{Mut, Ref, World, WorldId};
use crate::{
archetype::{Archetype, Archetypes},
bundle::Bundles,
change_detection::{
ComponentTickCells, ComponentTicks, ComponentTicksMut, ComponentTicksRef, MaybeLocation,
MutUntyped, Tick,
},
component::{ComponentId, Components, Mutable, StorageType},
entity::{
ContainsEntity, Entities, Entity, EntityAllocator, EntityLocation, EntityNotSpawnedError,
},
error::{ErrorHandler, FallbackErrorHandler},
lifecycle::RemovedComponentMessages,
observer::Observers,
prelude::Component,
query::{DebugCheckedUnwrap, QueryAccessError, ReleaseStateQueryData, SingleEntityQueryData},
resource::{Resource, ResourceEntities},
storage::{ComponentSparseSet, Storages, Table},
world::RawCommandQueue,
};
use bevy_platform::sync::atomic::Ordering;
use bevy_ptr::{Ptr, UnsafeCellDeref};
use core::{any::TypeId, cell::UnsafeCell, fmt::Debug, marker::PhantomData, ptr};
use thiserror::Error;
#[derive(Copy, Clone)]
pub struct UnsafeWorldCell<'w> {
ptr: *mut World,
#[cfg(debug_assertions)]
allows_mutable_access: bool,
_marker: PhantomData<(&'w World, &'w UnsafeCell<World>)>,
}
unsafe impl Send for UnsafeWorldCell<'_> {}
unsafe impl Sync for UnsafeWorldCell<'_> {}
impl<'w> From<&'w mut World> for UnsafeWorldCell<'w> {
fn from(value: &'w mut World) -> Self {
value.as_unsafe_world_cell()
}
}
impl<'w> From<&'w World> for UnsafeWorldCell<'w> {
fn from(value: &'w World) -> Self {
value.as_unsafe_world_cell_readonly()
}
}
impl<'w> UnsafeWorldCell<'w> {
#[inline]
pub(crate) fn new_readonly(world: &'w World) -> Self {
Self {
ptr: ptr::from_ref(world).cast_mut(),
#[cfg(debug_assertions)]
allows_mutable_access: false,
_marker: PhantomData,
}
}
#[inline]
pub(crate) fn new_mutable(world: &'w mut World) -> Self {
Self {
ptr: ptr::from_mut(world),
#[cfg(debug_assertions)]
allows_mutable_access: true,
_marker: PhantomData,
}
}
#[cfg_attr(debug_assertions, inline(never), track_caller)]
#[cfg_attr(not(debug_assertions), inline(always))]
pub(crate) fn assert_allows_mutable_access(self) {
#[cfg(debug_assertions)]
debug_assert!(
self.allows_mutable_access,
"mutating world data via `World::as_unsafe_world_cell_readonly` is forbidden"
);
}
#[inline]
pub unsafe fn world_mut(self) -> &'w mut World {
self.assert_allows_mutable_access();
unsafe { &mut *self.ptr }
}
#[inline]
pub unsafe fn world(self) -> &'w World {
unsafe { self.unsafe_world() }
}
#[inline]
pub unsafe fn world_metadata(self) -> &'w World {
unsafe { self.unsafe_world() }
}
#[inline]
unsafe fn unsafe_world(self) -> &'w World {
unsafe { &*self.ptr }
}
#[inline]
pub fn id(self) -> WorldId {
unsafe { self.world_metadata() }.id()
}
#[inline]
pub fn entities(self) -> &'w Entities {
&unsafe { self.world_metadata() }.entities
}
#[inline]
pub fn entity_allocator(self) -> &'w EntityAllocator {
&unsafe { self.world_metadata() }.entity_allocator
}
#[inline]
pub fn archetypes(self) -> &'w Archetypes {
&unsafe { self.world_metadata() }.archetypes
}
#[inline]
pub fn components(self) -> &'w Components {
&unsafe { self.world_metadata() }.components
}
#[inline]
pub unsafe fn resource_entities(self) -> &'w ResourceEntities {
&unsafe { self.world_metadata() }.resource_entities
}
pub fn removed_components(self) -> &'w RemovedComponentMessages {
&unsafe { self.world_metadata() }.removed_components
}
pub(crate) fn observers(self) -> &'w Observers {
&unsafe { self.world_metadata() }.observers
}
#[inline]
pub fn bundles(self) -> &'w Bundles {
&unsafe { self.world_metadata() }.bundles
}
#[inline]
pub fn change_tick(self) -> Tick {
unsafe { self.world_metadata() }.read_change_tick()
}
#[inline]
pub fn last_trigger_id(&self) -> u32 {
unsafe { self.world_metadata() }.last_trigger_id()
}
#[inline]
pub fn last_change_tick(self) -> Tick {
unsafe { self.world_metadata() }.last_change_tick()
}
#[inline]
pub fn increment_change_tick(self) -> Tick {
let change_tick = unsafe { &self.world_metadata().change_tick };
Tick::new(change_tick.fetch_add(1, Ordering::Relaxed))
}
#[inline]
pub unsafe fn storages(self) -> &'w Storages {
&unsafe { self.unsafe_world() }.storages
}
#[inline]
pub fn get_entity(self, entity: Entity) -> Result<UnsafeEntityCell<'w>, EntityNotSpawnedError> {
let location = self.entities().get_spawned(entity)?;
Ok(UnsafeEntityCell::new(
self,
entity,
location,
self.last_change_tick(),
self.change_tick(),
))
}
#[inline]
pub fn get_entity_with_ticks(
self,
entity: Entity,
last_run: Tick,
this_run: Tick,
) -> Result<UnsafeEntityCell<'w>, EntityNotSpawnedError> {
let location = self.entities().get_spawned(entity)?;
Ok(UnsafeEntityCell::new(
self, entity, location, last_run, this_run,
))
}
#[inline]
pub unsafe fn get_resource<R: Resource>(self) -> Option<&'w R> {
let component_id = self.components().get_valid_id(TypeId::of::<R>())?;
unsafe {
self.get_resource_by_id(component_id)
.map(|ptr| ptr.deref::<R>())
}
}
#[inline]
pub unsafe fn get_resource_ref<R: Resource>(self) -> Option<Ref<'w, R>> {
let component_id = self.components().get_valid_id(TypeId::of::<R>())?;
let (ptr, ticks) = unsafe { self.get_resource_with_ticks(component_id)? };
let value = unsafe { ptr.deref::<R>() };
let ticks = unsafe {
ComponentTicksRef::from_tick_cells(ticks, self.last_change_tick(), self.change_tick())
};
Some(Ref { value, ticks })
}
#[inline]
pub unsafe fn get_resource_by_id(self, component_id: ComponentId) -> Option<Ptr<'w>> {
let entity = unsafe { self.resource_entities() }.get(component_id)?;
let entity_cell = self.get_entity(entity).ok()?;
entity_cell.get_by_id(component_id)
}
#[deprecated(since = "0.19.0", note = "use UnsafeWorldCell::get_non_send")]
pub unsafe fn get_non_send_resource<R: 'static>(self) -> Option<&'w R> {
self.get_non_send::<R>()
}
#[inline]
pub unsafe fn get_non_send<R: 'static>(self) -> Option<&'w R> {
let component_id = self.components().get_valid_id(TypeId::of::<R>())?;
unsafe {
self.get_non_send_by_id(component_id)
.map(|ptr| ptr.deref::<R>())
}
}
#[deprecated(since = "0.19.0", note = "use UnsafeWorldCell::get_non_send_by_id")]
pub unsafe fn get_non_send_resource_by_id(self, component_id: ComponentId) -> Option<Ptr<'w>> {
self.get_non_send_by_id(component_id)
}
#[inline]
pub unsafe fn get_non_send_by_id(self, component_id: ComponentId) -> Option<Ptr<'w>> {
unsafe { self.storages() }
.non_sends
.get(component_id)?
.get_data()
}
#[inline]
pub unsafe fn get_resource_mut<R: Resource<Mutability = Mutable>>(self) -> Option<Mut<'w, R>> {
self.assert_allows_mutable_access();
let component_id = self.components().get_valid_id(TypeId::of::<R>())?;
unsafe {
self.get_resource_mut_by_id(component_id)
.map(|ptr| ptr.with_type::<R>())
}
}
#[inline]
pub unsafe fn get_resource_mut_by_id(
self,
component_id: ComponentId,
) -> Option<MutUntyped<'w>> {
self.assert_allows_mutable_access();
let entity = unsafe { self.resource_entities() }.get(component_id)?;
let entity_cell = self.get_entity(entity).ok()?;
entity_cell.get_mut_by_id(component_id).ok()
}
#[inline]
pub unsafe fn get_resource_mut_assume_mutable<R: Resource>(self) -> Option<Mut<'w, R>> {
let component_id = self.components().get_valid_id(TypeId::of::<R>())?;
unsafe {
self.get_resource_mut_by_id(component_id)
.map(|ptr| ptr.with_type::<R>())
}
}
#[deprecated(since = "0.19.0", note = "use UnsafeWorldCell::get_non_send_mut")]
pub unsafe fn get_non_send_resource_mut<R: 'static>(self) -> Option<Mut<'w, R>> {
self.get_non_send_mut::<R>()
}
#[inline]
pub unsafe fn get_non_send_mut<R: 'static>(self) -> Option<Mut<'w, R>> {
self.assert_allows_mutable_access();
let component_id = self.components().get_valid_id(TypeId::of::<R>())?;
unsafe {
self.get_non_send_mut_by_id(component_id)
.map(|ptr| ptr.with_type::<R>())
}
}
#[deprecated(since = "0.19.0", note = "use UnsafeWorldCell::get_non_send_mut_by_id")]
pub unsafe fn get_non_send_resource_mut_by_id<R: 'static>(
self,
component_id: ComponentId,
) -> Option<MutUntyped<'w>> {
self.get_non_send_mut_by_id(component_id)
}
#[inline]
pub unsafe fn get_non_send_mut_by_id(
self,
component_id: ComponentId,
) -> Option<MutUntyped<'w>> {
self.assert_allows_mutable_access();
let change_tick = self.change_tick();
let (ptr, ticks) = unsafe { self.storages() }
.non_sends
.get(component_id)?
.get_with_ticks()?;
let ticks =
unsafe { ComponentTicksMut::from_tick_cells(ticks, self.last_change_tick(), change_tick) };
Some(MutUntyped {
value: unsafe { ptr.assert_unique() },
ticks,
})
}
#[inline]
pub(crate) unsafe fn get_resource_with_ticks(
self,
component_id: ComponentId,
) -> Option<(Ptr<'w>, ComponentTickCells<'w>)> {
let entity = unsafe { self.resource_entities() }.get(component_id)?;
let storage_type = self.components().get_info(component_id)?.storage_type();
let location = self.get_entity(entity).ok()?.location();
get_component_and_ticks(self, component_id, storage_type, entity, location)
}
#[inline]
pub(crate) unsafe fn get_non_send_with_ticks(
self,
component_id: ComponentId,
) -> Option<(Ptr<'w>, ComponentTickCells<'w>)> {
unsafe { self.storages() }
.non_sends
.get(component_id)?
.get_with_ticks()
}
pub(crate) unsafe fn get_raw_command_queue(self) -> RawCommandQueue {
self.assert_allows_mutable_access();
unsafe { (*self.ptr).command_queue.clone() }
}
pub(crate) unsafe fn increment_trigger_id(self) {
self.assert_allows_mutable_access();
unsafe {
(*self.ptr).last_trigger_id = (*self.ptr).last_trigger_id.wrapping_add(1);
}
}
#[inline]
pub unsafe fn fallback_error_handler(&self) -> ErrorHandler {
unsafe { self.get_resource::<FallbackErrorHandler>() }
.copied()
.unwrap_or_default()
.0
}
}
impl Debug for UnsafeWorldCell<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
Debug::fmt(unsafe { self.world_metadata() }, f)
}
}
#[derive(Copy, Clone)]
pub struct UnsafeEntityCell<'w> {
world: UnsafeWorldCell<'w>,
entity: Entity,
location: EntityLocation,
last_run: Tick,
this_run: Tick,
}
impl<'w> UnsafeEntityCell<'w> {
#[inline]
pub(crate) fn new(
world: UnsafeWorldCell<'w>,
entity: Entity,
location: EntityLocation,
last_run: Tick,
this_run: Tick,
) -> Self {
UnsafeEntityCell {
world,
entity,
location,
last_run,
this_run,
}
}
#[inline]
#[must_use = "Omit the .id() call if you do not need to store the `Entity` identifier."]
pub fn id(self) -> Entity {
self.entity
}
#[inline]
pub fn location(self) -> EntityLocation {
self.location
}
#[inline]
pub fn archetype(self) -> &'w Archetype {
&self.world.archetypes()[self.location.archetype_id]
}
#[inline]
pub fn world(self) -> UnsafeWorldCell<'w> {
self.world
}
#[inline]
pub fn contains<T: Component>(self) -> bool {
self.contains_type_id(TypeId::of::<T>())
}
#[inline]
pub fn contains_id(self, component_id: ComponentId) -> bool {
self.archetype().contains(component_id)
}
#[inline]
pub fn contains_type_id(self, type_id: TypeId) -> bool {
let Some(id) = self.world.components().get_id(type_id) else {
return false;
};
self.contains_id(id)
}
#[inline]
pub unsafe fn get<T: Component>(self) -> Option<&'w T> {
let component_id = self.world.components().get_valid_id(TypeId::of::<T>())?;
unsafe {
get_component(
self.world,
component_id,
T::STORAGE_TYPE,
self.entity,
self.location,
)
.map(|value| value.deref::<T>())
}
}
#[inline]
pub unsafe fn get_ref<T: Component>(self) -> Option<Ref<'w, T>> {
let last_change_tick = self.last_run;
let change_tick = self.this_run;
let component_id = self.world.components().get_valid_id(TypeId::of::<T>())?;
unsafe {
get_component_and_ticks(
self.world,
component_id,
T::STORAGE_TYPE,
self.entity,
self.location,
)
.map(|(value, cells)| Ref {
value: value.deref::<T>(),
ticks: ComponentTicksRef::from_tick_cells(cells, last_change_tick, change_tick),
})
}
}
#[inline]
pub unsafe fn get_change_ticks<T: Component>(self) -> Option<ComponentTicks> {
let component_id = self.world.components().get_valid_id(TypeId::of::<T>())?;
unsafe {
get_ticks(
self.world,
component_id,
T::STORAGE_TYPE,
self.entity,
self.location,
)
}
}
#[inline]
pub unsafe fn get_changed_by<T: Component>(self) -> Option<MaybeLocation> {
let component_id = self.world.components().get_valid_id(TypeId::of::<T>())?;
unsafe {
get_changed_by(
self.world,
component_id,
T::STORAGE_TYPE,
self.entity,
self.location,
)
}
}
#[inline]
pub unsafe fn get_change_ticks_by_id(
&self,
component_id: ComponentId,
) -> Option<ComponentTicks> {
let info = self.world.components().get_info(component_id)?;
unsafe {
get_ticks(
self.world,
component_id,
info.storage_type(),
self.entity,
self.location,
)
}
}
#[inline]
pub unsafe fn get_mut<T: Component<Mutability = Mutable>>(self) -> Option<Mut<'w, T>> {
unsafe { self.get_mut_assume_mutable() }
}
#[inline]
pub unsafe fn get_mut_assume_mutable<T: Component>(self) -> Option<Mut<'w, T>> {
unsafe { self.get_mut_using_ticks_assume_mutable(self.last_run, self.this_run) }
}
#[inline]
pub(crate) unsafe fn get_mut_using_ticks_assume_mutable<T: Component>(
&self,
last_change_tick: Tick,
change_tick: Tick,
) -> Option<Mut<'w, T>> {
self.world.assert_allows_mutable_access();
let component_id = self.world.components().get_valid_id(TypeId::of::<T>())?;
unsafe {
get_component_and_ticks(
self.world,
component_id,
T::STORAGE_TYPE,
self.entity,
self.location,
)
.map(|(value, cells)| Mut {
value: value.assert_unique().deref_mut::<T>(),
ticks: ComponentTicksMut::from_tick_cells(cells, last_change_tick, change_tick),
})
}
}
pub(crate) unsafe fn get_components<Q: ReleaseStateQueryData + SingleEntityQueryData>(
&self,
) -> Result<Q::Item<'w, 'static>, QueryAccessError> {
let state = unsafe {
let world = self.world().world();
Q::get_state(world.components()).ok_or(QueryAccessError::ComponentNotRegistered)?
};
let location = self.location();
let archetype = unsafe {
self.world
.archetypes()
.get(location.archetype_id)
.debug_checked_unwrap()
};
if Q::matches_component_set(&state, &|id| archetype.contains(id)) {
let mut fetch =
unsafe { Q::init_fetch(self.world, &state, self.last_run, self.this_run) };
let table = unsafe {
self.world
.storages()
.tables
.get(location.table_id)
.debug_checked_unwrap()
};
unsafe { Q::set_archetype(&mut fetch, &state, archetype, table) }
let item = unsafe { Q::fetch(&state, &mut fetch, self.id(), location.table_row) };
item.map(Q::release_state)
.ok_or(QueryAccessError::EntityDoesNotMatch)
} else {
Err(QueryAccessError::EntityDoesNotMatch)
}
}
#[inline]
pub unsafe fn get_by_id(self, component_id: ComponentId) -> Option<Ptr<'w>> {
let info = self.world.components().get_info(component_id)?;
unsafe {
get_component(
self.world,
component_id,
info.storage_type(),
self.entity,
self.location,
)
}
}
#[inline]
pub unsafe fn get_mut_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)?;
if !info.mutable() {
return Err(GetEntityMutByIdError::ComponentIsImmutable);
}
unsafe {
get_component_and_ticks(
self.world,
component_id,
info.storage_type(),
self.entity,
self.location,
)
.map(|(value, cells)| MutUntyped {
value: value.assert_unique(),
ticks: ComponentTicksMut::from_tick_cells(cells, self.last_run, self.this_run),
})
.ok_or(GetEntityMutByIdError::ComponentNotFound)
}
}
#[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)?;
unsafe {
get_component_and_ticks(
self.world,
component_id,
info.storage_type(),
self.entity,
self.location,
)
.map(|(value, cells)| MutUntyped {
value: value.assert_unique(),
ticks: ComponentTicksMut::from_tick_cells(cells, self.last_run, self.this_run),
})
.ok_or(GetEntityMutByIdError::ComponentNotFound)
}
}
pub fn spawned_by(self) -> MaybeLocation {
self.world()
.entities()
.entity_get_spawned_or_despawned_by(self.entity)
.map(|o| o.unwrap())
}
pub fn spawn_tick(self) -> Tick {
unsafe {
self.world()
.entities()
.entity_get_spawned_or_despawned_unchecked(self.entity)
.1
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum GetEntityMutByIdError {
#[error("the `ComponentInfo` could not be found")]
InfoNotFound,
#[error("the `Component` is immutable")]
ComponentIsImmutable,
#[error("the `Component` could not be found")]
ComponentNotFound,
}
impl<'w> UnsafeWorldCell<'w> {
#[inline]
unsafe fn fetch_table(self, location: EntityLocation) -> Option<&'w Table> {
unsafe { self.storages().tables.get(location.table_id) }
}
#[inline]
unsafe fn fetch_sparse_set(self, component_id: ComponentId) -> Option<&'w ComponentSparseSet> {
unsafe { self.storages() }.sparse_sets.get(component_id)
}
}
#[inline]
unsafe fn get_component(
world: UnsafeWorldCell<'_>,
component_id: ComponentId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<Ptr<'_>> {
match storage_type {
StorageType::Table => {
let table = world.fetch_table(location)?;
table.get_component(component_id, location.table_row)
}
StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get(entity),
}
}
#[inline]
unsafe fn get_component_and_ticks(
world: UnsafeWorldCell<'_>,
component_id: ComponentId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<(Ptr<'_>, ComponentTickCells<'_>)> {
match storage_type {
StorageType::Table => {
let table = world.fetch_table(location)?;
Some((
table.get_component(component_id, location.table_row)?,
ComponentTickCells {
added: table
.get_added_tick(component_id, location.table_row)
.debug_checked_unwrap(),
changed: table
.get_changed_tick(component_id, location.table_row)
.debug_checked_unwrap(),
changed_by: table
.get_changed_by(component_id, location.table_row)
.map(|changed_by| changed_by.debug_checked_unwrap()),
},
))
}
StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get_with_ticks(entity),
}
}
#[inline]
unsafe fn get_ticks(
world: UnsafeWorldCell<'_>,
component_id: ComponentId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<ComponentTicks> {
match storage_type {
StorageType::Table => {
let table = world.fetch_table(location)?;
table.get_ticks_unchecked(component_id, location.table_row)
}
StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get_ticks(entity),
}
}
#[inline]
unsafe fn get_changed_by(
world: UnsafeWorldCell<'_>,
component_id: ComponentId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<MaybeLocation> {
let caller = match storage_type {
StorageType::Table => world
.fetch_table(location)?
.get_changed_by(component_id, location.table_row),
StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get_changed_by(entity),
};
Some(
caller
.transpose()?
.map(|changed_by| unsafe { *changed_by.deref() }),
)
}
impl ContainsEntity for UnsafeEntityCell<'_> {
fn entity(&self) -> Entity {
self.id()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic = "is forbidden"]
fn as_unsafe_world_cell_readonly_world_mut_forbidden() {
let world = World::new();
let world_cell = world.as_unsafe_world_cell_readonly();
let _ = unsafe { world_cell.world_mut() };
}
#[derive(Resource)]
struct R;
#[test]
#[should_panic = "is forbidden"]
fn as_unsafe_world_cell_readonly_resource_mut_forbidden() {
let mut world = World::new();
world.insert_resource(R);
let world_cell = world.as_unsafe_world_cell_readonly();
let _ = unsafe { world_cell.get_resource_mut::<R>() };
}
#[derive(Component)]
struct C;
#[test]
#[should_panic = "is forbidden"]
fn as_unsafe_world_cell_readonly_component_mut_forbidden() {
let mut world = World::new();
let entity = world.spawn(C).id();
let world_cell = world.as_unsafe_world_cell_readonly();
let entity_cell = world_cell.get_entity(entity).unwrap();
let _ = unsafe { entity_cell.get_mut::<C>() };
}
}