edict/
bundle.rs

1//! This module defines [`Bundle`], [`ComponentBundle`], [`DynamicBundle`] and [`DynamicComponentBundle`] traits.
2//!
3//! Tuples of up to 26 elements implement [`Bundle`] and [`DynamicBundle`] if all elements are `'static`.
4//! They additionally implement [`ComponentBundle`] and [`DynamicComponentBundle`] if all elements implement [`Component`].
5//!
6//! Bundles can be used to spawn entities with a set of components or insert multiple components at once.
7//! This is more efficient than spawning an entity and then inserting components one by one.
8
9use core::{
10    alloc::Layout,
11    any::TypeId,
12    fmt,
13    marker::PhantomData,
14    mem::{align_of, replace, size_of, ManuallyDrop},
15    ptr::{self, NonNull},
16};
17
18use smallvec::SmallVec;
19
20use crate::{
21    component::{Component, ComponentInfo},
22    type_id,
23};
24
25/// Possibly dynamic collection of components that may be inserted into the `World`.
26///
27/// # Safety
28///
29/// Implementors must uphold requirements:
30/// Bundle instance must have a set components.
31/// [`DynamicBundle::valid`] must return true only if components are not repeated.
32/// [`DynamicBundle::key`] must return unique value for a set of components or `None`
33/// [`DynamicBundle::contains_id`] must return true if component type with specified id is contained in bundle.
34/// [`DynamicBundle::with_ids`] must call provided function with a list of type ids of all contained components.
35/// [`DynamicBundle::put`] must call provided function for each component with pointer to component value, its type id and size.
36pub unsafe trait DynamicBundle {
37    /// Returns `true` if given bundle is valid.
38    fn valid(&self) -> bool;
39
40    /// Returns static key if the bundle type have one.
41    fn key() -> Option<TypeId> {
42        None
43    }
44
45    /// Returns true if bundle has specified type id.
46    fn contains_id(&self, ty: TypeId) -> bool;
47
48    /// Calls provided closure with slice of ids of types that this bundle contains.
49    fn with_ids<R>(&self, f: impl FnOnce(&[TypeId]) -> R) -> R;
50
51    /// Calls provided closure with pointer to a component, its type and size.
52    /// Closure is expected to read components from the pointer and take ownership.
53    fn put(self, f: impl FnMut(NonNull<u8>, TypeId, usize));
54}
55
56/// Possibly dynamic collection of components that may be inserted into the `World`.
57/// Where all elements implement `Component` and so support auto-registration.
58///
59/// # Safety
60///
61/// [`DynamicComponentBundle::with_components`] must call provided function with a list of component infos of all contained components.
62pub unsafe trait DynamicComponentBundle: DynamicBundle + 'static {
63    /// Calls provided closure with slice of component infos of types that this bundle contains.
64    fn with_components<R>(&self, f: impl FnOnce(&[ComponentInfo]) -> R) -> R;
65}
66
67/// Static collection of components that may be inserted into the `World`.
68///
69/// # Safety
70///
71/// Implementors must uphold requirements:
72/// Bundle instance must have a set components.
73/// [`Bundle::static_valid`] must return true only if components are not repeated.
74/// [`Bundle::static_key`] must return unique value for a set of components.
75/// [`Bundle::static_contains_id`] must return true if component type with specified id is contained in bundle.
76/// [`Bundle::static_with_ids`] must call provided function with a list of type ids of all contained components.
77
78pub unsafe trait Bundle: DynamicBundle {
79    /// Returns `true` if given bundle is valid.
80    fn static_valid() -> bool;
81
82    /// Returns static key for the bundle type.
83    fn static_key() -> TypeId;
84
85    /// Returns true if bundle has specified type id.
86    fn static_contains_id(ty: TypeId) -> bool;
87
88    /// Calls provided closure with slice of ids of types that this bundle contains.
89    fn static_with_ids<R>(f: impl FnOnce(&[TypeId]) -> R) -> R;
90}
91
92/// Static collection of components that may be inserted into the `World`.
93/// Where all elements implement `Component` and so support auto-registration.
94///
95/// # Safety
96///
97/// [`ComponentBundle::static_with_components`] must call provided function with a list of component infos of all contained components.
98pub unsafe trait ComponentBundle: Bundle + DynamicComponentBundle {
99    /// Calls provided closure with slice of component infos of types that this bundle contains.
100    fn static_with_components<R>(f: impl FnOnce(&[ComponentInfo]) -> R) -> R;
101}
102
103macro_rules! impl_bundle {
104    () => {
105        unsafe impl DynamicBundle for () {
106            #[inline(always)]
107            fn valid(&self) -> bool { true }
108
109            #[inline(always)]
110            fn key() -> Option<TypeId> {
111                Some(Self::static_key())
112            }
113
114            #[inline(always)]
115            fn contains_id(&self, ty: TypeId) -> bool {
116                Self::static_contains_id(ty)
117            }
118
119            #[inline(always)]
120            fn with_ids<R>(&self, f: impl FnOnce(&[TypeId]) -> R) -> R {
121                Self::static_with_ids(f)
122            }
123
124            #[inline(always)]
125            fn put(self, _f: impl FnMut(NonNull<u8>, TypeId, usize)) {}
126        }
127
128        unsafe impl DynamicComponentBundle for () {
129            #[inline(always)]
130            fn with_components<R>(&self, f: impl FnOnce(&[ComponentInfo]) -> R) -> R {
131                Self::static_with_components(f)
132            }
133        }
134
135        unsafe impl Bundle for () {
136            fn static_valid() -> bool { true }
137
138            #[inline(always)]
139            fn static_key() -> TypeId {
140                type_id::<()>()
141            }
142
143            #[inline(always)]
144            fn static_contains_id(_ty: TypeId) -> bool {
145                false
146            }
147
148            #[inline(always)]
149            fn static_with_ids<R>(f: impl FnOnce(&[TypeId]) -> R) -> R {
150                f(&[])
151            }
152        }
153
154        unsafe impl ComponentBundle for () {
155            #[inline(always)]
156            fn static_with_components<R>(f: impl FnOnce(&[ComponentInfo]) -> R) -> R {
157                f(&[])
158            }
159        }
160    };
161
162    ($($a:ident)+) => {
163        unsafe impl<$($a),+> DynamicBundle for ($($a,)+)
164        where $($a: 'static,)+
165        {
166            #[inline(always)]
167            fn valid(&self) -> bool {
168                <Self as Bundle>::static_valid()
169            }
170
171            #[inline(always)]
172            fn key() -> Option<TypeId> {
173                Some(<Self as Bundle>::static_key())
174            }
175
176            #[inline(always)]
177            fn contains_id(&self, ty: TypeId) -> bool {
178                <Self as Bundle>::static_contains_id(ty)
179            }
180
181            #[inline(always)]
182            fn with_ids<R>(&self, f: impl FnOnce(&[TypeId]) -> R) -> R {
183                <Self as Bundle>::static_with_ids(f)
184            }
185
186            #[inline(always)]
187            fn put(self, mut f: impl FnMut(NonNull<u8>, TypeId, usize)) {
188                #![allow(non_snake_case)]
189
190                let ($($a,)+) = self;
191                let ($($a,)+) = ($(ManuallyDrop::new($a),)+);
192                $(
193                    f(NonNull::from(&*$a).cast(), type_id::<$a>(), size_of::<$a>());
194                )+
195            }
196        }
197
198        unsafe impl<$($a),+> DynamicComponentBundle for ($($a,)+)
199        where $($a: Component,)+
200        {
201            #[inline(always)]
202            fn with_components<R>(&self, f: impl FnOnce(&[ComponentInfo]) -> R) -> R {
203                <Self as ComponentBundle>::static_with_components(f)
204            }
205        }
206
207        unsafe impl<$($a),+> Bundle for ($($a,)+)
208        where $($a: 'static,)+
209        {
210            fn static_valid() -> bool {
211                let mut ids: &[_] = &[$(type_id::<$a>(),)+];
212                while let [check, rest @ ..] = ids {
213                    let mut rest = rest;
214                    if let [head, tail @ ..] = rest {
215                        if head == check {
216                            return false;
217                        }
218                        rest = tail;
219                    }
220                    ids = rest;
221                }
222                true
223            }
224
225            #[inline(always)]
226            fn static_key() -> TypeId {
227                type_id::<Self>()
228            }
229
230            #[inline(always)]
231            fn static_contains_id(ty: TypeId) -> bool {
232                $( type_id::<$a>() == ty )|| *
233            }
234
235            #[inline(always)]
236            fn static_with_ids<R>(f: impl FnOnce(&[TypeId]) -> R) -> R {
237                f(&[$(type_id::<$a>(),)+])
238            }
239        }
240
241
242        unsafe impl<$($a),+> ComponentBundle for ($($a,)+)
243        where $($a: Component,)+
244        {
245            #[inline(always)]
246            fn static_with_components<R>(f: impl FnOnce(&[ComponentInfo]) -> R) -> R {
247                f(&[$(ComponentInfo::of::<$a>(),)+])
248            }
249        }
250    };
251}
252
253for_tuple!(impl_bundle);
254
255/// Build entities when exact set of components is not known at compile time.
256///
257/// Components can be added to [`EntityBuilder`] at runtime using [`EntityBuilder::add`] or [`EntityBuilder::with`].
258/// [`EntityBuilder`] then can be used to insert components into entity or spawn a new entity.
259pub struct EntityBuilder {
260    ptr: NonNull<u8>,
261    layout: Layout,
262    len: usize,
263
264    ids: SmallVec<[TypeId; 8]>,
265    infos: SmallVec<[ComponentInfo; 8]>,
266    offsets: SmallVec<[usize; 8]>,
267}
268
269// # Safety
270// Stores only `Send` values.
271unsafe impl Send for EntityBuilder {}
272
273impl Drop for EntityBuilder {
274    fn drop(&mut self) {
275        for (info, &offset) in self.infos.iter().zip(&self.offsets) {
276            let ptr = unsafe { NonNull::new_unchecked(self.ptr.as_ptr().add(offset)) };
277            info.final_drop(ptr, 1);
278        }
279    }
280}
281
282impl fmt::Debug for EntityBuilder {
283    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284        let mut ds = f.debug_struct("EntityBuilder");
285        for info in &self.infos {
286            ds.field("component", &info.name());
287        }
288        ds.finish()
289    }
290}
291
292impl EntityBuilder {
293    /// Creates new empty entity builder.
294    #[inline(always)]
295    pub fn new() -> Self {
296        EntityBuilder {
297            ptr: NonNull::dangling(),
298            len: 0,
299            layout: Layout::new::<[u8; 0]>(),
300            ids: SmallVec::new(),
301            infos: SmallVec::new(),
302            offsets: SmallVec::new(),
303        }
304    }
305
306    /// Adds component to the builder.
307    /// If builder already had this component, old value is replaced.
308    #[inline(always)]
309    pub fn with<T>(mut self, value: T) -> Self
310    where
311        T: Component + Send,
312    {
313        self.add(value);
314        self
315    }
316
317    /// Adds component to the builder.
318    /// If builder already had this component, old value is replaced.
319    pub fn add<T>(&mut self, value: T) -> &mut Self
320    where
321        T: Component + Send,
322    {
323        if let Some(existing) = self.get_mut::<T>() {
324            // Replace existing value.
325            *existing = value;
326            return self;
327        }
328
329        debug_assert!(self.len <= self.layout.size());
330        let value_layout = Layout::from_size_align(self.len, self.layout.align()).unwrap();
331
332        let (new_value_layout, value_offset) = value_layout
333            .extend(Layout::new::<T>())
334            .expect("EntityBuilder overflow");
335
336        self.ids.reserve(1);
337        self.infos.reserve(1);
338        self.offsets.reserve(1);
339
340        if self.layout.align() != new_value_layout.align()
341            || self.layout.size() < new_value_layout.size()
342        {
343            // Those thresholds helps avoiding reallocation.
344            const MIN_LAYOUT_ALIGN: usize = align_of::<u128>();
345            const MIN_LAYOUT_SIZE: usize = 128;
346
347            let cap = if self.layout.size() < new_value_layout.size() {
348                if MIN_LAYOUT_SIZE >= new_value_layout.size() {
349                    MIN_LAYOUT_SIZE
350                } else {
351                    match self.layout.size().checked_mul(2) {
352                        Some(cap) if cap >= new_value_layout.size() => cap,
353                        _ => new_value_layout.size(),
354                    }
355                }
356            } else {
357                self.layout.size()
358            };
359
360            let align = new_value_layout.align().max(MIN_LAYOUT_ALIGN);
361            let new_layout = Layout::from_size_align(cap, align).unwrap_or(new_value_layout);
362
363            unsafe {
364                let new_ptr = alloc::alloc::alloc(new_layout);
365                let new_ptr = NonNull::new(new_ptr).unwrap();
366
367                ptr::copy_nonoverlapping(self.ptr.as_ptr(), new_ptr.as_ptr(), self.len);
368
369                let old_ptr = replace(&mut self.ptr, new_ptr);
370                let old_layout = replace(&mut self.layout, new_layout);
371
372                alloc::alloc::dealloc(old_ptr.as_ptr(), old_layout);
373            }
374        }
375
376        unsafe {
377            debug_assert!(self.len <= self.layout.size());
378            debug_assert!(self.len <= value_offset);
379            debug_assert!(value_offset + size_of::<T>() <= self.layout.size());
380
381            ptr::write(self.ptr.as_ptr().add(value_offset).cast(), value);
382            self.len = value_offset + size_of::<T>();
383        }
384
385        self.ids.push(type_id::<T>());
386        self.infos.push(ComponentInfo::of::<T>());
387        self.offsets.push(value_offset);
388
389        self
390    }
391
392    /// Returns reference to component from builder.
393    #[inline(always)]
394    pub fn get<T>(&self) -> Option<&T>
395    where
396        T: 'static,
397    {
398        let idx = self.ids.iter().position(|id| *id == type_id::<T>())?;
399        let offset = self.offsets[idx];
400        Some(unsafe { &*self.ptr.as_ptr().add(offset).cast::<T>() })
401    }
402
403    /// Returns mutable reference to component from builder.
404    #[inline(always)]
405    pub fn get_mut<T>(&mut self) -> Option<&mut T>
406    where
407        T: 'static,
408    {
409        let idx = self.ids.iter().position(|id| *id == type_id::<T>())?;
410        let offset = self.offsets[idx];
411        Some(unsafe { &mut *self.ptr.as_ptr().add(offset).cast::<T>() })
412    }
413
414    /// Returns iterator over component types in this builder.
415    #[inline(always)]
416    pub fn component_types(&self) -> impl Iterator<Item = &ComponentInfo> {
417        self.infos.iter()
418    }
419
420    /// Returns true of the builder is empty.
421    #[inline(always)]
422    pub fn is_empty(&self) -> bool {
423        self.ids.is_empty()
424    }
425}
426
427unsafe impl DynamicBundle for EntityBuilder {
428    #[inline(always)]
429    fn valid(&self) -> bool {
430        // Validity is ensured by construction
431        true
432    }
433
434    #[inline(always)]
435    fn contains_id(&self, ty: TypeId) -> bool {
436        self.ids.iter().any(|id| *id == ty)
437    }
438
439    #[inline(always)]
440    fn with_ids<R>(&self, f: impl FnOnce(&[TypeId]) -> R) -> R {
441        f(&self.ids)
442    }
443
444    #[inline(always)]
445    fn put(self, mut f: impl FnMut(NonNull<u8>, TypeId, usize)) {
446        let me = ManuallyDrop::new(self);
447        for (info, &offset) in me.infos.iter().zip(&me.offsets) {
448            let ptr = unsafe { NonNull::new_unchecked(me.ptr.as_ptr().add(offset)) };
449            f(ptr, info.id(), info.layout().size());
450        }
451    }
452}
453
454unsafe impl DynamicComponentBundle for EntityBuilder {
455    #[inline(always)]
456    fn with_components<R>(&self, f: impl FnOnce(&[ComponentInfo]) -> R) -> R {
457        f(&self.infos)
458    }
459}
460
461/// Umbrella trait for [`DynamicBundle`] and [`Bundle`].
462pub(super) trait BundleDesc {
463    /// Returns static key if the bundle type have one.
464    fn key() -> Option<TypeId>;
465
466    /// Calls provided closure with slice of ids of types that this bundle contains.
467    fn with_ids<R>(&self, f: impl FnOnce(&[TypeId]) -> R) -> R;
468}
469
470/// Umbrella trait for [`DynamicBundle`] and [`Bundle`].
471pub(super) trait ComponentBundleDesc: BundleDesc {
472    /// Calls provided closure with slice of component types that this bundle contains.
473    fn with_components<R>(&self, f: impl FnOnce(&[ComponentInfo]) -> R) -> R;
474}
475
476impl<B> BundleDesc for B
477where
478    B: DynamicBundle,
479{
480    #[inline(always)]
481    fn key() -> Option<TypeId> {
482        <B as DynamicBundle>::key()
483    }
484
485    #[inline(always)]
486    fn with_ids<R>(&self, f: impl FnOnce(&[TypeId]) -> R) -> R {
487        DynamicBundle::with_ids(self, f)
488    }
489}
490
491impl<B> ComponentBundleDesc for B
492where
493    B: DynamicComponentBundle,
494{
495    #[inline(always)]
496    fn with_components<R>(&self, f: impl FnOnce(&[ComponentInfo]) -> R) -> R {
497        DynamicComponentBundle::with_components(self, f)
498    }
499}
500
501impl<B> BundleDesc for PhantomData<B>
502where
503    B: Bundle,
504{
505    #[inline(always)]
506    fn key() -> Option<TypeId> {
507        Some(B::static_key())
508    }
509
510    #[inline(always)]
511    fn with_ids<R>(&self, f: impl FnOnce(&[TypeId]) -> R) -> R {
512        B::static_with_ids(f)
513    }
514}
515
516impl<B> ComponentBundleDesc for PhantomData<B>
517where
518    B: ComponentBundle,
519{
520    #[inline(always)]
521    fn with_components<R>(&self, f: impl FnOnce(&[ComponentInfo]) -> R) -> R {
522        B::static_with_components(f)
523    }
524}