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