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
use specs::{Component, Entity, World, WorldExt};
use std::{
    any::{self, Any, TypeId},
    fmt::{self, Debug, Formatter},
};

/// A vtable which gives the engine reflection-like abilities.
#[derive(Copy, Clone)]
pub struct ComponentVtable {
    type_id: TypeId,
    name: &'static str,
    register: fn(world: &mut World),
    get_cloned: fn(world: &World, entity: Entity) -> Option<Box<dyn Any>>,
    set: fn(world: &mut World, entity: Entity, value: &dyn Any),
    debug: fn(item: &dyn Any, f: &mut Formatter<'_>) -> fmt::Result,
}

impl ComponentVtable {
    /// Create the [`ComponentVtable`] corresponding to a particular type.
    pub fn for_type<T>() -> Self
    where
        T: Component + Clone + Debug,
        <T as Component>::Storage: Default,
    {
        ComponentVtable {
            type_id: TypeId::of::<T>(),
            name: any::type_name::<T>(),
            register: |world| {
                world.register::<T>();
            },
            get_cloned: |world, entity| {
                world
                    .read_storage::<T>()
                    .get(entity)
                    .cloned()
                    .map(|item| Box::new(item) as Box<dyn Any>)
            },
            set: |world, entity, value| {
                let value: &T = match value.downcast_ref() {
                    Some(boxed) => &*boxed,
                    None => panic!("Expected a {}", any::type_name::<T>()),
                };

                world
                    .write_storage::<T>()
                    .insert(entity, value.clone())
                    .unwrap();
            },
            debug: |item, f| match item.downcast_ref::<T>() {
                Some(item) => Debug::fmt(item, f),
                None => panic!("Expected a {}", any::type_name::<T>()),
            },
        }
    }

    pub fn applies_to<T: 'static>(&self) -> bool {
        self.type_id == TypeId::of::<T>()
    }

    /// A human-readable version of the [`Component`]'s name.
    pub fn name(&self) -> &'static str { self.name }

    /// Get the [`Debug`] representation of this type.
    pub fn debug(&self, item: &dyn Any, f: &mut Formatter<'_>) -> fmt::Result {
        (self.debug)(item, f)
    }

    /// Register this component with the [`World`].
    pub(crate) fn register(&self, world: &mut World) { (self.register)(world); }

    /// Lookup the component associated with an entity, returning a copy if
    /// anything is found.
    pub(crate) fn get_cloned(
        &self,
        world: &World,
        entity: Entity,
    ) -> Option<Box<dyn Any>> {
        (self.get_cloned)(world, entity)
    }

    /// Associate the entity with a new [`Component`] value.
    pub(crate) fn set(
        &self,
        world: &mut World,
        entity: Entity,
        value: &dyn Any,
    ) {
        (self.set)(world, entity, value);
    }
}

inventory::collect!(ComponentVtable);