2025-03-30 02:54:07 -04:00
|
|
|
|
use std::any::{Any, TypeId};
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use std::sync::Mutex;
|
|
|
|
|
|
2025-02-04 05:10:53 -05:00
|
|
|
|
pub trait Component: Sized + 'static {
|
|
|
|
|
fn update(&mut self, delta_time: f32);
|
|
|
|
|
fn serialize(&self) -> Vec<u8>;
|
2025-03-22 18:19:01 -04:00
|
|
|
|
fn deserialize(data: &[u8; 6]) -> Self;
|
2025-02-04 05:10:53 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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>;
|
2025-03-22 18:19:01 -04:00
|
|
|
|
fn deserialize(data: &[u8; 6]) -> Self;
|
|
|
|
|
}
|
2025-03-30 02:54:07 -04:00
|
|
|
|
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 object‐safe)
|
|
|
|
|
// we wrap them in an object–safe 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;
|
|
|
|
|
}
|
|
|
|
|
}
|