2024-08-23 11:22:09 +00:00
|
|
|
use core::fmt;
|
|
|
|
use std::any::TypeId;
|
2025-02-26 11:22:32 +00:00
|
|
|
use std::collections::HashSet;
|
2024-08-23 11:22:09 +00:00
|
|
|
use std::{num::ParseIntError, str::FromStr};
|
|
|
|
|
|
|
|
use anyhow::Result;
|
2025-02-26 11:22:32 +00:00
|
|
|
use indexmap::IndexMap;
|
2025-02-26 07:39:19 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2024-08-23 11:22:09 +00:00
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
use crate::{ComponentDebug, ComponentNotFoundError, EntityComponent, MultiMut, TypeMap};
|
2024-08-23 11:22:09 +00:00
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
pub const COMPONENT_SEPARATOR: char = '§';
|
2024-08-23 11:22:09 +00:00
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct EntityNotFoundError {
|
|
|
|
entity: Entity,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EntityNotFoundError {
|
|
|
|
pub(crate) fn new(entity: Entity) -> Self {
|
|
|
|
Self { entity }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Display for EntityNotFoundError {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "Entity (ID: {}) not found!", self.entity)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for EntityNotFoundError {}
|
|
|
|
|
2024-08-23 11:22:09 +00:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct ActivationState {
|
|
|
|
activated: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ActivationState {
|
|
|
|
pub fn is_active(&self) -> bool {
|
|
|
|
self.activated
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn apply_change(&mut self) {
|
|
|
|
self.activated = !self.activated;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
pub struct EntityObject {
|
2024-08-23 11:22:09 +00:00
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
pub debug_name: Option<String>,
|
|
|
|
|
|
|
|
// gltf file name
|
2025-02-27 05:29:28 +00:00
|
|
|
pub gltf_file: Option<String>,
|
2024-08-23 11:22:09 +00:00
|
|
|
|
|
|
|
// activation state of Entity
|
|
|
|
pub(crate) activation_state: ActivationState,
|
|
|
|
|
|
|
|
// program local ID
|
|
|
|
pub entity_id: u32,
|
|
|
|
|
|
|
|
// component map
|
2025-02-26 11:22:32 +00:00
|
|
|
pub components: TypeMap,
|
2024-08-23 11:22:09 +00:00
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
component_names: Vec<String>,
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
unsafe impl Send for EntityObject {}
|
|
|
|
unsafe impl Sync for EntityObject {}
|
2024-08-23 11:22:09 +00:00
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
impl EntityObject {
|
2024-08-23 11:22:09 +00:00
|
|
|
pub(crate) fn new(id: u32) -> Self {
|
|
|
|
Self {
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
debug_name: None,
|
|
|
|
|
|
|
|
gltf_file: None,
|
|
|
|
|
|
|
|
activation_state: ActivationState::default(),
|
|
|
|
|
|
|
|
entity_id: id,
|
|
|
|
|
|
|
|
components: TypeMap::default(),
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
component_names: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gltf_file(&self) -> Option<&String> {
|
|
|
|
self.gltf_file.as_ref()
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
pub fn multi_mut(&mut self) -> MultiMut<'_> {
|
2024-08-23 11:22:09 +00:00
|
|
|
self.components.multi_mut()
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
pub fn insert_component<T: EntityComponent + ComponentDebug>(
|
2024-08-23 11:22:09 +00:00
|
|
|
&mut self,
|
|
|
|
component: T,
|
|
|
|
) -> Option<T> {
|
|
|
|
assert!(
|
|
|
|
!self.activation_state.is_active(),
|
|
|
|
"inserting components while the entity is activated is not allowed"
|
|
|
|
);
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
{
|
|
|
|
let name = component.name().to_string();
|
|
|
|
|
|
|
|
if !self.component_names.contains(&name) {
|
|
|
|
self.component_names.push(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.components.insert(component)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn insert_component_by_id(
|
|
|
|
&mut self,
|
|
|
|
type_id: TypeId,
|
2025-02-26 11:22:32 +00:00
|
|
|
component: Box<dyn EntityComponent>,
|
|
|
|
) -> Option<Box<dyn EntityComponent>> {
|
2024-08-23 11:22:09 +00:00
|
|
|
assert!(
|
|
|
|
!self.activation_state.is_active(),
|
|
|
|
"inserting components while the entity is activated is not allowed"
|
|
|
|
);
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
{
|
|
|
|
let name = component.name().to_string();
|
|
|
|
|
|
|
|
if !self.component_names.contains(&name) {
|
|
|
|
self.component_names.push(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.components.insert_type(type_id, component)
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
pub fn remove_component<T: EntityComponent>(&mut self) -> Option<T> {
|
2024-08-23 11:22:09 +00:00
|
|
|
assert!(
|
|
|
|
!self.activation_state.is_active(),
|
|
|
|
"removing components while the entity is activated is not allowed"
|
|
|
|
);
|
|
|
|
|
|
|
|
let t: Option<T> = self.components.remove();
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
if let Some(t) = &t {
|
|
|
|
if let Some(index) = self
|
|
|
|
.component_names
|
|
|
|
.iter()
|
|
|
|
.position(|name| name == t.name())
|
|
|
|
{
|
|
|
|
self.component_names.remove(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
t
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn remove_component_by_id(
|
|
|
|
&mut self,
|
|
|
|
type_id: TypeId,
|
2025-02-26 11:22:32 +00:00
|
|
|
) -> Option<Box<dyn EntityComponent>> {
|
2024-08-23 11:22:09 +00:00
|
|
|
assert!(
|
|
|
|
!self.activation_state.is_active(),
|
|
|
|
"removing components while the entity is activated is not allowed"
|
|
|
|
);
|
|
|
|
|
|
|
|
let t = self.components.remove_by_type_id(&type_id);
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
if let Some(t) = &t {
|
|
|
|
if let Some(index) = self
|
|
|
|
.component_names
|
|
|
|
.iter()
|
|
|
|
.position(|name| name == t.name())
|
|
|
|
{
|
|
|
|
self.component_names.remove(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
t
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
pub fn component_names(&self) -> &[String] {
|
|
|
|
&self.component_names
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
pub fn get_component<T: EntityComponent + ComponentDebug>(
|
2024-08-23 11:22:09 +00:00
|
|
|
&self,
|
|
|
|
) -> std::result::Result<&T, ComponentNotFoundError> {
|
|
|
|
self.components.get()
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
pub fn get_component_mut<T: EntityComponent + ComponentDebug>(
|
2024-08-23 11:22:09 +00:00
|
|
|
&mut self,
|
|
|
|
) -> std::result::Result<&mut T, ComponentNotFoundError> {
|
|
|
|
self.components.get_mut()
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
pub fn contains_component<T: EntityComponent>(&self) -> bool {
|
2024-08-23 11:22:09 +00:00
|
|
|
self.components.contains::<T>()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_activated(&self) -> bool {
|
|
|
|
self.activation_state.is_active()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn as_entity(&self) -> Entity {
|
|
|
|
Entity { id: self.entity_id }
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
pub(crate) fn clone_without_components(&self, id: u32) -> EntityObject {
|
2024-08-23 11:22:09 +00:00
|
|
|
EntityObject {
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
debug_name: self.debug_name.clone(),
|
|
|
|
|
|
|
|
// gltf file name
|
|
|
|
gltf_file: self.gltf_file.clone(),
|
|
|
|
|
|
|
|
activation_state: Default::default(),
|
|
|
|
|
2025-02-26 07:39:19 +00:00
|
|
|
entity_id: id,
|
2024-08-23 11:22:09 +00:00
|
|
|
|
|
|
|
// component map
|
2025-02-26 07:39:19 +00:00
|
|
|
components: TypeMap::default(),
|
2024-08-23 11:22:09 +00:00
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
component_names: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
pub fn clone_component_from<T: EntityComponent + ComponentDebug + Clone>(
|
2024-08-23 11:22:09 +00:00
|
|
|
&mut self,
|
|
|
|
other: &Self,
|
|
|
|
) -> Result<()> {
|
|
|
|
let component = other.get_component::<T>()?;
|
|
|
|
self.insert_component(component.clone());
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::derive_hash_xor_eq)]
|
|
|
|
#[derive(Debug, Clone, Copy, Hash, Eq, Serialize, Deserialize)]
|
|
|
|
pub struct Entity {
|
|
|
|
pub(crate) id: u32,
|
|
|
|
}
|
|
|
|
|
2025-02-26 11:22:32 +00:00
|
|
|
impl PartialEq<EntityObject> for Entity {
|
|
|
|
fn eq(&self, entity: &EntityObject) -> bool {
|
2024-08-23 11:22:09 +00:00
|
|
|
self.id == entity.entity_id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PartialEq<Entity> for Entity {
|
|
|
|
fn eq(&self, entity: &Entity) -> bool {
|
|
|
|
self.id == entity.id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Entity {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "{}", self.id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for Entity {
|
|
|
|
type Err = ParseIntError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
|
|
Ok(Self { id: s.parse()? })
|
|
|
|
}
|
|
|
|
}
|
2025-02-26 11:22:32 +00:00
|
|
|
|
|
|
|
pub struct EntityMultiMut<'a> {
|
|
|
|
entities: &'a mut IndexMap<Entity, EntityObject>,
|
|
|
|
buffer: HashSet<Entity>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> EntityMultiMut<'a> {
|
|
|
|
pub(crate) fn new(entities: &'a mut IndexMap<Entity, EntityObject>) -> Self {
|
|
|
|
Self {
|
|
|
|
entities,
|
|
|
|
buffer: HashSet::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get(
|
|
|
|
&mut self,
|
|
|
|
entity: Entity,
|
|
|
|
) -> std::result::Result<&'a mut EntityObject, EntityNotFoundError> {
|
|
|
|
match self.entities.get_mut(&entity) {
|
|
|
|
Some(v) => {
|
|
|
|
let ptr = v as *mut EntityObject;
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
self.buffer.get(&entity).is_none(),
|
|
|
|
"Entity ({}) already borrowed",
|
|
|
|
entity.id
|
|
|
|
);
|
|
|
|
self.buffer.insert(entity);
|
|
|
|
|
|
|
|
let e: Option<&'a mut EntityObject> = Some(unsafe { &mut *ptr });
|
|
|
|
|
|
|
|
e.ok_or_else(|| EntityNotFoundError::new(entity))
|
|
|
|
}
|
|
|
|
None => Err(EntityNotFoundError::new(entity)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|