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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
//! Simple entity-component system. Macro-free stable Rust using compile-time reflection!
//!
//! # Example
//! ```
//! extern crate recs;
//! use recs::{Ecs, EntityId};
//!
//! #[derive(Clone, PartialEq, Debug)]
//! struct Age{years: u32}
//!
//! #[derive(Clone, PartialEq, Debug)]
//! struct Iq{points: i32}
//!
//! fn main() {
//!
//!     // Create an ECS instance
//!     let mut system: Ecs = Ecs::new();
//!
//!     // Add entity to the system
//!     let forrest: EntityId = system.create_entity();
//!
//!     // Attach components to the entity
//!     // The Ecs.set method returns an EcsResult that will be set to Err if
//!     // the specified entity does not exist. If you're sure that the entity exists, suppress
//!     // Rust's "unused result" warning by prefixing your calls to set(..) with "let _ = ..."
//!     let _ = system.set(forrest, Age{years: 22});
//!     let _ = system.set(forrest, Iq{points: 75}); // "I may not be a smart man..."
//!
//!     // Get clone of attached component data from entity
//!     let age = system.get::<Age>(forrest).unwrap();
//!     assert_eq!(age.years, 22);
//!
//!     // Annotating the variable's type may let you skip type parameters
//!     let iq: Iq = system.get(forrest).unwrap();
//!     assert_eq!(iq.points, 75);
//!
//!     // Modify an entity's component
//!     let older = Age{years: age.years + 1};
//!     let _ = system.set(forrest, older);
//!
//!     // Modify a component in-place with a mutable borrow
//!     system.borrow_mut::<Iq>(forrest).map(|iq| iq.points += 5);
//!
//!     // Inspect a component in-place without cloning
//!     assert_eq!(system.borrow::<Age>(forrest), Ok(&Age{years: 23}));
//!
//!     // Inspect a component via cloning
//!     assert_eq!(system.get::<Iq>(forrest), Ok(Iq{points: 80}));
//!
//! }
//! ```

#![allow(unknown_lints)] // for rust-clippy
#![warn(missing_docs)]
use std::any::{TypeId, Any};
use std::collections::{HashMap, HashSet};

type IdNumber = u64;

/// Value type representing an entity in the entity-component system.
///
/// To avoid duplicate entity IDs, these can only be created by calling `Ecs.create_entity()`.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct EntityId(IdNumber);

/// Error type for ECS results that require a specific entity or component.
#[derive(Debug, PartialEq, Eq)]
pub enum NotFound {
    /// A requested entity ID was not present in the system.
    Entity(EntityId),
    /// A requested component was not present on an entity.
    Component(TypeId),
}

/// Result type for ECS operations that may fail without a specific entity or component.
pub type EcsResult<T> = Result<T, NotFound>;

/// Marker trait for types which can be used as components.
///
/// `Component` is automatically implemented for all eligible types by the
/// provided `impl`, so you don't have to worry about this. Hooray!
pub trait Component: Any {}
impl<T: Any> Component for T {}

/// List of component types.
///
/// The `Ecs` methods `has_all` and `collect_with` each take a `ComponentFilter` instance. The
/// recommended way to actually create a `ComponentFilter` is with the
/// [`component_filter!` macro](macro.component_filter!.html).
#[derive(Default, PartialEq, Eq, Debug, Clone)]
pub struct ComponentFilter {
    set: HashSet<TypeId>,
}

impl ComponentFilter {
    /// Create a new component filter.
    pub fn new() -> Self {
        Default::default()
    }
    /// Add component type `C` to the filter.
    pub fn add<C: Component>(&mut self) {
        self.set.insert(TypeId::of::<C>());
    }
    /// Remove component type `C` from the filter.
    pub fn remove<C: Component>(&mut self) {
        self.set.remove(&TypeId::of::<C>());
    }
    /// Return `true` if the filter already contains component type `C`; otherwise `false`.
    pub fn contains<C: Component>(&mut self) -> bool {
        self.set.contains(&TypeId::of::<C>())
    }
    /// Create a component filter from a vector/slice of `TypeId` instances. (Not recommended;
    /// used by the `component_filter!` macro.)
    pub fn from_slice(slice: &[TypeId]) -> Self {
        let mut this = Self::new();
        for type_id in slice.iter() {
            this.set.insert(*type_id);
        }
        this
    }
    /// Return an iterator over all the contained component types.
    #[allow(needless_lifetimes)] // https://github.com/Manishearth/rust-clippy/issues/740
    pub fn iter<'a>(&'a self) -> Box<Iterator<Item = TypeId> + 'a> {
        Box::new(self.set.iter().cloned())
    }
}

/// Create a `ComponentFilter` by type name.
///
/// If you want all entities with components `Foo` and `Bar`:
///
/// ```
/// #[macro_use] // The macro won't be imported without this flag!
/// extern crate recs;
/// use recs::{Ecs, EntityId};
///
/// struct Foo;
/// struct Bar;
///
/// fn main() {
///     let sys = Ecs::new();
///     // ... add some entities and components ...
///     let mut ids: Vec<EntityId> = Vec::new();
///     let filter = component_filter!(Foo, Bar);
///     sys.collect_with(&filter, &mut ids);
///     for id in ids {
///         // Will only iterate over entities that have been assigned both `Foo` and `Bar`
///         // components
///     }
/// }
/// ```
#[macro_export]
macro_rules! component_filter {
  ($($x:ty),*) => (
    $crate::ComponentFilter::from_slice(
      &vec![$(std::any::TypeId::of::<$x>()),*]
    )
  );
  ($($x:ty,)*) => (component_filter![$($x),*])
}

/// Primary data structure containing entity and component data.
///
/// Notice that `Ecs` itself has no type parameters. Its methods to interact
/// with components do, but runtime reflection (via `std::any::TypeId`) is
/// used to retrieve components from an internal `HashMap`. Therefore, you
/// can create and use any data structure you want for components.
///
/// Tip: using `#[derive(Clone)]` on your component types will make your life a little easier by
/// enabling the `get` method, which avoids locking down the `Ecs` with a mutable or immutable
/// borrow.
#[derive(Default)]
pub struct Ecs {
    ids: IdNumber,
    data: HashMap<EntityId, ComponentMap>,
}

#[derive(Default)]
struct ComponentMap {
    map: HashMap<TypeId, Box<Any>>,
}

impl ComponentMap {
    fn set<C: Component>(&mut self, component: C) -> Option<C> {
        self.map
            .insert(TypeId::of::<C>(), Box::new(component))
            .map(|old| *old.downcast::<C>().expect("ComponentMap.set: internal downcast error"))
    }
    fn borrow<C: Component>(&self) -> EcsResult<&C> {
        self.map
            .get(&TypeId::of::<C>())
            .map(|c| {
                c.downcast_ref()
                 .expect("ComponentMap.borrow: internal downcast error")
            })
            .ok_or_else(|| NotFound::Component(TypeId::of::<C>()))
    }
    #[allow(map_clone)]
    fn get<C: Component + Clone>(&self) -> EcsResult<C> {
        self.borrow::<C>()
            .map(Clone::clone)
    }
    fn contains_type_id(&self, id: &TypeId) -> bool {
        self.map.contains_key(id)
    }
    fn contains<C: Component>(&self) -> bool {
        self.contains_type_id(&TypeId::of::<C>())
    }
    fn borrow_mut<C: Component>(&mut self) -> EcsResult<&mut C> {
        match self.map.get_mut(&TypeId::of::<C>()) {
            Some(c) => {
                Ok(c.downcast_mut()
                    .expect("ComponentMap.borrow_mut: internal downcast error"))
            }
            None => Err(NotFound::Component(TypeId::of::<C>())),
        }
    }
}

impl Ecs {
    /// Create a new and empty entity-component system (ECS).
    pub fn new() -> Self {
        Default::default()
    }
    /// Create a new entity in the ECS without components and return its ID.
    pub fn create_entity(&mut self) -> EntityId {
        let new_id = EntityId(self.ids);
        self.ids += 1;
        self.data.insert(new_id, Default::default());
        new_id
    }
    /// Return `true` if the provided entity exists in the system.
    pub fn exists(&self, id: EntityId) -> bool {
        self.data.contains_key(&id)
    }
    /// Destroy the provided entity, automatically removing any of its components.
    ///
    /// Return `NotFound::Entity` if the entity does not exist or was already deleted.
    pub fn destroy_entity(&mut self, id: EntityId) -> EcsResult<()> {
        self.data.remove(&id).map(|_| ()).ok_or_else(|| NotFound::Entity(id))
    }
    /// For the specified entity, add a component of type `C` to the system.
    ///
    /// If the entity already has a component `prev` of type `C`, return `Some(prev)`. If not,
    /// return `None`. If the entity does not exist, return `NotFound::Entity`.
    ///
    /// To modify an existing component in place, see `borrow_mut`.
    pub fn set<C: Component>(&mut self, id: EntityId, comp: C) -> EcsResult<Option<C>> {
        self.data
            .get_mut(&id)
            .ok_or_else(|| NotFound::Entity(id))
            .map(|map| map.set(comp))
    }
    /// Return a clone of the requested entity's component of type `C`, or a `NotFound` variant
    /// if the entity does not exist or does not have that component.
    ///
    /// To examine or modify a component without making a clone, see `borrow` and `borrow_mut`.
    pub fn get<C: Component + Clone>(&self, id: EntityId) -> EcsResult<C> {
        self.data
            .get(&id)
            .ok_or_else(|| NotFound::Entity(id))
            .and_then(|map| map.get())
    }
    /// Return `true` if the specified entity has a component of type `C` in the system, or
    /// `NotFound::Entity` if the entity does not exist.
    pub fn has<C: Component>(&self, id: EntityId) -> EcsResult<bool> {
        self.data
            .get(&id)
            .ok_or_else(|| NotFound::Entity(id))
            .map(|map| map.contains::<C>())
    }
    /// Return `true` if each component type in the filter is present on the entity `id`.
    pub fn has_all(&self, id: EntityId, set: &ComponentFilter) -> EcsResult<bool> {
        let map = try!(self.data.get(&id).ok_or_else(|| NotFound::Entity(id)));
        Ok(set.iter().all(|type_id| map.contains_type_id(&type_id)))
    }
    /// Return a shared reference to the requested entity's component of type `C`, or a
    /// `NotFound` variant if the entity does not exist or does not have that component.
    pub fn borrow<C: Component>(&self, id: EntityId) -> EcsResult<&C> {
        self.data
            .get(&id)
            .ok_or_else(|| NotFound::Entity(id))
            .and_then(|map| map.borrow())
    }
    /// Return a mutable reference to the requested entity's component of type `C`, or a
    /// `NotFound` variant if the entity does not exist or does not have that component.
    pub fn borrow_mut<C: Component>(&mut self, id: EntityId) -> EcsResult<&mut C> {
        self.data
            .get_mut(&id)
            .ok_or_else(|| NotFound::Entity(id))
            .and_then(|map| map.borrow_mut())
    }
    /// Return an iterator over every ID in the system.
    #[allow(needless_lifetimes)] // https://github.com/Manishearth/rust-clippy/issues/740
    pub fn iter<'a>(&'a self) -> Box<Iterator<Item = EntityId> + 'a> {
        Box::new(self.data.keys().cloned())
    }
    /// Collect all entity IDs into a vector (after emptying the vector).
    ///
    /// Useful for accessing entity IDs without borrowing the ECS.
    pub fn collect(&self, dest: &mut Vec<EntityId>) {
        dest.clear();
        dest.extend(self.iter());
    }
    /// Collect the IDs of all entities containing a certain set of component types into a vector.
    ///
    /// After calling this method, the vector `dest` will contain *only* those entities who have
    /// at least each type of component specified in the filter.
    pub fn collect_with<'a>(&'a self, components: &'a ComponentFilter, dest: &mut Vec<EntityId>) {
        let ids = self.data.keys().cloned();
        dest.clear();
        dest.extend(ids.filter(|e| {
            self.has_all(*e, components)
                .expect("Ecs.collect_with: internal id filter error")
        }))
    }
}