use core::{any::TypeId, fmt, marker::PhantomData, num::NonZeroU64};
use crate::{
bundle::{Bundle, DynamicBundle, DynamicComponentBundle},
component::Component,
flow::{spawn_local_for, IntoEntityFlow},
query::{DefaultQuery, ImmutableQuery, IntoQuery, QueryItem},
view::ViewOne,
world::{World, WorldLocal},
NoSuchEntity,
ResultEntityError,
};
use super::{EntitySet, Location};
pub trait Entity: Copy {
fn id(&self) -> EntityId;
fn lookup(&self, entities: &EntitySet) -> Option<Location>;
fn is_alive(&self, entities: &EntitySet) -> bool;
fn entity_loc<'a>(&self, entities: &'a EntitySet) -> Option<EntityLoc<'a>>;
fn entity_ref<'a>(&self, world: &'a mut World) -> Option<EntityRef<'a>>;
}
pub trait AliveEntity: Entity {
#[inline(always)]
fn locate(&self, entities: &EntitySet) -> Location {
entities
.get_location(self.id())
.expect("Entity is not alive")
}
#[inline(always)]
fn entity_loc<'a>(&self, entities: &'a EntitySet) -> EntityLoc<'a> {
EntityLoc::from_alive(*self, entities)
}
#[inline(always)]
fn entity_ref<'a>(&self, _world: &'a mut World) -> EntityRef<'a> {
unreachable!()
}
}
pub trait LocatedEntity: AliveEntity {
fn location(&self) -> Location;
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct EntityId {
id: NonZeroU64,
}
#[cfg(feature = "serde")]
impl serde::ser::Serialize for EntityId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
self.id.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::de::Deserialize<'de> for EntityId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let id = NonZeroU64::deserialize(deserializer)?;
Ok(EntityId { id })
}
}
impl PartialEq<EntityBound<'_>> for EntityId {
#[inline(always)]
fn eq(&self, other: &EntityBound<'_>) -> bool {
*self == other.id
}
}
impl PartialEq<EntityLoc<'_>> for EntityId {
#[inline(always)]
fn eq(&self, other: &EntityLoc<'_>) -> bool {
*self == other.id
}
}
impl PartialEq<EntityRef<'_>> for EntityId {
#[inline(always)]
fn eq(&self, other: &EntityRef<'_>) -> bool {
*self == other.id
}
}
impl fmt::Debug for EntityId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("EntityId").field(&self.id).finish()
}
}
impl fmt::Display for EntityId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)
}
}
impl EntityId {
const DANGLING: NonZeroU64 = super::allocator::END;
pub const fn dangling() -> Self {
EntityId { id: Self::DANGLING }
}
#[inline(always)]
pub(super) const fn new(id: NonZeroU64) -> Self {
EntityId { id }
}
#[inline(always)]
pub(super) const fn non_zero(&self) -> NonZeroU64 {
self.id
}
#[inline(always)]
pub const fn bits(&self) -> u64 {
self.id.get()
}
#[inline(always)]
pub const fn from_bits(bits: u64) -> Option<Self> {
match NonZeroU64::new(bits) {
Some(id) => Some(EntityId { id }),
None => None,
}
}
}
impl Entity for EntityId {
#[inline(always)]
fn id(&self) -> EntityId {
*self
}
#[inline(always)]
fn lookup(&self, entities: &EntitySet) -> Option<Location> {
entities.get_location(*self)
}
#[inline(always)]
fn is_alive(&self, entities: &EntitySet) -> bool {
entities.is_alive(*self)
}
#[inline(always)]
fn entity_loc<'a>(&self, entities: &'a EntitySet) -> Option<EntityLoc<'a>> {
Some(EntityLoc::from_parts(*self, entities.get_location(*self)?))
}
#[inline(always)]
fn entity_ref<'a>(&self, world: &'a mut World) -> Option<EntityRef<'a>> {
EntityRef::new(*self, world).ok()
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct EntityBound<'a> {
id: EntityId,
world: PhantomData<&'a World>,
}
impl PartialEq<EntityId> for EntityBound<'_> {
#[inline(always)]
fn eq(&self, other: &EntityId) -> bool {
self.id == *other
}
}
impl PartialEq<EntityLoc<'_>> for EntityBound<'_> {
#[inline(always)]
fn eq(&self, other: &EntityLoc<'_>) -> bool {
self.id == other.id
}
}
impl PartialEq<EntityRef<'_>> for EntityBound<'_> {
#[inline(always)]
fn eq(&self, other: &EntityRef<'_>) -> bool {
self.id == other.id
}
}
impl fmt::Debug for EntityBound<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("EntityBound").field(&self.id).finish()
}
}
impl fmt::Display for EntityBound<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)
}
}
impl EntityBound<'_> {
#[inline(always)]
pub fn id(&self) -> EntityId {
self.id
}
#[inline(always)]
pub(crate) fn new(id: EntityId) -> Self {
EntityBound {
id,
world: PhantomData,
}
}
#[inline(always)]
pub(crate) fn wrap_slice(ids: &[EntityId]) -> &[Self] {
unsafe { &*(ids as *const [EntityId] as *const [Self]) }
}
}
impl<'a> Entity for EntityBound<'a> {
#[inline(always)]
fn id(&self) -> EntityId {
self.id
}
#[inline(always)]
fn lookup(&self, entities: &EntitySet) -> Option<Location> {
Some(self.locate(entities))
}
#[inline(always)]
fn is_alive(&self, _entities: &EntitySet) -> bool {
true
}
#[inline(always)]
fn entity_loc<'b>(&self, entities: &'b EntitySet) -> Option<EntityLoc<'b>> {
Some(EntityLoc::from_alive(*self, entities))
}
#[inline(always)]
fn entity_ref<'b>(&self, _world: &'b mut World) -> Option<EntityRef<'b>> {
unreachable!()
}
}
impl<'a> AliveEntity for EntityBound<'a> {
#[inline(always)]
fn locate(&self, entities: &EntitySet) -> Location {
entities.get_location(self.id()).expect("Bound entity")
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EntityLoc<'a> {
id: EntityId,
loc: Location,
world: PhantomData<&'a World>,
}
impl PartialEq<EntityId> for EntityLoc<'_> {
#[inline(always)]
fn eq(&self, other: &EntityId) -> bool {
self.id == *other
}
}
impl PartialEq<EntityBound<'_>> for EntityLoc<'_> {
#[inline(always)]
fn eq(&self, other: &EntityBound<'_>) -> bool {
self.id == other.id
}
}
impl PartialEq<EntityRef<'_>> for EntityLoc<'_> {
#[inline(always)]
fn eq(&self, other: &EntityRef<'_>) -> bool {
self.id == other.id
}
}
impl fmt::Debug for EntityLoc<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EntityLoc")
.field("id", &self.id)
.field("loc", &self.loc)
.finish()
}
}
impl fmt::Display for EntityLoc<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)
}
}
impl EntityLoc<'_> {
#[inline(always)]
pub(crate) fn from_parts(id: EntityId, loc: Location) -> Self {
EntityLoc {
id,
loc,
world: PhantomData,
}
}
#[inline(always)]
pub(crate) fn from_alive(entity: impl AliveEntity, entities: &EntitySet) -> Self {
EntityLoc {
id: entity.id(),
loc: entity.locate(entities),
world: PhantomData,
}
}
#[inline(always)]
pub fn id(&self) -> EntityId {
self.id
}
}
impl<'a> Entity for EntityLoc<'a> {
#[inline(always)]
fn id(&self) -> EntityId {
self.id
}
#[inline(always)]
fn lookup(&self, _entities: &EntitySet) -> Option<Location> {
Some(self.loc)
}
#[inline(always)]
fn is_alive(&self, _entities: &EntitySet) -> bool {
true
}
#[inline(always)]
fn entity_loc<'b>(&self, _entities: &'b EntitySet) -> Option<EntityLoc<'b>> {
Some(EntityLoc::from_parts(self.id, self.loc))
}
#[inline(always)]
fn entity_ref<'b>(&self, _world: &'b mut World) -> Option<EntityRef<'b>> {
unreachable!();
}
}
impl<'a> AliveEntity for EntityLoc<'a> {
#[inline(always)]
fn locate(&self, _entities: &EntitySet) -> Location {
self.loc
}
#[inline(always)]
fn entity_loc<'b>(&self, _entities: &'b EntitySet) -> EntityLoc<'b> {
EntityLoc::from_parts(self.id, self.loc)
}
#[inline(always)]
fn entity_ref<'b>(&self, _world: &'b mut World) -> EntityRef<'b> {
unreachable!()
}
}
impl<'a> LocatedEntity for EntityLoc<'a> {
#[inline(always)]
fn location(&self) -> Location {
self.loc
}
}
pub struct EntityRef<'a> {
id: EntityId,
loc: Location,
world: &'a mut WorldLocal,
}
impl PartialEq<EntityId> for EntityRef<'_> {
#[inline(always)]
fn eq(&self, other: &EntityId) -> bool {
self.id == *other
}
}
impl PartialEq<EntityBound<'_>> for EntityRef<'_> {
#[inline(always)]
fn eq(&self, other: &EntityBound<'_>) -> bool {
self.id == other.id
}
}
impl PartialEq<EntityLoc<'_>> for EntityRef<'_> {
#[inline(always)]
fn eq(&self, other: &EntityLoc<'_>) -> bool {
self.id == other.id
}
}
impl fmt::Debug for EntityRef<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EntityRef")
.field("id", &self.id)
.field("loc", &self.loc)
.finish()
}
}
impl fmt::Display for EntityRef<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)
}
}
impl<'a> EntityRef<'a> {
#[inline(always)]
pub fn new(id: EntityId, world: &'a mut World) -> Result<Self, NoSuchEntity> {
world.maintenance();
let loc = world.entities().get_location(id).ok_or(NoSuchEntity)?;
Ok(EntityRef {
id,
loc,
world: world.local(),
})
}
#[inline(always)]
pub(crate) fn from_parts(id: EntityId, loc: Location, world: &'a mut World) -> Self {
debug_assert_eq!(world.entities().get_location(id), Some(loc));
EntityRef {
id,
loc,
world: world.local(),
}
}
#[inline(always)]
pub fn id(&self) -> EntityId {
self.id
}
#[inline(always)]
pub fn get<'b, Q>(&'b self) -> Option<QueryItem<'b, Q>>
where
Q: DefaultQuery,
Q::Query: ImmutableQuery,
{
self.get_with(Q::default_query())
}
#[inline(always)]
pub fn get_mut<'b, Q>(&'b mut self) -> Option<QueryItem<'b, Q>>
where
Q: DefaultQuery,
{
self.get_with_mut(Q::default_query())
}
#[inline(always)]
pub fn get_with<'b, Q>(&'b self, query: Q) -> Option<QueryItem<'b, Q::Query>>
where
Q: IntoQuery,
Q::Query: ImmutableQuery,
{
unsafe { self.get_with_unchecked(query) }
}
#[inline(always)]
pub fn get_with_mut<'b, Q>(&'b mut self, query: Q) -> Option<QueryItem<'b, Q::Query>>
where
Q: IntoQuery,
{
unsafe { self.get_with_unchecked(query) }
}
#[inline(always)]
pub unsafe fn get_unchecked<'b, Q>(&'b self) -> Option<QueryItem<'b, Q>>
where
Q: DefaultQuery,
{
unsafe { self.get_with_unchecked(Q::default_query()) }
}
#[inline(always)]
pub unsafe fn get_with_unchecked<'b, Q>(&'b self, query: Q) -> Option<QueryItem<'b, Q::Query>>
where
Q: IntoQuery,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
unsafe {
self.world
.get_with_unchecked(loc, query)
.assume_entity_exists()
}
}
#[inline(always)]
pub fn view_one<'b, Q>(&'b self) -> ViewOne<'b, Q>
where
Q: DefaultQuery,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
self.world.view_one::<Q>(loc)
}
#[inline(always)]
pub fn view_one_with<'b, Q>(&'b self, query: Q) -> ViewOne<'b, (Q,)>
where
Q: IntoQuery,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
self.world.view_one_with::<Q>(loc, query)
}
pub fn get_cloned<T>(&self) -> Option<T>
where
T: Clone + Sync + 'static,
{
self.view_one::<&T>().map(Clone::clone)
}
#[inline(always)]
pub fn insert<T>(self, component: T) -> Option<Self>
where
T: Component,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
unsafe {
self.world.insert(loc, component).unwrap_unchecked();
}
let loc = self.world.entities().get_location(self.id)?;
Some(EntityRef {
id: self.id,
loc,
world: self.world,
})
}
#[inline(always)]
pub fn insert_external<T>(self, component: T) -> Option<Self>
where
T: 'static,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
unsafe {
self.world
.insert_external(loc, component)
.unwrap_unchecked();
}
let loc = self.world.entities().get_location(self.id)?;
Some(EntityRef {
id: self.id,
loc,
world: self.world,
})
}
#[inline(always)]
pub fn with<T>(&mut self, f: impl FnOnce() -> T) -> &mut T
where
T: Component,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
self.loc = unsafe { self.world.with(loc, f).unwrap_unchecked() }.loc;
unsafe {
self.world
.archetypes_mut()
.get_unchecked_mut(self.loc.arch as usize)
.get_mut_nobump(self.loc.idx)
}
}
#[inline(always)]
pub fn with_external<T>(&mut self, component: impl FnOnce() -> T) -> &mut T
where
T: 'static,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
self.loc = unsafe { self.world.with_external(loc, component).unwrap_unchecked() }.loc;
unsafe {
self.world
.archetypes_mut()
.get_unchecked_mut(self.loc.arch as usize)
.get_mut_nobump(self.loc.idx)
}
}
#[inline(always)]
pub fn insert_bundle<B>(self, bundle: B) -> Option<Self>
where
B: DynamicComponentBundle,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
unsafe {
self.world.insert_bundle(loc, bundle).unwrap_unchecked();
}
let loc = self.world.entities().get_location(self.id)?;
Some(EntityRef {
id: self.id,
loc,
world: self.world,
})
}
#[inline(always)]
pub fn insert_external_bundle<B>(self, bundle: B) -> Option<Self>
where
B: DynamicBundle,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
unsafe {
self.world
.insert_external_bundle(loc, bundle)
.unwrap_unchecked();
}
let loc = self.world.entities().get_location(self.id)?;
Some(EntityRef {
id: self.id,
loc,
world: self.world,
})
}
#[inline(always)]
pub fn with_bundle<B>(&mut self, bundle: B)
where
B: DynamicComponentBundle,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
self.loc = unsafe { self.world.with_bundle(loc, bundle).unwrap_unchecked() }.loc;
}
#[inline(always)]
pub fn with_external_bundle<B>(&mut self, bundle: B)
where
B: DynamicBundle,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
self.loc = unsafe {
self.world
.with_external_bundle(loc, bundle)
.unwrap_unchecked()
}
.loc;
}
#[inline(always)]
pub fn remove<T>(&mut self) -> Option<T>
where
T: 'static,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
let (c, e) = unsafe { self.world.remove(loc).unwrap_unchecked() };
self.loc = e.loc;
c
}
#[inline(always)]
pub fn drop<T>(self) -> Option<Self>
where
T: 'static,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
unsafe {
self.world.drop::<T>(loc).unwrap_unchecked();
}
let loc = self.world.entities().get_location(self.id)?;
Some(EntityRef {
id: self.id,
loc,
world: self.world,
})
}
#[inline(always)]
pub fn drop_erased(self, ty: TypeId) -> Option<Self> {
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
unsafe {
self.world.drop_erased(loc, ty).unwrap_unchecked();
}
let loc = self.world.entities().get_location(self.id)?;
Some(EntityRef {
id: self.id,
loc,
world: self.world,
})
}
#[inline(always)]
pub fn drop_bundle<B>(self) -> Option<Self>
where
B: Bundle,
{
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
unsafe {
self.world.drop_bundle::<B>(loc).unwrap_unchecked();
}
let loc = self.world.entities().get_location(self.id)?;
Some(EntityRef {
id: self.id,
loc,
world: self.world,
})
}
#[inline(always)]
pub fn despawn(self) {
unsafe { self.world.despawn_ref(self.id, self.loc) }
}
#[inline(always)]
pub fn has_component<T: 'static>(&self) -> bool {
let loc = EntityLoc {
id: self.id,
loc: self.loc,
world: PhantomData,
};
self.world.has_component::<T>(loc)
}
pub fn spawn<F>(&mut self, flow_fn: F)
where
F: IntoEntityFlow,
{
spawn_local_for(self.world.local(), self.id, flow_fn);
}
}