use bevy_app::prelude::*;
use bevy_ecs::component::Immutable;
use bevy_ecs::component::StorageType;
use bevy_ecs::lifecycle::ComponentHook;
use bevy_ecs::prelude::*;
use std::collections::HashMap;
use uuid::Uuid;
pub trait CommandsEntityUuid {
fn spawn_uuid<T: Bundle>(&mut self, uuid: Uuid, bundle: T) -> EntityCommands<'_>;
fn spawn_empty_uuid(&mut self, uuid: Uuid) -> EntityCommands<'_>;
}
impl CommandsEntityUuid for Commands<'_, '_> {
fn spawn_uuid<T: Bundle>(&mut self, uuid: Uuid, bundle: T) -> EntityCommands<'_> {
let mut cmd = self.spawn(bundle);
cmd.insert(EntityUuid { uuid });
cmd
}
fn spawn_empty_uuid(&mut self, uuid: Uuid) -> EntityCommands<'_> {
let mut cmd = self.spawn_empty();
cmd.insert(EntityUuid { uuid });
cmd
}
}
pub trait WorldEntityUuid {
fn spawn_uuid<B: Bundle>(&mut self, uuid: Uuid, bundle: B) -> EntityWorldMut<'_>;
fn spawn_empty_uuid(&mut self, uuid: Uuid) -> EntityWorldMut<'_>;
}
impl WorldEntityUuid for World {
fn spawn_uuid<B: Bundle>(&mut self, uuid: Uuid, bundle: B) -> EntityWorldMut<'_> {
let map = self.resource_mut::<EntityLookup>();
if let Some(id) = map.uuid_to_entity(&uuid) {
return self.entity_mut(id);
}
let mut cmd = self.spawn(bundle);
cmd.insert(EntityUuid { uuid });
cmd
}
fn spawn_empty_uuid(&mut self, uuid: Uuid) -> EntityWorldMut<'_> {
let map = self.resource_mut::<EntityLookup>();
if let Some(id) = map.uuid_to_entity(&uuid) {
return self.entity_mut(id);
}
let mut cmd = self.spawn_empty();
cmd.insert(EntityUuid { uuid });
cmd
}
}
pub struct EntityUuid {
pub uuid: Uuid,
}
impl Component for EntityUuid {
const STORAGE_TYPE: StorageType = StorageType::Table;
type Mutability = Immutable;
fn on_add() -> Option<ComponentHook> {
Some(|mut world, ctx| {
let uuid = world.entity(ctx.entity).get::<EntityUuid>().unwrap().uuid;
let mut map = world.resource_mut::<EntityLookup>();
map.add_new(ctx.entity, uuid);
})
}
fn on_remove() -> Option<ComponentHook> {
Some(|mut world, ctx| {
let uuid = world.entity(ctx.entity).get::<EntityUuid>().unwrap().uuid;
let mut map = world.resource_mut::<EntityLookup>();
map.remove(ctx.entity, uuid);
world.write_message(EntityUuidDeleted(uuid));
})
}
}
#[derive(Message)]
pub struct EntityUuidDeleted(pub Uuid);
#[derive(Resource, Default, Clone, Debug)]
pub struct EntityLookup {
uuid_to_entity: HashMap<Uuid, Entity>,
entity_to_uuid: HashMap<Entity, Uuid>,
}
impl EntityLookup {
pub fn uuid_to_entity(&self, uuid: &Uuid) -> Option<Entity> {
self.uuid_to_entity.get(uuid).copied()
}
pub fn entity_to_uuid(&self, id: &Entity) -> Option<Uuid> {
self.entity_to_uuid.get(id).copied()
}
pub(crate) fn add_new(&mut self, id: Entity, uuid: Uuid) {
self.uuid_to_entity.insert(uuid, id);
self.entity_to_uuid.insert(id, uuid);
}
pub(crate) fn remove(&mut self, id: Entity, uuid: Uuid) {
if let Some(id) = self.uuid_to_entity.remove(&uuid) {
self.entity_to_uuid.remove(&id);
}
if let Some(uuid) = self.entity_to_uuid.remove(&id) {
self.uuid_to_entity.remove(&uuid);
}
}
}
pub struct EntityUuidPlugin;
impl Plugin for EntityUuidPlugin {
fn build(&self, app: &mut App) {
app.add_message::<EntityUuidDeleted>();
app.init_resource::<EntityLookup>();
}
}