use bevy_ecs::component::Component;
use bevy_ecs::entity::Entity;
use bevy_ecs::world::World;
use crate::handle::EntityHandle;
#[derive(Clone, Copy)]
pub struct WorldRef {
world: &'static World,
}
impl WorldRef {
#[inline]
pub unsafe fn new(world: &World) -> Self {
Self {
world: unsafe { std::mem::transmute::<&World, &'static World>(world) },
}
}
#[inline]
pub fn entity(&self, entity: Entity) -> EntityPtr {
EntityPtr::new(entity, self.world)
}
#[inline]
#[must_use]
pub fn entity_opt(&self, entity: Entity) -> Option<EntityPtr> {
if self.world.get_entity(entity).is_ok() {
Some(EntityPtr::new(entity, self.world))
} else {
None
}
}
#[inline]
pub fn from_handle(&self, handle: EntityHandle) -> EntityPtr {
EntityPtr::new(handle.entity(), self.world)
}
#[inline]
pub fn get<T: Component>(&self, entity: Entity) -> Option<&T> {
self.world.get::<T>(entity)
}
#[inline]
pub fn world(&self) -> &World {
self.world
}
}
impl std::fmt::Debug for WorldRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WorldRef").finish_non_exhaustive()
}
}
#[derive(Clone, Copy)]
pub struct EntityPtr {
entity: Entity,
world: &'static World,
}
impl EntityPtr {
#[inline]
pub(crate) const fn new(entity: Entity, world: &'static World) -> Self {
Self { entity, world }
}
#[inline]
pub const fn entity(self) -> Entity {
self.entity
}
#[inline]
pub const fn handle(self) -> EntityHandle {
EntityHandle::new(self.entity)
}
#[inline]
#[must_use]
pub fn get<T: Component>(self) -> Option<&'static T> {
self.world.get::<T>(self.entity)
}
#[inline]
pub fn has<T: Component>(self) -> bool {
self.world.get::<T>(self.entity).is_some()
}
#[inline]
pub fn is_alive(self) -> bool {
self.world.get_entity(self.entity).is_ok()
}
#[inline]
#[must_use]
pub fn follow<T, F>(self, f: F) -> Option<EntityPtr>
where
T: Component,
F: FnOnce(&T) -> EntityHandle,
{
self.get::<T>()
.map(|c| EntityPtr::new(f(c).entity(), self.world))
}
#[inline]
#[must_use]
pub fn follow_opt<T, F>(self, f: F) -> Option<EntityPtr>
where
T: Component,
F: FnOnce(&T) -> Option<EntityHandle>,
{
self.get::<T>()
.and_then(|c| f(c).map(|h| EntityPtr::new(h.entity(), self.world)))
}
#[inline]
pub fn follow_handle(self, handle: EntityHandle) -> EntityPtr {
EntityPtr::new(handle.entity(), self.world)
}
#[inline]
pub const fn nav(self) -> EntityPtrNav {
EntityPtrNav(self)
}
#[inline]
pub const fn nav_many(self) -> EntityPtrNavMany {
EntityPtrNavMany(self)
}
#[inline]
#[cfg_attr(not(feature = "nav-traits"), allow(dead_code))]
pub(crate) const fn world(self) -> &'static World {
self.world
}
}
impl std::fmt::Debug for EntityPtr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EntityPtr")
.field("entity", &self.entity)
.finish_non_exhaustive()
}
}
impl PartialEq for EntityPtr {
fn eq(&self, other: &Self) -> bool {
self.entity == other.entity
}
}
impl Eq for EntityPtr {}
impl std::hash::Hash for EntityPtr {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.entity.hash(state);
}
}
#[derive(Clone, Copy)]
pub struct EntityPtrNav(pub(crate) EntityPtr);
impl EntityPtrNav {
#[inline]
pub const fn inner(self) -> EntityPtr {
self.0
}
}
impl std::fmt::Debug for EntityPtrNav {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("EntityPtrNav").field(&self.0).finish()
}
}
#[derive(Clone, Copy)]
pub struct EntityPtrNavMany(pub(crate) EntityPtr);
impl EntityPtrNavMany {
#[inline]
pub const fn inner(self) -> EntityPtr {
self.0
}
}
impl std::fmt::Debug for EntityPtrNavMany {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("EntityPtrNavMany").field(&self.0).finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Component)]
struct Name(&'static str);
#[derive(Component)]
struct Health(i32);
#[derive(Component)]
struct Target(EntityHandle);
#[derive(Component)]
struct OptionalTarget(Option<EntityHandle>);
#[test]
fn worldref_is_copy() {
let mut world = World::new();
let _entity = world.spawn(Name("test")).id();
let w = unsafe { WorldRef::new(&world) };
let w2 = w; let w3 = w; assert_eq!(std::mem::size_of_val(&w2), std::mem::size_of_val(&w3));
}
#[test]
fn worldref_entity_access() {
let mut world = World::new();
let entity = world.spawn((Name("test"), Health(100))).id();
let world_ref = unsafe { WorldRef::new(&world) };
let ptr = world_ref.entity(entity);
assert_eq!(ptr.get::<Name>().unwrap().0, "test");
assert_eq!(ptr.get::<Health>().unwrap().0, 100);
assert!(ptr.is_alive());
}
#[test]
fn worldref_entity_opt() {
let mut world = World::new();
let entity = world.spawn(Name("exists")).id();
let fake = Entity::from_raw_u32(9999).unwrap();
let world_ref = unsafe { WorldRef::new(&world) };
assert!(world_ref.entity_opt(entity).is_some());
assert!(world_ref.entity_opt(fake).is_none());
}
#[test]
fn worldref_from_handle() {
let mut world = World::new();
let entity = world.spawn(Name("handle")).id();
let handle = EntityHandle::new(entity);
let world_ref = unsafe { WorldRef::new(&world) };
let ptr = world_ref.from_handle(handle);
assert_eq!(ptr.get::<Name>().unwrap().0, "handle");
}
#[test]
fn worldref_get_direct() {
let mut world = World::new();
let entity = world.spawn(Name("direct")).id();
let world_ref = unsafe { WorldRef::new(&world) };
assert_eq!(world_ref.get::<Name>(entity).unwrap().0, "direct");
}
#[test]
fn entityptr_handle_conversion() {
let mut world = World::new();
let entity = world.spawn(Name("convert")).id();
let world_ref = unsafe { WorldRef::new(&world) };
let ptr = world_ref.entity(entity);
assert_eq!(ptr.entity(), entity);
assert_eq!(ptr.handle().entity(), entity);
}
#[test]
fn entityptr_follow() {
let mut world = World::new();
let target_entity = world.spawn(Name("target")).id();
let source_entity = world
.spawn((Name("source"), Target(EntityHandle::new(target_entity))))
.id();
let world_ref = unsafe { WorldRef::new(&world) };
let source = world_ref.entity(source_entity);
let target = source.follow::<Target, _>(|t| t.0).unwrap();
assert_eq!(target.get::<Name>().unwrap().0, "target");
}
#[test]
fn entityptr_follow_opt() {
let mut world = World::new();
let target_entity = world.spawn(Name("target")).id();
let with_target = world
.spawn(OptionalTarget(Some(EntityHandle::new(target_entity))))
.id();
let without_target = world.spawn(OptionalTarget(None)).id();
let world_ref = unsafe { WorldRef::new(&world) };
let with = world_ref.entity(with_target);
let without = world_ref.entity(without_target);
assert!(with.follow_opt::<OptionalTarget, _>(|t| t.0).is_some());
assert!(without.follow_opt::<OptionalTarget, _>(|t| t.0).is_none());
}
#[test]
fn entityptr_follow_handle() {
let mut world = World::new();
let target_entity = world.spawn(Name("target")).id();
let source_entity = world.spawn(Name("source")).id();
let world_ref = unsafe { WorldRef::new(&world) };
let source = world_ref.entity(source_entity);
let handle = EntityHandle::new(target_entity);
let target = source.follow_handle(handle);
assert_eq!(target.entity(), target_entity);
assert_eq!(target.get::<Name>().unwrap().0, "target");
}
#[test]
fn memory_layout() {
assert_eq!(std::mem::size_of::<WorldRef>(), 8);
assert_eq!(std::mem::size_of::<EntityPtr>(), 16);
}
#[test]
fn nav_wrapper_access() {
let mut world = World::new();
let entity = world.spawn(Name("nav")).id();
let world_ref = unsafe { WorldRef::new(&world) };
let ptr = world_ref.entity(entity);
let nav = ptr.nav();
let nav_many = ptr.nav_many();
assert_eq!(nav.inner().entity(), entity);
assert_eq!(nav_many.inner().entity(), entity);
}
}