async_ecs/join/
mod.rs

1mod impls;
2mod iter;
3mod maybe;
4mod parallel;
5
6pub use iter::JoinIter;
7pub use maybe::MaybeJoin;
8pub use parallel::JoinParIter;
9
10use hibitset::BitSetLike;
11
12use crate::entity::Index;
13
14/// The purpose of the `Join` trait is to provide a way
15/// to access multiple storages at the same time with
16/// the merged bit set.
17///
18/// Joining component storages means that you'll only get values where
19/// for a given entity every storage has an associated component.
20///
21/// ## Example
22///
23/// ```
24/// # use async_ecs::*;
25/// #
26/// # #[derive(Debug, PartialEq)]
27/// # struct Pos;
28/// # impl Component for Pos { type Storage = VecStorage<Self>; }
29/// #
30/// # #[derive(Debug, PartialEq)]
31/// # struct Vel;
32/// # impl Component for Vel { type Storage = VecStorage<Self>; }
33/// #
34/// let mut world = World::default();
35///
36/// world.register_component::<Pos>();
37/// world.register_component::<Vel>();
38///
39/// {
40///     let pos = world.component::<Pos>();
41///     let vel = world.component::<Vel>();
42///
43///     // There are no entities yet, so no pair will be returned.
44///     let joined: Vec<_> = (&pos, &vel).join().collect();
45///     assert_eq!(joined, vec![]);
46/// }
47///
48/// world.create_entity().with(Pos).build();
49///
50/// {
51///     let pos = world.component::<Pos>();
52///     let vel = world.component::<Vel>();
53///
54///     // Although there is an entity, it only has `Pos`.
55///     let joined: Vec<_> = (&pos, &vel).join().collect();
56///     assert_eq!(joined, vec![]);
57/// }
58///
59/// let ent = world.create_entity().with(Pos).with(Vel).build();
60///
61/// {
62///     let pos = world.component::<Pos>();
63///     let vel = world.component::<Vel>();
64///
65///     // Now there is one entity that has both a `Vel` and a `Pos`.
66///     let joined: Vec<_> = (&pos, &vel).join().collect();
67///     assert_eq!(joined, vec![(&Pos, &Vel)]);
68///
69///     // If we want to get the entity the components are associated to,
70///     // we need to join over `Entities`:
71///
72///     let entities = world.entities();
73///     // note: `Entities` is the fetched resource; we get back
74///     // `Read<Entities>`.
75///     // `Read<Entities>` can also be referred to by `Entities` which
76///     // is a shorthand type definition to the former type.
77///
78///     let joined: Vec<_> = (&entities, &pos, &vel).join().collect();
79///     assert_eq!(joined, vec![(ent, &Pos, &Vel)]);
80/// }
81/// ```
82///
83/// ## Iterating over a single storage
84///
85/// `Join` can also be used to iterate over a single
86/// storage, just by writing `(&storage).join()`.
87pub trait Join {
88    /// Type of joined components.
89    type Type;
90
91    /// Type of joined storages.
92    type Value;
93
94    /// Type of joined bit mask.
95    type Mask: BitSetLike;
96
97    /// Create a joined iterator over the contents.
98    fn join(self) -> JoinIter<Self>
99    where
100        Self: Sized,
101    {
102        JoinIter::new(self)
103    }
104
105    /// Returns a `Join`-able structure that yields all indices, returning
106    /// `None` for all missing elements and `Some(T)` for found elements.
107    ///
108    /// WARNING: Do not have a join of only `MaybeJoin`s. Otherwise the join
109    /// will iterate over every single index of the bitset. If you want a
110    /// join with all `MaybeJoin`s, add an `Entities` to the join as well
111    /// to bound the join to all entities that are alive.
112    ///
113    /// ```
114    /// # use async_ecs::*;
115    /// #
116    /// # #[derive(Debug, PartialEq)]
117    /// # struct Pos { x: i32, y: i32 }
118    /// # impl Component for Pos { type Storage = VecStorage<Self>; }
119    /// #
120    /// # #[derive(Debug, PartialEq)]
121    /// # struct Vel { x: i32, y: i32 }
122    /// # impl Component for Vel { type Storage = VecStorage<Self>; }
123    /// #
124    /// struct ExampleSystem;
125    ///
126    /// impl<'a> System<'a> for ExampleSystem {
127    ///     type SystemData = (WriteStorage<'a, Pos>, ReadStorage<'a, Vel>);
128    ///
129    ///     fn run(&mut self, (mut positions, velocities): Self::SystemData) {
130    ///         for (mut position, maybe_velocity) in (&mut positions, velocities.maybe()).join() {
131    ///             if let Some(velocity) = maybe_velocity {
132    ///                 position.x += velocity.x;
133    ///                 position.y += velocity.y;
134    ///             }
135    ///         }
136    ///     }
137    /// }
138    ///
139    /// #[tokio::main]
140    /// async fn main() {
141    ///     let mut world = World::default();
142    ///     let mut dispatcher = Dispatcher::setup_builder(&mut world)
143    ///         .with(ExampleSystem, "example_system", &[])
144    ///         .unwrap()
145    ///         .build();
146    ///
147    ///     let e1 = world
148    ///         .create_entity()
149    ///         .with(Pos { x: 0, y: 0 })
150    ///         .with(Vel { x: 5, y: 2 })
151    ///         .build();
152    ///
153    ///     let e2 = world.create_entity().with(Pos { x: 0, y: 0 }).build();
154    ///
155    ///     dispatcher.dispatch(&mut world).await;
156    ///
157    ///     let positions = world.component::<Pos>();
158    ///     assert_eq!(positions.get(e1), Some(&Pos { x: 5, y: 2 }));
159    ///     assert_eq!(positions.get(e2), Some(&Pos { x: 0, y: 0 }));
160    /// }
161    /// ```
162    fn maybe(self) -> MaybeJoin<Self>
163    where
164        Self: Sized,
165    {
166        MaybeJoin(self)
167    }
168
169    /// Open this join by returning the mask and the storages.
170    ///
171    /// # Safety
172    ///
173    /// This is unsafe because implementations of this trait can permit
174    /// the `Value` to be mutated independently of the `Mask`.
175    /// If the `Mask` does not correctly report the status of the `Value`
176    /// then illegal memory access can occur.
177    unsafe fn open(self) -> (Self::Mask, Self::Value);
178
179    /// Get a joined component value by a given index.
180    ///
181    /// # Safety
182    ///
183    /// * A call to `get` must be preceded by a check if `id` is part of
184    ///   `Self::Mask`
185    /// * The implementation of this method may use unsafe code, but has no
186    ///   invariants to meet
187    unsafe fn get(value: &mut Self::Value, index: Index) -> Self::Type;
188
189    /// If this `Join` typically returns all indices in the mask, then iterating
190    /// over only it or combined with other joins that are also dangerous
191    /// will cause the `JoinIter`/`ParJoin` to go through all indices which
192    /// is usually not what is wanted and will kill performance.
193    #[inline]
194    fn is_unconstrained() -> bool {
195        false
196    }
197}
198
199// SAFETY: This is safe as long as `T` implements `ParJoin` safely. `MaybeJoin`
200// relies on `T as Join` for all storage access and safely wraps the inner
201// `Join` API, so it should also be able to implement `ParJoin`.
202pub trait ParJoin: Join {
203    fn par_join(self) -> JoinParIter<Self>
204    where
205        Self: Sized,
206    {
207        if <Self as Join>::is_unconstrained() {
208            log::warn!(
209                "`ParJoin` possibly iterating through all indices, you might've made a join with all `MaybeJoin`s, which is unbounded in length."
210            );
211        }
212
213        JoinParIter::new(self)
214    }
215}