zenyx-engine/engine/src/core/ecs/mod.rs
2025-04-21 20:13:23 -04:00

170 lines
No EOL
5.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::Mutex;
pub trait Component: Sized + 'static {
fn update(&mut self, delta_time: f32);
fn serialize(&self) -> Vec<u8>;
fn deserialize(data: &[u8; 6]) -> Self;
}
pub trait Entity: Sized {
fn add_component<C: Component>(&mut self, component: C);
fn remove_component<C: Component>(&mut self);
fn get_component<C: Component>(&self) -> Option<&C>;
fn serialize(&self) -> Vec<u8>;
fn deserialize(data: &[u8; 6]) -> Self;
}
lazy_static::lazy_static! {
// Global registry mapping component TypeId to a unique bit flag.
static ref COMPONENT_REGISTRY: Mutex<HashMap<TypeId, u64>> = Mutex::new(HashMap::new());
static ref NEXT_COMPONENT_BIT: Mutex<u64> = Mutex::new(1);
}
// To allow dynamic dispatch on components (even though Component itself is not objectsafe)
// we wrap them in an objectsafe trait.
pub trait ComponentObject: Any {
fn update_obj(&mut self, delta_time: f32);
fn serialize_obj(&self) -> Vec<u8>;
fn as_any(&self) -> &dyn Any;
}
impl<T: Component + 'static> ComponentObject for T {
fn update_obj(&mut self, delta_time: f32) {
T::update(self, delta_time)
}
fn serialize_obj(&self) -> Vec<u8> {
T::serialize(self)
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct EntityImpl {
id: usize,
bitmask: u64,
// The key is the unique bit flag for the component type.
components: HashMap<u64, Box<dyn ComponentObject>>,
}
impl EntityImpl {
pub fn new(id: usize) -> Self {
EntityImpl {
id,
bitmask: 0,
components: HashMap::new(),
}
}
}
impl Entity for EntityImpl {
fn add_component<C: Component>(&mut self, component: C) {
let type_id = TypeId::of::<C>();
let mut registry = COMPONENT_REGISTRY.lock().unwrap();
let bit = registry.entry(type_id).or_insert_with(|| {
let mut next_bit = NEXT_COMPONENT_BIT.lock().unwrap();
let current = *next_bit;
*next_bit *= 2;
current
});
self.bitmask |= *bit;
self.components.insert(*bit, Box::new(component));
}
fn remove_component<C: Component>(&mut self) {
let type_id = TypeId::of::<C>();
if let Some(&bit) = COMPONENT_REGISTRY.lock().unwrap().get(&type_id) {
self.bitmask &= !bit;
}
}
fn get_component<C: Component>(&self) -> Option<&C> {
let type_id = TypeId::of::<C>();
if let Some(&bit) = COMPONENT_REGISTRY.lock().unwrap().get(&type_id) {
self.components.get(&bit)
.and_then(|boxed| boxed.as_any().downcast_ref::<C>())
} else {
None
}
}
fn serialize(&self) -> Vec<u8> {
// Serialize the entity's bitmask into 6 bytes (lowest 48 bits).
let mut bytes = self.bitmask.to_le_bytes().to_vec();
bytes.truncate(6);
bytes
}
fn deserialize(data: &[u8; 6]) -> Self {
let mut full = [0u8; 8];
full[..6].copy_from_slice(data);
let bitmask = u64::from_le_bytes(full);
// When deserializing, we recreate an entity with the restored bitmask.
// Note: The individual component data are not restored here.
Self {
id: 0,
bitmask,
components: HashMap::new(),
}
}
}
pub struct ECS {
next_entity_id: usize,
pub entities: HashMap<usize, EntityImpl>,
}
impl ECS {
pub fn new() -> Self {
ECS {
next_entity_id: 0,
entities: HashMap::new(),
}
}
pub fn create_entity(&mut self) -> &mut EntityImpl {
let entity = EntityImpl::new(self.next_entity_id);
self.entities.insert(self.next_entity_id, entity);
self.next_entity_id += 1;
self.entities.get_mut(&(self.next_entity_id - 1)).unwrap()
}
pub fn update(&mut self, delta_time: f32) {
for entity in self.entities.values_mut() {
// Update each component attached to the entity.
for comp in entity.components.values_mut() {
comp.update_obj(delta_time);
}
}
}
pub fn serialize(&self) -> Vec<u8> {
let mut data = Vec::new();
// For each entity, store its id (8 bytes) and its 6-byte bitmask.
for (id, entity) in &self.entities {
data.extend_from_slice(&id.to_le_bytes());
data.extend_from_slice(&entity.serialize());
}
data
}
pub fn deserialize(&mut self, data: &[u8]) {
self.entities.clear();
// Each serialized entity uses 8 (id) + 6 (bitmask) = 14 bytes.
let entity_size = 14;
let count = data.len() / entity_size;
for i in 0..count {
let offset = i * entity_size;
let mut id_bytes = [0u8; 8];
id_bytes.copy_from_slice(&data[offset..offset + 8]);
let id = usize::from_le_bytes(id_bytes);
let mut mask_bytes = [0u8; 6];
mask_bytes.copy_from_slice(&data[offset + 8..offset + 14]);
let entity = EntityImpl::deserialize(&mask_bytes);
self.entities.insert(id, entity);
}
self.next_entity_id = count;
}
}