Skip to main content

ponko_ecs/
world.rs

1//! The [`World`]: owns every entity, component and resource.
2
3use std::alloc::Layout;
4use std::any::{Any, TypeId};
5use std::collections::HashMap;
6
7use crate::bundle::{Bundle, Component};
8use crate::commands::Commands;
9use crate::entity::{Entities, Entity, Location};
10use crate::query::{Query, QueryData};
11use crate::storage::{Archetype, ComponentId, ComponentInfo, DropFn, TypeMap, drop_ptr};
12
13struct BundleInfo {
14    archetype: usize,
15    /// Column index in the archetype for each tuple element.
16    columns: Box<[usize]>,
17}
18
19/// One chunk of entities handed to [`World::for_each_chunk`]: `len` entities
20/// and one column pointer per requested component, in request order.
21pub struct Chunk<'a> {
22    /// The entities in this chunk; row `i` of each column belongs to `entities[i]`.
23    pub entities: &'a [Entity],
24    /// Column start pointers, each pointing at `entities.len()` packed values.
25    pub columns: &'a [*mut u8],
26}
27
28impl Chunk<'_> {
29    /// Number of entities (rows) in the chunk.
30    #[must_use]
31    pub fn len(&self) -> usize {
32        self.entities.len()
33    }
34
35    /// `true` if the chunk has no rows (never true for chunks passed to callbacks).
36    #[must_use]
37    pub fn is_empty(&self) -> bool {
38        self.entities.is_empty()
39    }
40}
41
42/// Holds everything in the simulation: entities, their components, and
43/// global resources.
44///
45/// ```
46/// use ponko_ecs::*;
47///
48/// struct Pos(f32, f32);
49/// struct Vel(f32, f32);
50///
51/// let mut world = World::new();
52/// let crab = world.spawn((Pos(0.0, 0.0), Vel(1.0, 2.0)));
53///
54/// world.query::<(&mut Pos, &Vel)>().for_each(|(p, v)| {
55///     p.0 += v.0;
56///     p.1 += v.1;
57/// });
58///
59/// assert_eq!(world.get::<Pos>(crab).unwrap().1, 2.0);
60/// world.despawn(crab);
61/// assert!(world.get::<Pos>(crab).is_none());
62/// ```
63pub struct World {
64    infos: Vec<ComponentInfo>,
65    by_type: TypeMap<ComponentId>,
66    by_name: HashMap<String, ComponentId>,
67    pub(crate) archetypes: Vec<Archetype>,
68    archetype_by_ids: HashMap<Box<[ComponentId]>, usize>,
69    pub(crate) entities: Entities,
70    bundles: TypeMap<BundleInfo>,
71    resources: TypeMap<Box<dyn Any + Send + Sync>>,
72    /// Resources without a Rust type (the C and C++ path), by name.
73    dynamic_resources: HashMap<String, RawValue>,
74    scratch_ids: Vec<ComponentId>,
75}
76
77/// One value with no Rust type: plain bytes in their own allocation, so a
78/// pointer to it stays valid until the value is replaced or removed.
79struct RawValue {
80    ptr: std::ptr::NonNull<u8>,
81    layout: Layout,
82}
83
84// SAFETY: a RawValue is plain bytes with no destructor and no shared state;
85// the world hands out access to it under its own borrows.
86unsafe impl Send for RawValue {}
87unsafe impl Sync for RawValue {}
88
89impl RawValue {
90    /// A copy of `layout.size()` bytes at `data`.
91    ///
92    /// # Safety
93    /// `data` points to `layout.size()` readable bytes (any pointer for size 0).
94    unsafe fn copy_of(layout: Layout, data: *const u8) -> Self {
95        let ptr = if layout.size() == 0 {
96            // SAFETY: an alignment is never zero; nothing is read or written.
97            unsafe { std::ptr::NonNull::new_unchecked(std::ptr::without_provenance_mut(layout.align())) }
98        } else {
99            // SAFETY: the size is non-zero.
100            let p = unsafe { std::alloc::alloc(layout) };
101            std::ptr::NonNull::new(p).unwrap_or_else(|| std::alloc::handle_alloc_error(layout))
102        };
103        if layout.size() != 0 {
104            // SAFETY: the caller's bytes are readable; ours are fresh and don't overlap.
105            unsafe { std::ptr::copy_nonoverlapping(data, ptr.as_ptr(), layout.size()) };
106        }
107        RawValue { ptr, layout }
108    }
109}
110
111impl Drop for RawValue {
112    fn drop(&mut self) {
113        if self.layout.size() != 0 {
114            // SAFETY: allocated in `copy_of` with this layout.
115            unsafe { std::alloc::dealloc(self.ptr.as_ptr(), self.layout) };
116        }
117    }
118}
119
120impl Default for World {
121    fn default() -> Self {
122        Self::new()
123    }
124}
125
126impl World {
127    /// An empty world.
128    #[must_use]
129    pub fn new() -> Self {
130        let mut w = World {
131            infos: Vec::new(),
132            by_type: TypeMap::default(),
133            by_name: HashMap::new(),
134            archetypes: Vec::new(),
135            archetype_by_ids: HashMap::new(),
136            entities: Entities::default(),
137            bundles: TypeMap::default(),
138            resources: TypeMap::default(),
139            dynamic_resources: HashMap::new(),
140            scratch_ids: Vec::new(),
141        };
142        w.archetype_for(&[]); // archetype 0: entities with no components
143        w
144    }
145
146    // ---- entities ----------------------------------------------------------
147
148    /// Creates an entity with the given components and returns its handle.
149    ///
150    /// Components come as a tuple; note the trailing comma for one component:
151    /// `world.spawn((Health(10),))`.
152    pub fn spawn<B: Bundle>(&mut self, bundle: B) -> Entity {
153        let a = self.bundle_archetype::<B>();
154        let e = self.entities.alloc();
155        self.spawn_in::<B>(a, bundle, e)
156    }
157
158    /// Spawns many entities of the same shape. Faster than calling `spawn` in a loop.
159    pub fn spawn_batch<B: Bundle>(&mut self, bundles: impl IntoIterator<Item = B>) {
160        let iter = bundles.into_iter();
161        let a = self.bundle_archetype::<B>();
162        self.archetypes[a].reserve(iter.size_hint().0);
163        for b in iter {
164            let e = self.entities.alloc();
165            self.spawn_in::<B>(a, b, e);
166        }
167    }
168
169    /// Spawns an entity with exactly the handle `e`, for restoring saved
170    /// worlds so that components referring to entities stay valid. Returns
171    /// `false` (dropping `bundle`) if `e` is taken, or was despawned in this
172    /// world already (old handles stay dead).
173    ///
174    /// ```
175    /// use ponko_ecs::{Entity, World};
176    ///
177    /// let mut world = World::new();
178    /// let saved = Entity::from_bits(7 | 3 << 32); // slot 7, generation 3
179    /// assert!(world.spawn_at(saved, ("hello",)));
180    /// assert_eq!(world.get::<&str>(saved), Some(&"hello"));
181    /// assert!(!world.spawn_at(saved, ("again",))); // taken
182    /// let fresh = world.spawn(("new",)); // fresh handles never collide
183    /// assert_ne!(fresh, saved);
184    /// ```
185    pub fn spawn_at<B: Bundle>(&mut self, e: Entity, bundle: B) -> bool {
186        if !self.entities.alloc_at(e) {
187            return false;
188        }
189        let a = self.bundle_archetype::<B>();
190        self.spawn_in::<B>(a, bundle, e);
191        true
192    }
193
194    /// Memory held for entity data: component columns and handles of every
195    /// archetype, including spare capacity. Doesn't count resources or the
196    /// world's bookkeeping (per-slot generation, archetype index).
197    ///
198    /// ```
199    /// use ponko_ecs::World;
200    ///
201    /// struct Pos(f32, f32);
202    /// let mut world = World::new();
203    /// world.spawn_batch((0..1000).map(|i| (Pos(i as f32, 0.0),)));
204    /// // 8 bytes of Pos + 8 bytes of entity handle each, at least.
205    /// assert!(world.allocated_bytes() >= 1000 * 16);
206    /// ```
207    pub fn allocated_bytes(&self) -> usize {
208        self.archetypes.iter().map(Archetype::allocated_bytes).sum()
209    }
210
211    /// Despawns every entity. Resources stay.
212    ///
213    /// ```
214    /// use ponko_ecs::World;
215    ///
216    /// let mut world = World::new();
217    /// let e = world.spawn((1u8,));
218    /// world.clear();
219    /// assert!(world.is_empty() && !world.is_alive(e));
220    /// ```
221    pub fn clear(&mut self) {
222        let all: Vec<Entity> = self
223            .archetypes
224            .iter()
225            .flat_map(|a| a.entities.iter().copied())
226            .collect();
227        for e in all {
228            self.despawn(e);
229        }
230    }
231
232    /// Despawns every entity **and forgets every entity id**, as if the world
233    /// were new; resources and registered components stay. Afterwards, old
234    /// handles may match new entities, so use this only when replacing the
235    /// whole world, such as when loading a save (whose ids then come back
236    /// with [`spawn_at`](Self::spawn_at)). For everything else use
237    /// [`clear`](Self::clear).
238    ///
239    /// ```
240    /// use ponko_ecs::World;
241    ///
242    /// let mut world = World::new();
243    /// let e = world.spawn((1u8,));
244    /// world.reset();
245    /// assert!(world.spawn_at(e, (2u8,))); // the id is free again
246    /// ```
247    pub fn reset(&mut self) {
248        self.clear();
249        self.entities = Default::default();
250    }
251
252    #[inline]
253    fn spawn_in<B: Bundle>(&mut self, a: usize, bundle: B, e: Entity) -> Entity {
254        let cols = &self.bundles[&TypeId::of::<B>()].columns;
255        let arch = &mut self.archetypes[a];
256        arch.reserve(1);
257        let row = arch.len();
258        unsafe {
259            bundle.write(&mut |i, src| {
260                let c = &arch.columns[cols[i]];
261                std::ptr::copy_nonoverlapping(src, c.ptr(row), c.size());
262            });
263        }
264        arch.entities.push(e);
265        self.entities.set(
266            e,
267            Location {
268                archetype: a as u32,
269                row: row as u32,
270            },
271        );
272        e
273    }
274
275    /// Removes an entity and drops its components. Returns `false` if it was
276    /// already gone.
277    pub fn despawn(&mut self, e: Entity) -> bool {
278        let Some(loc) = self.entities.get(e) else { return false };
279        // Free the slot first: if a component's drop panics, the entity is already gone.
280        self.entities.free(e);
281        let moved = unsafe { self.archetypes[loc.archetype as usize].swap_remove_drop(loc.row as usize) };
282        if let Some(moved) = moved {
283            self.entities.set_row(moved, loc.row as usize);
284        }
285        true
286    }
287
288    /// `true` if `e` hasn't been despawned.
289    #[must_use]
290    pub fn is_alive(&self, e: Entity) -> bool {
291        self.entities.get(e).is_some()
292    }
293
294    /// Number of live entities.
295    #[must_use]
296    pub fn len(&self) -> usize {
297        self.entities.len()
298    }
299
300    /// `true` if there are no live entities.
301    #[must_use]
302    pub fn is_empty(&self) -> bool {
303        self.len() == 0
304    }
305
306    // ---- single-entity access -----------------------------------------------
307
308    /// Component `T` of entity `e`, or `None` if `e` is dead or doesn't have one.
309    #[must_use]
310    pub fn get<T: Component>(&self, e: Entity) -> Option<&T> {
311        let ptr = self.get_dynamic(e, self.component_id::<T>()?)?;
312        Some(unsafe { &*ptr.cast::<T>() })
313    }
314
315    /// Mutable version of [`get`](Self::get).
316    pub fn get_mut<T: Component>(&mut self, e: Entity) -> Option<&mut T> {
317        let ptr = self.get_dynamic(e, self.component_id::<T>()?)?;
318        Some(unsafe { &mut *ptr.cast::<T>() })
319    }
320
321    /// `true` if `e` is alive and has component `T`.
322    #[must_use]
323    pub fn has<T: Component>(&self, e: Entity) -> bool {
324        self.component_id::<T>()
325            .is_some_and(|id| self.get_dynamic(e, id).is_some())
326    }
327
328    /// Adds component `value` to `e`, replacing any existing `T`.
329    /// Returns `false` (and drops `value`) if `e` is dead.
330    pub fn insert<T: Component>(&mut self, e: Entity, value: T) -> bool {
331        let id = self.register::<T>();
332        let value = std::mem::ManuallyDrop::new(value);
333        let ok = unsafe { self.insert_dynamic(e, id, (&*value as *const T).cast()) };
334        if !ok {
335            drop(std::mem::ManuallyDrop::into_inner(value));
336        }
337        ok
338    }
339
340    /// Takes component `T` off `e` and returns it.
341    pub fn remove<T: Component>(&mut self, e: Entity) -> Option<T> {
342        let id = self.component_id::<T>()?;
343        let loc = self.entities.get(e)?;
344        let arch = &self.archetypes[loc.archetype as usize];
345        let col = arch.column_index(id)?;
346        let value = unsafe { arch.columns[col].ptr(loc.row as usize).cast::<T>().read() };
347        self.move_without(e, loc, id);
348        Some(value)
349    }
350
351    // ---- queries -------------------------------------------------------------
352
353    /// Loops over every entity that has the components in `Q`.
354    ///
355    /// `Q` is `&T`, `&mut T`, `Option<&T>`, `Option<&mut T>`, `Entity`, or a
356    /// tuple of those. See [`Query`] for filters and iterators.
357    ///
358    /// # Panics
359    /// If `Q` asks for the same component twice and at least once mutably.
360    pub fn query<Q: QueryData>(&mut self) -> Query<'_, Q> {
361        Query::new(self)
362    }
363
364    /// Reorders entities in memory so that, within each archetype that has
365    /// component `T`, rows are sorted by `key(&T)` (ascending; ties keep no
366    /// particular order). Handles and component values don't change, only
367    /// the order queries visit them in.
368    ///
369    /// Use it to put entities that are processed together next to each
370    /// other: sorting by a spatial cell key every few dozen ticks makes
371    /// neighbor queries hit the CPU cache instead of main memory.
372    ///
373    /// ```
374    /// # use ponko_ecs::*;
375    /// struct Pos(f32);
376    /// let mut world = World::new();
377    /// for x in [3.0, 1.0, 2.0] {
378    ///     world.spawn((Pos(x),));
379    /// }
380    /// world.sort_by::<Pos>(|p| p.0 as u64);
381    /// let order: Vec<f32> = world.query::<&Pos>().iter().map(|p| p.0).collect();
382    /// assert_eq!(order, [1.0, 2.0, 3.0]);
383    /// ```
384    pub fn sort_by<T: Component>(&mut self, key: impl Fn(&T) -> u64 + Sync) {
385        let Some(id) = self.component_id::<T>() else { return };
386        let mut keyed: Vec<(u64, u32)> = Vec::new();
387        for a in 0..self.archetypes.len() {
388            let arch = &self.archetypes[a];
389            let Some(base) = arch.column_ptr(id) else { continue };
390            let len = arch.len();
391            if len < 2 {
392                continue;
393            }
394            let values = unsafe { std::slice::from_raw_parts(base as *const T, len) };
395            keyed.clear();
396            keyed.extend(values.iter().enumerate().map(|(row, v)| (key(v), row as u32)));
397            if keyed.windows(2).all(|w| w[0].0 <= w[1].0) {
398                continue; // already in order
399            }
400            sort_keys(&mut keyed);
401            let perm: Vec<u32> = keyed.iter().map(|&(_, row)| row).collect();
402            let arch = &mut self.archetypes[a];
403            arch.permute(&perm);
404            for (row, &e) in arch.entities.iter().enumerate() {
405                self.entities.set_row(e, row);
406            }
407        }
408    }
409
410    // ---- resources -----------------------------------------------------------
411
412    /// Stores a global value (game settings, a random generator, the score...),
413    /// replacing any existing value of the same type.
414    pub fn insert_resource<R: Send + Sync + 'static>(&mut self, value: R) {
415        self.resources.insert(TypeId::of::<R>(), Box::new(value));
416    }
417
418    /// The resource of type `R`.
419    ///
420    /// # Panics
421    /// If there is none; use [`get_resource`](Self::get_resource) to check.
422    #[track_caller]
423    #[must_use]
424    pub fn resource<R: Send + Sync + 'static>(&self) -> &R {
425        self.get_resource().unwrap_or_else(|| missing_resource::<R>())
426    }
427
428    /// Mutable version of [`resource`](Self::resource).
429    #[track_caller]
430    pub fn resource_mut<R: Send + Sync + 'static>(&mut self) -> &mut R {
431        self.get_resource_mut().unwrap_or_else(|| missing_resource::<R>())
432    }
433
434    /// The resource of type `R`, if there is one.
435    #[must_use]
436    pub fn get_resource<R: Send + Sync + 'static>(&self) -> Option<&R> {
437        self.resources
438            .get(&TypeId::of::<R>())
439            .map(|r| r.downcast_ref().unwrap())
440    }
441
442    /// Mutable version of [`get_resource`](Self::get_resource).
443    pub fn get_resource_mut<R: Send + Sync + 'static>(&mut self) -> Option<&mut R> {
444        self.resources
445            .get_mut(&TypeId::of::<R>())
446            .map(|r| r.downcast_mut().unwrap())
447    }
448
449    /// Removes and returns the resource of type `R`.
450    pub fn remove_resource<R: Send + Sync + 'static>(&mut self) -> Option<R> {
451        self.resources
452            .remove(&TypeId::of::<R>())
453            .map(|r| *r.downcast().unwrap())
454    }
455
456    /// Stores a resource that has no Rust type, by name: a copy of
457    /// `layout.size()` bytes from `value`, replacing any resource of that
458    /// name. This is how C and C++ games keep global values.
459    ///
460    /// ```
461    /// use ponko_ecs::*;
462    /// use std::alloc::Layout;
463    ///
464    /// let mut world = World::new();
465    /// let score: [u32; 2] = [3, 1];
466    /// // SAFETY: `score` is 8 readable bytes.
467    /// unsafe { world.insert_resource_dynamic("Score", Layout::new::<[u32; 2]>(), score.as_ptr().cast()) };
468    /// let p = world.get_resource_dynamic("Score").unwrap() as *const [u32; 2];
469    /// // SAFETY: the resource holds the bytes of a [u32; 2].
470    /// assert_eq!(unsafe { *p }, [3, 1]);
471    /// ```
472    ///
473    /// # Safety
474    /// `value` points to `layout.size()` readable bytes (any pointer for size 0).
475    ///
476    /// # Panics
477    /// If a resource of that name exists with a different layout.
478    #[track_caller]
479    pub unsafe fn insert_resource_dynamic(&mut self, name: &str, layout: Layout, value: *const u8) {
480        if let Some(old) = self.dynamic_resources.get(name) {
481            assert!(
482                old.layout == layout,
483                "insert_resource_dynamic: `{name}` exists with a different size or alignment"
484            );
485        }
486        // SAFETY: forwarded from the caller.
487        let raw = unsafe { RawValue::copy_of(layout, value) };
488        self.dynamic_resources.insert(name.to_string(), raw);
489    }
490
491    /// Pointer to resource `name` (one stored with
492    /// [`insert_resource_dynamic`](Self::insert_resource_dynamic)), or `None`.
493    /// It stays valid until the resource is replaced or removed.
494    #[must_use]
495    pub fn get_resource_dynamic(&self, name: &str) -> Option<*mut u8> {
496        self.dynamic_resources.get(name).map(|r| r.ptr.as_ptr())
497    }
498
499    /// The size and alignment of resource `name`, if it exists.
500    #[must_use]
501    pub fn resource_layout_dynamic(&self, name: &str) -> Option<Layout> {
502        self.dynamic_resources.get(name).map(|r| r.layout)
503    }
504
505    /// Removes resource `name`; `false` if there was none.
506    pub fn remove_resource_dynamic(&mut self, name: &str) -> bool {
507        self.dynamic_resources.remove(name).is_some()
508    }
509
510    // ---- commands --------------------------------------------------------------
511
512    /// Applies commands recorded while the world was busy (e.g. during a query).
513    /// Leaves `commands` empty and ready to reuse.
514    pub fn apply(&mut self, commands: &mut Commands) {
515        commands.apply(self);
516    }
517
518    // ---- components (also the C/C++ path) ---------------------------------------
519
520    /// The id of Rust component `T`, registering it on first use.
521    pub fn register<T: Component>(&mut self) -> ComponentId {
522        if let Some(&id) = self.by_type.get(&TypeId::of::<T>()) {
523            return id;
524        }
525        let drop = std::mem::needs_drop::<T>().then_some(drop_ptr::<T> as DropFn);
526        let id = self.push_info(ComponentInfo {
527            name: std::any::type_name::<T>().into(),
528            layout: Layout::new::<T>(),
529            drop,
530            type_id: Some(TypeId::of::<T>()),
531        });
532        self.by_type.insert(TypeId::of::<T>(), id);
533        id
534    }
535
536    /// Registers a component known only by name, size and alignment: the path
537    /// for components defined in C/C++ or loaded at runtime.
538    ///
539    /// Registering the same name again with the same layout returns the same id.
540    ///
541    /// # Safety
542    /// Values of this component must be safe to move with `memcpy`, and to send
543    /// and share between threads. `drop`, if given, must accept any value
544    /// written as this component.
545    ///
546    /// # Panics
547    /// If the name is taken with a different layout, or the layout is invalid.
548    pub unsafe fn register_component(
549        &mut self,
550        name: &str,
551        size: usize,
552        align: usize,
553        drop: Option<DropFn>,
554    ) -> ComponentId {
555        let layout = Layout::from_size_align(size, align).expect("invalid component size/alignment");
556        if let Some(&id) = self.by_name.get(name) {
557            let info = &self.infos[id.index()];
558            assert!(
559                info.layout == layout && info.type_id.is_none(),
560                "component name `{name}` is already registered with a different layout"
561            );
562            return id;
563        }
564        self.push_info(ComponentInfo {
565            name: name.into(),
566            layout,
567            drop,
568            type_id: None,
569        })
570    }
571
572    fn push_info(&mut self, info: ComponentInfo) -> ComponentId {
573        let id = ComponentId(u32::try_from(self.infos.len()).expect("too many component types"));
574        self.by_name.entry(info.name.clone()).or_insert(id);
575        self.infos.push(info);
576        id
577    }
578
579    /// The id of Rust component `T`, if it has been used in this world.
580    #[inline]
581    #[must_use]
582    pub fn component_id<T: Component>(&self) -> Option<ComponentId> {
583        self.by_type.get(&TypeId::of::<T>()).copied()
584    }
585
586    /// Looks a component up by name (registered name, or Rust type name).
587    #[must_use]
588    pub fn component_by_name(&self, name: &str) -> Option<ComponentId> {
589        self.by_name.get(name).copied()
590    }
591
592    /// The component with number `index` ([`ComponentId::index`]), if registered.
593    ///
594    /// ```
595    /// let mut world = ponko_ecs::World::new();
596    /// world.spawn((1.5f32,));
597    /// let id = world.component_id::<f32>().unwrap();
598    /// assert_eq!(world.component_at(id.index()), Some(id));
599    /// assert_eq!(world.component_at(99), None);
600    /// ```
601    #[must_use]
602    pub fn component_at(&self, index: usize) -> Option<ComponentId> {
603        (index < self.infos.len()).then_some(ComponentId(index as u32))
604    }
605
606    /// What the world knows about component `id`.
607    ///
608    /// # Panics
609    /// If `id` came from a different world.
610    #[must_use]
611    pub fn component_info(&self, id: ComponentId) -> &ComponentInfo {
612        &self.infos[id.index()]
613    }
614
615    /// Every component type registered so far, indexed by [`ComponentId::index`].
616    #[must_use]
617    pub fn components(&self) -> &[ComponentInfo] {
618        &self.infos
619    }
620
621    /// The archetype tables, for tools such as the inspector.
622    #[must_use]
623    pub fn archetypes(&self) -> &[Archetype] {
624        &self.archetypes
625    }
626
627    /// The components entity `e` has (sorted by id), or `None` if it is dead.
628    ///
629    /// ```
630    /// # use ponko_ecs::*;
631    /// struct Pos(f32);
632    /// struct Vel(f32);
633    /// let mut world = World::new();
634    /// let e = world.spawn((Pos(0.0), Vel(1.0)));
635    /// let names: Vec<&str> = world.components_of(e).unwrap()
636    ///     .iter()
637    ///     .map(|&id| world.component_info(id).name())
638    ///     .collect();
639    /// assert_eq!(names.len(), 2);
640    /// world.despawn(e);
641    /// assert!(world.components_of(e).is_none());
642    /// ```
643    #[must_use]
644    pub fn components_of(&self, e: Entity) -> Option<&[ComponentId]> {
645        let loc = self.entities.get(e)?;
646        Some(self.archetypes[loc.archetype as usize].component_ids())
647    }
648
649    /// Pointer to component `id` of entity `e`, or `None`.
650    #[must_use]
651    pub fn get_dynamic(&self, e: Entity, id: ComponentId) -> Option<*mut u8> {
652        let loc = self.entities.get(e)?;
653        let arch = &self.archetypes[loc.archetype as usize];
654        let col = arch.column_index(id)?;
655        Some(arch.columns[col].ptr(loc.row as usize))
656    }
657
658    /// Spawns an entity from raw component values.
659    ///
660    /// # Safety
661    /// Each pointer must point to a valid value of its component; the world
662    /// takes ownership (the caller must not drop it). Ids must be from this world.
663    ///
664    /// # Panics
665    /// If a component id appears twice.
666    pub unsafe fn spawn_dynamic(&mut self, components: &[(ComponentId, *const u8)]) -> Entity {
667        let a = self.dynamic_archetype(components, "spawn_dynamic");
668        let e = self.entities.alloc();
669        // SAFETY: forwarded caller contract; `e` was just allocated.
670        unsafe { self.write_dynamic(a, e, components) };
671        e
672    }
673
674    /// [`spawn_dynamic`](Self::spawn_dynamic) with a given id, like
675    /// [`spawn_at`](Self::spawn_at): returns `false` (and takes nothing) if
676    /// that slot is taken.
677    ///
678    /// ```
679    /// use ponko_ecs::{Entity, World};
680    ///
681    /// let mut world = World::new();
682    /// // SAFETY: f32 is plain bytes.
683    /// let id = unsafe { world.register_component("Speed", 4, 4, None) };
684    /// let saved = Entity::from_bits(5 | 2 << 32);
685    /// let speed = 1.5f32;
686    /// // SAFETY: points to a valid f32, which needs no drop.
687    /// assert!(unsafe { world.spawn_at_dynamic(saved, &[(id, (&speed as *const f32).cast())]) });
688    /// assert!(!unsafe { world.spawn_at_dynamic(saved, &[(id, (&speed as *const f32).cast())]) });
689    /// assert_eq!(world.len(), 1);
690    /// ```
691    ///
692    /// # Safety
693    /// As for [`spawn_dynamic`](Self::spawn_dynamic). When this returns
694    /// `false`, the values stay the caller's.
695    ///
696    /// # Panics
697    /// If a component id appears twice.
698    pub unsafe fn spawn_at_dynamic(&mut self, e: Entity, components: &[(ComponentId, *const u8)]) -> bool {
699        let a = self.dynamic_archetype(components, "spawn_at_dynamic");
700        if !self.entities.alloc_at(e) {
701            return false;
702        }
703        // SAFETY: forwarded caller contract; `e` was just allocated.
704        unsafe { self.write_dynamic(a, e, components) };
705        true
706    }
707
708    /// The archetype holding exactly these components (panics on duplicates).
709    fn dynamic_archetype(&mut self, components: &[(ComponentId, *const u8)], caller: &str) -> usize {
710        let mut ids = std::mem::take(&mut self.scratch_ids);
711        ids.clear();
712        ids.extend(components.iter().map(|c| c.0));
713        ids.sort_unstable();
714        let unique = ids.windows(2).all(|w| w[0] != w[1]);
715        let a = if unique { Some(self.archetype_for(&ids)) } else { None };
716        self.scratch_ids = ids;
717        a.unwrap_or_else(|| panic!("{caller}: the same component was given twice"))
718    }
719
720    /// Appends freshly allocated `e` to archetype `a` with these values.
721    ///
722    /// # Safety
723    /// `a` is the archetype of exactly these ids, `e` is allocated and has no
724    /// location yet, and each pointer points to a valid value of its component.
725    unsafe fn write_dynamic(&mut self, a: usize, e: Entity, components: &[(ComponentId, *const u8)]) {
726        let arch = &mut self.archetypes[a];
727        arch.reserve(1);
728        let row = arch.len();
729        for &(id, src) in components {
730            let c = &arch.columns[arch.column_index(id).unwrap()];
731            // SAFETY: the caller's value fits the column (same component), and
732            // `row` is reserved spare capacity.
733            unsafe { std::ptr::copy_nonoverlapping(src, c.ptr(row), c.size()) };
734        }
735        arch.entities.push(e);
736        self.entities.set(
737            e,
738            Location {
739                archetype: a as u32,
740                row: row as u32,
741            },
742        );
743    }
744
745    /// Adds (or replaces) component `id` on `e` from a raw value.
746    /// Returns `false` if `e` is dead; the value is then still the caller's.
747    ///
748    /// # Safety
749    /// `value` must point to a valid value of component `id`; on success the
750    /// world takes ownership of it.
751    pub unsafe fn insert_dynamic(&mut self, e: Entity, id: ComponentId, value: *const u8) -> bool {
752        let Some(loc) = self.entities.get(e) else { return false };
753        let src = loc.archetype as usize;
754        if let Some(col) = self.archetypes[src].column_index(id) {
755            let c = &self.archetypes[src].columns[col];
756            let dst = c.ptr(loc.row as usize);
757            unsafe {
758                if let Some(d) = c.drop_fn() {
759                    d(dst);
760                }
761                std::ptr::copy_nonoverlapping(value, dst, c.size());
762            }
763            return true;
764        }
765        let dst = match self.archetypes[src].add_edges.get(&id) {
766            Some(&d) => d,
767            None => {
768                let mut ids = self.archetypes[src].ids.to_vec();
769                ids.push(id);
770                ids.sort_unstable();
771                let d = self.archetype_for(&ids);
772                self.archetypes[src].add_edges.insert(id, d);
773                self.archetypes[d].remove_edges.insert(id, src);
774                d
775            }
776        };
777        let row = self.move_row(e, loc, dst);
778        let arch = &self.archetypes[dst];
779        let c = &arch.columns[arch.column_index(id).unwrap()];
780        unsafe { std::ptr::copy_nonoverlapping(value, c.ptr(row), c.size()) };
781        true
782    }
783
784    /// Removes component `id` from `e`, dropping the value. Returns `false` if
785    /// `e` is dead or didn't have it.
786    pub fn remove_dynamic(&mut self, e: Entity, id: ComponentId) -> bool {
787        let Some(loc) = self.entities.get(e) else { return false };
788        let arch = &self.archetypes[loc.archetype as usize];
789        let Some(col) = arch.column_index(id) else { return false };
790        let c = &arch.columns[col];
791        let Some(drop) = c.drop_fn() else {
792            self.move_without(e, loc, id);
793            return true;
794        };
795        // Move the value out first, then drop it: if drop panics, the world is consistent.
796        let ptr = c.ptr(loc.row as usize);
797        let mut tmp = TempValue::new(self.infos[id.index()].layout);
798        unsafe { std::ptr::copy_nonoverlapping(ptr, tmp.ptr(), c.size()) };
799        self.move_without(e, loc, id);
800        unsafe { drop(tmp.ptr()) };
801        true
802    }
803
804    /// Calls `f` once per archetype that has all of `ids`, with raw column
805    /// pointers in `ids` order. This is how C/C++ systems see the world: whole
806    /// columns, never one call per entity.
807    ///
808    /// # Panics
809    ///
810    /// With more than 32 component ids.
811    pub fn for_each_chunk(&mut self, ids: &[ComponentId], mut f: impl FnMut(Chunk<'_>)) {
812        const MAX: usize = 32;
813        assert!(ids.len() <= MAX, "for_each_chunk: at most {MAX} components");
814        let mut ptrs = [std::ptr::null_mut(); MAX];
815        'arch: for arch in self.archetypes.iter() {
816            if arch.is_empty() {
817                continue;
818            }
819            for (i, &id) in ids.iter().enumerate() {
820                match arch.column_ptr(id) {
821                    Some(p) => ptrs[i] = p,
822                    None => continue 'arch,
823                }
824            }
825            f(Chunk {
826                entities: &arch.entities,
827                columns: &ptrs[..ids.len()],
828            });
829        }
830    }
831
832    // ---- internals -------------------------------------------------------------
833
834    /// `ids` must be sorted and unique.
835    fn archetype_for(&mut self, ids: &[ComponentId]) -> usize {
836        if let Some(&a) = self.archetype_by_ids.get(ids) {
837            return a;
838        }
839        let a = self.archetypes.len();
840        self.archetypes.push(Archetype::new(ids.into(), &self.infos));
841        self.archetype_by_ids.insert(ids.into(), a);
842        a
843    }
844
845    fn bundle_archetype<B: Bundle>(&mut self) -> usize {
846        if let Some(b) = self.bundles.get(&TypeId::of::<B>()) {
847            return b.archetype;
848        }
849        let mut ids = Vec::new();
850        B::component_ids(self, &mut ids);
851        let mut sorted = ids.clone();
852        sorted.sort_unstable();
853        sorted.dedup();
854        assert_eq!(
855            sorted.len(),
856            ids.len(),
857            "bundle `{}` has the same component twice",
858            std::any::type_name::<B>()
859        );
860        let a = self.archetype_for(&sorted);
861        let columns = ids
862            .iter()
863            .map(|&id| self.archetypes[a].column_index(id).unwrap())
864            .collect();
865        self.bundles
866            .insert(TypeId::of::<B>(), BundleInfo { archetype: a, columns });
867        a
868    }
869
870    /// Moves `e` to the archetype without component `id`. The value of `id`
871    /// is forgotten: the caller must have read or copied it out.
872    fn move_without(&mut self, e: Entity, loc: Location, id: ComponentId) {
873        let src = loc.archetype as usize;
874        let dst = match self.archetypes[src].remove_edges.get(&id) {
875            Some(&d) => d,
876            None => {
877                let ids: Vec<_> = self.archetypes[src].ids.iter().copied().filter(|&c| c != id).collect();
878                let d = self.archetype_for(&ids);
879                self.archetypes[src].remove_edges.insert(id, d);
880                self.archetypes[d].add_edges.insert(id, src);
881                d
882            }
883        };
884        self.move_row(e, loc, dst);
885    }
886
887    /// Moves `e`'s row to archetype `dst`, copying shared columns and
888    /// forgetting the rest. Returns the new row; columns of `dst` that the
889    /// source lacks are left uninitialized for the caller to fill.
890    fn move_row(&mut self, e: Entity, loc: Location, dst: usize) -> usize {
891        let src = loc.archetype as usize;
892        let row = loc.row as usize;
893        let (s, d) = two_mut(&mut self.archetypes, src, dst);
894        d.reserve(1);
895        let new_row = d.len();
896        let last = s.len() - 1;
897        for (i, col) in s.columns.iter_mut().enumerate() {
898            if let Some(j) = d.column_index(s.ids[i]) {
899                unsafe { std::ptr::copy_nonoverlapping(col.ptr(row), d.columns[j].ptr(new_row), col.size()) };
900            }
901            unsafe { col.swap_remove_forget(row, last) };
902        }
903        s.entities.swap_remove(row);
904        if row != last {
905            let moved = s.entities[row];
906            self.entities.set_row(moved, row);
907        }
908        d.entities.push(e);
909        self.entities.set(
910            e,
911            Location {
912                archetype: dst as u32,
913                row: new_row as u32,
914            },
915        );
916        new_row
917    }
918}
919
920fn sort_keys(keyed: &mut [(u64, u32)]) {
921    #[cfg(feature = "parallel")]
922    if keyed.len() > 50_000 {
923        use rayon::slice::ParallelSliceMut;
924        keyed.par_sort_unstable_by_key(|k| k.0);
925        return;
926    }
927    keyed.sort_unstable_by_key(|k| k.0);
928}
929
930#[cold]
931#[track_caller]
932fn missing_resource<R>() -> ! {
933    panic!(
934        "no resource of type `{0}`; add one first with `world.insert_resource(...)`",
935        std::any::type_name::<R>()
936    )
937}
938
939fn two_mut<T>(v: &mut [T], a: usize, b: usize) -> (&mut T, &mut T) {
940    assert_ne!(a, b);
941    if a < b {
942        let (l, r) = v.split_at_mut(b);
943        (&mut l[a], &mut r[0])
944    } else {
945        let (l, r) = v.split_at_mut(a);
946        (&mut r[0], &mut l[b])
947    }
948}
949
950/// A heap slot for one value of a runtime layout (freed without dropping).
951struct TempValue {
952    ptr: *mut u8,
953    layout: Layout,
954}
955
956impl TempValue {
957    fn new(layout: Layout) -> Self {
958        let ptr = if layout.size() == 0 {
959            std::ptr::without_provenance_mut(layout.align())
960        } else {
961            let p = unsafe { std::alloc::alloc(layout) };
962            if p.is_null() {
963                std::alloc::handle_alloc_error(layout);
964            }
965            p
966        };
967        TempValue { ptr, layout }
968    }
969
970    fn ptr(&mut self) -> *mut u8 {
971        self.ptr
972    }
973}
974
975impl Drop for TempValue {
976    fn drop(&mut self) {
977        if self.layout.size() != 0 {
978            unsafe { std::alloc::dealloc(self.ptr, self.layout) }
979        }
980    }
981}