Skip to main content

proof_engine/ecs/
mod.rs

1//! # Entity Component System (ECS)
2//!
3//! A standalone archetype-based ECS for the Proof Engine.
4//! All types live in this single module — no external submodules required.
5//!
6//! ## Design
7//!
8//! Components are grouped by type signature into `Archetype` tables for
9//! cache-friendly iteration. Fetch markers (`Read<T>`, `Write<T>`,
10//! `OptionRead<T>`) carry no lifetimes so they satisfy `WorldQuery: 'static`.
11//!
12//! ## Quick start
13//!
14//! ```rust,ignore
15//! use proof_engine::ecs::*;
16//!
17//! #[derive(Clone)] struct Pos(f32, f32);
18//! impl Component for Pos {}
19//!
20//! let mut world = World::new();
21//! let e = world.spawn(Pos(0.0, 0.0));
22//! for pos in world.query::<Read<Pos>, ()>() { let _ = pos.0; }
23//! ```
24
25#![allow(dead_code)]
26
27use std::{any::{Any, TypeId}, collections::HashMap};
28
29// ---------------------------------------------------------------------------
30// Component trait
31// ---------------------------------------------------------------------------
32
33/// Marker trait for ECS components. Implement this for any `'static + Send +
34/// Sync + Clone` type to use it as a component.
35pub trait Component: 'static + Send + Sync + Clone {}
36
37// ---------------------------------------------------------------------------
38// Entity (generational ID)
39// ---------------------------------------------------------------------------
40
41/// A lightweight generational handle identifying a live entity.
42///
43/// `index` is the slot in the allocator; `generation` differentiates reused
44/// slots so stale handles can be detected.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub struct Entity {
47    /// Slot index.
48    pub index: u32,
49    /// Generation counter (incremented on free).
50    pub generation: u32,
51}
52
53impl Entity {
54    /// Constructs an entity from raw parts.
55    #[inline] pub fn from_raw(index: u32, generation: u32) -> Self { Self { index, generation } }
56    /// Returns the null sentinel (never alive).
57    #[inline] pub fn null() -> Self { Self { index: u32::MAX, generation: u32::MAX } }
58    /// True if this is the null sentinel.
59    #[inline] pub fn is_null(self) -> bool { self.index == u32::MAX }
60}
61
62impl std::fmt::Display for Entity {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        write!(f, "Entity({}v{})", self.index, self.generation)
65    }
66}
67
68// ---------------------------------------------------------------------------
69// Entity allocator
70// ---------------------------------------------------------------------------
71
72#[derive(Debug, Clone)]
73struct EntityRecord { generation: u32, location: Option<EntityLocation> }
74
75#[derive(Debug, Clone, Copy)]
76struct EntityLocation { archetype_id: ArchetypeId, row: usize }
77
78#[derive(Debug, Default)]
79struct EntityAllocator { records: Vec<EntityRecord>, free: Vec<u32> }
80
81impl EntityAllocator {
82    fn alloc(&mut self) -> Entity {
83        if let Some(idx) = self.free.pop() {
84            let gen = self.records[idx as usize].generation;
85            Entity { index: idx, generation: gen }
86        } else {
87            let idx = self.records.len() as u32;
88            self.records.push(EntityRecord { generation: 0, location: None });
89            Entity { index: idx, generation: 0 }
90        }
91    }
92    fn free(&mut self, e: Entity) {
93        let r = &mut self.records[e.index as usize];
94        r.generation = r.generation.wrapping_add(1);
95        r.location = None;
96        self.free.push(e.index);
97    }
98    fn is_alive(&self, e: Entity) -> bool {
99        self.records.get(e.index as usize)
100            .map(|r| r.generation == e.generation && r.location.is_some())
101            .unwrap_or(false)
102    }
103    fn location(&self, e: Entity) -> Option<EntityLocation> {
104        self.records.get(e.index as usize)
105            .filter(|r| r.generation == e.generation)
106            .and_then(|r| r.location)
107    }
108    fn set_location(&mut self, e: Entity, loc: EntityLocation) {
109        if let Some(r) = self.records.get_mut(e.index as usize) {
110            if r.generation == e.generation { r.location = Some(loc); }
111        }
112    }
113}
114
115// ---------------------------------------------------------------------------
116// AnyVec — type-erased dense vector
117// ---------------------------------------------------------------------------
118
119struct AnyVec {
120    type_id: TypeId,
121    len: usize,
122    data: Vec<u8>,
123    element_size: usize,
124    element_align: usize,
125    drop_fn: unsafe fn(*mut u8),
126    clone_fn: unsafe fn(*const u8, *mut u8),
127}
128unsafe impl Send for AnyVec {}
129unsafe impl Sync for AnyVec {}
130
131impl AnyVec {
132    fn new<T: Component>() -> Self {
133        let align = std::mem::align_of::<T>();
134        let size = std::mem::size_of::<T>();
135        // Round element_size up to alignment so every slot is aligned
136        let padded = if align > 0 && size > 0 { (size + align - 1) & !(align - 1) } else { size };
137        Self {
138            type_id: TypeId::of::<T>(),
139            len: 0,
140            data: Vec::new(),
141            element_size: padded,
142            element_align: align,
143            drop_fn: |p| unsafe { std::ptr::drop_in_place(p as *mut T) },
144            clone_fn: |s, d| unsafe { std::ptr::write(d as *mut T, (*(s as *const T)).clone()) },
145        }
146    }
147
148    fn reserve_one(&mut self) {
149        let sz = self.element_size;
150        if sz == 0 { return; }
151        let need = (self.len + 1) * sz + self.element_align; // extra for alignment padding
152        if self.data.len() < need {
153            self.data.resize(need.max(self.data.len() * 2).max(sz * 4 + self.element_align), 0);
154        }
155    }
156
157    /// Return the base pointer, aligned to element_align.
158    fn aligned_base(&self) -> *const u8 {
159        let ptr = self.data.as_ptr() as usize;
160        let align = self.element_align.max(1);
161        let aligned = (ptr + align - 1) & !(align - 1);
162        aligned as *const u8
163    }
164
165    fn aligned_base_mut(&mut self) -> *mut u8 {
166        let ptr = self.data.as_mut_ptr() as usize;
167        let align = self.element_align.max(1);
168        let aligned = (ptr + align - 1) & !(align - 1);
169        aligned as *mut u8
170    }
171
172    fn push<T: Component>(&mut self, v: T) {
173        self.reserve_one();
174        unsafe { std::ptr::write(self.aligned_base_mut().add(self.len * self.element_size) as *mut T, v); }
175        self.len += 1;
176    }
177
178    fn get<T: Component>(&self, row: usize) -> &T {
179        unsafe { &*(self.aligned_base().add(row * self.element_size) as *const T) }
180    }
181
182    fn get_mut<T: Component>(&mut self, row: usize) -> &mut T {
183        unsafe { &mut *(self.aligned_base_mut().add(row * self.element_size) as *mut T) }
184    }
185
186    fn get_ptr(&self, row: usize) -> *const u8 {
187        unsafe { self.aligned_base().add(row * self.element_size) }
188    }
189
190    fn swap_remove_raw(&mut self, row: usize) {
191        let last = self.len - 1;
192        let sz = self.element_size;
193        unsafe {
194            let base = self.aligned_base_mut();
195            let dst = base.add(row * sz);
196            (self.drop_fn)(dst);
197            if row != last {
198                let src = base.add(last * sz) as *const u8;
199                std::ptr::copy_nonoverlapping(src, dst, sz);
200            }
201        }
202        self.len -= 1;
203    }
204}
205
206impl Drop for AnyVec {
207    fn drop(&mut self) {
208        for i in 0..self.len {
209            unsafe { (self.drop_fn)(self.aligned_base_mut().add(i * self.element_size)); }
210        }
211    }
212}
213
214// ---------------------------------------------------------------------------
215// ComponentStorage — column + change-detection ticks
216// ---------------------------------------------------------------------------
217
218struct ComponentStorage {
219    column: AnyVec,
220    added_ticks: Vec<u32>,
221    changed_ticks: Vec<u32>,
222}
223
224impl ComponentStorage {
225    fn new<T: Component>() -> Self {
226        Self { column: AnyVec::new::<T>(), added_ticks: Vec::new(), changed_ticks: Vec::new() }
227    }
228
229    fn push<T: Component>(&mut self, v: T, tick: u32) {
230        self.column.push(v);
231        self.added_ticks.push(tick);
232        self.changed_ticks.push(tick);
233    }
234
235    fn swap_remove(&mut self, row: usize) {
236        self.column.swap_remove_raw(row);
237        let last = self.added_ticks.len() - 1;
238        self.added_ticks.swap(row, last); self.added_ticks.pop();
239        let last = self.changed_ticks.len() - 1;
240        self.changed_ticks.swap(row, last); self.changed_ticks.pop();
241    }
242
243    /// Clone row `row` from this storage into `dst`, appending it.
244    fn clone_row_into(&self, row: usize, dst: &mut ComponentStorage, tick: u32) {
245        let sz = self.column.element_size;
246        let need = (dst.column.len + 1) * sz;
247        if dst.column.data.len() < need {
248            dst.column.data.resize(need.max(dst.column.data.len() * 2).max(sz * 4), 0);
249        }
250        unsafe {
251            let s = self.column.aligned_base().add(row * sz);
252            let d = dst.column.aligned_base_mut().add(dst.column.len * sz);
253            (self.column.clone_fn)(s, d);
254        }
255        dst.column.len += 1;
256        dst.added_ticks.push(self.added_ticks[row]);
257        dst.changed_ticks.push(tick);
258    }
259}
260
261// ---------------------------------------------------------------------------
262// Archetype
263// ---------------------------------------------------------------------------
264
265/// Unique index into [`World::archetypes`].
266#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
267pub struct ArchetypeId(pub u32);
268
269/// A table of entities that share exactly the same component type set.
270pub struct Archetype {
271    /// Unique id for this archetype.
272    pub id: ArchetypeId,
273    /// Sorted, deduplicated component `TypeId`s.
274    pub component_types: Vec<TypeId>,
275    columns: HashMap<TypeId, ComponentStorage>,
276    /// Entity handle at each row.
277    pub entities: Vec<Entity>,
278}
279
280impl Archetype {
281    fn new(id: ArchetypeId, types: Vec<TypeId>) -> Self {
282        Self { id, component_types: types, columns: HashMap::new(), entities: Vec::new() }
283    }
284
285    fn register_column<T: Component>(&mut self) {
286        self.columns.entry(TypeId::of::<T>()).or_insert_with(ComponentStorage::new::<T>);
287    }
288
289    /// Returns `true` if this archetype has a column for `tid`.
290    pub fn contains(&self, tid: TypeId) -> bool { self.columns.contains_key(&tid) }
291
292    /// Number of entities in this archetype.
293    pub fn len(&self) -> usize { self.entities.len() }
294
295    /// `true` if empty.
296    pub fn is_empty(&self) -> bool { self.entities.is_empty() }
297
298    /// Swap-removes row `row`. Returns the entity swapped into that row, if any.
299    fn swap_remove(&mut self, row: usize) -> Option<Entity> {
300        let last = self.entities.len() - 1;
301        for col in self.columns.values_mut() { col.swap_remove(row); }
302        self.entities.swap(row, last);
303        self.entities.pop();
304        if row < self.entities.len() { Some(self.entities[row]) } else { None }
305    }
306}
307
308// ---------------------------------------------------------------------------
309// Bundle
310// ---------------------------------------------------------------------------
311
312/// A heterogeneous set of components spawned together.
313pub trait Bundle: 'static + Send + Sync {
314    /// Sorted `TypeId`s of all components.
315    fn type_ids() -> Vec<TypeId>;
316    /// Ensure all columns exist on `arch`.
317    fn register_columns(arch: &mut Archetype);
318    /// Push all components into `arch`.
319    fn insert_into(self, arch: &mut Archetype, tick: u32);
320}
321
322impl<C: Component> Bundle for C {
323    fn type_ids() -> Vec<TypeId> { vec![TypeId::of::<C>()] }
324    fn register_columns(a: &mut Archetype) { a.register_column::<C>(); }
325    fn insert_into(self, a: &mut Archetype, tick: u32) {
326        a.columns.get_mut(&TypeId::of::<C>()).expect("col missing").push(self, tick);
327    }
328}
329
330macro_rules! impl_bundle {
331    ($($C:ident),+) => {
332        #[allow(non_snake_case)]
333        impl<$($C: Component),+> Bundle for ($($C,)+) {
334            fn type_ids() -> Vec<TypeId> {
335                let mut v = vec![$(TypeId::of::<$C>()),+]; v.sort(); v.dedup(); v
336            }
337            fn register_columns(a: &mut Archetype) { $(a.register_column::<$C>();)+ }
338            fn insert_into(self, a: &mut Archetype, tick: u32) {
339                let ($($C,)+) = self;
340                $(a.columns.get_mut(&TypeId::of::<$C>()).expect("col missing").push($C, tick);)+
341            }
342        }
343    };
344}
345impl_bundle!(A, B);
346impl_bundle!(A, B, C);
347impl_bundle!(A, B, C, D);
348impl_bundle!(A, B, C, D, E);
349impl_bundle!(A, B, C, D, E, F);
350impl_bundle!(A, B, C, D, E, F, G);
351impl_bundle!(A, B, C, D, E, F, G, H);
352
353// ---------------------------------------------------------------------------
354// Resources
355// ---------------------------------------------------------------------------
356
357/// Type-map of global singleton resources.
358#[derive(Default)]
359pub struct Resources {
360    map: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
361}
362
363impl Resources {
364    /// Creates an empty resource map.
365    pub fn new() -> Self { Self::default() }
366    /// Inserts (or replaces) a resource.
367    pub fn insert<T: 'static + Send + Sync>(&mut self, v: T) {
368        self.map.insert(TypeId::of::<T>(), Box::new(v));
369    }
370    /// Returns `&T` if present.
371    pub fn get<T: 'static + Send + Sync>(&self) -> Option<&T> {
372        self.map.get(&TypeId::of::<T>())?.downcast_ref()
373    }
374    /// Returns `&mut T` if present.
375    pub fn get_mut<T: 'static + Send + Sync>(&mut self) -> Option<&mut T> {
376        self.map.get_mut(&TypeId::of::<T>())?.downcast_mut()
377    }
378    /// Removes and returns `T`.
379    pub fn remove<T: 'static + Send + Sync>(&mut self) -> Option<T> {
380        self.map.remove(&TypeId::of::<T>())
381            .and_then(|b| b.downcast::<T>().ok()).map(|b| *b)
382    }
383    /// `true` if resource `T` is present.
384    pub fn contains<T: 'static + Send + Sync>(&self) -> bool {
385        self.map.contains_key(&TypeId::of::<T>())
386    }
387}
388
389// ---------------------------------------------------------------------------
390// Res<T> / ResMut<T>
391// ---------------------------------------------------------------------------
392
393/// Immutable resource borrow.
394pub struct Res<'a, T: 'static + Send + Sync> { inner: &'a T }
395impl<'a, T: 'static + Send + Sync> Res<'a, T> {
396    /// Wraps a reference.
397    pub fn new(inner: &'a T) -> Self { Self { inner } }
398}
399impl<'a, T: 'static + Send + Sync> std::ops::Deref for Res<'a, T> {
400    type Target = T; fn deref(&self) -> &T { self.inner }
401}
402
403/// Mutable resource borrow.
404pub struct ResMut<'a, T: 'static + Send + Sync> { inner: &'a mut T }
405impl<'a, T: 'static + Send + Sync> ResMut<'a, T> {
406    /// Wraps a mutable reference.
407    pub fn new(inner: &'a mut T) -> Self { Self { inner } }
408}
409impl<'a, T: 'static + Send + Sync> std::ops::Deref for ResMut<'a, T> {
410    type Target = T; fn deref(&self) -> &T { self.inner }
411}
412impl<'a, T: 'static + Send + Sync> std::ops::DerefMut for ResMut<'a, T> {
413    fn deref_mut(&mut self) -> &mut T { self.inner }
414}
415
416// ---------------------------------------------------------------------------
417// Local<T>
418// ---------------------------------------------------------------------------
419
420/// System-local persistent state.
421pub struct Local<T: Default + 'static> { value: T }
422impl<T: Default + 'static> Local<T> {
423    /// Initialises with `value`.
424    pub fn new(value: T) -> Self { Self { value } }
425    /// Initialises with `T::default()`.
426    pub fn default_value() -> Self { Self { value: T::default() } }
427}
428impl<T: Default + 'static> std::ops::Deref for Local<T> {
429    type Target = T; fn deref(&self) -> &T { &self.value }
430}
431impl<T: Default + 'static> std::ops::DerefMut for Local<T> {
432    fn deref_mut(&mut self) -> &mut T { &mut self.value }
433}
434
435// ---------------------------------------------------------------------------
436// Events<E>
437// ---------------------------------------------------------------------------
438
439/// Double-buffered typed event queue.
440pub struct Events<E: 'static + Send + Sync> {
441    buffers: [Vec<E>; 2],
442    current: usize,
443    event_count: usize,
444    start_event_count: usize,
445}
446
447impl<E: 'static + Send + Sync> Default for Events<E> {
448    fn default() -> Self {
449        Self { buffers: [Vec::new(), Vec::new()], current: 0, event_count: 0, start_event_count: 0 }
450    }
451}
452
453impl<E: 'static + Send + Sync> Events<E> {
454    /// Creates a new empty queue.
455    pub fn new() -> Self { Self::default() }
456    /// Appends an event.
457    pub fn send(&mut self, e: E) { self.buffers[self.current].push(e); self.event_count += 1; }
458    /// Swaps buffers and clears the stale one (call once per frame).
459    pub fn update(&mut self) {
460        let next = 1 - self.current;
461        self.buffers[next].clear();
462        self.start_event_count = self.event_count - self.buffers[self.current].len();
463        self.current = next;
464    }
465    /// Returns a cursor at the current end (reads only future events).
466    pub fn get_reader(&self) -> EventCursor { EventCursor { last: self.event_count } }
467    /// Returns a cursor at the oldest buffered event.
468    pub fn get_reader_current(&self) -> EventCursor { EventCursor { last: self.start_event_count } }
469    /// Reads all events since `cursor`, advancing it.
470    pub fn read<'a>(&'a self, cursor: &mut EventCursor) -> impl Iterator<Item = &'a E> {
471        let start = cursor.last;
472        cursor.last = self.event_count;
473        let old = &self.buffers[1 - self.current];
474        let new = &self.buffers[self.current];
475        let base = self.start_event_count;
476        let sk0 = start.saturating_sub(base);
477        let tk0 = old.len().saturating_sub(sk0);
478        let sk1 = start.saturating_sub(base + old.len());
479        let tk1 = new.len().saturating_sub(sk1);
480        old.iter().skip(sk0).take(tk0).chain(new.iter().skip(sk1).take(tk1))
481    }
482    /// Total events sent since creation.
483    pub fn len(&self) -> usize { self.event_count }
484    /// `true` if both buffers are empty.
485    pub fn is_empty(&self) -> bool { self.buffers[0].is_empty() && self.buffers[1].is_empty() }
486    /// Clears all buffered events.
487    pub fn clear(&mut self) { self.buffers[0].clear(); self.buffers[1].clear(); }
488}
489
490/// An independent read cursor into [`Events<E>`].
491#[derive(Debug, Clone, Default)]
492pub struct EventCursor { last: usize }
493
494/// Write handle for [`Events<E>`].
495pub struct EventWriter<'a, E: 'static + Send + Sync> { events: &'a mut Events<E> }
496impl<'a, E: 'static + Send + Sync> EventWriter<'a, E> {
497    /// Creates a writer.
498    pub fn new(events: &'a mut Events<E>) -> Self { Self { events } }
499    /// Sends an event.
500    pub fn send(&mut self, e: E) { self.events.send(e); }
501    /// Sends events from an iterator.
502    pub fn send_batch(&mut self, it: impl IntoIterator<Item = E>) { for e in it { self.events.send(e); } }
503}
504
505/// Read handle for [`Events<E>`] with its own cursor.
506pub struct EventReader<'a, E: 'static + Send + Sync> { events: &'a Events<E>, cursor: EventCursor }
507impl<'a, E: 'static + Send + Sync> EventReader<'a, E> {
508    /// Reads only future events.
509    pub fn new(events: &'a Events<E>) -> Self { Self { cursor: events.get_reader(), events } }
510    /// Reads all buffered events.
511    pub fn new_current(events: &'a Events<E>) -> Self { Self { cursor: events.get_reader_current(), events } }
512    /// Returns an iterator over unread events.
513    pub fn read(&mut self) -> impl Iterator<Item = &E> { self.events.read(&mut self.cursor) }
514}
515
516// ---------------------------------------------------------------------------
517// Commands
518// ---------------------------------------------------------------------------
519
520enum Cmd {
521    Spawn(Box<dyn FnOnce(&mut World) + Send + Sync>),
522    Despawn(Entity),
523    Insert(Box<dyn FnOnce(&mut World) + Send + Sync>),
524    Remove(Entity, TypeId),
525    InsertRes(Box<dyn FnOnce(&mut World) + Send + Sync>),
526    RemoveRes(TypeId),
527}
528
529/// Deferred world mutations applied at end-of-frame.
530pub struct Commands { queue: Vec<Cmd> }
531impl Commands {
532    /// Creates an empty command queue.
533    pub fn new() -> Self { Self { queue: Vec::new() } }
534    /// Queues a bundle spawn.
535    pub fn spawn<B: Bundle>(&mut self, b: B) {
536        self.queue.push(Cmd::Spawn(Box::new(move |w| { w.spawn(b); })));
537    }
538    /// Queues a despawn.
539    pub fn despawn(&mut self, e: Entity) { self.queue.push(Cmd::Despawn(e)); }
540    /// Queues a component insertion.
541    pub fn insert<C: Component>(&mut self, e: Entity, c: C) {
542        self.queue.push(Cmd::Insert(Box::new(move |w| w.insert(e, c))));
543    }
544    /// Queues a component removal.
545    pub fn remove<C: Component>(&mut self, e: Entity) {
546        self.queue.push(Cmd::Remove(e, TypeId::of::<C>()));
547    }
548    /// Queues a resource insertion.
549    pub fn insert_resource<T: 'static + Send + Sync>(&mut self, v: T) {
550        self.queue.push(Cmd::InsertRes(Box::new(move |w| w.resources.insert(v))));
551    }
552    /// Queues a resource removal.
553    pub fn remove_resource<T: 'static + Send + Sync>(&mut self) {
554        self.queue.push(Cmd::RemoveRes(TypeId::of::<T>()));
555    }
556    /// Applies all queued commands and clears the queue.
557    pub fn apply(&mut self, world: &mut World) {
558        for cmd in self.queue.drain(..) {
559            match cmd {
560                Cmd::Spawn(f) => f(world),
561                Cmd::Despawn(e) => { world.despawn(e); }
562                Cmd::Insert(f) => f(world),
563                Cmd::Remove(e, tid) => world.remove_by_type_id(e, tid),
564                Cmd::InsertRes(f) => f(world),
565                Cmd::RemoveRes(tid) => { world.resources.map.remove(&tid); }
566            }
567        }
568    }
569    /// `true` if no commands are queued.
570    pub fn is_empty(&self) -> bool { self.queue.is_empty() }
571}
572impl Default for Commands { fn default() -> Self { Self::new() } }
573
574// ---------------------------------------------------------------------------
575// Query filters
576// ---------------------------------------------------------------------------
577
578/// Requires component `T` to be present (not fetched).
579pub struct With<T: Component>(std::marker::PhantomData<T>);
580/// Requires component `T` to be absent.
581pub struct Without<T: Component>(std::marker::PhantomData<T>);
582/// Change-detection filter: component `T` was added.
583pub struct Added<T: Component>(std::marker::PhantomData<T>);
584/// Change-detection filter: component `T` was mutably accessed.
585pub struct Changed<T: Component>(std::marker::PhantomData<T>);
586
587/// Archetype-level filter for queries.
588pub trait QueryFilter {
589    /// `true` if the archetype passes this filter.
590    fn matches_archetype(arch: &Archetype) -> bool;
591}
592impl QueryFilter for () { fn matches_archetype(_: &Archetype) -> bool { true } }
593impl<T: Component> QueryFilter for With<T> {
594    fn matches_archetype(a: &Archetype) -> bool { a.contains(TypeId::of::<T>()) }
595}
596impl<T: Component> QueryFilter for Without<T> {
597    fn matches_archetype(a: &Archetype) -> bool { !a.contains(TypeId::of::<T>()) }
598}
599impl<A: QueryFilter, B: QueryFilter> QueryFilter for (A, B) {
600    fn matches_archetype(a: &Archetype) -> bool { A::matches_archetype(a) && B::matches_archetype(a) }
601}
602impl<A: QueryFilter, B: QueryFilter, C: QueryFilter> QueryFilter for (A, B, C) {
603    fn matches_archetype(a: &Archetype) -> bool {
604        A::matches_archetype(a) && B::matches_archetype(a) && C::matches_archetype(a)
605    }
606}
607
608// ---------------------------------------------------------------------------
609// WorldQuery fetch markers and trait
610// ---------------------------------------------------------------------------
611
612/// Fetch marker: yields `&'w T`.
613pub struct Read<T: Component>(std::marker::PhantomData<T>);
614/// Fetch marker: yields `&'w mut T`.
615pub struct Write<T: Component>(std::marker::PhantomData<T>);
616/// Fetch marker: yields `Option<&'w T>` (entity need not have T).
617pub struct OptionRead<T: Component>(std::marker::PhantomData<T>);
618
619/// Trait for types that describe how to fetch data from an archetype row.
620pub trait WorldQuery: 'static {
621    /// The type yielded per entity.
622    type Item<'w>;
623    /// Required component `TypeId`s (must all be present in the archetype).
624    fn required_types() -> Vec<TypeId>;
625    /// `true` if `arch` satisfies this query.
626    fn matches(arch: &Archetype) -> bool;
627    /// Fetches the item for `row` in `arch`.
628    ///
629    /// # Safety
630    /// `row` must be valid; aliasing rules must be upheld by the caller.
631    unsafe fn fetch<'w>(arch: &'w Archetype, row: usize) -> Self::Item<'w>;
632}
633
634impl<T: Component> WorldQuery for Read<T> {
635    type Item<'w> = &'w T;
636    fn required_types() -> Vec<TypeId> { vec![TypeId::of::<T>()] }
637    fn matches(a: &Archetype) -> bool { a.contains(TypeId::of::<T>()) }
638    unsafe fn fetch<'w>(a: &'w Archetype, row: usize) -> &'w T {
639        a.columns[&TypeId::of::<T>()].column.get::<T>(row)
640    }
641}
642
643impl<T: Component> WorldQuery for Write<T> {
644    type Item<'w> = &'w mut T;
645    fn required_types() -> Vec<TypeId> { vec![TypeId::of::<T>()] }
646    fn matches(a: &Archetype) -> bool { a.contains(TypeId::of::<T>()) }
647    unsafe fn fetch<'w>(a: &'w Archetype, row: usize) -> &'w mut T {
648        let ptr = a.columns.get(&TypeId::of::<T>()).unwrap().column.get_ptr(row) as *mut T;
649        &mut *ptr
650    }
651}
652
653impl<T: Component> WorldQuery for OptionRead<T> {
654    type Item<'w> = Option<&'w T>;
655    fn required_types() -> Vec<TypeId> { vec![] }
656    fn matches(_: &Archetype) -> bool { true }
657    unsafe fn fetch<'w>(a: &'w Archetype, row: usize) -> Option<&'w T> {
658        a.columns.get(&TypeId::of::<T>()).map(|c| c.column.get::<T>(row))
659    }
660}
661
662impl WorldQuery for Entity {
663    type Item<'w> = Entity;
664    fn required_types() -> Vec<TypeId> { vec![] }
665    fn matches(_: &Archetype) -> bool { true }
666    unsafe fn fetch<'w>(a: &'w Archetype, row: usize) -> Entity { a.entities[row] }
667}
668
669macro_rules! impl_wq_tuple {
670    ($($Q:ident),+) => {
671        #[allow(non_snake_case)]
672        impl<$($Q: WorldQuery),+> WorldQuery for ($($Q,)+) {
673            type Item<'w> = ($($Q::Item<'w>,)+);
674            fn required_types() -> Vec<TypeId> {
675                let mut v = Vec::new(); $(v.extend($Q::required_types());)+ v.sort(); v.dedup(); v
676            }
677            fn matches(a: &Archetype) -> bool { $($Q::matches(a))&&+ }
678            unsafe fn fetch<'w>(a: &'w Archetype, row: usize) -> ($($Q::Item<'w>,)+) {
679                ($($Q::fetch(a, row),)+)
680            }
681        }
682    };
683}
684impl_wq_tuple!(A);
685impl_wq_tuple!(A, B);
686impl_wq_tuple!(A, B, C);
687impl_wq_tuple!(A, B, C, D);
688impl_wq_tuple!(A, B, C, D, E);
689impl_wq_tuple!(A, B, C, D, E, F);
690impl_wq_tuple!(A, B, C, D, E, F, G);
691impl_wq_tuple!(A, B, C, D, E, F, G, H);
692
693// ---------------------------------------------------------------------------
694// QueryIter
695// ---------------------------------------------------------------------------
696
697/// Iterator over archetypes matching query `Q` and filter `F`.
698pub struct QueryIter<'w, Q: WorldQuery, F: QueryFilter> {
699    archetypes: &'w [Archetype],
700    arch_index: usize,
701    row: usize,
702    _q: std::marker::PhantomData<Q>,
703    _f: std::marker::PhantomData<F>,
704}
705impl<'w, Q: WorldQuery, F: QueryFilter> QueryIter<'w, Q, F> {
706    fn new(archetypes: &'w [Archetype]) -> Self {
707        Self { archetypes, arch_index: 0, row: 0, _q: std::marker::PhantomData, _f: std::marker::PhantomData }
708    }
709}
710impl<'w, Q: WorldQuery, F: QueryFilter> Iterator for QueryIter<'w, Q, F> {
711    type Item = Q::Item<'w>;
712    fn next(&mut self) -> Option<Self::Item> {
713        loop {
714            let arch = self.archetypes.get(self.arch_index)?;
715            if !Q::matches(arch) || !F::matches_archetype(arch) {
716                self.arch_index += 1; self.row = 0; continue;
717            }
718            if self.row >= arch.len() {
719                self.arch_index += 1; self.row = 0; continue;
720            }
721            let row = self.row; self.row += 1;
722            return Some(unsafe { Q::fetch(arch, row) });
723        }
724    }
725}
726
727// ---------------------------------------------------------------------------
728// EntityRef / EntityMut
729// ---------------------------------------------------------------------------
730
731/// Read-only view into a single entity's components.
732pub struct EntityRef<'w> { arch: &'w Archetype, row: usize }
733impl<'w> EntityRef<'w> {
734    fn new(arch: &'w Archetype, row: usize) -> Self { Self { arch, row } }
735    /// The entity's handle.
736    pub fn entity(&self) -> Entity { self.arch.entities[self.row] }
737    /// Returns `&T` if the entity has component `T`.
738    pub fn get<T: Component>(&self) -> Option<&T> {
739        self.arch.columns.get(&TypeId::of::<T>()).map(|c| c.column.get::<T>(self.row))
740    }
741    /// `true` if the entity has component `T`.
742    pub fn has<T: Component>(&self) -> bool { self.arch.contains(TypeId::of::<T>()) }
743    /// All component types on this entity.
744    pub fn component_types(&self) -> &[TypeId] { &self.arch.component_types }
745}
746
747/// Read-write view into a single entity's components.
748pub struct EntityMut<'w> { arch: &'w mut Archetype, row: usize }
749impl<'w> EntityMut<'w> {
750    fn new(arch: &'w mut Archetype, row: usize) -> Self { Self { arch, row } }
751    /// The entity's handle.
752    pub fn entity(&self) -> Entity { self.arch.entities[self.row] }
753    /// Returns `&T` if present.
754    pub fn get<T: Component>(&self) -> Option<&T> {
755        self.arch.columns.get(&TypeId::of::<T>()).map(|c| c.column.get::<T>(self.row))
756    }
757    /// Returns `&mut T` if present.
758    pub fn get_mut<T: Component>(&mut self) -> Option<&mut T> {
759        self.arch.columns.get_mut(&TypeId::of::<T>()).map(|c| c.column.get_mut::<T>(self.row))
760    }
761    /// `true` if the entity has component `T`.
762    pub fn has<T: Component>(&self) -> bool { self.arch.contains(TypeId::of::<T>()) }
763}
764
765// ---------------------------------------------------------------------------
766// World
767// ---------------------------------------------------------------------------
768
769/// The central ECS container: archetypes, entity allocator, resources, events.
770pub struct World {
771    archetypes: Vec<Archetype>,
772    archetype_index: HashMap<Vec<TypeId>, usize>,
773    entities: EntityAllocator,
774    /// Global singleton resources.
775    pub resources: Resources,
776    events: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
777    tick: u32,
778}
779
780impl World {
781    /// Creates an empty world.
782    pub fn new() -> Self {
783        Self {
784            archetypes: Vec::new(),
785            archetype_index: HashMap::new(),
786            entities: EntityAllocator::default(),
787            resources: Resources::new(),
788            events: HashMap::new(),
789            tick: 0,
790        }
791    }
792
793    /// Returns the current change-detection tick.
794    pub fn tick(&self) -> u32 { self.tick }
795    /// Advances the tick counter by 1.
796    pub fn increment_tick(&mut self) { self.tick = self.tick.wrapping_add(1); }
797
798    // -- Archetype helpers --------------------------------------------------
799
800    fn get_or_create_archetype(&mut self, mut types: Vec<TypeId>) -> usize {
801        types.sort(); types.dedup();
802        if let Some(&i) = self.archetype_index.get(&types) { return i; }
803        let id = ArchetypeId(self.archetypes.len() as u32);
804        let arch = Archetype::new(id, types.clone());
805        let i = self.archetypes.len();
806        self.archetypes.push(arch);
807        self.archetype_index.insert(types, i);
808        i
809    }
810
811    // -- Spawn / despawn ----------------------------------------------------
812
813    /// Spawns an entity with the given bundle and returns its handle.
814    pub fn spawn<B: Bundle>(&mut self, bundle: B) -> Entity {
815        let types = B::type_ids();
816        let ai = self.get_or_create_archetype(types);
817        let entity = self.entities.alloc();
818        let tick = self.tick;
819        let arch = &mut self.archetypes[ai];
820        B::register_columns(arch);
821        let row = arch.entities.len();
822        arch.entities.push(entity);
823        bundle.insert_into(arch, tick);
824        self.entities.set_location(entity, EntityLocation { archetype_id: ArchetypeId(ai as u32), row });
825        entity
826    }
827
828    /// Despawns an entity, returning `true` if it was alive.
829    pub fn despawn(&mut self, entity: Entity) -> bool {
830        let loc = match self.entities.location(entity) { Some(l) => l, None => return false };
831        let ai = loc.archetype_id.0 as usize;
832        let swapped = self.archetypes[ai].swap_remove(loc.row);
833        self.entities.free(entity);
834        if let Some(se) = swapped {
835            self.entities.set_location(se, EntityLocation { archetype_id: loc.archetype_id, row: loc.row });
836        }
837        true
838    }
839
840    /// `true` if entity is currently alive.
841    pub fn is_alive(&self, entity: Entity) -> bool { self.entities.is_alive(entity) }
842
843    // -- Component insert / remove -----------------------------------------
844
845    /// Inserts component `C` onto an entity, migrating its archetype if needed.
846    pub fn insert<C: Component>(&mut self, entity: Entity, component: C) {
847        let loc = match self.entities.location(entity) { Some(l) => l, None => return };
848        let ai = loc.archetype_id.0 as usize;
849        // Fast path: replace in-place.
850        if self.archetypes[ai].contains(TypeId::of::<C>()) {
851            let tick = self.tick;
852            let col = self.archetypes[ai].columns.get_mut(&TypeId::of::<C>()).unwrap();
853            *col.column.get_mut::<C>(loc.row) = component;
854            col.changed_ticks[loc.row] = tick;
855            return;
856        }
857        // Slow path: migrate to larger archetype.
858        let mut new_types = self.archetypes[ai].component_types.clone();
859        new_types.push(TypeId::of::<C>());
860        let new_ai = self.get_or_create_archetype(new_types);
861        let tick = self.tick;
862        self.migrate_add::<C>(entity, loc, new_ai, component, tick);
863    }
864
865    fn migrate_add<C: Component>(
866        &mut self, entity: Entity, loc: EntityLocation, new_ai: usize, extra: C, tick: u32,
867    ) {
868        let old_ai = loc.archetype_id.0 as usize;
869        let row = loc.row;
870        let old_types: Vec<TypeId> = self.archetypes[old_ai].component_types.clone();
871        // Ensure destination columns exist.
872        self.ensure_cols(old_ai, new_ai, &old_types);
873        if !self.archetypes[new_ai].columns.contains_key(&TypeId::of::<C>()) {
874            self.archetypes[new_ai].register_column::<C>();
875        }
876        let new_row = self.archetypes[new_ai].entities.len();
877        for &tid in &old_types {
878            Self::copy_row(&mut self.archetypes, old_ai, new_ai, tid, row, tick);
879        }
880        self.archetypes[new_ai].columns.get_mut(&TypeId::of::<C>()).unwrap().push(extra, tick);
881        self.archetypes[new_ai].entities.push(entity);
882        let swapped = self.archetypes[old_ai].swap_remove(row);
883        self.entities.set_location(entity, EntityLocation { archetype_id: ArchetypeId(new_ai as u32), row: new_row });
884        if let Some(se) = swapped {
885            self.entities.set_location(se, EntityLocation { archetype_id: loc.archetype_id, row });
886        }
887    }
888
889    fn ensure_cols(&mut self, src: usize, dst: usize, types: &[TypeId]) {
890        for &tid in types {
891            if self.archetypes[dst].columns.contains_key(&tid) { continue; }
892            let s = &self.archetypes[src].columns[&tid];
893            let nc = ComponentStorage {
894                column: AnyVec {
895                    type_id: s.column.type_id, len: 0, data: Vec::new(),
896                    element_size: s.column.element_size,
897                    element_align: s.column.element_align,
898                    drop_fn: s.column.drop_fn, clone_fn: s.column.clone_fn,
899                },
900                added_ticks: Vec::new(), changed_ticks: Vec::new(),
901            };
902            self.archetypes[dst].columns.insert(tid, nc);
903        }
904    }
905
906    fn copy_row(archetypes: &mut Vec<Archetype>, src: usize, dst: usize, tid: TypeId, row: usize, tick: u32) {
907        let (sa, da) = if src < dst {
908            let (l, r) = archetypes.split_at_mut(dst); (&l[src], &mut r[0])
909        } else {
910            let (l, r) = archetypes.split_at_mut(src); (&r[0], &mut l[dst])
911        };
912        if let (Some(sc), Some(dc)) = (sa.columns.get(&tid), da.columns.get_mut(&tid)) {
913            sc.clone_row_into(row, dc, tick);
914        }
915    }
916
917    /// Removes component `C` from an entity.
918    pub fn remove<C: Component>(&mut self, entity: Entity) {
919        self.remove_by_type_id(entity, TypeId::of::<C>());
920    }
921
922    /// Removes component by raw `TypeId`.
923    pub(crate) fn remove_by_type_id(&mut self, entity: Entity, tid: TypeId) {
924        let loc = match self.entities.location(entity) { Some(l) => l, None => return };
925        let old_ai = loc.archetype_id.0 as usize;
926        if !self.archetypes[old_ai].contains(tid) { return; }
927        let row = loc.row;
928        let new_types: Vec<TypeId> = self.archetypes[old_ai].component_types.iter()
929            .copied().filter(|&t| t != tid).collect();
930        let new_ai = self.get_or_create_archetype(new_types.clone());
931        let tick = self.tick;
932        let old_types: Vec<TypeId> = self.archetypes[old_ai].component_types.clone();
933        self.ensure_cols(old_ai, new_ai, &new_types);
934        let new_row = self.archetypes[new_ai].entities.len();
935        for &t in &old_types {
936            if t == tid { continue; }
937            Self::copy_row(&mut self.archetypes, old_ai, new_ai, t, row, tick);
938        }
939        self.archetypes[new_ai].entities.push(entity);
940        let swapped = self.archetypes[old_ai].swap_remove(row);
941        self.entities.set_location(entity, EntityLocation { archetype_id: ArchetypeId(new_ai as u32), row: new_row });
942        if let Some(se) = swapped {
943            self.entities.set_location(se, EntityLocation { archetype_id: loc.archetype_id, row });
944        }
945    }
946
947    // -- Component access --------------------------------------------------
948
949    /// Returns `&C` for `entity`, or `None`.
950    pub fn get<C: Component>(&self, entity: Entity) -> Option<&C> {
951        let loc = self.entities.location(entity)?;
952        self.archetypes[loc.archetype_id.0 as usize]
953            .columns.get(&TypeId::of::<C>()).map(|c| c.column.get::<C>(loc.row))
954    }
955
956    /// Returns `&mut C` for `entity`, updating changed tick.
957    pub fn get_mut<C: Component>(&mut self, entity: Entity) -> Option<&mut C> {
958        let loc = self.entities.location(entity)?;
959        let tick = self.tick;
960        let arch = &mut self.archetypes[loc.archetype_id.0 as usize];
961        arch.columns.get_mut(&TypeId::of::<C>()).map(|c| {
962            c.changed_ticks[loc.row] = tick;
963            c.column.get_mut::<C>(loc.row)
964        })
965    }
966
967    /// Read-only view of an entity's components.
968    pub fn entity_ref(&self, entity: Entity) -> Option<EntityRef<'_>> {
969        let loc = self.entities.location(entity)?;
970        Some(EntityRef::new(&self.archetypes[loc.archetype_id.0 as usize], loc.row))
971    }
972
973    /// Read-write view of an entity's components.
974    pub fn entity_mut(&mut self, entity: Entity) -> Option<EntityMut<'_>> {
975        let loc = self.entities.location(entity)?;
976        let ai = loc.archetype_id.0 as usize;
977        Some(EntityMut::new(&mut self.archetypes[ai], loc.row))
978    }
979
980    // -- Queries -----------------------------------------------------------
981
982    /// Returns an iterator over entities matching `Q` and filter `F`.
983    pub fn query<Q: WorldQuery, F: QueryFilter>(&self) -> QueryIter<'_, Q, F> {
984        QueryIter::new(&self.archetypes)
985    }
986
987    /// Query with no filter.
988    pub fn query_all<Q: WorldQuery>(&self) -> QueryIter<'_, Q, ()> {
989        QueryIter::new(&self.archetypes)
990    }
991
992    /// Returns the single matching entity, or `None` if zero or multiple.
993    pub fn query_single<Q: WorldQuery>(&self) -> Option<Q::Item<'_>> {
994        let mut it = self.query::<Q, ()>();
995        let first = it.next()?;
996        if it.next().is_some() { return None; }
997        Some(first)
998    }
999
1000    // -- Resources ---------------------------------------------------------
1001
1002    /// Inserts a resource.
1003    pub fn insert_resource<T: 'static + Send + Sync>(&mut self, v: T) { self.resources.insert(v); }
1004    /// Returns `Res<T>`.
1005    pub fn resource<T: 'static + Send + Sync>(&self) -> Option<Res<'_, T>> {
1006        self.resources.get::<T>().map(Res::new)
1007    }
1008    /// Returns `ResMut<T>`.
1009    pub fn resource_mut<T: 'static + Send + Sync>(&mut self) -> Option<ResMut<'_, T>> {
1010        self.resources.get_mut::<T>().map(ResMut::new)
1011    }
1012    /// Removes and returns `T`.
1013    pub fn remove_resource<T: 'static + Send + Sync>(&mut self) -> Option<T> { self.resources.remove::<T>() }
1014
1015    // -- Events ------------------------------------------------------------
1016
1017    /// Registers event type `E`.
1018    pub fn add_event<E: 'static + Send + Sync>(&mut self) {
1019        self.events.entry(TypeId::of::<Events<E>>())
1020            .or_insert_with(|| Box::new(Events::<E>::new()));
1021    }
1022    /// Returns `&Events<E>`.
1023    pub fn events<E: 'static + Send + Sync>(&self) -> Option<&Events<E>> {
1024        self.events.get(&TypeId::of::<Events<E>>())?.downcast_ref()
1025    }
1026    /// Returns `&mut Events<E>`.
1027    pub fn events_mut<E: 'static + Send + Sync>(&mut self) -> Option<&mut Events<E>> {
1028        self.events.get_mut(&TypeId::of::<Events<E>>())?.downcast_mut()
1029    }
1030    /// Sends event `E` (auto-creates queue).
1031    pub fn send_event<E: 'static + Send + Sync>(&mut self, event: E) {
1032        self.events.entry(TypeId::of::<Events<E>>())
1033            .or_insert_with(|| Box::new(Events::<E>::new()))
1034            .downcast_mut::<Events<E>>().unwrap().send(event);
1035    }
1036
1037    // -- Entity iteration --------------------------------------------------
1038
1039    /// Iterator over all live entities.
1040    pub fn entities(&self) -> impl Iterator<Item = Entity> + '_ {
1041        self.archetypes.iter().flat_map(|a| a.entities.iter().copied())
1042    }
1043    /// Total number of live entities.
1044    pub fn entity_count(&self) -> usize { self.archetypes.iter().map(|a| a.len()).sum() }
1045    /// Number of archetypes.
1046    pub fn archetype_count(&self) -> usize { self.archetypes.len() }
1047}
1048
1049impl Default for World { fn default() -> Self { Self::new() } }
1050
1051// ---------------------------------------------------------------------------
1052// SystemParam trait
1053// ---------------------------------------------------------------------------
1054
1055/// Types extractable from a [`World`] as system parameters.
1056pub trait SystemParam: Sized {
1057    /// Per-system persistent state.
1058    type State: Default + Send + Sync + 'static;
1059    /// Initialises state from the world.
1060    fn init_state(world: &mut World) -> Self::State;
1061}
1062
1063// ---------------------------------------------------------------------------
1064// System trait
1065// ---------------------------------------------------------------------------
1066
1067/// A unit of logic operating on the [`World`].
1068pub trait System: Send + Sync + 'static {
1069    /// Executes the system.
1070    fn run(&mut self, world: &mut World);
1071    /// Human-readable name.
1072    fn name(&self) -> &str;
1073}
1074
1075/// A system wrapping a plain closure.
1076pub struct FunctionSystem<F: FnMut(&mut World) + Send + Sync + 'static> { f: F, name: String }
1077impl<F: FnMut(&mut World) + Send + Sync + 'static> FunctionSystem<F> {
1078    /// Creates a named function system.
1079    pub fn new(name: impl Into<String>, f: F) -> Self { Self { f, name: name.into() } }
1080}
1081impl<F: FnMut(&mut World) + Send + Sync + 'static> System for FunctionSystem<F> {
1082    fn run(&mut self, w: &mut World) { (self.f)(w); }
1083    fn name(&self) -> &str { &self.name }
1084}
1085
1086/// Wraps a closure as a boxed `System`.
1087pub fn into_system(name: impl Into<String>, f: impl FnMut(&mut World) + Send + Sync + 'static) -> Box<dyn System> {
1088    Box::new(FunctionSystem::new(name, f))
1089}
1090
1091// ---------------------------------------------------------------------------
1092// Schedule
1093// ---------------------------------------------------------------------------
1094
1095/// A human-readable system identifier.
1096#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1097pub struct SystemLabel(pub String);
1098impl SystemLabel {
1099    /// Creates a new label.
1100    pub fn new(s: impl Into<String>) -> Self { Self(s.into()) }
1101}
1102impl<S: Into<String>> From<S> for SystemLabel { fn from(s: S) -> Self { Self(s.into()) } }
1103
1104/// Boxed run-condition closure.
1105pub type RunCondition = Box<dyn Fn(&World) -> bool + Send + Sync>;
1106
1107struct SystemEntry { system: Box<dyn System>, label: Option<SystemLabel>, run_if: Option<RunCondition> }
1108
1109/// Ordered list of systems with optional labels and run conditions.
1110pub struct Schedule { systems: Vec<SystemEntry> }
1111impl Schedule {
1112    /// Creates an empty schedule.
1113    pub fn new() -> Self { Self { systems: Vec::new() } }
1114    /// Adds a system.
1115    pub fn add_system(&mut self, s: Box<dyn System>) -> &mut Self {
1116        self.systems.push(SystemEntry { system: s, label: None, run_if: None }); self
1117    }
1118    /// Adds a labelled system.
1119    pub fn add_system_with_label(&mut self, s: Box<dyn System>, label: impl Into<SystemLabel>) -> &mut Self {
1120        self.systems.push(SystemEntry { system: s, label: Some(label.into()), run_if: None }); self
1121    }
1122    /// Adds a system with a run condition.
1123    pub fn add_system_with_condition(
1124        &mut self, s: Box<dyn System>,
1125        cond: impl Fn(&World) -> bool + Send + Sync + 'static,
1126    ) -> &mut Self {
1127        self.systems.push(SystemEntry { system: s, label: None, run_if: Some(Box::new(cond)) }); self
1128    }
1129    /// Adds a labelled system with a run condition.
1130    pub fn add_system_full(
1131        &mut self, s: Box<dyn System>, label: impl Into<SystemLabel>,
1132        cond: impl Fn(&World) -> bool + Send + Sync + 'static,
1133    ) -> &mut Self {
1134        self.systems.push(SystemEntry { system: s, label: Some(label.into()), run_if: Some(Box::new(cond)) }); self
1135    }
1136    /// Removes all systems with `label`.
1137    pub fn remove_system(&mut self, label: &SystemLabel) {
1138        self.systems.retain(|e| e.label.as_ref() != Some(label));
1139    }
1140    /// Number of system slots.
1141    pub fn system_count(&self) -> usize { self.systems.len() }
1142    /// Runs all systems, incrementing the world tick first.
1143    pub fn run(&mut self, world: &mut World) {
1144        world.increment_tick();
1145        for e in &mut self.systems {
1146            if e.run_if.as_ref().map(|c| c(world)).unwrap_or(true) { e.system.run(world); }
1147        }
1148    }
1149    /// Labels of all systems in order.
1150    pub fn labels(&self) -> Vec<Option<&SystemLabel>> { self.systems.iter().map(|e| e.label.as_ref()).collect() }
1151}
1152impl Default for Schedule { fn default() -> Self { Self::new() } }
1153
1154// ---------------------------------------------------------------------------
1155// Change-detection helpers
1156// ---------------------------------------------------------------------------
1157
1158/// `true` if component `C` on `entity` was added after tick `since`.
1159pub fn was_added<C: Component>(w: &World, entity: Entity, since: u32) -> bool {
1160    let loc = match w.entities.location(entity) { Some(l) => l, None => return false };
1161    w.archetypes[loc.archetype_id.0 as usize].columns
1162        .get(&TypeId::of::<C>()).map(|c| c.added_ticks[loc.row] > since).unwrap_or(false)
1163}
1164
1165/// `true` if component `C` on `entity` was changed after tick `since`.
1166pub fn was_changed<C: Component>(w: &World, entity: Entity, since: u32) -> bool {
1167    let loc = match w.entities.location(entity) { Some(l) => l, None => return false };
1168    w.archetypes[loc.archetype_id.0 as usize].columns
1169        .get(&TypeId::of::<C>()).map(|c| c.changed_ticks[loc.row] > since).unwrap_or(false)
1170}
1171
1172/// Entities with `C` added after tick `since`.
1173pub fn query_added<C: Component>(w: &World, since: u32) -> impl Iterator<Item = Entity> + '_ {
1174    w.archetypes.iter().flat_map(move |arch| {
1175        if !arch.contains(TypeId::of::<C>()) { return vec![]; }
1176        let col = &arch.columns[&TypeId::of::<C>()];
1177        arch.entities.iter().enumerate()
1178            .filter(move |(r, _)| col.added_ticks[*r] > since)
1179            .map(|(_, &e)| e).collect::<Vec<_>>()
1180    })
1181}
1182
1183/// Entities with `C` changed after tick `since`.
1184pub fn query_changed<C: Component>(w: &World, since: u32) -> impl Iterator<Item = Entity> + '_ {
1185    w.archetypes.iter().flat_map(move |arch| {
1186        if !arch.contains(TypeId::of::<C>()) { return vec![]; }
1187        let col = &arch.columns[&TypeId::of::<C>()];
1188        arch.entities.iter().enumerate()
1189            .filter(move |(r, _)| col.changed_ticks[*r] > since)
1190            .map(|(_, &e)| e).collect::<Vec<_>>()
1191    })
1192}
1193
1194// ---------------------------------------------------------------------------
1195// Prelude
1196// ---------------------------------------------------------------------------
1197
1198/// Common ECS re-exports.
1199pub mod prelude {
1200    pub use super::{
1201        Component, Entity, World, Bundle,
1202        Archetype, ArchetypeId, EntityRef, EntityMut,
1203        Resources, Res, ResMut, Local,
1204        Events, EventCursor, EventWriter, EventReader,
1205        Commands, With, Without, Added, Changed,
1206        Read, Write, OptionRead,
1207        WorldQuery, QueryFilter, QueryIter,
1208        System, SystemParam, FunctionSystem, into_system,
1209        Schedule, SystemLabel,
1210        was_added, was_changed, query_added, query_changed,
1211    };
1212}
1213
1214// ---------------------------------------------------------------------------
1215// Tests
1216// ---------------------------------------------------------------------------
1217
1218#[cfg(test)]
1219mod tests {
1220    use super::*;
1221
1222    #[derive(Debug, Clone, PartialEq)] struct Position { x: f32, y: f32 }
1223    impl Component for Position {}
1224
1225    #[derive(Debug, Clone, PartialEq)] struct Velocity { dx: f32, dy: f32 }
1226    impl Component for Velocity {}
1227
1228    #[derive(Debug, Clone, PartialEq)] struct Health(f32);
1229    impl Component for Health {}
1230
1231    #[derive(Debug, Clone)] struct Tag; impl Component for Tag {}
1232    #[derive(Debug, Clone)] struct Enemy; impl Component for Enemy {}
1233    #[derive(Debug, Clone)] struct Player; impl Component for Player {}
1234
1235    // Entity lifecycle
1236    #[test] fn test_spawn_alive() {
1237        let mut w = World::new();
1238        let e = w.spawn(Position { x: 1.0, y: 2.0 });
1239        assert!(w.is_alive(e)); assert_eq!(w.entity_count(), 1);
1240    }
1241    #[test] fn test_despawn() {
1242        let mut w = World::new();
1243        let e = w.spawn(Position { x: 0.0, y: 0.0 });
1244        assert!(w.despawn(e)); assert!(!w.is_alive(e)); assert_eq!(w.entity_count(), 0);
1245    }
1246    #[test] fn test_despawn_nonexistent() {
1247        let mut w = World::new(); assert!(!w.despawn(Entity::from_raw(99, 99)));
1248    }
1249    #[test] fn test_null_entity() {
1250        assert!(Entity::null().is_null()); assert!(!Entity::from_raw(0,0).is_null());
1251    }
1252    #[test] fn test_generation_reuse() {
1253        let mut w = World::new();
1254        let e1 = w.spawn(Position { x: 0.0, y: 0.0 }); w.despawn(e1);
1255        let e2 = w.spawn(Position { x: 1.0, y: 0.0 });
1256        assert_eq!(e1.index, e2.index); assert_ne!(e1.generation, e2.generation);
1257        assert!(!w.is_alive(e1)); assert!(w.is_alive(e2));
1258    }
1259    #[test] fn test_entity_display() { assert_eq!(format!("{}", Entity::from_raw(5,2)), "Entity(5v2)"); }
1260
1261    // Component access
1262    #[test] fn test_get_component() {
1263        let mut w = World::new(); let e = w.spawn(Position { x: 3.0, y: 4.0 });
1264        assert_eq!(w.get::<Position>(e).unwrap().x, 3.0);
1265    }
1266    #[test] fn test_get_mut_component() {
1267        let mut w = World::new(); let e = w.spawn(Position { x: 0.0, y: 0.0 });
1268        w.get_mut::<Position>(e).unwrap().x = 10.0;
1269        assert_eq!(w.get::<Position>(e).unwrap().x, 10.0);
1270    }
1271    #[test] fn test_missing_component_none() {
1272        let mut w = World::new(); let e = w.spawn(Position { x: 0.0, y: 0.0 });
1273        assert!(w.get::<Velocity>(e).is_none());
1274    }
1275    #[test] fn test_entity_ref() {
1276        let mut w = World::new(); let e = w.spawn((Position { x: 1.0, y: 2.0 }, Health(100.0)));
1277        let er = w.entity_ref(e).unwrap();
1278        assert_eq!(er.get::<Position>().unwrap(), &Position { x: 1.0, y: 2.0 });
1279        assert!(er.has::<Position>()); assert!(!er.has::<Velocity>());
1280    }
1281    #[test] fn test_entity_mut() {
1282        let mut w = World::new(); let e = w.spawn(Health(50.0));
1283        w.entity_mut(e).unwrap().get_mut::<Health>().unwrap().0 = 75.0;
1284        assert_eq!(w.get::<Health>(e).unwrap().0, 75.0);
1285    }
1286
1287    // Bundle
1288    #[test] fn test_spawn_tuple_bundle() {
1289        let mut w = World::new();
1290        let e = w.spawn((Position { x: 1.0, y: 0.0 }, Velocity { dx: 0.5, dy: 0.0 }, Health(100.0)));
1291        assert!(w.get::<Position>(e).is_some()); assert!(w.get::<Velocity>(e).is_some()); assert!(w.get::<Health>(e).is_some());
1292    }
1293    #[test] fn test_same_archetype() {
1294        let mut w = World::new();
1295        let e1 = w.spawn((Position { x: 0.0, y: 0.0 }, Velocity { dx: 1.0, dy: 0.0 }));
1296        let e2 = w.spawn((Position { x: 5.0, y: 5.0 }, Velocity { dx: -1.0, dy: 0.0 }));
1297        assert_eq!(w.archetype_count(), 1);
1298        assert_eq!(w.get::<Position>(e1).unwrap().x, 0.0);
1299        assert_eq!(w.get::<Position>(e2).unwrap().x, 5.0);
1300    }
1301
1302    // Insert / remove
1303    #[test] fn test_insert_new_component() {
1304        let mut w = World::new(); let e = w.spawn(Position { x: 0.0, y: 0.0 });
1305        w.insert(e, Velocity { dx: 1.0, dy: 0.0 });
1306        assert!(w.get::<Velocity>(e).is_some()); assert!(w.get::<Position>(e).is_some());
1307    }
1308    #[test] fn test_insert_replaces() {
1309        let mut w = World::new(); let e = w.spawn(Health(100.0));
1310        w.insert(e, Health(50.0)); assert_eq!(w.get::<Health>(e).unwrap().0, 50.0);
1311    }
1312    #[test] fn test_remove_component() {
1313        let mut w = World::new();
1314        let e = w.spawn((Position { x: 0.0, y: 0.0 }, Velocity { dx: 1.0, dy: 0.0 }));
1315        w.remove::<Velocity>(e);
1316        assert!(w.get::<Velocity>(e).is_none()); assert!(w.get::<Position>(e).is_some());
1317    }
1318    #[test] fn test_remove_missing_noop() {
1319        let mut w = World::new(); let e = w.spawn(Position { x: 0.0, y: 0.0 });
1320        w.remove::<Velocity>(e); assert!(w.is_alive(e));
1321    }
1322    #[test] fn test_insert_remove_roundtrip() {
1323        let mut w = World::new(); let e = w.spawn(Position { x: 1.0, y: 2.0 });
1324        w.insert(e, Health(99.0)); assert_eq!(w.get::<Health>(e).unwrap().0, 99.0);
1325        w.remove::<Health>(e); assert!(w.get::<Health>(e).is_none());
1326        assert_eq!(w.get::<Position>(e).unwrap().x, 1.0);
1327    }
1328
1329    // Queries
1330    #[test] fn test_query_single_component() {
1331        let mut w = World::new();
1332        w.spawn(Position { x: 1.0, y: 0.0 }); w.spawn(Position { x: 2.0, y: 0.0 }); w.spawn(Position { x: 3.0, y: 0.0 });
1333        assert_eq!(w.query::<Read<Position>, ()>().count(), 3);
1334    }
1335    #[test] fn test_query_tuple() {
1336        let mut w = World::new();
1337        w.spawn((Position { x: 0.0, y: 0.0 }, Velocity { dx: 1.0, dy: 0.0 }));
1338        w.spawn(Position { x: 5.0, y: 0.0 });
1339        assert_eq!(w.query::<(Read<Position>, Read<Velocity>), ()>().count(), 1);
1340    }
1341    #[test] fn test_query_with_filter() {
1342        let mut w = World::new();
1343        w.spawn((Position { x: 0.0, y: 0.0 }, Tag)); w.spawn(Position { x: 1.0, y: 0.0 });
1344        assert_eq!(w.query::<Read<Position>, With<Tag>>().count(), 1);
1345    }
1346    #[test] fn test_query_without_filter() {
1347        let mut w = World::new();
1348        w.spawn((Position { x: 0.0, y: 0.0 }, Enemy));
1349        w.spawn((Position { x: 1.0, y: 0.0 }, Player));
1350        w.spawn(Position { x: 2.0, y: 0.0 });
1351        assert_eq!(w.query::<Read<Position>, Without<Enemy>>().count(), 2);
1352    }
1353    #[test] fn test_query_option_read() {
1354        let mut w = World::new();
1355        w.spawn((Position { x: 0.0, y: 0.0 }, Health(100.0))); w.spawn(Position { x: 1.0, y: 0.0 });
1356        let r: Vec<_> = w.query::<(Read<Position>, OptionRead<Health>), ()>().collect();
1357        assert_eq!(r.len(), 2); assert_eq!(r.iter().filter(|(_, h)| h.is_some()).count(), 1);
1358    }
1359    #[test] fn test_query_entity() {
1360        let mut w = World::new();
1361        let e1 = w.spawn(Position { x: 0.0, y: 0.0 }); let e2 = w.spawn(Position { x: 1.0, y: 0.0 });
1362        let es: Vec<Entity> = w.query::<Entity, ()>().collect();
1363        assert!(es.contains(&e1)); assert!(es.contains(&e2));
1364    }
1365    #[test] fn test_query_mutable() {
1366        let mut w = World::new();
1367        w.spawn(Position { x: 0.0, y: 0.0 }); w.spawn(Position { x: 1.0, y: 0.0 });
1368        for p in w.query::<Write<Position>, ()>() { p.x += 10.0; }
1369        assert!(w.query::<Read<Position>, ()>().all(|p| p.x >= 10.0));
1370    }
1371    #[test] fn test_query_single() {
1372        let mut w = World::new(); w.spawn(Player);
1373        assert!(w.query_single::<Read<Player>>().is_some());
1374    }
1375    #[test] fn test_query_single_none_multiple() {
1376        let mut w = World::new(); w.spawn(Player); w.spawn(Player);
1377        assert!(w.query_single::<Read<Player>>().is_none());
1378    }
1379
1380    // Resources
1381    #[test] fn test_resource_insert_get() {
1382        let mut w = World::new(); w.insert_resource(42u32);
1383        assert_eq!(*w.resource::<u32>().unwrap(), 42);
1384    }
1385    #[test] fn test_resource_mut() {
1386        let mut w = World::new(); w.insert_resource(0u32);
1387        *w.resource_mut::<u32>().unwrap() = 99;
1388        assert_eq!(*w.resource::<u32>().unwrap(), 99);
1389    }
1390    #[test] fn test_resource_remove() {
1391        let mut w = World::new(); w.insert_resource(42u32);
1392        assert_eq!(w.remove_resource::<u32>(), Some(42));
1393        assert!(w.resource::<u32>().is_none());
1394    }
1395    #[test] fn test_resources_standalone() {
1396        let mut r = Resources::new(); r.insert(42u32);
1397        assert!(r.contains::<u32>()); assert!(!r.contains::<i32>());
1398        r.remove::<u32>(); assert!(!r.contains::<u32>());
1399    }
1400
1401    // Commands
1402    #[test] fn test_commands_spawn() {
1403        let mut w = World::new(); let mut c = Commands::new();
1404        c.spawn(Position { x: 7.0, y: 8.0 }); c.apply(&mut w);
1405        assert_eq!(w.query_single::<Read<Position>>().unwrap().x, 7.0);
1406    }
1407    #[test] fn test_commands_despawn() {
1408        let mut w = World::new(); let e = w.spawn(Position { x: 0.0, y: 0.0 });
1409        let mut c = Commands::new(); c.despawn(e); c.apply(&mut w); assert!(!w.is_alive(e));
1410    }
1411    #[test] fn test_commands_insert() {
1412        let mut w = World::new(); let e = w.spawn(Position { x: 0.0, y: 0.0 });
1413        let mut c = Commands::new(); c.insert(e, Health(50.0)); c.apply(&mut w);
1414        assert_eq!(w.get::<Health>(e).unwrap().0, 50.0);
1415    }
1416    #[test] fn test_commands_remove() {
1417        let mut w = World::new(); let e = w.spawn((Position { x: 0.0, y: 0.0 }, Health(100.0)));
1418        let mut c = Commands::new(); c.remove::<Health>(e); c.apply(&mut w);
1419        assert!(w.get::<Health>(e).is_none());
1420    }
1421    #[test] fn test_commands_insert_resource() {
1422        let mut w = World::new(); let mut c = Commands::new();
1423        c.insert_resource(100i32); c.apply(&mut w);
1424        assert_eq!(*w.resource::<i32>().unwrap(), 100);
1425    }
1426
1427    // Events
1428    #[derive(Debug, Clone, PartialEq)] struct Dmg { amount: f32 }
1429    #[test] fn test_events_send_read() {
1430        let mut ev: Events<Dmg> = Events::new(); let mut cur = ev.get_reader_current();
1431        ev.send(Dmg { amount: 10.0 }); ev.send(Dmg { amount: 20.0 });
1432        let r: Vec<_> = ev.read(&mut cur).collect();
1433        assert_eq!(r.len(), 2); assert_eq!(r[0].amount, 10.0);
1434    }
1435    #[test] fn test_events_update_clears() {
1436        let mut ev: Events<Dmg> = Events::new(); ev.send(Dmg { amount: 5.0 });
1437        ev.update(); ev.update(); assert!(ev.is_empty());
1438    }
1439    #[test] fn test_event_writer_reader() {
1440        let mut ev: Events<Dmg> = Events::new();
1441        EventWriter::new(&mut ev).send(Dmg { amount: 42.0 });
1442        let mut reader = EventReader::new_current(&ev);
1443        let r: Vec<_> = reader.read().collect();
1444        assert_eq!(r.len(), 1); assert_eq!(r[0].amount, 42.0);
1445    }
1446    #[test] fn test_world_events() {
1447        let mut w = World::new(); w.add_event::<Dmg>();
1448        w.send_event(Dmg { amount: 99.0 }); assert!(!w.events::<Dmg>().unwrap().is_empty());
1449    }
1450
1451    // Schedule
1452    #[test] fn test_schedule_runs_systems() {
1453        let mut w = World::new(); w.insert_resource(0u32);
1454        let mut s = Schedule::new();
1455        s.add_system(into_system("inc", |w| { *w.resource_mut::<u32>().unwrap() += 1; }));
1456        s.run(&mut w); s.run(&mut w); assert_eq!(*w.resource::<u32>().unwrap(), 2);
1457    }
1458    #[test] fn test_schedule_run_condition() {
1459        let mut w = World::new(); w.insert_resource(0u32); w.insert_resource(false);
1460        let mut s = Schedule::new();
1461        s.add_system_with_condition(
1462            into_system("g", |w| { *w.resource_mut::<u32>().unwrap() += 1; }),
1463            |w| *w.resource::<bool>().unwrap(),
1464        );
1465        s.run(&mut w); assert_eq!(*w.resource::<u32>().unwrap(), 0);
1466        *w.resource_mut::<bool>().unwrap() = true;
1467        s.run(&mut w); assert_eq!(*w.resource::<u32>().unwrap(), 1);
1468    }
1469    #[test] fn test_schedule_remove_label() {
1470        let mut s = Schedule::new();
1471        s.add_system_with_label(into_system("n", |_|{}), "lbl");
1472        assert_eq!(s.system_count(), 1);
1473        s.remove_system(&SystemLabel::new("lbl"));
1474        assert_eq!(s.system_count(), 0);
1475    }
1476    #[test] fn test_schedule_tick_increment() {
1477        let mut w = World::new(); let mut s = Schedule::new();
1478        s.run(&mut w); assert_eq!(w.tick(), 1); s.run(&mut w); assert_eq!(w.tick(), 2);
1479    }
1480
1481    // Change detection
1482    #[test] fn test_was_added() {
1483        let mut w = World::new(); w.increment_tick();
1484        let e = w.spawn(Health(100.0));
1485        assert!(was_added::<Health>(&w, e, 0)); assert!(!was_added::<Health>(&w, e, 1));
1486    }
1487    #[test] fn test_was_changed() {
1488        let mut w = World::new(); w.increment_tick();
1489        let e = w.spawn(Health(100.0)); w.increment_tick();
1490        w.get_mut::<Health>(e).unwrap().0 = 50.0;
1491        assert!(was_changed::<Health>(&w, e, 1)); assert!(!was_changed::<Health>(&w, e, 2));
1492    }
1493    #[test] fn test_query_added() {
1494        let mut w = World::new(); w.increment_tick();
1495        let e1 = w.spawn(Health(100.0)); w.increment_tick(); let _e2 = w.spawn(Health(50.0));
1496        let added: Vec<_> = query_added::<Health>(&w, 1).collect();
1497        assert_eq!(added.len(), 1); assert!(!added.contains(&e1));
1498    }
1499
1500    // Local
1501    #[test] fn test_local() {
1502        let mut l: Local<u32> = Local::default_value();
1503        assert_eq!(*l, 0); *l += 5; assert_eq!(*l, 5);
1504    }
1505
1506    // Structural
1507    #[test] fn test_swap_remove_updates_location() {
1508        let mut w = World::new();
1509        let e1 = w.spawn(Position { x: 1.0, y: 0.0 });
1510        let _e2 = w.spawn(Position { x: 2.0, y: 0.0 });
1511        let e3 = w.spawn(Position { x: 3.0, y: 0.0 });
1512        w.despawn(e1);
1513        assert!(w.is_alive(e3)); assert_eq!(w.get::<Position>(e3).unwrap().x, 3.0);
1514    }
1515    #[test] fn test_spawn_many() {
1516        let mut w = World::new();
1517        for i in 0..1000u32 { w.spawn(Position { x: i as f32, y: 0.0 }); }
1518        assert_eq!(w.entity_count(), 1000);
1519        assert_eq!(w.query::<Read<Position>, ()>().count(), 1000);
1520    }
1521    #[test] fn test_world_default() { let w = World::default(); assert_eq!(w.entity_count(), 0); }
1522    #[test] fn test_multiple_archetypes() {
1523        let mut w = World::new();
1524        w.spawn(Position { x: 0.0, y: 0.0 }); w.spawn(Health(100.0));
1525        w.spawn((Position { x: 1.0, y: 0.0 }, Health(50.0)));
1526        assert_eq!(w.archetype_count(), 3);
1527        assert_eq!(w.query::<Read<Position>, ()>().count(), 2);
1528        assert_eq!(w.query::<Read<Health>, ()>().count(), 2);
1529        assert_eq!(w.query::<(Read<Position>, Read<Health>), ()>().count(), 1);
1530    }
1531    #[test] fn test_entity_count_after_despawn() {
1532        let mut w = World::new();
1533        let es: Vec<Entity> = (0..10).map(|i| w.spawn(Health(i as f32))).collect();
1534        for &e in &es[..5] { w.despawn(e); }
1535        assert_eq!(w.entity_count(), 5);
1536    }
1537}