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
132
133
134
135
136
137
138
139
140
141
use bevy_ecs::{Archetype, Component, Entity, FromResources, Resources, World};
use bevy_property::{Properties, Property, PropertyTypeRegistration, PropertyTypeRegistry};
use std::{
    any::TypeId,
    collections::{HashMap, HashSet},
    sync::{Arc, RwLock},
};

#[derive(Clone, Default)]
pub struct TypeRegistry {
    pub property: Arc<RwLock<PropertyTypeRegistry>>,
    pub component: Arc<RwLock<ComponentRegistry>>,
}

#[derive(Default)]
pub struct ComponentRegistry {
    pub registrations: HashMap<TypeId, ComponentRegistration>,
    pub short_names: HashMap<String, TypeId>,
    pub full_names: HashMap<String, TypeId>,
    pub ambigous_names: HashSet<String>,
}

impl ComponentRegistry {
    pub fn register<T>(&mut self)
    where
        T: Properties + Component + FromResources,
    {
        let registration = ComponentRegistration::of::<T>();
        let short_name = registration.short_name.to_string();
        self.full_names
            .insert(registration.long_name.to_string(), registration.ty);
        if self.short_names.contains_key(&short_name) || self.ambigous_names.contains(&short_name) {
            // name is ambiguous. fall back to long names for all ambiguous types
            self.short_names.remove(&short_name);
            self.ambigous_names.insert(short_name);
        } else {
            self.short_names.insert(short_name, registration.ty);
        }
        self.registrations.insert(registration.ty, registration);
    }

    pub fn get(&self, type_id: &TypeId) -> Option<&ComponentRegistration> {
        self.registrations.get(type_id)
    }

    pub fn get_with_full_name(&self, full_name: &str) -> Option<&ComponentRegistration> {
        self.full_names
            .get(full_name)
            .and_then(|id| self.registrations.get(id))
    }

    pub fn get_with_short_name(&self, short_name: &str) -> Option<&ComponentRegistration> {
        self.short_names
            .get(short_name)
            .and_then(|id| self.registrations.get(id))
    }

    pub fn get_with_name(&self, type_name: &str) -> Option<&ComponentRegistration> {
        let mut registration = self.get_with_short_name(type_name);
        if registration.is_none() {
            registration = self.get_with_full_name(type_name);
            if registration.is_none() {
                if self.ambigous_names.contains(type_name) {
                    panic!("Type name is ambiguous: {}", type_name);
                }
            }
        }
        registration
    }
}

#[derive(Clone)]
pub struct ComponentRegistration {
    pub ty: TypeId,
    component_add_fn: fn(&mut World, resources: &Resources, Entity, &dyn Property),
    component_apply_fn: fn(&mut World, Entity, &dyn Property),
    component_properties_fn: fn(&Archetype, usize) -> &dyn Properties,
    pub short_name: String,
    pub long_name: &'static str,
}

impl ComponentRegistration {
    pub fn of<T: Properties + Component + FromResources>() -> Self {
        let ty = TypeId::of::<T>();
        Self {
            ty,
            component_add_fn: |world: &mut World,
                               resources: &Resources,
                               entity: Entity,
                               property: &dyn Property| {
                let mut component = T::from_resources(resources);
                component.apply(property);
                world.insert_one(entity, component).unwrap();
            },
            component_apply_fn: |world: &mut World, entity: Entity, property: &dyn Property| {
                let mut component = world.get_mut::<T>(entity).unwrap();
                component.apply(property);
            },
            component_properties_fn: |archetype: &Archetype, index: usize| {
                // the type has been looked up by the caller, so this is safe
                unsafe {
                    let ptr = archetype
                        .get::<T>()
                        .unwrap()
                        .as_ptr()
                        .offset(index as isize);
                    ptr.as_ref().unwrap()
                }
            },
            short_name: PropertyTypeRegistration::get_short_name(std::any::type_name::<T>()),
            long_name: std::any::type_name::<T>(),
        }
    }

    pub fn add_component_to_entity(
        &self,
        world: &mut World,
        resources: &Resources,
        entity: Entity,
        property: &dyn Property,
    ) {
        (self.component_add_fn)(world, resources, entity, property);
    }

    pub fn apply_component_to_entity(
        &self,
        world: &mut World,
        entity: Entity,
        property: &dyn Property,
    ) {
        (self.component_apply_fn)(world, entity, property);
    }

    pub fn get_component_properties<'a>(
        &self,
        archetype: &'a Archetype,
        entity_index: usize,
    ) -> &'a dyn Properties {
        (self.component_properties_fn)(archetype, entity_index)
    }
}