Skip to main content

galeon_engine/
world.rs

1// SPDX-License-Identifier: AGPL-3.0-only OR Commercial
2
3use std::any::TypeId;
4use std::collections::HashSet;
5
6use crate::archetype::{ArchetypeLayout, ArchetypeStore, EntityLocation};
7use crate::commands::CommandBuffer;
8use crate::component::Component;
9use crate::deadline::{Clock, DeadlineId, Deadlines, Timestamp};
10use crate::entity::{Entity, EntityMetaStore};
11use crate::event::Events;
12use crate::query::{
13    AddedIter, ChangedIter, Mut, Query2Iter, Query2MutIter, Query3Iter, Query3MutIter, QueryFilter,
14    QueryIter, QueryIterMut, QuerySpec, QuerySpecMut,
15};
16use crate::resource::Resources;
17
18// =============================================================================
19// Bundle trait
20// =============================================================================
21
22/// A bundle of components that can be spawned together.
23///
24/// Implemented for tuples of components up to 8 elements.
25/// Provides type IDs for archetype layout computation and column registration.
26pub trait Bundle: 'static {
27    /// Sorted type IDs for all component types in this bundle.
28    ///
29    /// Duplicate component types are rejected to preserve the invariant that
30    /// each archetype column has exactly one value per entity row.
31    fn type_ids() -> Vec<TypeId>;
32
33    /// Register column factories for all component types in this bundle.
34    fn register_columns(store: &mut ArchetypeStore);
35
36    /// Push all component values into the archetype's columns.
37    ///
38    /// The archetype must contain columns for all types in this bundle.
39    fn push_into_columns(self, archetype: &mut crate::archetype::Archetype);
40}
41
42// Implement Bundle for single component.
43impl<A: Component> Bundle for (A,) {
44    fn type_ids() -> Vec<TypeId> {
45        vec![TypeId::of::<A>()]
46    }
47
48    fn register_columns(store: &mut ArchetypeStore) {
49        store.register_column::<A>();
50    }
51
52    fn push_into_columns(self, archetype: &mut crate::archetype::Archetype) {
53        archetype.column_mut::<A>().unwrap().push(self.0);
54    }
55}
56
57// Implement Bundle for tuples of 2-8 components via macro.
58macro_rules! impl_bundle {
59    ($($t:ident),+) => {
60        #[allow(non_snake_case)]
61        impl<$($t: Component),+> Bundle for ($($t,)+) {
62            fn type_ids() -> Vec<TypeId> {
63                let mut ids = vec![$(TypeId::of::<$t>()),+];
64                let original_len = ids.len();
65                ids.sort();
66                ids.dedup();
67                assert_eq!(
68                    ids.len(),
69                    original_len,
70                    "duplicate component types are not allowed in a Bundle"
71                );
72                ids
73            }
74
75            fn register_columns(store: &mut ArchetypeStore) {
76                $(store.register_column::<$t>();)+
77            }
78
79            fn push_into_columns(self, archetype: &mut crate::archetype::Archetype) {
80                let ($($t,)+) = self;
81                $(archetype.column_mut::<$t>().unwrap().push($t);)+
82            }
83        }
84    };
85}
86
87impl_bundle!(A, B);
88impl_bundle!(A, B, C);
89impl_bundle!(A, B, C, D);
90impl_bundle!(A, B, C, D, E);
91impl_bundle!(A, B, C, D, E, F);
92impl_bundle!(A, B, C, D, E, F, G);
93impl_bundle!(A, B, C, D, E, F, G, H);
94
95// =============================================================================
96// UnsafeWorldCell
97// =============================================================================
98
99/// A raw pointer wrapper around `World` that provides field-level access
100/// without creating intermediate `&World` or `&mut World` references.
101///
102/// This eliminates the Stacked Borrows aliasing UB that occurs when
103/// multiple `SystemParam::fetch()` calls create overlapping shared/exclusive
104/// world references. Instead of `(*world).resource::<T>()` (which creates
105/// `&World`), the cell uses `addr_of!` to reach individual fields directly.
106///
107/// # Safety Contract
108///
109/// The caller must ensure:
110/// - The pointed-to `World` is valid for the `'w` lifetime of any returned
111///   reference.
112/// - No two accessors create aliasing mutable references to the same
113///   underlying data. This is guaranteed by the conflict detection system
114///   at system registration time.
115#[derive(Copy, Clone)]
116pub struct UnsafeWorldCell(*mut World);
117
118impl UnsafeWorldCell {
119    /// Create a new cell from a raw world pointer.
120    ///
121    /// # Safety
122    ///
123    /// The pointer must be non-null, well-aligned, and the `World` must
124    /// live for the duration of all accesses through this cell.
125    #[inline]
126    pub unsafe fn new(world: *mut World) -> Self {
127        debug_assert!(!world.is_null());
128        Self(world)
129    }
130
131    /// Get a shared reference to a resource without creating `&World`.
132    ///
133    /// Both `get_resource` and `get_resource_mut` create only `&Resources`
134    /// (shared) via `addr_of!`, using `UnsafeCell`-based interior mutability
135    /// for the write path. This eliminates `&Resources` / `&mut Resources`
136    /// overlap when `Res<A>` and `ResMut<B>` are fetched concurrently.
137    ///
138    /// # Safety
139    ///
140    /// - The resource of type `T` must exist in the world.
141    /// - No mutable reference to the same resource may exist concurrently.
142    #[inline]
143    pub unsafe fn get_resource<'w, T: Send + 'static>(self) -> &'w T {
144        // SAFETY: addr_of! avoids creating &World. The resulting &Resources
145        // is shared — combined with get_unchecked (which also uses &self),
146        // no &mut Resources is ever created on this path.
147        unsafe {
148            let resources_ptr: *const Resources = std::ptr::addr_of!((*self.0).resources);
149            (*resources_ptr).get_unchecked::<T>()
150        }
151    }
152
153    /// Get a mutable reference to a resource without creating `&mut World`
154    /// or `&mut Resources`.
155    ///
156    /// Uses `addr_of!` (not `addr_of_mut!`) to create `&Resources` (shared),
157    /// then reaches into the `UnsafeCell`-wrapped value for interior
158    /// mutability. This ensures `Res<A>` + `ResMut<B>` never produce
159    /// overlapping `&Resources` / `&mut Resources`.
160    ///
161    /// # Safety
162    ///
163    /// - The resource of type `T` must exist in the world.
164    /// - No other reference (shared or mutable) to the same resource may
165    ///   exist concurrently.
166    #[inline]
167    pub unsafe fn get_resource_mut<'w, T: Send + 'static>(self) -> &'w mut T {
168        // SAFETY: addr_of! avoids creating &World or &mut World. We create
169        // only &Resources (shared). get_mut_unchecked uses UnsafeCell for
170        // interior mutability — caller guarantees exclusive resource access.
171        unsafe {
172            let resources_ptr: *const Resources = std::ptr::addr_of!((*self.0).resources);
173            (*resources_ptr).get_mut_unchecked::<T>()
174        }
175    }
176
177    /// Get shared access to the archetype store without creating `&World`.
178    ///
179    /// # Safety
180    ///
181    /// No mutable reference to the archetype store may exist concurrently
182    /// (i.e., no `QueryMut` for any component type may be live).
183    #[inline]
184    pub unsafe fn archetypes<'w>(self) -> &'w ArchetypeStore {
185        // SAFETY: Caller guarantees no mutable archetype access exists.
186        unsafe {
187            let ptr: *const ArchetypeStore = std::ptr::addr_of!((*self.0).archetypes);
188            &*ptr
189        }
190    }
191
192    /// Get a raw mutable pointer to the archetype store without creating
193    /// `&mut World` or `&mut ArchetypeStore`.
194    ///
195    /// Returns `*mut ArchetypeStore` (not a reference) so that
196    /// `QueryIterMut` can be constructed without creating `&mut ArchetypeStore`,
197    /// eliminating the `&ArchetypeStore` / `&mut ArchetypeStore` overlap
198    /// when `Query<A>` and `QueryMut<B>` are fetched concurrently.
199    ///
200    /// # Safety
201    ///
202    /// - The caller must ensure no other reference to the archetype store
203    ///   exists when writing through this pointer.
204    /// - The conflict detection system guarantees disjoint component access.
205    #[inline]
206    pub unsafe fn archetypes_mut_ptr(self) -> *mut ArchetypeStore {
207        // SAFETY: addr_of_mut! computes the field offset without creating
208        // any intermediate reference.
209        unsafe { std::ptr::addr_of_mut!((*self.0).archetypes) }
210    }
211
212    /// Get a mutable reference to the command buffer without creating
213    /// `&mut World`.
214    ///
215    /// # Safety
216    ///
217    /// - No other reference to the command buffer may exist concurrently.
218    /// - The command buffer is a separate field from resources and archetypes,
219    ///   so this does not alias other `UnsafeWorldCell` accessors.
220    #[inline]
221    pub unsafe fn commands_mut<'w>(self) -> &'w mut CommandBuffer {
222        // SAFETY: addr_of_mut! avoids creating &mut World. Caller guarantees
223        // exclusive access to the command buffer field.
224        unsafe {
225            let ptr: *mut CommandBuffer = std::ptr::addr_of_mut!((*self.0).commands);
226            &mut *ptr
227        }
228    }
229
230    /// Read the current change-detection tick without creating `&World`.
231    ///
232    /// # Safety
233    ///
234    /// The `World` must be valid for the duration of this call.
235    #[inline]
236    pub unsafe fn change_tick(self) -> u64 {
237        unsafe {
238            let ptr: *const u64 = std::ptr::addr_of!((*self.0).change_tick);
239            *ptr
240        }
241    }
242}
243
244// =============================================================================
245// World
246// =============================================================================
247
248/// Type-erased closure that advances a single `Events<T>` double buffer.
249type EventUpdater = Box<dyn Fn(&mut World) + Send>;
250
251/// Type-erased closure that drains overdue deadlines for a single `Deadlines<T>`.
252type DeadlineDrainer = Box<dyn Fn(&mut World, Timestamp) + Send>;
253
254/// The ECS world: owns entities, archetype storage, resources, and commands.
255pub struct World {
256    meta: EntityMetaStore,
257    archetypes: ArchetypeStore,
258    resources: Resources,
259    commands: CommandBuffer,
260    /// Monotonically increasing change-detection tick. Starts at 1; tick 0 is
261    /// the sentinel meaning "never observed". Incremented by `advance_tick()`.
262    change_tick: u64,
263    /// Type-erased closures that advance each registered `Events<T>` buffer.
264    ///
265    /// Populated by [`World::add_event::<T>()`]. Each closure calls
266    /// `Events::<T>::update()` on the corresponding resource. Called by
267    /// [`World::update_events()`] at the start of every `Schedule::run()`.
268    event_updaters: Vec<EventUpdater>,
269    /// TypeIds of event types that have been registered via `add_event`.
270    /// Guards against duplicate updater registration even if the `Events<T>`
271    /// resource is removed and re-added.
272    registered_events: HashSet<TypeId>,
273    /// Type-erased closures that drain overdue deadlines for each registered
274    /// `Deadlines<T>`. Called by [`World::drain_all_deadlines()`] at the
275    /// start of every `Schedule::run()`, *before* `update_events()`.
276    deadline_drainers: Vec<DeadlineDrainer>,
277    /// TypeIds of deadline types registered via `add_deadline_type`.
278    registered_deadlines: HashSet<TypeId>,
279    /// `(entity, component, tick)` for each successful [`World::remove::<C>`].
280    ///
281    /// Surviving components keep their `changed_tick` when an entity migrates
282    /// after a removal, so change iterators alone cannot observe that a type
283    /// is gone. Consumers such as render extraction use
284    /// [`World::component_removals_since`] to reconcile defaults (for example
285    /// implicit `ObjectType::Mesh` after `ObjectType` is removed).
286    component_removals: Vec<(Entity, TypeId, u64)>,
287    /// Counter incremented each time `update_events()` swaps the double buffers.
288    /// Used by render-event extractors to detect swaps and reset their offsets.
289    event_swap_epoch: u64,
290}
291
292impl World {
293    /// Create an empty world with a single empty archetype.
294    pub fn new() -> Self {
295        let mut archetypes = ArchetypeStore::new();
296        // Create the "void" archetype for entities with no components.
297        archetypes.get_or_create(ArchetypeLayout::empty());
298        Self {
299            meta: EntityMetaStore::new(),
300            archetypes,
301            resources: Resources::new(),
302            commands: CommandBuffer::new(),
303            change_tick: 1,
304            event_updaters: Vec::new(),
305            registered_events: HashSet::new(),
306            deadline_drainers: Vec::new(),
307            registered_deadlines: HashSet::new(),
308            component_removals: Vec::new(),
309            event_swap_epoch: 0,
310        }
311    }
312
313    // -------------------------------------------------------------------------
314    // Change-detection tick
315    // -------------------------------------------------------------------------
316
317    /// The current change-detection tick. Starts at 1; 0 is the sentinel.
318    pub fn change_tick(&self) -> u64 {
319        self.change_tick
320    }
321
322    /// Advance the change-detection tick by one. Returns the new tick value.
323    pub fn advance_tick(&mut self) -> u64 {
324        self.change_tick += 1;
325        let ct = self.change_tick;
326        // Prevent unbounded growth: removals older than this window are only
327        // relevant if `since_tick` lags by more than `REMOVAL_LOG_TICK_WINDOW`,
328        // which normal game-loop extraction should not do.
329        const REMOVAL_LOG_TICK_WINDOW: u64 = 10_000;
330        self.component_removals
331            .retain(|(_, _, tick)| *tick + REMOVAL_LOG_TICK_WINDOW > ct);
332        ct
333    }
334
335    // -------------------------------------------------------------------------
336    // Entity lifecycle
337    // -------------------------------------------------------------------------
338
339    /// Spawn an entity with the given component bundle.
340    pub fn spawn<B: Bundle>(&mut self, bundle: B) -> Entity {
341        let type_ids = B::type_ids();
342        let entity = self.meta.alloc();
343        B::register_columns(&mut self.archetypes);
344        let layout = ArchetypeLayout::from_type_ids(&type_ids);
345        let arch_id = self.archetypes.get_or_create(layout);
346        let tick = self.change_tick;
347        let arch = self.archetypes.get_mut(arch_id);
348        bundle.push_into_columns(arch);
349        let row = arch.push_entity(entity);
350        // Stamp all columns: newly spawned entity is both added and changed.
351        arch.stamp_all_ticks(row, tick, tick);
352        self.meta.set_location(
353            entity,
354            EntityLocation {
355                archetype_id: arch_id,
356                row,
357            },
358        );
359        entity
360    }
361
362    /// Despawn an entity, removing all its components.
363    pub fn despawn(&mut self, entity: Entity) -> bool {
364        let Some(loc) = self.meta.get_location(entity) else {
365            return false;
366        };
367
368        let arch = self.archetypes.get_mut(loc.archetype_id);
369        let (_removed, moved) = arch.swap_remove_entity(loc.row as usize);
370
371        // If another entity was swapped into the removed row, update its location.
372        if let Some(moved_entity) = moved {
373            self.meta.set_location(
374                moved_entity,
375                EntityLocation {
376                    archetype_id: loc.archetype_id,
377                    row: loc.row,
378                },
379            );
380        }
381
382        self.meta.dealloc(entity);
383        true
384    }
385
386    /// Check whether an entity is alive.
387    pub fn is_alive(&self, entity: Entity) -> bool {
388        self.meta.is_alive(entity)
389    }
390
391    // -------------------------------------------------------------------------
392    // Resources
393    // -------------------------------------------------------------------------
394
395    /// Insert a resource (world-global singleton).
396    pub fn insert_resource<T: Send + 'static>(&mut self, value: T) {
397        self.resources.insert(value);
398    }
399
400    /// Get a reference to a resource. Panics if not present.
401    pub fn resource<T: Send + 'static>(&self) -> &T {
402        self.resources.get::<T>()
403    }
404
405    /// Get a mutable reference to a resource. Panics if not present.
406    pub fn resource_mut<T: Send + 'static>(&mut self) -> &mut T {
407        self.resources.get_mut::<T>()
408    }
409
410    /// Try to get a reference to a resource. Returns `None` if not present.
411    pub fn try_resource<T: Send + 'static>(&self) -> Option<&T> {
412        self.resources.try_get::<T>()
413    }
414
415    /// Remove and return a resource. Panics if not present.
416    pub fn take_resource<T: Send + 'static>(&mut self) -> T {
417        self.resources.take::<T>()
418    }
419
420    /// Try to remove and return a resource. Returns `None` if not present.
421    pub fn try_take_resource<T: Send + 'static>(&mut self) -> Option<T> {
422        self.resources.try_take::<T>()
423    }
424
425    // -------------------------------------------------------------------------
426    // Commands
427    // -------------------------------------------------------------------------
428
429    /// Drain and apply all queued commands.
430    ///
431    /// Called automatically between schedule stages. Can also be called
432    /// manually for setup code that uses deferred mutations.
433    pub fn apply_commands(&mut self) {
434        // Take the queue out to avoid borrowing self.commands while
435        // executing commands that need &mut World.
436        let commands = self.commands.take();
437        for cmd in commands {
438            cmd(self);
439        }
440    }
441
442    /// Get a mutable reference to the command buffer.
443    ///
444    /// This is the low-level escape hatch for tests and setup code. Systems
445    /// should use the [`Commands`](crate::commands::Commands) system parameter.
446    pub fn command_buffer_mut(&mut self) -> &mut CommandBuffer {
447        &mut self.commands
448    }
449
450    // -------------------------------------------------------------------------
451    // Events
452    // -------------------------------------------------------------------------
453
454    /// Register an event type and insert its `Events<T>` resource.
455    ///
456    /// Must be called before any system uses `EventWriter<T>` or
457    /// `EventReader<T>`. Idempotent:
458    ///
459    /// - **First call**: inserts `Events<T>` resource and registers the
460    ///   updater closure.
461    /// - **Duplicate call (resource still exists)**: no-op — does not reset
462    ///   queued events or duplicate the updater.
463    /// - **Re-call after resource removal**: restores a fresh `Events<T>`
464    ///   resource without duplicating the updater.
465    pub fn add_event<T: Send + 'static>(&mut self) {
466        if self.registered_events.insert(TypeId::of::<Events<T>>()) {
467            // First registration: insert resource + updater.
468            self.resources.insert(Events::<T>::new());
469            self.event_updaters.push(Box::new(|world: &mut World| {
470                world.resources.get_mut::<Events<T>>().update();
471            }));
472        } else if !self.resources.contains::<Events<T>>() {
473            // Resource was removed externally — restore it. Updater already
474            // exists so we only need the resource back.
475            self.resources.insert(Events::<T>::new());
476        }
477    }
478
479    /// Advance all registered event buffers.
480    ///
481    /// Swaps each `Events<T>`'s `current` buffer into `previous` and clears
482    /// `current`. Called automatically at the start of every
483    /// [`Schedule::run()`](crate::schedule::Schedule::run) so that events
484    /// written in tick N are readable in tick N+1.
485    /// Flush registered render events from `Events<T>::current` into the
486    /// [`RenderEventRegistry`] accumulation buffer.
487    ///
488    /// Called by [`Schedule::run`] after all systems, **before** the next
489    /// tick's `update_events()` swaps the buffers. This ensures every tick's
490    /// events are captured even when multiple ticks run per render frame
491    /// (same pattern as Bevy's deferred buffer swap).
492    pub fn flush_render_events(&self) {
493        if let Some(registry) = self.try_resource::<crate::render_event::RenderEventRegistry>() {
494            registry.accumulate(self);
495        }
496    }
497
498    pub fn update_events(&mut self) {
499        // SAFETY: We must call the updaters without holding a borrow on
500        // `event_updaters` while also needing `&mut self` inside each closure.
501        // We swap the vec out, call each closure, then swap it back.
502        //
503        // INVARIANT: EventUpdater closures MUST only access `self.resources`.
504        // They must never touch `self.event_updaters` (which is empty during
505        // this loop due to mem::take), `self.archetypes`, `self.commands`, or
506        // any other World field. This invariant is maintained by construction —
507        // all closures registered by `add_event` only call
508        // `world.resources.get_mut::<Events<T>>().update()`.
509        let mut updaters = std::mem::take(&mut self.event_updaters);
510        for updater in &updaters {
511            updater(self);
512        }
513        // Restore the updaters vec (reuse the allocation).
514        std::mem::swap(&mut self.event_updaters, &mut updaters);
515        self.event_swap_epoch += 1;
516    }
517
518    /// Monotonic counter incremented each time `update_events()` swaps.
519    /// Used by render-event extractors to detect swaps and reset offsets.
520    pub fn event_swap_epoch(&self) -> u64 {
521        self.event_swap_epoch
522    }
523
524    // -------------------------------------------------------------------------
525    // Deadlines
526    // -------------------------------------------------------------------------
527
528    /// Register a deadline event type and insert its `Deadlines<T>` and
529    /// `Events<T>` resources.
530    ///
531    /// Must be called before scheduling deadlines of type `T`. Idempotent.
532    /// Registers a drainer closure so that [`drain_all_deadlines()`](World::drain_all_deadlines)
533    /// automatically fires overdue deadlines of this type.
534    pub fn add_deadline_type<T: Send + 'static>(&mut self) {
535        if self.try_resource::<Deadlines<T>>().is_none() {
536            self.insert_resource(Deadlines::<T>::new());
537        }
538        // Ensure the Events<T> resource exists for fired deadline delivery.
539        self.add_event::<T>();
540        // Register the drainer (once per type).
541        if self
542            .registered_deadlines
543            .insert(TypeId::of::<Deadlines<T>>())
544        {
545            self.deadline_drainers
546                .push(Box::new(|world: &mut World, now: Timestamp| {
547                    world.drain_deadlines::<T>(now);
548                }));
549        }
550    }
551
552    /// Schedule an event to fire when `now >= deadline`.
553    ///
554    /// Returns a [`DeadlineId`] for cancellation. The event type must have
555    /// been registered with [`add_deadline_type::<T>()`](World::add_deadline_type).
556    ///
557    /// # Panics
558    ///
559    /// Panics if `Deadlines<T>` has not been registered.
560    pub fn schedule_deadline<T: Send + 'static>(
561        &mut self,
562        deadline: Timestamp,
563        event: T,
564    ) -> DeadlineId {
565        self.resource_mut::<Deadlines<T>>()
566            .schedule(deadline, event)
567    }
568
569    /// Cancel a previously scheduled deadline.
570    ///
571    /// Returns `true` if the deadline was found and removed.
572    ///
573    /// # Panics
574    ///
575    /// Panics if `Deadlines<T>` has not been registered.
576    pub fn cancel_deadline<T: Send + 'static>(&mut self, id: DeadlineId) -> bool {
577        self.resource_mut::<Deadlines<T>>().cancel(id)
578    }
579
580    /// Drain all overdue deadlines of type `T` and write them as events.
581    ///
582    /// Fires all entries where `now >= deadline`, supporting batch
583    /// reconciliation. Can be called manually for a single type, but
584    /// prefer [`drain_all_deadlines()`](World::drain_all_deadlines) which
585    /// drains all registered types automatically.
586    ///
587    /// # Panics
588    ///
589    /// Panics if `Deadlines<T>` or `Events<T>` has not been registered.
590    pub fn drain_deadlines<T: Send + 'static>(&mut self, now: Timestamp) {
591        let fired = self.resource_mut::<Deadlines<T>>().drain_overdue(now);
592        let events = self.resource_mut::<Events<T>>();
593        for event in fired {
594            events.send(event);
595        }
596    }
597
598    /// Drain all overdue deadlines for every registered deadline type.
599    ///
600    /// Reads the [`Clock`] resource to determine "now", then calls
601    /// [`drain_deadlines::<T>(now)`](World::drain_deadlines) for each type
602    /// registered via [`add_deadline_type::<T>()`](World::add_deadline_type).
603    ///
604    /// Called automatically by [`Schedule::run()`](crate::schedule::Schedule::run)
605    /// *before* [`update_events()`](World::update_events), so that fired
606    /// deadline events are readable by `EventReader<T>` in the same tick.
607    ///
608    /// If no `Clock` resource is present, this is a no-op (deadlines are
609    /// only active when a clock is installed).
610    pub fn drain_all_deadlines(&mut self) {
611        // Read the clock. If no clock resource, skip silently.
612        let now = match self.resources.try_get::<Box<dyn Clock>>() {
613            Some(clock) => clock.now(),
614            None => return,
615        };
616
617        // Same swap-out pattern as update_events: take the drainers vec,
618        // call each one, then restore it.
619        let mut drainers = std::mem::take(&mut self.deadline_drainers);
620        for drainer in &drainers {
621            drainer(self, now);
622        }
623        std::mem::swap(&mut self.deadline_drainers, &mut drainers);
624    }
625
626    // -------------------------------------------------------------------------
627    // Component access
628    // -------------------------------------------------------------------------
629
630    /// Get a component for an entity.
631    pub fn get<T: Component>(&self, entity: Entity) -> Option<&T> {
632        self.one::<&T>(entity)
633    }
634
635    /// Get a mutable component for an entity.
636    ///
637    /// Returns `Mut<T>` — reading via `Deref` does not stamp the change tick;
638    /// only writing via `DerefMut` does.
639    pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<'_, T>> {
640        self.one_mut::<&mut T>(entity)
641    }
642
643    /// Fetch a typed query item for a single entity.
644    pub fn one<Q: QuerySpec>(&self, entity: Entity) -> Option<Q::Item<'_>> {
645        let loc = self.meta.get_location(entity)?;
646        let arch = self.archetypes.get(loc.archetype_id);
647        if !Q::matches(arch.layout()) {
648            return None;
649        }
650        let state = Q::init_state(arch)?;
651        Some(Q::fetch(&state, loc.row as usize))
652    }
653
654    /// Fetch a typed mutable query item for a single entity.
655    pub fn one_mut<Q: QuerySpecMut>(&mut self, entity: Entity) -> Option<Q::Item<'_>> {
656        let loc = self.meta.get_location(entity)?;
657        let tick = self.change_tick;
658        let arch = self.archetypes.get_mut(loc.archetype_id);
659        if !Q::matches(arch.layout()) {
660            return None;
661        }
662        let mut state = Q::init_state(arch, tick)?;
663        let row = loc.row as usize;
664        // SAFETY: `loc.row` identifies one concrete row in the matched
665        // archetype, so returning the fetched mutable references cannot alias
666        // any other result from this method call.
667        // Change-tick stamping is deferred to `Mut<T>::deref_mut()`.
668        let item = unsafe { Q::fetch(&mut state, row) };
669        Some(item)
670    }
671
672    // -------------------------------------------------------------------------
673    // Component mutations (archetype migration)
674    // -------------------------------------------------------------------------
675
676    /// Insert a component into an entity. If the entity already has this
677    /// component type, the value is overwritten in place. Otherwise, the
678    /// entity migrates to an archetype that includes the new component.
679    ///
680    /// # Change detection
681    ///
682    /// When overwriting an existing component, only `changed_tick` is stamped
683    /// at the current tick. `added_tick` is preserved from the original
684    /// insertion (typically `spawn`). Use `query_added` to detect newly added
685    /// components; it will not fire for overwrites.
686    pub fn insert<C: Component>(&mut self, entity: Entity, value: C) {
687        let Some(loc) = self.meta.get_location(entity) else {
688            return; // dead entity
689        };
690
691        let src_arch_id = loc.archetype_id;
692        let row = loc.row as usize;
693
694        // If archetype already has C, overwrite in place.
695        if self
696            .archetypes
697            .get(src_arch_id)
698            .layout()
699            .contains(TypeId::of::<C>())
700        {
701            let tick = self.change_tick;
702            let arch = self.archetypes.get_mut(src_arch_id);
703            if let Some(col) = arch.column_mut::<C>() {
704                if let Some(slot) = col.get_mut(row) {
705                    *slot = value;
706                }
707                col.set_changed_tick(row, tick);
708            }
709            return;
710        }
711
712        // Register column factory for C.
713        self.archetypes.register_column::<C>();
714
715        // Compute target archetype.
716        let new_layout = self
717            .archetypes
718            .get(src_arch_id)
719            .layout()
720            .with_added(TypeId::of::<C>());
721        let dst_arch_id = self.archetypes.get_or_create(new_layout);
722
723        // Collect source layout type IDs before mutable borrow.
724        let src_type_ids: Vec<TypeId> = self.archetypes.get(src_arch_id).layout().iter().collect();
725
726        let tick = self.change_tick;
727
728        // Move all existing columns from source to destination.
729        // Ticks are preserved by move_to.
730        let (src_arch, dst_arch) = self.archetypes.get_two_mut(src_arch_id, dst_arch_id);
731        for &tid in &src_type_ids {
732            let src_col = src_arch.column_raw_mut(tid).unwrap();
733            let dst_col = dst_arch.column_raw_mut(tid).unwrap();
734            src_col.move_to(row, dst_col);
735        }
736
737        // Push the new component into the destination.
738        dst_arch.column_mut::<C>().unwrap().push(value);
739
740        // Swap-remove entity entry from source (column data already moved).
741        let (_removed, moved) = src_arch.swap_remove_entity_entry(row);
742
743        // Push entity into destination.
744        let new_row = dst_arch.push_entity(entity);
745
746        // Stamp ticks on the newly added component C (added + changed).
747        dst_arch.stamp_column_ticks(TypeId::of::<C>(), new_row, tick, tick);
748
749        // Update locations.
750        self.meta.set_location(
751            entity,
752            EntityLocation {
753                archetype_id: dst_arch_id,
754                row: new_row,
755            },
756        );
757        if let Some(moved_entity) = moved {
758            self.meta.set_location(
759                moved_entity,
760                EntityLocation {
761                    archetype_id: src_arch_id,
762                    row: loc.row,
763                },
764            );
765        }
766    }
767
768    /// Remove a component from an entity. If the entity doesn't have this
769    /// component, this is a no-op. Otherwise, the entity migrates to an
770    /// archetype without the component.
771    pub fn remove<C: Component>(&mut self, entity: Entity) {
772        let Some(loc) = self.meta.get_location(entity) else {
773            return; // dead entity
774        };
775
776        let src_arch_id = loc.archetype_id;
777        let row = loc.row as usize;
778
779        // If archetype doesn't have C, no-op.
780        if !self
781            .archetypes
782            .get(src_arch_id)
783            .layout()
784            .contains(TypeId::of::<C>())
785        {
786            return;
787        }
788
789        // Compute target archetype (without C).
790        let new_layout = self
791            .archetypes
792            .get(src_arch_id)
793            .layout()
794            .with_removed(TypeId::of::<C>());
795        let dst_arch_id = self.archetypes.get_or_create(new_layout);
796
797        // Collect source layout type IDs.
798        let src_type_ids: Vec<TypeId> = self.archetypes.get(src_arch_id).layout().iter().collect();
799
800        let c_type_id = TypeId::of::<C>();
801
802        // Move/drop columns.
803        let (src_arch, dst_arch) = self.archetypes.get_two_mut(src_arch_id, dst_arch_id);
804        for &tid in &src_type_ids {
805            let src_col = src_arch.column_raw_mut(tid).unwrap();
806            if tid == c_type_id {
807                // Drop the removed component's data.
808                src_col.swap_remove_and_drop(row);
809            } else {
810                // Move to destination.
811                let dst_col = dst_arch.column_raw_mut(tid).unwrap();
812                src_col.move_to(row, dst_col);
813            }
814        }
815
816        // Swap-remove entity entry from source.
817        let (_removed, moved) = src_arch.swap_remove_entity_entry(row);
818
819        // Push entity into destination.
820        let new_row = dst_arch.push_entity(entity);
821
822        // Update locations.
823        self.meta.set_location(
824            entity,
825            EntityLocation {
826                archetype_id: dst_arch_id,
827                row: new_row,
828            },
829        );
830        if let Some(moved_entity) = moved {
831            self.meta.set_location(
832                moved_entity,
833                EntityLocation {
834                    archetype_id: src_arch_id,
835                    row: loc.row,
836                },
837            );
838        }
839
840        self.component_removals
841            .push((entity, c_type_id, self.change_tick));
842    }
843
844    /// Entities that had component `C` removed after `since_tick` (exclusive of
845    /// `since_tick`, inclusive of the current [`World::change_tick`]).
846    ///
847    /// Recorded only for successful [`World::remove::<C>`] calls (not despawns).
848    /// The same entity may appear more than once if removals occurred on
849    /// different ticks; callers typically deduplicate.
850    pub fn component_removals_since<C: Component>(
851        &self,
852        since_tick: u64,
853    ) -> impl Iterator<Item = Entity> + '_ {
854        let tid = TypeId::of::<C>();
855        self.component_removals
856            .iter()
857            .filter(move |(_, t, tick)| *t == tid && *tick > since_tick)
858            .map(|(e, _, _)| *e)
859    }
860
861    // -------------------------------------------------------------------------
862    // Queries
863    // -------------------------------------------------------------------------
864
865    /// Query all entities matching `Q`.
866    pub fn query<Q: QuerySpec>(&self) -> QueryIter<'_, Q> {
867        QueryIter::new(&self.archetypes)
868    }
869
870    /// Query all entities matching `Q` and `F`.
871    pub fn query_filtered<Q: QuerySpec, F: QueryFilter>(&self) -> QueryIter<'_, Q, F> {
872        QueryIter::new(&self.archetypes)
873    }
874
875    /// Query all entities mutably matching `Q`.
876    pub fn query_mut<Q: QuerySpecMut>(&mut self) -> QueryIterMut<'_, Q> {
877        let tick = self.change_tick;
878        QueryIterMut::new(&mut self.archetypes, tick)
879    }
880
881    /// Query all entities mutably matching `Q` and `F`.
882    pub fn query_filtered_mut<Q: QuerySpecMut, F: QueryFilter>(
883        &mut self,
884    ) -> QueryIterMut<'_, Q, F> {
885        let tick = self.change_tick;
886        QueryIterMut::new(&mut self.archetypes, tick)
887    }
888
889    /// Convenience wrapper for two-component immutable queries.
890    pub fn query2<A: Component, B: Component>(&self) -> Query2Iter<'_, A, B> {
891        self.query::<(&A, &B)>()
892    }
893
894    /// Convenience wrapper for two-component mutable queries.
895    pub fn query2_mut<A: Component, B: Component>(&mut self) -> Query2MutIter<'_, A, B> {
896        assert_ne!(
897            TypeId::of::<A>(),
898            TypeId::of::<B>(),
899            "cannot borrow the same column mutably twice"
900        );
901        self.query_mut::<(&mut A, &mut B)>()
902    }
903
904    /// Convenience wrapper for three-component immutable queries.
905    pub fn query3<A: Component, B: Component, C: Component>(&self) -> Query3Iter<'_, A, B, C> {
906        self.query::<(&A, &B, &C)>()
907    }
908
909    /// Convenience wrapper for three-component mutable queries.
910    pub fn query3_mut<A: Component, B: Component, C: Component>(
911        &mut self,
912    ) -> Query3MutIter<'_, A, B, C> {
913        assert_ne!(
914            TypeId::of::<A>(),
915            TypeId::of::<B>(),
916            "cannot borrow the same column mutably twice"
917        );
918        assert_ne!(
919            TypeId::of::<A>(),
920            TypeId::of::<C>(),
921            "cannot borrow the same column mutably twice"
922        );
923        assert_ne!(
924            TypeId::of::<B>(),
925            TypeId::of::<C>(),
926            "cannot borrow the same column mutably twice"
927        );
928        self.query_mut::<(&mut A, &mut B, &mut C)>()
929    }
930
931    // -------------------------------------------------------------------------
932    // Change-detection queries
933    // -------------------------------------------------------------------------
934
935    /// Iterate entities whose component `T` changed after `since_tick`.
936    pub fn query_changed<T: Component>(&self, since_tick: u64) -> ChangedIter<'_, T> {
937        ChangedIter::new(&self.archetypes, since_tick)
938    }
939
940    /// Iterate entities whose component `T` was added after `since_tick`.
941    pub fn query_added<T: Component>(&self, since_tick: u64) -> AddedIter<'_, T> {
942        AddedIter::new(&self.archetypes, since_tick)
943    }
944
945    // -------------------------------------------------------------------------
946    // Metadata
947    // -------------------------------------------------------------------------
948
949    /// Returns the number of alive entities.
950    pub fn entity_count(&self) -> usize {
951        self.meta.alive_entities().count()
952    }
953
954    /// Shared access to the archetype store (for change-detection inspection).
955    pub fn archetypes(&self) -> &ArchetypeStore {
956        &self.archetypes
957    }
958
959    /// Look up where an entity lives in archetype storage.
960    pub fn entity_location(&self, entity: Entity) -> Option<EntityLocation> {
961        self.meta.get_location(entity)
962    }
963}
964
965impl Default for World {
966    fn default() -> Self {
967        Self::new()
968    }
969}
970
971// =============================================================================
972// Tests
973// =============================================================================
974
975#[cfg(test)]
976mod tests {
977    use super::*;
978
979    #[derive(Debug, Clone, PartialEq)]
980    struct Pos {
981        x: f32,
982        y: f32,
983    }
984    impl Component for Pos {}
985
986    #[derive(Debug, Clone, PartialEq)]
987    struct Vel {
988        x: f32,
989        y: f32,
990    }
991    impl Component for Vel {}
992
993    #[derive(Debug, Clone)]
994    #[allow(dead_code)]
995    struct Health(i32);
996    impl Component for Health {}
997
998    // -- Original tests (behavioral equivalence) --
999
1000    #[test]
1001    fn world_is_send() {
1002        fn assert_send<T: Send>() {}
1003        assert_send::<World>();
1004    }
1005
1006    #[test]
1007    fn spawn_and_get() {
1008        let mut world = World::new();
1009        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1010        assert!(world.is_alive(e));
1011        let pos = world.get::<Pos>(e).unwrap();
1012        assert_eq!(pos.x, 1.0);
1013        assert_eq!(pos.y, 2.0);
1014    }
1015
1016    #[test]
1017    fn spawn_multi_component() {
1018        let mut world = World::new();
1019        let e = world.spawn((Pos { x: 0.0, y: 0.0 }, Vel { x: 1.0, y: 2.0 }));
1020        assert!(world.get::<Pos>(e).is_some());
1021        assert!(world.get::<Vel>(e).is_some());
1022    }
1023
1024    #[test]
1025    fn despawn_removes_entity_and_components() {
1026        let mut world = World::new();
1027        let e = world.spawn((Pos { x: 0.0, y: 0.0 }, Health(100)));
1028        assert!(world.despawn(e));
1029        assert!(!world.is_alive(e));
1030        assert!(world.get::<Pos>(e).is_none());
1031        assert!(world.get::<Health>(e).is_none());
1032    }
1033
1034    #[test]
1035    fn query_iterates_matching_entities() {
1036        let mut world = World::new();
1037        world.spawn((Pos { x: 1.0, y: 0.0 },));
1038        world.spawn((Pos { x: 2.0, y: 0.0 }, Vel { x: 0.0, y: 0.0 }));
1039        world.spawn((Vel { x: 3.0, y: 0.0 },)); // no Pos
1040
1041        let positions: Vec<f32> = world.query::<&Pos>().map(|(_, p)| p.x).collect();
1042        assert_eq!(positions.len(), 2);
1043        assert!(positions.contains(&1.0));
1044        assert!(positions.contains(&2.0));
1045    }
1046
1047    #[test]
1048    fn query_mut_allows_modification() {
1049        let mut world = World::new();
1050        world.spawn((Pos { x: 0.0, y: 0.0 },));
1051        world.spawn((Pos { x: 10.0, y: 10.0 },));
1052
1053        for (_, mut pos) in world.query_mut::<&mut Pos>() {
1054            pos.x += 1.0;
1055        }
1056
1057        let xs: Vec<f32> = world.query::<&Pos>().map(|(_, p)| p.x).collect();
1058        assert!(xs.contains(&1.0));
1059        assert!(xs.contains(&11.0));
1060    }
1061
1062    #[test]
1063    fn resources() {
1064        let mut world = World::new();
1065
1066        struct DeltaTime(f64);
1067
1068        world.insert_resource(DeltaTime(0.016));
1069        assert_eq!(world.resource::<DeltaTime>().0, 0.016);
1070
1071        world.resource_mut::<DeltaTime>().0 = 0.032;
1072        assert_eq!(world.resource::<DeltaTime>().0, 0.032);
1073    }
1074
1075    #[test]
1076    fn entity_count() {
1077        let mut world = World::new();
1078        assert_eq!(world.entity_count(), 0);
1079        let e1 = world.spawn((Pos { x: 0.0, y: 0.0 },));
1080        let _e2 = world.spawn((Pos { x: 0.0, y: 0.0 },));
1081        assert_eq!(world.entity_count(), 2);
1082        world.despawn(e1);
1083        assert_eq!(world.entity_count(), 1);
1084    }
1085
1086    #[test]
1087    fn query2_returns_entities_with_both_components() {
1088        let mut world = World::new();
1089        world.spawn((Pos { x: 1.0, y: 0.0 },));
1090        world.spawn((Pos { x: 2.0, y: 0.0 }, Vel { x: 5.0, y: 0.0 }));
1091        world.spawn((Vel { x: 3.0, y: 0.0 },));
1092
1093        let results: Vec<_> = world.query::<(&Pos, &Vel)>().collect();
1094        assert_eq!(results.len(), 1);
1095        assert_eq!(results[0].1.0.x, 2.0);
1096        assert_eq!(results[0].1.1.x, 5.0);
1097    }
1098
1099    #[test]
1100    fn query2_mut_mutates_both_components() {
1101        let mut world = World::new();
1102        let e1 = world.spawn((Pos { x: 1.0, y: 1.0 }, Vel { x: 10.0, y: 10.0 }));
1103        let e2 = world.spawn((Pos { x: 2.0, y: 2.0 }, Vel { x: 20.0, y: 20.0 }));
1104        let e3 = world.spawn((Pos { x: 3.0, y: 3.0 }, Vel { x: 30.0, y: 30.0 }));
1105
1106        for (_, (mut pos, mut vel)) in world.query_mut::<(&mut Pos, &mut Vel)>() {
1107            pos.x += 100.0;
1108            vel.y += 200.0;
1109        }
1110
1111        assert_eq!(world.get::<Pos>(e1).unwrap().x, 101.0);
1112        assert_eq!(world.get::<Vel>(e1).unwrap().y, 210.0);
1113        assert_eq!(world.get::<Pos>(e2).unwrap().x, 102.0);
1114        assert_eq!(world.get::<Vel>(e2).unwrap().y, 220.0);
1115        assert_eq!(world.get::<Pos>(e3).unwrap().x, 103.0);
1116        assert_eq!(world.get::<Vel>(e3).unwrap().y, 230.0);
1117    }
1118
1119    #[test]
1120    fn query2_mut_skips_entities_missing_one_component() {
1121        let mut world = World::new();
1122        let e1 = world.spawn((Pos { x: 5.0, y: 5.0 },));
1123        let e2 = world.spawn((Pos { x: 7.0, y: 7.0 }, Vel { x: 9.0, y: 9.0 }));
1124        let e3 = world.spawn((Vel { x: 11.0, y: 11.0 },));
1125
1126        let results: Vec<_> = world.query_mut::<(&mut Pos, &mut Vel)>().collect();
1127        assert_eq!(results.len(), 1);
1128
1129        let (entity, (pos, vel)) = &results[0];
1130        assert_eq!(*entity, e2);
1131        assert_eq!(pos.x, 7.0);
1132        assert_eq!(vel.x, 9.0);
1133
1134        // e1's Pos must be unchanged.
1135        assert_eq!(world.get::<Pos>(e1).unwrap().x, 5.0);
1136        // e3's Vel must be unchanged.
1137        assert_eq!(world.get::<Vel>(e3).unwrap().x, 11.0);
1138    }
1139
1140    #[test]
1141    #[should_panic(expected = "cannot borrow the same column mutably twice")]
1142    fn query2_mut_same_type_panics() {
1143        let mut world = World::new();
1144        world.spawn((Pos { x: 0.0, y: 0.0 },));
1145        let _ = world.query_mut::<(&mut Pos, &mut Pos)>().next();
1146    }
1147
1148    #[test]
1149    fn query_filtered_with_and_without() {
1150        let mut world = World::new();
1151        let e1 = world.spawn((Pos { x: 1.0, y: 0.0 }, Vel { x: 2.0, y: 0.0 }));
1152        let _e2 = world.spawn((Pos { x: 3.0, y: 0.0 },));
1153        let _e3 = world.spawn((Pos { x: 4.0, y: 0.0 }, Health(100)));
1154
1155        let results: Vec<_> = world
1156            .query_filtered::<&Pos, (crate::query::With<Vel>, crate::query::Without<Health>)>()
1157            .collect();
1158        assert_eq!(results.len(), 1);
1159        assert_eq!(results[0].0, e1);
1160        assert_eq!(results[0].1.x, 1.0);
1161    }
1162
1163    #[test]
1164    fn one_and_one_mut_fetch_single_entity() {
1165        let mut world = World::new();
1166        let e = world.spawn((Pos { x: 1.0, y: 2.0 }, Vel { x: 3.0, y: 4.0 }));
1167
1168        let (pos, vel) = world.one::<(&Pos, &Vel)>(e).unwrap();
1169        assert_eq!(pos.x, 1.0);
1170        assert_eq!(vel.x, 3.0);
1171
1172        let (mut pos, mut vel) = world.one_mut::<(&mut Pos, &mut Vel)>(e).unwrap();
1173        pos.x = 10.0;
1174        vel.y = 20.0;
1175
1176        assert_eq!(world.get::<Pos>(e).unwrap().x, 10.0);
1177        assert_eq!(world.get::<Vel>(e).unwrap().y, 20.0);
1178    }
1179
1180    // -- New tests for insert/remove (archetype migration) --
1181
1182    #[test]
1183    fn insert_adds_component_to_entity() {
1184        let mut world = World::new();
1185        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1186        assert!(world.get::<Vel>(e).is_none());
1187
1188        world.insert(e, Vel { x: 3.0, y: 4.0 });
1189
1190        assert_eq!(world.get::<Vel>(e).unwrap().x, 3.0);
1191        // Original component preserved.
1192        assert_eq!(world.get::<Pos>(e).unwrap().x, 1.0);
1193    }
1194
1195    #[test]
1196    fn insert_overwrites_existing_component() {
1197        let mut world = World::new();
1198        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1199        world.insert(e, Pos { x: 99.0, y: 99.0 });
1200        assert_eq!(world.get::<Pos>(e).unwrap().x, 99.0);
1201    }
1202
1203    #[test]
1204    fn insert_dead_entity_is_noop() {
1205        let mut world = World::new();
1206        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1207        world.despawn(e);
1208        world.insert(e, Vel { x: 1.0, y: 1.0 }); // should not panic
1209    }
1210
1211    #[test]
1212    fn insert_preserves_other_entities() {
1213        let mut world = World::new();
1214        let e1 = world.spawn((Pos { x: 1.0, y: 1.0 },));
1215        let e2 = world.spawn((Pos { x: 2.0, y: 2.0 },));
1216
1217        world.insert(e1, Vel { x: 10.0, y: 10.0 });
1218
1219        // e1 migrated, e2 stays in original archetype.
1220        assert_eq!(world.get::<Pos>(e1).unwrap().x, 1.0);
1221        assert_eq!(world.get::<Vel>(e1).unwrap().x, 10.0);
1222        assert_eq!(world.get::<Pos>(e2).unwrap().x, 2.0);
1223        assert!(world.get::<Vel>(e2).is_none());
1224    }
1225
1226    #[test]
1227    fn remove_removes_component_from_entity() {
1228        let mut world = World::new();
1229        let e = world.spawn((Pos { x: 1.0, y: 2.0 }, Vel { x: 3.0, y: 4.0 }));
1230        world.remove::<Vel>(e);
1231
1232        assert!(world.get::<Vel>(e).is_none());
1233        // Pos preserved.
1234        assert_eq!(world.get::<Pos>(e).unwrap().x, 1.0);
1235    }
1236
1237    #[test]
1238    fn remove_absent_component_is_noop() {
1239        let mut world = World::new();
1240        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1241        world.remove::<Vel>(e); // no-op
1242        assert_eq!(world.get::<Pos>(e).unwrap().x, 1.0);
1243    }
1244
1245    #[test]
1246    fn remove_dead_entity_is_noop() {
1247        let mut world = World::new();
1248        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1249        world.despawn(e);
1250        world.remove::<Pos>(e); // should not panic
1251    }
1252
1253    #[test]
1254    fn remove_preserves_other_entities() {
1255        let mut world = World::new();
1256        let e1 = world.spawn((Pos { x: 1.0, y: 1.0 }, Vel { x: 10.0, y: 10.0 }));
1257        let e2 = world.spawn((Pos { x: 2.0, y: 2.0 }, Vel { x: 20.0, y: 20.0 }));
1258
1259        world.remove::<Vel>(e1);
1260
1261        assert!(world.get::<Vel>(e1).is_none());
1262        assert_eq!(world.get::<Pos>(e1).unwrap().x, 1.0);
1263        // e2 unaffected.
1264        assert_eq!(world.get::<Pos>(e2).unwrap().x, 2.0);
1265        assert_eq!(world.get::<Vel>(e2).unwrap().x, 20.0);
1266    }
1267
1268    #[test]
1269    fn component_removals_since_records_successful_remove() {
1270        let mut world = World::new();
1271        let e = world.spawn((Pos { x: 1.0, y: 0.0 }, Vel { x: 2.0, y: 0.0 }));
1272        let since = world.change_tick();
1273        world.advance_tick();
1274
1275        world.remove::<Vel>(e);
1276
1277        let rem: Vec<_> = world.component_removals_since::<Vel>(since).collect();
1278        assert_eq!(rem, vec![e]);
1279        assert!(
1280            world
1281                .component_removals_since::<Vel>(world.change_tick())
1282                .next()
1283                .is_none()
1284        );
1285    }
1286
1287    #[test]
1288    fn component_removals_since_ignores_noop_remove() {
1289        let mut world = World::new();
1290        let e = world.spawn((Pos { x: 1.0, y: 0.0 },));
1291        let since = world.change_tick();
1292        world.advance_tick();
1293
1294        world.remove::<Vel>(e);
1295
1296        let rem: Vec<_> = world.component_removals_since::<Vel>(since).collect();
1297        assert!(rem.is_empty());
1298    }
1299
1300    #[test]
1301    fn insert_then_query_finds_migrated_entity() {
1302        let mut world = World::new();
1303        let e1 = world.spawn((Pos { x: 1.0, y: 0.0 },));
1304        let _e2 = world.spawn((Pos { x: 2.0, y: 0.0 }, Vel { x: 20.0, y: 0.0 }));
1305
1306        // e1 migrates from {Pos} to {Pos, Vel}
1307        world.insert(e1, Vel { x: 10.0, y: 0.0 });
1308
1309        // Both entities now have Pos+Vel
1310        let results: Vec<_> = world.query::<(&Pos, &Vel)>().collect();
1311        assert_eq!(results.len(), 2);
1312    }
1313
1314    #[test]
1315    fn remove_last_component_moves_to_empty_archetype() {
1316        let mut world = World::new();
1317        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1318        world.remove::<Pos>(e);
1319        assert!(world.is_alive(e));
1320        assert!(world.get::<Pos>(e).is_none());
1321        // Entity is alive but has no components (empty archetype).
1322        assert_eq!(world.entity_count(), 1);
1323    }
1324
1325    #[test]
1326    fn despawn_after_migration_cleans_up() {
1327        let mut world = World::new();
1328        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1329        world.insert(e, Vel { x: 3.0, y: 4.0 });
1330        assert!(world.despawn(e));
1331        assert!(!world.is_alive(e));
1332        assert_eq!(world.entity_count(), 0);
1333    }
1334
1335    #[test]
1336    fn spawn_duplicate_component_types_panics_without_mutating_world() {
1337        let mut world = World::new();
1338
1339        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1340            world.spawn((Pos { x: 1.0, y: 2.0 }, Pos { x: 3.0, y: 4.0 }));
1341        }));
1342
1343        assert!(result.is_err());
1344        assert_eq!(world.entity_count(), 0);
1345        assert!(world.query::<&Pos>().next().is_none());
1346    }
1347
1348    // ---- Change-detection tick -----------------------------------------------
1349
1350    #[test]
1351    fn change_tick_starts_at_one() {
1352        let world = World::new();
1353        assert_eq!(world.change_tick(), 1);
1354    }
1355
1356    #[test]
1357    fn advance_tick_increments() {
1358        let mut world = World::new();
1359        assert_eq!(world.change_tick(), 1);
1360        let t = world.advance_tick();
1361        assert_eq!(t, 2);
1362        assert_eq!(world.change_tick(), 2);
1363        world.advance_tick();
1364        assert_eq!(world.change_tick(), 3);
1365    }
1366
1367    #[test]
1368    fn unsafe_world_cell_reads_change_tick() {
1369        let mut world = World::new();
1370        world.advance_tick();
1371        let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
1372        assert_eq!(unsafe { cell.change_tick() }, 2);
1373    }
1374
1375    // ---- Tick stamping on mutation -------------------------------------------
1376
1377    #[test]
1378    fn spawn_stamps_added_and_changed_ticks() {
1379        let mut world = World::new();
1380        // change_tick starts at 1
1381        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1382        let loc = world.entity_location(e).unwrap();
1383        let arch = world.archetypes().get(loc.archetype_id);
1384        let col = arch.column::<Pos>().unwrap();
1385        assert_eq!(col.added_tick(loc.row as usize), 1);
1386        assert_eq!(col.changed_tick(loc.row as usize), 1);
1387    }
1388
1389    #[test]
1390    fn get_mut_stamps_changed_tick() {
1391        let mut world = World::new();
1392        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1393        world.advance_tick(); // tick → 2
1394        world.get_mut::<Pos>(e).unwrap().x = 99.0;
1395
1396        let loc = world.entity_location(e).unwrap();
1397        let arch = world.archetypes().get(loc.archetype_id);
1398        let col = arch.column::<Pos>().unwrap();
1399        assert_eq!(col.added_tick(loc.row as usize), 1); // unchanged
1400        assert_eq!(col.changed_tick(loc.row as usize), 2); // stamped
1401    }
1402
1403    #[test]
1404    fn insert_overwrite_stamps_changed_tick() {
1405        let mut world = World::new();
1406        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1407        world.advance_tick(); // tick → 2
1408        world.insert(e, Pos { x: 99.0, y: 0.0 });
1409
1410        let loc = world.entity_location(e).unwrap();
1411        let arch = world.archetypes().get(loc.archetype_id);
1412        let col = arch.column::<Pos>().unwrap();
1413        assert_eq!(col.added_tick(loc.row as usize), 1);
1414        assert_eq!(col.changed_tick(loc.row as usize), 2);
1415    }
1416
1417    #[test]
1418    fn insert_migration_stamps_new_component_ticks() {
1419        let mut world = World::new();
1420        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1421        world.advance_tick(); // tick → 2
1422        world.insert(e, Vel { x: 3.0, y: 4.0 });
1423
1424        let loc = world.entity_location(e).unwrap();
1425        let arch = world.archetypes().get(loc.archetype_id);
1426
1427        // Pos was migrated — ticks preserved from spawn (tick 1).
1428        let pos_col = arch.column::<Pos>().unwrap();
1429        assert_eq!(pos_col.added_tick(loc.row as usize), 1);
1430        assert_eq!(pos_col.changed_tick(loc.row as usize), 1);
1431
1432        // Vel is newly added at tick 2.
1433        let vel_col = arch.column::<Vel>().unwrap();
1434        assert_eq!(vel_col.added_tick(loc.row as usize), 2);
1435        assert_eq!(vel_col.changed_tick(loc.row as usize), 2);
1436    }
1437
1438    #[test]
1439    fn query_mut_stamps_changed_tick() {
1440        let mut world = World::new();
1441        world.spawn((Pos { x: 1.0, y: 2.0 },));
1442        world.advance_tick(); // tick → 2
1443        for (_, mut pos) in world.query_mut::<&mut Pos>() {
1444            pos.x = 99.0;
1445        }
1446
1447        // Check that changed_tick was stamped at tick 2 (via Mut::deref_mut).
1448        for (e, _) in world.query::<&Pos>() {
1449            let loc = world.entity_location(e).unwrap();
1450            let arch = world.archetypes().get(loc.archetype_id);
1451            let col = arch.column::<Pos>().unwrap();
1452            assert_eq!(col.changed_tick(loc.row as usize), 2);
1453        }
1454    }
1455
1456    #[test]
1457    fn query_mut_read_only_does_not_stamp_tick() {
1458        let mut world = World::new();
1459        let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
1460        world.advance_tick(); // tick → 2
1461
1462        // Iterate via query_mut but only READ through Deref — no DerefMut.
1463        for (_, pos) in world.query_mut::<&mut Pos>() {
1464            let _read = pos.x; // Deref only — should NOT stamp
1465        }
1466
1467        // changed_tick must still be 1 (from spawn), not 2.
1468        let loc = world.entity_location(e).unwrap();
1469        let arch = world.archetypes().get(loc.archetype_id);
1470        let col = arch.column::<Pos>().unwrap();
1471        assert_eq!(col.changed_tick(loc.row as usize), 1);
1472
1473        // query_changed should NOT see this entity.
1474        let changed: Vec<Entity> = world.query_changed::<Pos>(1).map(|(e, _)| e).collect();
1475        assert!(changed.is_empty());
1476    }
1477
1478    #[test]
1479    fn query_mut_selective_mutation_stamps_only_mutated() {
1480        let mut world = World::new();
1481        let e1 = world.spawn((Pos { x: 1.0, y: 0.0 },));
1482        let _e2 = world.spawn((Pos { x: 2.0, y: 0.0 },));
1483        world.advance_tick(); // tick → 2
1484
1485        // Mutate only e1, read e2.
1486        for (entity, mut pos) in world.query_mut::<&mut Pos>() {
1487            if entity == e1 {
1488                pos.x = 99.0; // DerefMut — stamps tick
1489            } else {
1490                let _read = pos.x; // Deref only — no stamp
1491            }
1492        }
1493
1494        let changed: Vec<Entity> = world.query_changed::<Pos>(1).map(|(e, _)| e).collect();
1495        assert_eq!(changed.len(), 1);
1496        assert_eq!(changed[0], e1);
1497    }
1498
1499    #[test]
1500    fn set_changed_stamps_for_interior_mutability() {
1501        use std::sync::atomic::{AtomicUsize, Ordering};
1502
1503        #[derive(Debug)]
1504        struct Counter(AtomicUsize);
1505        impl crate::Component for Counter {}
1506
1507        let mut world = World::new();
1508        let e = world.spawn((Counter(AtomicUsize::new(0)),));
1509        world.advance_tick(); // tick → 2
1510
1511        // Mutate through interior mutability (Deref, not DerefMut).
1512        for (_, mut counter) in world.query_mut::<&mut Counter>() {
1513            counter.0.fetch_add(1, Ordering::Relaxed); // via Deref — no auto stamp
1514            counter.set_changed(); // explicit stamp
1515        }
1516
1517        // query_changed sees the entity because set_changed was called.
1518        let changed: Vec<Entity> = world.query_changed::<Counter>(1).map(|(e, _)| e).collect();
1519        assert_eq!(changed, vec![e]);
1520    }
1521
1522    #[test]
1523    fn interior_mutation_without_set_changed_is_invisible() {
1524        use std::sync::atomic::{AtomicUsize, Ordering};
1525
1526        #[derive(Debug)]
1527        struct Counter(AtomicUsize);
1528        impl crate::Component for Counter {}
1529
1530        let mut world = World::new();
1531        let _e = world.spawn((Counter(AtomicUsize::new(0)),));
1532        world.advance_tick(); // tick → 2
1533
1534        // Mutate through interior mutability WITHOUT set_changed.
1535        for (_, counter) in world.query_mut::<&mut Counter>() {
1536            counter.0.fetch_add(1, Ordering::Relaxed);
1537            // No set_changed — change detection will NOT see this.
1538        }
1539
1540        let changed: Vec<Entity> = world.query_changed::<Counter>(1).map(|(e, _)| e).collect();
1541        assert!(
1542            changed.is_empty(),
1543            "interior mutation without set_changed should be invisible"
1544        );
1545    }
1546
1547    // ---- query_changed / query_added ----------------------------------------
1548
1549    #[test]
1550    fn query_changed_returns_mutated_entities() {
1551        let mut world = World::new();
1552        let e1 = world.spawn((Pos { x: 1.0, y: 0.0 },));
1553        let e2 = world.spawn((Pos { x: 2.0, y: 0.0 },));
1554        let _e3 = world.spawn((Pos { x: 3.0, y: 0.0 },));
1555
1556        let since = world.change_tick(); // 1
1557        world.advance_tick(); // tick → 2
1558
1559        // Mutate only e1 and e2.
1560        world.get_mut::<Pos>(e1).unwrap().x = 10.0;
1561        world.get_mut::<Pos>(e2).unwrap().x = 20.0;
1562
1563        let changed: Vec<Entity> = world.query_changed::<Pos>(since).map(|(e, _)| e).collect();
1564        assert_eq!(changed.len(), 2);
1565        assert!(changed.contains(&e1));
1566        assert!(changed.contains(&e2));
1567    }
1568
1569    #[test]
1570    fn query_changed_excludes_untouched() {
1571        let mut world = World::new();
1572        world.spawn((Pos { x: 1.0, y: 0.0 },));
1573
1574        let since = world.change_tick();
1575        world.advance_tick();
1576        // Don't mutate anything.
1577
1578        let changed: Vec<Entity> = world.query_changed::<Pos>(since).map(|(e, _)| e).collect();
1579        assert!(changed.is_empty());
1580    }
1581
1582    #[test]
1583    fn query_added_returns_newly_spawned() {
1584        let mut world = World::new();
1585        let _old = world.spawn((Pos { x: 1.0, y: 0.0 },));
1586
1587        let since = world.change_tick(); // 1
1588        world.advance_tick(); // tick → 2
1589
1590        let new = world.spawn((Pos { x: 2.0, y: 0.0 },));
1591
1592        let added: Vec<Entity> = world.query_added::<Pos>(since).map(|(e, _)| e).collect();
1593        assert_eq!(added, vec![new]);
1594    }
1595
1596    #[test]
1597    fn query_added_returns_component_inserted_via_migration() {
1598        let mut world = World::new();
1599        let e = world.spawn((Pos { x: 1.0, y: 0.0 },));
1600
1601        let since = world.change_tick();
1602        world.advance_tick();
1603
1604        world.insert(e, Vel { x: 3.0, y: 4.0 });
1605
1606        // Vel was added after since_tick.
1607        let added: Vec<Entity> = world.query_added::<Vel>(since).map(|(e, _)| e).collect();
1608        assert_eq!(added, vec![e]);
1609
1610        // Pos was added at tick 1, not after since_tick (also 1).
1611        let added_pos: Vec<Entity> = world.query_added::<Pos>(since).map(|(e, _)| e).collect();
1612        assert!(added_pos.is_empty());
1613    }
1614
1615    #[test]
1616    fn query_changed_across_multiple_archetypes() {
1617        let mut world = World::new();
1618        // Archetype 1: (Pos,)
1619        let e1 = world.spawn((Pos { x: 1.0, y: 0.0 },));
1620        // Archetype 2: (Pos, Vel)
1621        let e2 = world.spawn((Pos { x: 2.0, y: 0.0 }, Vel { x: 0.0, y: 0.0 }));
1622
1623        let since = world.change_tick();
1624        world.advance_tick();
1625
1626        // Mutate Pos on both.
1627        world.get_mut::<Pos>(e1).unwrap().x = 10.0;
1628        world.get_mut::<Pos>(e2).unwrap().x = 20.0;
1629
1630        let changed: Vec<Entity> = world.query_changed::<Pos>(since).map(|(e, _)| e).collect();
1631        assert_eq!(changed.len(), 2);
1632        assert!(changed.contains(&e1));
1633        assert!(changed.contains(&e2));
1634    }
1635}