mc_core/entity/
mod.rs

1//! A tiny specific Entity Component System (ECS) for Minecraft entity ecosystem.
2
3use std::collections::HashMap;
4use crate::util::OpaquePtr;
5
6mod codec;
7pub use codec::*;
8
9
10/// A type for static definition of entity types.
11pub struct EntityType {
12    /// The namespaced name of this entity.
13    pub name: &'static str,
14    /// Default data components' codecs *(refer to `EntityCodec` doc)* specifications.
15    pub codecs: &'static [&'static dyn EntityCodec]
16}
17
18/// A trait to implement to component structures that support a codec, this is used by `entities!`
19/// macro in order to reduce boilerplate.
20pub trait EntityComponent {
21    const CODEC: &'static dyn EntityCodec;
22}
23
24
25/// The global entities palette used in level environment.
26pub struct GlobalEntities {
27    name_to_entity_type: HashMap<&'static str, &'static EntityType>,
28    entity_type_to_codecs: HashMap<OpaquePtr<EntityType>, Vec<&'static dyn EntityCodec>>
29}
30
31impl GlobalEntities {
32
33    pub fn new() -> Self {
34        Self {
35            name_to_entity_type: HashMap::new(),
36            entity_type_to_codecs: HashMap::new()
37        }
38    }
39
40    /// A simple constructor to directly call `register_all` with given entity types slice.
41    pub fn with_all(slice: &[&'static EntityType]) -> Self {
42        let mut entities = Self::new();
43        entities.register_all(slice);
44        entities
45    }
46
47    /// Register a single entity type to this palette.
48    pub fn register(&mut self, entity_type: &'static EntityType) {
49
50        let default_codecs = entity_type.codecs.iter()
51            .copied()
52            .collect();
53
54        self.name_to_entity_type.insert(entity_type.name, entity_type);
55        self.entity_type_to_codecs.insert(OpaquePtr::new(entity_type), default_codecs);
56
57    }
58
59    /// An optimized way to call `register` multiple times for each given entity type.
60    pub fn register_all(&mut self, slice: &[&'static EntityType]) {
61        self.name_to_entity_type.reserve(slice.len());
62        self.entity_type_to_codecs.reserve(slice.len());
63        for &entity_type in slice {
64            self.register(entity_type);
65        }
66    }
67
68    /// Get an entity type from its name.
69    pub fn get_entity_type(&self, name: &str) -> Option<&'static EntityType> {
70        self.name_to_entity_type.get(name).copied()
71    }
72
73    pub fn get_codecs(&self, entity_type: &'static EntityType) -> Option<&Vec<&'static dyn EntityCodec>> {
74        self.entity_type_to_codecs.get(&OpaquePtr::new(entity_type))
75    }
76
77    pub fn get_entity_type_and_codecs(&self, name: &str) -> Option<(&'static EntityType, &Vec<&'static dyn EntityCodec>)> {
78        match self.name_to_entity_type.get(name) {
79            Some(&typ) => {
80                // SAFETY: Unwrap should be safe because every registered
81                //  entity type also adds a components vec.
82                Some((typ, self.entity_type_to_codecs.get(&OpaquePtr::new(typ)).unwrap()))
83            },
84            None => None
85        }
86    }
87
88    pub fn has_entity_type(&self, entity_type: &'static EntityType) -> bool {
89        self.entity_type_to_codecs.contains_key(&OpaquePtr::new(entity_type))
90    }
91
92    pub fn entity_types_count(&self) -> usize {
93        self.name_to_entity_type.len()
94    }
95
96}
97
98
99#[macro_export]
100macro_rules! entities {
101    ($global_vis:vis $static_id:ident $namespace:literal [
102        $($entity_id:ident $entity_name:literal [$($component_struct:ident),*]),*
103        $(,)?
104    ]) => {
105
106        $($global_vis static $entity_id: $crate::entity::EntityType = $crate::entity::EntityType {
107            name: concat!($namespace, ':', $entity_name),
108            codecs: &[$(<$component_struct as $crate::entity::EntityComponent>::CODEC),*]
109        };)*
110
111        $global_vis static $static_id: [&'static $crate::entity::EntityType; $crate::count!($($entity_id)*)] = [
112            $(&$entity_id),*
113        ];
114
115    };
116}
117
118#[macro_export]
119macro_rules! entity_component {
120    ($struct_id:ident: default) => {
121        impl $crate::entity::EntityComponent for $struct_id {
122            const CODEC: &'static dyn $crate::entity::EntityCodec =
123                &$crate::entity::DefaultEntityCodec::<$struct_id>::new();
124        }
125    };
126    ($struct_id:ident: $codec_struct_id:ident) => {
127        impl $crate::entity::EntityComponent for $struct_id {
128            const CODEC: &'static dyn $crate::entity::EntityCodec = &$codec_struct_id;
129        }
130    };
131}