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
#![deny(missing_docs)]

//! # Constellation ECS
//!
//! A data-oriented entity component system optimized for cache coherent resource access
//! and parallel system execution.
//!
//! Constellation does not have any native understanding of a "component". Instead, the library
//! is concerned about ensuring safe concurrent access to shared resources, while providing direct
//! access to each resource's own APIs. These resources may store per-entity data such as
//! positions, or may represent application wide services such as asset loaders or input devices.
//!
//! Systems request read or write access to a set of resources, which can then be scheduled to
//! be potentially executed in parallel by recording them into a `SystemCommandBuffer` and
//! executing the command buffer within a `World`.
//!
//! # Examples
//!
//! Defining Resources:
//!
//! ```
//! # use self::constellation::*;
//! // Per-entity position data.
//! struct Position {
//!     x: f32,
//!     y: f32,
//!     z: f32
//! }
//!
//! // Store position data into a vector resource
//! type Positions = VecResource<Position>;
//!
//! // Per-entity debug names.
//! struct DebugName {
//!     name: String
//! }
//!
//! // Store debug names in a map resource
//! type DebugNames = MapResource<DebugName>;
//!
//! let mut world = World::new();
//! world.register_resource(Positions::new());
//! world.register_resource(DebugNames::new());
//! ```
//!
//! Update the world with Systems:
//!
//! ```
//! # use self::constellation::*;
//! # #[derive(Debug)]
//! # struct Position {
//! #     x: f32,
//! #     y: f32,
//! #     z: f32
//! # }
//! # type Positions = VecResource<Position>;
//! #
//! # struct Velocity {
//! #     x: f32,
//! #     y: f32,
//! #     z: f32
//! # }
//! # type Velocities = VecResource<Velocity>;
//! #
//! # struct DebugName {
//! #     name: String
//! # }
//! # type DebugNames = MapResource<DebugName>;
//! #
//! # let mut world = World::new();
//! # world.register_resource(Positions::new());
//! # world.register_resource(Velocities::new());
//! # world.register_resource(DebugNames::new());
//! let mut update = SystemCommandBuffer::default();
//! update.queue_systems(|scope| {
//!     scope.run_r1w1(|ctx, velocities: &Velocities, positions: &mut Positions| {
//!         println!("Updating positions");
//!
//!         // iterate through all components for entities with data in both
//!         // position and velocity resources
//!         ctx.iter_r1w1(velocities, positions).components(|entity, v, p| {
//!             p.x += v.x;
//!             p.y += v.y;
//!             p.z += v.z;
//!         });
//!     });
//!
//!     scope.run_r2w0(|ctx, names: &DebugNames, positions: &Positions| {
//!         println!("Printing positions");
//!
//!         // iterate through all entity IDs for entities with data in both
//!         // `names` and `positions`
//!         ctx.iter_r2w0(names, positions).entities(|entity_iter, n, p| {
//!             // `n` and `p` allow (potentially mutable) access to entity data inside
//!             // the resource without the ability to add or remove entities from the resource
//!             // - which would otherwise invalidate the iterator
//!             for e in entity_iter {
//!                 println!("Entity {} is at {:?}",
//!                          n.get(e).unwrap().name,
//!                          p.get(e).unwrap());
//!             }
//!         });
//!     });
//! });
//!
//! world.run(&mut update);
//! ```
//!
//! # Parallel System Execution
//!
//! Systems queued into a command buffer within a single call to `queue_systems` may be executed
//! in parallel by the world.
//!
//! The order in which systems are queued is significant in one way: the scheduler guarantees that
//! any changes to resources will always be observed in the same order in which systems were
//! queued.
//!
//! For example, given two systems - `ReadPositions` and `WritePositions` - if *WritePositions* was
//! queued before *ReadPositions*, then it is guarenteed that *ReadPositions* will see any changes
//! made by *WritePositions*. Conversely, if the order were to be swapped, then *ReadPositions*
//! is guaranteed to *not* observe the changes made by *WritePositions*.
//!
//! There is one exception to this. Entity deletions are committed when all concurrently executing
//! systems have completed. This behavior is deterministic, but not always obvious. If you wish to
//! ensure that entity deletions from one system are always seen by a later system, then queue
//! the two systems in separate `queue_systems` calls.

extern crate fnv;
extern crate crossbeam;
extern crate rayon;
extern crate arrayvec;
extern crate atom;
extern crate tuple_utils;
#[macro_use]
extern crate bitflags;

mod entities;
mod world;
mod resource;

pub mod bitset;
pub mod join;

pub use entities::*;
pub use world::*;
pub use resource::*;

#[cfg(test)]
mod tests {
    use super::*;

    struct Position {
        x: f32,
        y: f32,
        z: f32,
    }

    type Positions = VecResource<Position>;

    struct Velocity {
        x: f32,
        y: f32,
        z: f32,
    }

    type Velocities = VecResource<Velocity>;

    #[test]
    fn example() {
        let mut world = World::new();
        world.register_resource(Positions::new());
        world.register_resource(Velocities::new());

        let mut setup = SystemCommandBuffer::default();
        setup.queue_systems(|scope| {
            scope.run_r0w2(|tx, velocities: &mut Velocities, positions: &mut Positions| {
                for i in 0..1000 {
                    let e = tx.create();
                    let i = (i as f32) * 10f32;
                    velocities.add(e, Velocity { x: i, y: i, z: i });
                    positions.add(e, Position { x: i, y: i, z: i });
                }
            });
        });

        world.run(&mut setup);

        let mut update = SystemCommandBuffer::default();
        update.queue_systems(|scope| {
            scope.run_r1w1(|ctx, velocities: &Velocities, positions: &mut Positions| {
                println!("Updating positions");

                ctx.iter_r1w1(velocities, positions).components(|e, v, p| {
                    p.x += v.x;
                    p.y += v.y;
                    p.z += v.z;

                    ctx.destroy(e);
                });
            });
        });

        world.run(&mut update);
    }
}