specs_static/
lib.rs

1//! A library that is meant to used together with Specs in cases
2//! where you need another id strategy which isn't as flexible, but much faster
3//! because there's no need for complex allocations.
4//!
5//! One specific example is a tile map, where each tile is basically like an entity.
6//! Because we won't delete any tiles, we can optimize the allocator.
7//! The current revision doesn't even need an allocator at all, the user can manage the ids
8//! freely.
9//!
10
11#[macro_use]
12extern crate derivative;
13extern crate hibitset;
14extern crate shred;
15extern crate specs;
16extern crate shrev;
17
18use std::hash::Hash;
19use std::marker::PhantomData;
20
21use hibitset::BitSet;
22use specs::storage::{UnprotectedStorage, ComponentEvent};
23use specs::{Component, Join, World, Tracked};
24use shrev::EventChannel;
25type Index = u32;
26
27/// The ids component storages are indexed with. This is mostly just a newtype wrapper with a `u32`
28/// in it.
29///
30/// # Examples
31///
32/// ```
33/// use specs_static::Id;
34///
35/// #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
36/// pub struct MyAmazingId(pub u32);
37///
38/// impl Id for MyAmazingId {
39///     fn from_u32(value: u32) -> Self {
40///         MyAmazingId(value)
41///     }
42///
43///     fn id(&self) -> u32 {
44///         self.0
45///     }
46/// }
47/// ```
48pub trait Id: Copy + Eq + Hash + Ord + Send + Sync + Sized + 'static {
49    /// Creates an idea from a `u32`.
50    fn from_u32(value: u32) -> Self;
51
52    /// Returns the `id` integer value.
53    fn id(&self) -> u32;
54}
55
56/// A storage for components managed with `specs_static::Id` instead of `Entity`.
57/// This `Storage` behaves very similar to `specs`' `Storage`.
58///
59/// # Registering
60///
61/// These component storages also have to be registered. This can be done using the `WorldExt`
62/// trait and its `register_tile_comp` method.
63#[derive(Derivative)]
64#[derivative(Default(bound = "D: Default"))]
65pub struct Storage<C, D: UnprotectedStorage<C>, I> {
66    data: D,
67    bitset: BitSet,
68    phantom: PhantomData<(C, I)>,
69}
70
71impl<C, D, I> Storage<C, D, I>
72where
73    C: Component,
74    D: UnprotectedStorage<C>,
75    I: Id,
76{
77    /// Tries to retrieve a component by its `Id`.
78    /// This will only check whether a component is inserted or not, without doing
79    /// any liveness checks for the id.
80    pub fn get(&self, id: I) -> Option<&C> {
81        match self.bitset.contains(id.id()) {
82            true => unsafe { Some(self.data.get(id.id())) },
83            false => None,
84        }
85    }
86
87    /// Tries to retrieve a component mutably by its `Id`.
88    /// This will only check whether a component is inserted or not, without doing
89    /// any liveness checks for the id.
90    pub fn get_mut(&mut self, id: I) -> Option<&mut C> {
91        match self.bitset.contains(id.id()) {
92            true => unsafe { Some(self.data.get_mut(id.id())) },
93            false => None,
94        }
95    }
96
97    /// Inserts `comp` at `id`. If there already was a value, it will be returned.
98    ///
99    /// In contrast to entities, **there are no invalid ids.**
100    pub fn insert(&mut self, id: I, comp: C) -> Option<C> {
101        let old = match self.bitset.add(id.id()) {
102            true => unsafe { Some(self.data.remove(id.id())) },
103            false => None,
104        };
105
106        unsafe {
107            self.data.insert(id.id(), comp);
108        }
109
110        old
111    }
112
113    /// Removes the component at `id`.
114    pub fn remove(&mut self, id: I) -> Option<C> {
115        match self.bitset.remove(id.id()) {
116            true => unsafe { Some(self.data.remove(id.id())) },
117            false => None,
118        }
119    }
120}
121
122impl<C, D, I> Tracked for Storage<C, D, I>
123    where D: Tracked + UnprotectedStorage<C>,
124          C: Component
125{
126    fn channel(&self) -> &EventChannel<ComponentEvent> { self.data.channel() }
127
128    fn channel_mut(&mut self) -> &mut EventChannel<ComponentEvent> { self.data.channel_mut() }
129}
130
131impl<C, D, I> Drop for Storage<C, D, I>
132where
133    D: UnprotectedStorage<C>,
134{
135    fn drop(&mut self) {
136        unsafe {
137            self.data.clean(&self.bitset);
138        }
139    }
140}
141
142impl<'a, C, D, I> Join for &'a Storage<C, D, I>
143where
144    D: UnprotectedStorage<C>,
145{
146    type Type = &'a C;
147    type Value = &'a D;
148    type Mask = &'a BitSet;
149
150    unsafe fn open(self) -> (Self::Mask, Self::Value) {
151        (&self.bitset, &self.data)
152    }
153
154    unsafe fn get(value: &mut Self::Value, id: Index) -> Self::Type {
155        (*value).get(id)
156    }
157}
158
159impl<'a, C, D, I> Join for &'a mut Storage<C, D, I>
160where
161    D: UnprotectedStorage<C>,
162{
163    type Type = &'a mut C;
164    type Value = &'a mut D;
165    type Mask = &'a BitSet;
166
167    unsafe fn open(self) -> (Self::Mask, Self::Value) {
168        (&self.bitset, &mut self.data)
169    }
170
171    unsafe fn get(value: &mut Self::Value, id: Index) -> Self::Type {
172        // This is horribly unsafe. Unfortunately, Rust doesn't provide a way
173        // to abstract mutable/immutable state at the moment, so we have to hack
174        // our way through it.
175        let value: *mut Self::Value = value as *mut Self::Value;
176        (*value).get_mut(id)
177    }
178}
179
180/// An extension trait for registering statically managed component storages.
181pub trait WorldExt {
182    /// Registers a `specs_static::Storage` for the components of type `C`.
183    /// This will be done automatically if your storage has a `Default` and you're fetching it with
184    /// `Read` / `Write`.
185    fn register_tile_comp<C, I>(&mut self)
186    where
187        C: Component + Send + Sync,
188        C::Storage: Default,
189        I: Id;
190}
191
192impl WorldExt for World {
193    fn register_tile_comp<C, I>(&mut self)
194    where
195        C: Component + Send + Sync,
196        C::Storage: Default,
197        I: Id,
198    {
199        self.add_resource(Storage::<C, C::Storage, I>::default());
200    }
201}