use crate::Tick;
use bevy::{
ecs::system::{Command, EntityCommands},
prelude::*,
};
use serde::{Deserialize, Serialize};
#[derive(Component, Clone, Copy, Debug, Deref, PartialEq, Eq)]
pub struct Owner(pub u32);
#[derive(
Component, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct Identifier {
pub(crate) entity_type: u8,
pub(crate) id: u32,
}
impl Identifier {
#[inline(always)]
pub fn is_client(&self) -> bool {
self.entity_type == 0
}
pub fn new(entity_type: impl Into<u8>, id: u32) -> Self {
Self {
entity_type: entity_type.into(),
id,
}
}
}
pub trait CommandsSpawnIdentifierExt<'a, 'w, 's> {
fn spawn_client(
&'a mut self,
client_id: u32,
bundle: impl Bundle,
) -> EntityCommands<'w, 's, 'a>;
fn spawn_with_id(
&'a mut self,
id_type: impl Into<u8>,
id: u32,
bundle: impl Bundle,
) -> EntityCommands<'w, 's, 'a>;
}
impl<'a, 'w, 's> CommandsSpawnIdentifierExt<'a, 'w, 's> for Commands<'w, 's> {
#[inline(always)]
fn spawn_client(
&'a mut self,
client_id: u32,
bundle: impl Bundle,
) -> EntityCommands<'w, 's, 'a> {
self.spawn_with_id(0, client_id, bundle)
}
#[inline(always)]
fn spawn_with_id(
&'a mut self,
id_type: impl Into<u8>,
id: u32,
bundle: impl Bundle,
) -> EntityCommands<'w, 's, 'a> {
let id = Identifier::new(id_type, id);
let entity = self.spawn((id, bundle)).id();
self.add(SpawnIdentifierCommand { id, entity });
self.entity(entity)
}
}
pub trait EntityCommandsInsertIdentifierExt {
fn insert_client(&mut self, client_id: u32) -> &mut Self;
fn insert_id(&mut self, id_type: impl Into<u8>, id: u32) -> &mut Self;
}
impl EntityCommandsInsertIdentifierExt for EntityCommands<'_, '_, '_> {
#[inline(always)]
fn insert_client(&mut self, client_id: u32) -> &mut Self {
self.insert_id(0, client_id)
}
#[inline(always)]
fn insert_id(&mut self, id_type: impl Into<u8>, id: u32) -> &mut Self {
let id = Identifier::new(id_type, id);
self.insert(id);
let entity = self.id();
self.commands().add(SpawnIdentifierCommand { id, entity });
self
}
}
pub trait WorldSpawnIdentifierExt<'w> {
fn spawn_client(&'w mut self, client_id: u32, bundle: impl Bundle) -> EntityWorldMut<'w>;
fn spawn_with_id(
&'w mut self,
id_type: impl Into<u8>,
id: u32,
bundle: impl Bundle,
) -> EntityWorldMut<'w>;
}
impl<'w> WorldSpawnIdentifierExt<'w> for World {
#[inline(always)]
fn spawn_client(&'w mut self, client_id: u32, bundle: impl Bundle) -> EntityWorldMut<'w> {
self.spawn_with_id(0, client_id, bundle)
}
#[inline(always)]
fn spawn_with_id(
&'w mut self,
id_type: impl Into<u8>,
id: u32,
bundle: impl Bundle,
) -> EntityWorldMut<'w> {
let id = Identifier::new(id_type, id);
let e = self.spawn((id, bundle)).id();
self.resource_mut::<IdentifierMap>().insert(id, e);
self.entity_mut(e)
}
}
#[derive(Resource, Default)]
pub struct IdentifierMap {
from_ident: bevy::utils::HashMap<Identifier, EntityStatus>,
to_ident: bevy::utils::HashMap<Entity, Identifier>,
}
#[derive(Debug)]
pub enum IdentifierError {
Despawned,
NonExistent,
}
pub type IdentifierResult<T> = Result<T, IdentifierError>;
#[repr(u8)]
pub enum EntityStatus {
Alive(Entity),
Despawned(Tick),
}
impl IdentifierMap {
pub fn n_alive(&self) -> usize {
self.to_ident.len()
}
pub fn n_total(&self) -> usize {
self.from_ident.len()
}
#[inline(always)]
pub fn insert(&mut self, ident: Identifier, entity: Entity) {
self.from_ident.insert(ident, EntityStatus::Alive(entity));
self.to_ident.insert(entity, ident);
}
#[inline(always)]
pub fn get(&self, ident: &Identifier, tick: Tick) -> IdentifierResult<&EntityStatus> {
let status = self.from_ident.get(ident);
if let Some(EntityStatus::Despawned(despawned_at)) = status {
if *despawned_at < tick {
return Err(IdentifierError::Despawned);
}
}
match status {
Some(v) => Ok(v),
None => Err(IdentifierError::NonExistent),
}
}
#[inline(always)]
pub fn get_alive(&self, ident: &Identifier) -> IdentifierResult<Entity> {
let status = self.from_ident.get(ident);
if let Some(EntityStatus::Alive(entity)) = status {
return Ok(*entity);
}
if let Some(EntityStatus::Despawned(_)) = status {
return Err(IdentifierError::Despawned);
}
Err(IdentifierError::NonExistent)
}
#[inline(always)]
pub fn get_id(&self, id_type: impl Into<u8>, id: u32) -> IdentifierResult<Entity> {
self.get_alive(&Identifier::new(id_type, id))
}
#[inline(always)]
pub fn get_client(&self, client_id: u32) -> IdentifierResult<Entity> {
self.get_alive(&Identifier::new(0, client_id))
}
pub fn is_alive(&self, ident: &Identifier, tick: Tick) -> bool {
let status = self.from_ident.get(ident);
if let Some(EntityStatus::Despawned(despawned_at)) = status {
if *despawned_at < tick {
return false;
}
}
status.is_some()
}
pub fn from_entity(&self, entity: &Entity) -> IdentifierResult<Identifier> {
match self.to_ident.get(entity) {
Some(ident) => Ok(*ident),
None => Err(IdentifierError::NonExistent),
}
}
pub fn despawn(&mut self, ident: &Identifier, entity: &Entity, tick: Tick) {
self.from_ident
.insert(*ident, EntityStatus::Despawned(tick));
self.to_ident.remove(entity);
}
pub fn remove_entity(&mut self, entity: &Entity) -> Option<Identifier> {
let ident = self.to_ident.remove(entity);
if let Some(ident) = ident {
self.from_ident.remove(&ident);
}
ident
}
}
pub struct SpawnIdentifierCommand {
id: Identifier,
entity: Entity,
}
impl Command for SpawnIdentifierCommand {
fn apply(self, world: &mut World) {
world
.resource_mut::<IdentifierMap>()
.insert(self.id, self.entity);
}
}