1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! A tiny specific Entity Component System (ECS) for Minecraft entity ecosystem.

use std::collections::HashMap;
use crate::util::OpaquePtr;

mod codec;
pub use codec::*;


/// A type for static definition of entity types.
pub struct EntityType {
    /// The namespaced name of this entity.
    pub name: &'static str,
    /// Default data components' codecs *(refer to `EntityCodec` doc)* specifications.
    pub codecs: &'static [&'static dyn EntityCodec]
}

/// A trait to implement to component structures that support a codec, this is used by `entities!`
/// macro in order to reduce boilerplate.
pub trait EntityComponent {
    const CODEC: &'static dyn EntityCodec;
}


/// The global entities palette used in level environment.
pub struct GlobalEntities {
    name_to_entity_type: HashMap<&'static str, &'static EntityType>,
    entity_type_to_codecs: HashMap<OpaquePtr<EntityType>, Vec<&'static dyn EntityCodec>>
}

impl GlobalEntities {

    pub fn new() -> Self {
        Self {
            name_to_entity_type: HashMap::new(),
            entity_type_to_codecs: HashMap::new()
        }
    }

    /// A simple constructor to directly call `register_all` with given entity types slice.
    pub fn with_all(slice: &[&'static EntityType]) -> Self {
        let mut entities = Self::new();
        entities.register_all(slice);
        entities
    }

    /// Register a single entity type to this palette.
    pub fn register(&mut self, entity_type: &'static EntityType) {

        let default_codecs = entity_type.codecs.iter()
            .copied()
            .collect();

        self.name_to_entity_type.insert(entity_type.name, entity_type);
        self.entity_type_to_codecs.insert(OpaquePtr::new(entity_type), default_codecs);

    }

    /// An optimized way to call `register` multiple times for each given entity type.
    pub fn register_all(&mut self, slice: &[&'static EntityType]) {
        self.name_to_entity_type.reserve(slice.len());
        self.entity_type_to_codecs.reserve(slice.len());
        for &entity_type in slice {
            self.register(entity_type);
        }
    }

    /// Get an entity type from its name.
    pub fn get_entity_type(&self, name: &str) -> Option<&'static EntityType> {
        self.name_to_entity_type.get(name).copied()
    }

    pub fn get_codecs(&self, entity_type: &'static EntityType) -> Option<&Vec<&'static dyn EntityCodec>> {
        self.entity_type_to_codecs.get(&OpaquePtr::new(entity_type))
    }

    pub fn get_entity_type_and_codecs(&self, name: &str) -> Option<(&'static EntityType, &Vec<&'static dyn EntityCodec>)> {
        match self.name_to_entity_type.get(name) {
            Some(&typ) => {
                // SAFETY: Unwrap should be safe because every registered
                //  entity type also adds a components vec.
                Some((typ, self.entity_type_to_codecs.get(&OpaquePtr::new(typ)).unwrap()))
            },
            None => None
        }
    }

    pub fn has_entity_type(&self, entity_type: &'static EntityType) -> bool {
        self.entity_type_to_codecs.contains_key(&OpaquePtr::new(entity_type))
    }

    pub fn entity_types_count(&self) -> usize {
        self.name_to_entity_type.len()
    }

}


#[macro_export]
macro_rules! entities {
    ($global_vis:vis $static_id:ident $namespace:literal [
        $($entity_id:ident $entity_name:literal [$($component_struct:ident),*]),*
        $(,)?
    ]) => {

        $($global_vis static $entity_id: $crate::entity::EntityType = $crate::entity::EntityType {
            name: concat!($namespace, ':', $entity_name),
            codecs: &[$(<$component_struct as $crate::entity::EntityComponent>::CODEC),*]
        };)*

        $global_vis static $static_id: [&'static $crate::entity::EntityType; $crate::count!($($entity_id)*)] = [
            $(&$entity_id),*
        ];

    };
}

#[macro_export]
macro_rules! entity_component {
    ($struct_id:ident: default) => {
        impl $crate::entity::EntityComponent for $struct_id {
            const CODEC: &'static dyn $crate::entity::EntityCodec =
                &$crate::entity::DefaultEntityCodec::<$struct_id>::new();
        }
    };
    ($struct_id:ident: $codec_struct_id:ident) => {
        impl $crate::entity::EntityComponent for $struct_id {
            const CODEC: &'static dyn $crate::entity::EntityCodec = &$codec_struct_id;
        }
    };
}