Skip to main content

freecs/
lib.rs

1//! A high-performance, archetype-based Entity Component System (ECS) for Rust.
2//!
3//! freecs provides a table-based storage system where entities with identical component sets
4//! are stored together in contiguous memory (Structure of Arrays layout), optimizing for cache
5//! coherency and SIMD operations.
6//!
7//! # Key Features
8//!
9//! - **Zero-cost Abstractions**: Fully statically dispatched, no custom traits
10//! - **Parallel Processing**: Multi-threaded iteration using Rayon (automatically enabled on non-WASM platforms)
11//! - **Sparse Set Tags**: Lightweight markers that don't fragment archetypes
12//! - **Command Buffers**: Queue structural changes during iteration
13//! - **Change Detection**: Track component modifications for incremental updates
14//! - **Events**: Type-safe double-buffered event system
15//! - **Multi-World**: Split components across multiple worlds for >64 component types
16//!
17//! The `ecs!` macro generates the entire ECS at compile time using only plain data structures, functions, and zero unsafe code.
18//!
19//! # Quick Start
20//!
21//! ```rust
22//! use freecs::{ecs, Entity};
23//!
24//! // First, define components (must implement Default)
25//! #[derive(Default, Clone, Debug)]
26//! struct Position { x: f32, y: f32 }
27//!
28//! #[derive(Default, Clone, Debug)]
29//! struct Velocity { x: f32, y: f32 }
30//!
31//! #[derive(Default, Clone, Debug)]
32//! struct Health { value: f32 }
33//!
34//! // Then, create a world with the `ecs!` macro
35//! ecs! {
36//!   World {
37//!     position: Position => POSITION,
38//!     velocity: Velocity => VELOCITY,
39//!     health: Health => HEALTH,
40//!   }
41//!   Tags {
42//!     player => PLAYER,
43//!     enemy => ENEMY,
44//!   }
45//!   Events {
46//!     collision: CollisionEvent,
47//!   }
48//!   Resources {
49//!     delta_time: f32
50//!   }
51//! }
52//!
53//! #[derive(Debug, Clone)]
54//! struct CollisionEvent {
55//!     entity_a: Entity,
56//!     entity_b: Entity,
57//! }
58//! ```
59//!
60//! ## Entity and Component Access
61//!
62//! ```rust
63//! let mut world = World::default();
64//!
65//! // Spawn entities with components by mask
66//! let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
67//!
68//! // Lookup and modify a component using generated methods
69//! if let Some(pos) = world.get_position_mut(entity) {
70//!     pos.x += 1.0;
71//! }
72//!
73//! // Read components
74//! if let Some(pos) = world.get_position(entity) {
75//!     println!("Position: ({}, {})", pos.x, pos.y);
76//! }
77//!
78//! // Set components (adds if not present)
79//! world.set_position(entity, Position { x: 10.0, y: 20.0 });
80//! world.set_velocity(entity, Velocity { x: 1.0, y: 0.0 });
81//!
82//! // Add new components to an entity by mask
83//! world.add_components(entity, HEALTH | VELOCITY);
84//!
85//! // Or use the generated add methods
86//! world.add_health(entity);
87//!
88//! // Remove components from an entity by mask
89//! world.remove_components(entity, VELOCITY | POSITION);
90//!
91//! // Or use the generated remove methods
92//! world.remove_velocity(entity);
93//!
94//! // Check if entity has components
95//! if world.entity_has_position(entity) {
96//!     println!("Entity has position component");
97//! }
98//!
99//! // Query all entities
100//! let entities = world.get_all_entities();
101//! println!("All entities: {entities:?}");
102//!
103//! // Query entities, iterating over all entities matching the component mask
104//! let entities = world.query_entities(POSITION | VELOCITY);
105//!
106//! // Query for the first entity matching the component mask, returning early when found
107//! let player = world.query_first_entity(POSITION | VELOCITY);
108//! ```
109//!
110//! ## Tags
111//!
112//! Tags are lightweight markers that don't cause archetype fragmentation:
113//!
114//! ```rust
115//! # use freecs::{ecs, Entity};
116//! # #[derive(Default, Clone)] struct Position { x: f32, y: f32 }
117//! # ecs! { World { position: Position => POSITION, } Tags { player => PLAYER, enemy => ENEMY, } Resources { delta_time: f32 } }
118//! # let mut world = World::default();
119//! # let entity = world.spawn_entities(POSITION, 1)[0];
120//! // Add tags to entities
121//! world.add_player(entity);
122//!
123//! // Check if entity has a tag
124//! if world.has_player(entity) {
125//!     println!("Entity is a player");
126//! }
127//!
128//! // Query entities by tag
129//! for entity in world.query_player() {
130//!     println!("Player: {:?}", entity);
131//! }
132//!
133//! // Remove tags
134//! world.remove_player(entity);
135//! ```
136//!
137//! ## Events
138//!
139//! Events provide type-safe communication between systems:
140//!
141//! ```rust
142//! # use freecs::{ecs, Entity};
143//! # #[derive(Default, Clone)] struct Position { x: f32, y: f32 }
144//! # #[derive(Debug, Clone)] struct CollisionEvent { entity_a: Entity, entity_b: Entity }
145//! # ecs! { World { position: Position => POSITION, } Events { collision: CollisionEvent, } Resources { delta_time: f32 } }
146//! # let mut world = World::default();
147//! # let entity = world.spawn_entities(POSITION, 1)[0];
148//! // Send events
149//! world.send_collision(CollisionEvent {
150//!     entity_a: entity,
151//!     entity_b: entity,
152//! });
153//!
154//! // Process events in systems
155//! for event in world.collect_collision() {
156//!     println!("Collision: {:?} and {:?}", event.entity_a, event.entity_b);
157//! }
158//!
159//! // Clean up events and increment tick at end of frame
160//! world.step();
161//! ```
162//!
163//! ## Systems
164//!
165//! Systems are functions that query entities and transform their components.
166//! For maximum performance, use the query builder API for direct table access:
167//!
168//! ```rust
169//! fn physics_system(world: &mut World) {
170//!     let dt = world.resources.delta_time;
171//!
172//!     // Method 1: High-performance query builder (recommended)
173//!     world.query()
174//!         .with(POSITION | VELOCITY)
175//!         .iter(|entity, table, idx| {
176//!             table.position[idx].x += table.velocity[idx].x * dt;
177//!             table.position[idx].y += table.velocity[idx].y * dt;
178//!         });
179//!
180//!     // Method 2: Per-entity lookups (simpler but slower)
181//!     for entity in world.query_entities(POSITION | VELOCITY) {
182//!         if let Some(position) = world.get_position_mut(entity) {
183//!             if let Some(velocity) = world.get_velocity(entity) {
184//!                 position.x += velocity.x * dt;
185//!                 position.y += velocity.y * dt;
186//!             }
187//!         }
188//!     }
189//! }
190//! ```
191//!
192//! ## Parallel Processing
193//!
194//! Process large entity counts across multiple CPU cores using Rayon. Parallel iteration is
195//! automatically available on non-WASM platforms:
196//!
197//! ```rust
198//! use freecs::rayon::prelude::*;
199//!
200//! fn parallel_physics(world: &mut World) {
201//!     let dt = world.resources.delta_time;
202//!
203//!     world.par_for_each_mut(POSITION | VELOCITY, 0, |entity, table, idx| {
204//!         table.position[idx].x += table.velocity[idx].x * dt;
205//!         table.position[idx].y += table.velocity[idx].y * dt;
206//!     });
207//! }
208//! ```
209//!
210//! Parallel iteration is best suited for processing 100K+ entities with non-trivial
211//! per-entity computation.
212//!
213//! ## Command Buffers
214//!
215//! Queue structural changes during iteration to avoid borrow conflicts:
216//!
217//! ```rust
218//! # use freecs::{ecs, Entity};
219//! # #[derive(Default, Clone)] struct Position { x: f32, y: f32 }
220//! # #[derive(Default, Clone)] struct Health { value: f32 }
221//! # ecs! { World { position: Position => POSITION, health: Health => HEALTH, } Resources { delta_time: f32 } }
222//! # let mut world = World::default();
223//! # world.spawn_entities(POSITION | HEALTH, 10);
224//! // Queue despawns during iteration
225//! let entities_to_despawn: Vec<Entity> = world
226//!     .query_entities(HEALTH)
227//!     .filter(|&entity| {
228//!         world.get_health(entity).map_or(false, |h| h.value <= 0.0)
229//!     })
230//!     .collect();
231//!
232//! for entity in entities_to_despawn {
233//!     world.queue_despawn_entity(entity);
234//! }
235//!
236//! // Apply all queued commands at once
237//! world.apply_commands();
238//! ```
239//!
240//! ## Change Detection
241//!
242//! Track which components have been modified since the last frame:
243//!
244//! ```rust
245//! # use freecs::{ecs, Entity};
246//! # #[derive(Default, Clone)] struct Position { x: f32, y: f32 }
247//! # ecs! { World { position: Position => POSITION, } Resources { delta_time: f32 } }
248//! # let mut world = World::default();
249//! // Process only entities with changed components
250//! world.for_each_mut_changed(POSITION, 0, |entity, table, idx| {
251//!     // Only processes entities where position changed since last step()
252//! });
253//!
254//! // Automatically increments tick counter
255//! world.step();
256//! ```
257//!
258//! ## System Scheduling
259//!
260//! Organize systems into a schedule:
261//!
262//! ```rust
263//! # use freecs::{ecs, Schedule, Entity};
264//! # #[derive(Default, Clone)] struct Position { x: f32, y: f32 }
265//! # ecs! { World { position: Position => POSITION, } Resources { delta_time: f32 } }
266//! # fn input_system(world: &mut World) {}
267//! # fn physics_system(world: &mut World) {}
268//! # fn render_system(world: &World) {}
269//! let mut world = World::default();
270//! let mut schedule = Schedule::new();
271//!
272//! schedule
273//!     .push("input", input_system)
274//!     .push("physics", physics_system)
275//!     .push_readonly("render", render_system);
276//!
277//! // Game loop
278//! loop {
279//!     schedule.run(&mut world);
280//!     world.step();
281//! #   break;
282//! }
283//! ```
284//!
285//! ## Entity Builder
286//!
287//! ```rust
288//! let mut world = World::default();
289//! let entities = EntityBuilder::new()
290//!     .with_position(Position { x: 1.0, y: 2.0 })
291//!     .with_velocity(Velocity { x: 0.0, y: 1.0 })
292//!     .spawn(&mut world, 2);
293//!
294//! // Access the spawned entities
295//! let first_pos = world.get_position(entities[0]).unwrap();
296//! assert_eq!(first_pos.x, 1.0);
297//! ```
298//!
299//! # Advanced Features
300//!
301//! ## Batch Spawning
302//!
303//! ```rust
304//! # use freecs::{ecs, Entity};
305//! # #[derive(Default, Clone)] struct Position { x: f32, y: f32 }
306//! # #[derive(Default, Clone)] struct Velocity { x: f32, y: f32 }
307//! # ecs! { World { position: Position => POSITION, velocity: Velocity => VELOCITY, } Resources { delta_time: f32 } }
308//! # let mut world = World::default();
309//! // Spawn with initialization callback
310//! let entities = world.spawn_batch(POSITION | VELOCITY, 1000, |table, idx| {
311//!     table.position[idx] = Position { x: idx as f32, y: 0.0 };
312//!     table.velocity[idx] = Velocity { x: 1.0, y: 0.0 };
313//! });
314//! ```
315//!
316//! ## Per-Component Iteration
317//!
318//! ```rust
319//! # use freecs::{ecs, Entity};
320//! # #[derive(Default, Clone)] struct Position { x: f32, y: f32 }
321//! # ecs! { World { position: Position => POSITION, } Resources { delta_time: f32 } }
322//! # let mut world = World::default();
323//! // Iterate over single component
324//! world.iter_position_mut(|position| {
325//!     position.x += 1.0;
326//! });
327//!
328//! // Slice-based iteration (most efficient)
329//! for slice in world.iter_position_slices_mut() {
330//!     for position in slice {
331//!         position.x *= 2.0;
332//!     }
333//! }
334//! ```
335//!
336//! ## Low-Level Iteration
337//!
338//! ```rust
339//! # use freecs::{ecs, Entity};
340//! # #[derive(Default, Clone)] struct Position { x: f32, y: f32 }
341//! # #[derive(Default, Clone)] struct Velocity { x: f32, y: f32 }
342//! # ecs! { World { position: Position => POSITION, velocity: Velocity => VELOCITY, } Tags { player => PLAYER, } Resources { delta_time: f32 } }
343//! # let mut world = World::default();
344//! // Include/exclude with masks
345//! world.for_each_mut(POSITION | VELOCITY, PLAYER, |entity, table, idx| {
346//!     // Process non-player entities
347//!     table.position[idx].x += table.velocity[idx].x;
348//! });
349//! ```
350//!
351//! ## Advanced Command Operations
352//!
353//! ```rust
354//! # use freecs::{ecs, Entity};
355//! # #[derive(Default, Clone)] struct Position { x: f32, y: f32 }
356//! # ecs! { World { position: Position => POSITION, } Tags { player => PLAYER, } Resources { delta_time: f32 } }
357//! # let mut world = World::default();
358//! # let entity = world.spawn_entities(POSITION, 1)[0];
359//! // Queue batch operations
360//! world.queue_spawn_entities(POSITION, 100);
361//! world.queue_set_position(entity, Position { x: 10.0, y: 20.0 });
362//! world.queue_add_player(entity);
363//!
364//! // Check command buffer status
365//! if world.command_count() > 100 {
366//!     world.apply_commands();
367//! }
368//!
369//! // Clear without applying
370//! world.clear_commands();
371//! ```
372//!
373//! ## Event Management
374//!
375//! ```rust
376//! # use freecs::{ecs, Entity};
377//! # #[derive(Default, Clone)] struct Position { x: f32, y: f32 }
378//! # #[derive(Debug, Clone)] struct CollisionEvent { entity_a: Entity, entity_b: Entity }
379//! # ecs! { World { position: Position => POSITION, } Events { collision: CollisionEvent, } Resources { delta_time: f32 } }
380//! # let mut world = World::default();
381//! # let entity = world.spawn_entities(POSITION, 1)[0];
382//! # world.send_collision(CollisionEvent { entity_a: entity, entity_b: entity });
383//! // Peek at events without consuming
384//! if let Some(event) = world.peek_collision() {
385//!     println!("Next collision: {:?}", event.entity_a);
386//! }
387//!
388//! // Check event count
389//! if !world.is_empty_collision() {
390//!     let count = world.len_collision();
391//!     println!("Processing {} events", count);
392//! }
393//!
394//! // Drain events (takes ownership)
395//! for event in world.drain_collision() {
396//!     // Process event
397//! }
398//! ```
399//!
400//! ## Conditional Compilation
401//!
402//! Both components and resources support `#[cfg(...)]` attributes for conditional compilation.
403//! This is useful for debug-only components, optional features, or platform-specific functionality:
404//!
405//! ```rust,ignore
406//! ecs! {
407//!     World {
408//!         position: Position => POSITION,
409//!         velocity: Velocity => VELOCITY,
410//!         #[cfg(debug_assertions)]
411//!         debug_info: DebugInfo => DEBUG_INFO,
412//!         #[cfg(feature = "physics")]
413//!         rigid_body: RigidBody => RIGID_BODY,
414//!     }
415//!     Resources {
416//!         delta_time: f32,
417//!         #[cfg(feature = "audio")]
418//!         audio_engine: AudioEngine,
419//!     }
420//! }
421//! ```
422//!
423//! When a component or resource has a `#[cfg(...)]` attribute, all related generated code
424//! (struct fields, accessor methods, mask constants, etc.) is conditionally compiled.
425
426pub use paste;
427
428#[cfg(not(target_family = "wasm"))]
429pub use rayon;
430
431#[derive(
432    Default, Clone, Copy, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
433)]
434pub struct Entity {
435    pub id: u32,
436    pub generation: u32,
437}
438
439impl std::fmt::Display for Entity {
440    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
441        let Self { id, generation } = self;
442        write!(f, "Id: {id} - Generation: {generation}")
443    }
444}
445
446#[derive(Default)]
447pub struct EntityAllocator {
448    pub next_id: u32,
449    pub free_ids: Vec<(u32, u32)>,
450}
451
452impl EntityAllocator {
453    pub fn allocate(&mut self) -> Entity {
454        if let Some((id, next_gen)) = self.free_ids.pop() {
455            Entity {
456                id,
457                generation: next_gen,
458            }
459        } else {
460            let id = self.next_id;
461            self.next_id += 1;
462            Entity { id, generation: 0 }
463        }
464    }
465
466    pub fn deallocate(&mut self, entity: Entity) {
467        self.free_ids
468            .push((entity.id, entity.generation.wrapping_add(1)));
469    }
470}
471
472#[derive(Copy, Clone, Default)]
473pub struct EntityLocation {
474    pub generation: u32,
475    pub table_index: u32,
476    pub array_index: u32,
477    pub allocated: bool,
478}
479
480#[derive(Default)]
481pub struct EntityLocations {
482    pub locations: Vec<EntityLocation>,
483}
484
485impl EntityLocations {
486    pub fn get(&self, id: u32) -> Option<&EntityLocation> {
487        self.locations.get(id as usize)
488    }
489
490    pub fn get_mut(&mut self, id: u32) -> Option<&mut EntityLocation> {
491        self.locations.get_mut(id as usize)
492    }
493
494    pub fn ensure_slot(&mut self, id: u32, generation: u32) {
495        let id_usize = id as usize;
496        if id_usize >= self.locations.len() {
497            self.locations.resize(
498                (self.locations.len() * 2).max(64).max(id_usize + 1),
499                EntityLocation::default(),
500            );
501        }
502        self.locations[id_usize].generation = generation;
503    }
504
505    pub fn insert(&mut self, id: u32, location: EntityLocation) {
506        let id_usize = id as usize;
507        if id_usize >= self.locations.len() {
508            self.locations.resize(
509                (self.locations.len() * 2).max(64).max(id_usize + 1),
510                EntityLocation::default(),
511            );
512        }
513        self.locations[id_usize] = location;
514    }
515
516    pub fn mark_deallocated(&mut self, id: u32) {
517        if let Some(loc) = self.locations.get_mut(id as usize) {
518            loc.allocated = false;
519        }
520    }
521}
522
523/// Double-buffered event queue for inter-system communication.
524///
525/// Events persist for 2 frames to prevent systems from missing events
526/// during execution. Call [`update()`](EventQueue::update) between frames to swap buffers.
527///
528/// # Examples
529///
530/// ```
531/// use freecs::EventQueue;
532///
533/// #[derive(Debug, Clone)]
534/// struct DamageEvent { amount: i32 }
535///
536/// let mut queue = EventQueue::new();
537///
538/// queue.send(DamageEvent { amount: 10 });
539/// assert_eq!(queue.len(), 1);
540///
541/// for event in queue.read() {
542///     println!("Damage: {}", event.amount);
543/// }
544///
545/// queue.update();
546/// assert_eq!(queue.len(), 1, "Event persists after first update");
547///
548/// queue.update();
549/// assert_eq!(queue.len(), 0, "Event cleared after second update");
550/// ```
551#[derive(Clone)]
552pub struct EventQueue<T> {
553    current: Vec<T>,
554    previous: Vec<T>,
555}
556
557impl<T> Default for EventQueue<T> {
558    fn default() -> Self {
559        Self::new()
560    }
561}
562
563impl<T> EventQueue<T> {
564    /// Creates a new empty event queue.
565    pub fn new() -> Self {
566        Self {
567            current: Vec::new(),
568            previous: Vec::new(),
569        }
570    }
571
572    /// Sends an event to the queue.
573    ///
574    /// The event will be available for reading until two [`update()`](EventQueue::update) calls have been made.
575    pub fn send(&mut self, event: T) {
576        #[cfg(debug_assertions)]
577        {
578            const WARN_THRESHOLD: usize = 10000;
579            let total = self.len();
580            if total > WARN_THRESHOLD {
581                eprintln!(
582                    "WARNING: EventQueue has {} events. Did you forget to call update()?",
583                    total
584                );
585            }
586        }
587        self.current.push(event);
588    }
589
590    /// Returns an iterator over all events in both buffers (previous frame, then current frame).
591    ///
592    /// Events are yielded in the order they were sent, with previous frame events first.
593    pub fn read(&self) -> impl Iterator<Item = &T> {
594        self.previous.iter().chain(self.current.iter())
595    }
596
597    /// Returns a reference to the first event without consuming it, if any exists.
598    pub fn peek(&self) -> Option<&T> {
599        self.previous.first().or_else(|| self.current.first())
600    }
601
602    /// Drains all events from both buffers, returning an iterator that takes ownership.
603    ///
604    /// After calling this, the queue will be empty.
605    pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
606        self.previous.drain(..).chain(self.current.drain(..))
607    }
608
609    /// Swaps the event buffers and clears old events.
610    ///
611    /// After calling this:
612    /// - Events from the previous frame are cleared
613    /// - Events from the current frame become the previous frame
614    /// - The current frame is empty
615    ///
616    /// Call this once per frame to maintain the 2-frame event lifetime.
617    pub fn update(&mut self) {
618        self.previous.clear();
619        std::mem::swap(&mut self.current, &mut self.previous);
620    }
621
622    /// Immediately clears all events from both buffers.
623    ///
624    /// Unlike [`update()`](EventQueue::update), this discards events immediately without the 2-frame persistence.
625    pub fn clear(&mut self) {
626        self.current.clear();
627        self.previous.clear();
628    }
629
630    /// Returns the total number of events in both buffers.
631    pub fn len(&self) -> usize {
632        self.current.len() + self.previous.len()
633    }
634
635    /// Returns `true` if both buffers are empty.
636    pub fn is_empty(&self) -> bool {
637        self.current.is_empty() && self.previous.is_empty()
638    }
639}
640
641struct ScheduleEntry<W> {
642    name: &'static str,
643    system: Box<dyn FnMut(&mut W) + Send>,
644}
645
646pub struct Schedule<W> {
647    entries: Vec<ScheduleEntry<W>>,
648}
649
650impl<W> Schedule<W> {
651    pub fn new() -> Self {
652        Self {
653            entries: Vec::new(),
654        }
655    }
656
657    pub fn push<F>(&mut self, name: &'static str, system: F) -> &mut Self
658    where
659        F: FnMut(&mut W) + Send + 'static,
660    {
661        self.assert_unique(name);
662        self.entries.push(ScheduleEntry {
663            name,
664            system: Box::new(system),
665        });
666        self
667    }
668
669    pub fn push_readonly<F>(&mut self, name: &'static str, mut system: F) -> &mut Self
670    where
671        F: FnMut(&W) + Send + 'static,
672    {
673        self.assert_unique(name);
674        self.entries.push(ScheduleEntry {
675            name,
676            system: Box::new(move |world: &mut W| {
677                system(&*world);
678            }),
679        });
680        self
681    }
682
683    pub fn insert_before<F>(&mut self, target: &str, name: &'static str, system: F) -> &mut Self
684    where
685        F: FnMut(&mut W) + Send + 'static,
686    {
687        self.assert_unique(name);
688        let index = self.index_of_or_panic(target, "insert_before");
689        self.entries.insert(
690            index,
691            ScheduleEntry {
692                name,
693                system: Box::new(system),
694            },
695        );
696        self
697    }
698
699    pub fn insert_after<F>(&mut self, target: &str, name: &'static str, system: F) -> &mut Self
700    where
701        F: FnMut(&mut W) + Send + 'static,
702    {
703        self.assert_unique(name);
704        let index = self.index_of_or_panic(target, "insert_after");
705        self.entries.insert(
706            index + 1,
707            ScheduleEntry {
708                name,
709                system: Box::new(system),
710            },
711        );
712        self
713    }
714
715    pub fn replace<F>(&mut self, name: &str, system: F) -> &mut Self
716    where
717        F: FnMut(&mut W) + Send + 'static,
718    {
719        let index = self
720            .index_of(name)
721            .unwrap_or_else(|| panic!("Schedule::replace: system \"{name}\" not found"));
722        self.entries[index].system = Box::new(system);
723        self
724    }
725
726    pub fn remove(&mut self, name: &str) -> bool {
727        let len_before = self.entries.len();
728        self.entries.retain(|entry| entry.name != name);
729        self.entries.len() != len_before
730    }
731
732    pub fn contains(&self, name: &str) -> bool {
733        self.entries.iter().any(|entry| entry.name == name)
734    }
735
736    pub fn names(&self) -> impl Iterator<Item = &'static str> + '_ {
737        self.entries.iter().map(|entry| entry.name)
738    }
739
740    pub fn len(&self) -> usize {
741        self.entries.len()
742    }
743
744    pub fn is_empty(&self) -> bool {
745        self.entries.is_empty()
746    }
747
748    pub fn run(&mut self, world: &mut W) {
749        for entry in &mut self.entries {
750            (entry.system)(world);
751        }
752    }
753
754    fn index_of(&self, name: &str) -> Option<usize> {
755        self.entries.iter().position(|entry| entry.name == name)
756    }
757
758    fn assert_unique(&self, name: &str) {
759        assert!(
760            !self.contains(name),
761            "Schedule: system \"{name}\" already exists"
762        );
763    }
764
765    fn index_of_or_panic(&self, target: &str, method: &str) -> usize {
766        self.index_of(target)
767            .unwrap_or_else(|| panic!("Schedule::{method}: system \"{target}\" not found"))
768    }
769}
770
771impl<W> Default for Schedule<W> {
772    fn default() -> Self {
773        Self::new()
774    }
775}
776
777#[macro_export]
778macro_rules! ecs {
779    (
780        $world:ident {
781            $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
782        }
783        Tags {
784            $($tag_name:ident => $tag_mask:ident),* $(,)?
785        }
786        Events {
787            $($event_name:ident: $event_type:ty),* $(,)?
788        }
789        $resources:ident {
790            $($(#[$attr:meta])*  $resource_name:ident: $resource_type:ty),* $(,)?
791        }
792    ) => {
793        $crate::ecs_impl! {
794            $world {
795                $($(#[$comp_attr])* $name: $type => $mask),*
796            }
797            Tags {
798                $($tag_name => $tag_mask),*
799            }
800            Events {
801                $($event_name: $event_type),*
802            }
803            $resources {
804                $($(#[$attr])* $resource_name: $resource_type),*
805            }
806        }
807    };
808
809    (
810        $world:ident {
811            $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
812        }
813        Tags {
814            $($tag_name:ident => $tag_mask:ident),* $(,)?
815        }
816        $resources:ident {
817            $($(#[$attr:meta])*  $resource_name:ident: $resource_type:ty),* $(,)?
818        }
819    ) => {
820        $crate::ecs_impl! {
821            $world {
822                $($(#[$comp_attr])* $name: $type => $mask),*
823            }
824            Tags {
825                $($tag_name => $tag_mask),*
826            }
827            Events {}
828            $resources {
829                $($(#[$attr])* $resource_name: $resource_type),*
830            }
831        }
832    };
833
834    (
835        $world:ident {
836            $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
837        }
838        Events {
839            $($event_name:ident: $event_type:ty),* $(,)?
840        }
841        $resources:ident {
842            $($(#[$attr:meta])*  $resource_name:ident: $resource_type:ty),* $(,)?
843        }
844    ) => {
845        $crate::ecs_impl! {
846            $world {
847                $($(#[$comp_attr])* $name: $type => $mask),*
848            }
849            Tags {}
850            Events {
851                $($event_name: $event_type),*
852            }
853            $resources {
854                $($(#[$attr])* $resource_name: $resource_type),*
855            }
856        }
857    };
858
859    (
860        $world:ident {
861            $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
862        }
863        $resources:ident {
864            $($(#[$attr:meta])*  $resource_name:ident: $resource_type:ty),* $(,)?
865        }
866    ) => {
867        $crate::ecs_impl! {
868            $world {
869                $($(#[$comp_attr])* $name: $type => $mask),*
870            }
871            Tags {}
872            Events {}
873            $resources {
874                $($(#[$attr])* $resource_name: $resource_type),*
875            }
876        }
877    };
878
879    (
880        $ecs:ident {
881            $($world_name:ident {
882                $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
883            })+
884        }
885        Tags {
886            $($tag_name:ident => $tag_mask:ident),* $(,)?
887        }
888        Events {
889            $($event_name:ident: $event_type:ty),* $(,)?
890        }
891        $resources:ident {
892            $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
893        }
894    ) => {
895        $crate::ecs_multi_impl! {
896            $ecs {
897                $($world_name {
898                    $($(#[$comp_attr])* $name: $type => $mask),*
899                })+
900            }
901            Tags {
902                $($tag_name => $tag_mask),*
903            }
904            Events {
905                $($event_name: $event_type),*
906            }
907            $resources {
908                $($(#[$attr])* $resource_name: $resource_type),*
909            }
910        }
911    };
912
913    (
914        $ecs:ident {
915            $($world_name:ident {
916                $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
917            })+
918        }
919        Tags {
920            $($tag_name:ident => $tag_mask:ident),* $(,)?
921        }
922        $resources:ident {
923            $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
924        }
925    ) => {
926        $crate::ecs_multi_impl! {
927            $ecs {
928                $($world_name {
929                    $($(#[$comp_attr])* $name: $type => $mask),*
930                })+
931            }
932            Tags {
933                $($tag_name => $tag_mask),*
934            }
935            Events {}
936            $resources {
937                $($(#[$attr])* $resource_name: $resource_type),*
938            }
939        }
940    };
941
942    (
943        $ecs:ident {
944            $($world_name:ident {
945                $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
946            })+
947        }
948        Events {
949            $($event_name:ident: $event_type:ty),* $(,)?
950        }
951        $resources:ident {
952            $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
953        }
954    ) => {
955        $crate::ecs_multi_impl! {
956            $ecs {
957                $($world_name {
958                    $($(#[$comp_attr])* $name: $type => $mask),*
959                })+
960            }
961            Tags {}
962            Events {
963                $($event_name: $event_type),*
964            }
965            $resources {
966                $($(#[$attr])* $resource_name: $resource_type),*
967            }
968        }
969    };
970
971    (
972        $ecs:ident {
973            $($world_name:ident {
974                $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
975            })+
976        }
977        $resources:ident {
978            $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
979        }
980    ) => {
981        $crate::ecs_multi_impl! {
982            $ecs {
983                $($world_name {
984                    $($(#[$comp_attr])* $name: $type => $mask),*
985                })+
986            }
987            Tags {}
988            Events {}
989            $resources {
990                $($(#[$attr])* $resource_name: $resource_type),*
991            }
992        }
993    };
994}
995
996#[macro_export]
997macro_rules! ecs_impl {
998    (
999        $world:ident {
1000            $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
1001        }
1002        Tags {
1003            $($tag_name:ident => $tag_mask:ident),* $(,)?
1004        }
1005        Events {
1006            $($event_name:ident: $event_type:ty),* $(,)?
1007        }
1008        $resources:ident {
1009            $($(#[$attr:meta])*  $resource_name:ident: $resource_type:ty),* $(,)?
1010        }
1011    ) => {
1012        #[allow(unused)]
1013        #[derive(Default, Debug, Clone)]
1014        pub struct EntityBuilder {
1015            $($(#[$comp_attr])* $name: Option<$type>,)*
1016        }
1017
1018        #[allow(unused)]
1019        impl EntityBuilder {
1020            pub fn new() -> Self {
1021                Self::default()
1022            }
1023
1024            $(
1025                $(#[$comp_attr])*
1026                $crate::paste::paste! {
1027                    pub fn [<with_$name>](mut self, value: $type) -> Self {
1028                        self.$name = Some(value);
1029                        self
1030                    }
1031                }
1032            )*
1033
1034            pub fn spawn(self, world: &mut $world, instances: usize) -> Vec<$crate::Entity> {
1035                let mut mask = 0;
1036                $(
1037                    $(#[$comp_attr])*
1038                    if self.$name.is_some() {
1039                        mask |= $mask;
1040                    }
1041                )*
1042                let entities = world.spawn_entities(mask, instances);
1043                let last_entity_index = entities.len().saturating_sub(1);
1044                for (entity_index, entity) in entities.iter().enumerate() {
1045                    if entity_index == last_entity_index {
1046                        $(
1047                            $(#[$comp_attr])*
1048                            $crate::paste::paste! {
1049                                if let Some(component) = self.$name {
1050                                    world.[<set_$name>](*entity, component);
1051                                }
1052                            }
1053                        )*
1054                        break;
1055                    } else {
1056                        $(
1057                            $(#[$comp_attr])*
1058                            $crate::paste::paste! {
1059                                if let Some(ref component) = self.$name {
1060                                    world.[<set_$name>](*entity, component.clone());
1061                                }
1062                            }
1063                        )*
1064                    }
1065                }
1066                entities
1067            }
1068        }
1069
1070        #[repr(u64)]
1071        #[allow(clippy::upper_case_acronyms)]
1072        #[allow(non_camel_case_types)]
1073        pub enum Component {
1074            $($(#[$comp_attr])* $mask,)*
1075            $($tag_mask,)*
1076        }
1077
1078        $($(#[$comp_attr])* pub const $mask: u64 = 1 << (Component::$mask as u64);)*
1079        $(pub const $tag_mask: u64 = 1 << (Component::$tag_mask as u64);)*
1080
1081        const ALL_TAGS_MASK: u64 = 0 $(| $tag_mask)*;
1082
1083        pub const COMPONENT_COUNT: usize = {
1084            let mut count = 0;
1085            $($(#[$comp_attr])* { count += 1; let _ = Component::$mask; })*
1086            $(count += 1; let _ = Component::$tag_mask;)*
1087            count
1088        };
1089
1090        $crate::paste::paste! {
1091            pub enum Command {
1092                SpawnEntities { mask: u64, count: usize },
1093                DespawnEntities { entities: Vec<$crate::Entity> },
1094                AddComponents { entity: $crate::Entity, mask: u64 },
1095                RemoveComponents { entity: $crate::Entity, mask: u64 },
1096                $(
1097                    $(#[$comp_attr])*
1098                    [<Set $mask:camel>] { entity: $crate::Entity, value: $type },
1099                )*
1100                $(
1101                    [<Add $tag_mask:camel>] { entity: $crate::Entity },
1102                    [<Remove $tag_mask:camel>] { entity: $crate::Entity },
1103                )*
1104            }
1105        }
1106
1107        #[allow(unused)]
1108        pub struct $world {
1109            entity_locations: $crate::EntityLocations,
1110            tables: Vec<ComponentArrays>,
1111            allocator: $crate::EntityAllocator,
1112            pub resources: $resources,
1113            table_edges: Vec<TableEdges>,
1114            table_lookup: std::collections::HashMap<u64, usize>,
1115            query_cache: std::collections::HashMap<u64, Vec<usize>>,
1116            current_tick: u32,
1117            last_tick: u32,
1118            $($tag_name: std::collections::HashSet<$crate::Entity>,)*
1119            command_buffer: Vec<Command>,
1120            $($event_name: $crate::EventQueue<$event_type>,)*
1121        }
1122
1123        impl Default for $world {
1124            fn default() -> Self {
1125                Self {
1126                    entity_locations: $crate::EntityLocations::default(),
1127                    tables: Vec::default(),
1128                    allocator: $crate::EntityAllocator::default(),
1129                    resources: $resources::default(),
1130                    table_edges: Vec::default(),
1131                    table_lookup: std::collections::HashMap::default(),
1132                    query_cache: std::collections::HashMap::default(),
1133                    current_tick: 0,
1134                    last_tick: 0,
1135                    $(
1136                        $tag_name: std::collections::HashSet::default(),
1137                    )*
1138                    command_buffer: Vec::default(),
1139                    $(
1140                        $event_name: $crate::EventQueue::new(),
1141                    )*
1142                }
1143            }
1144        }
1145
1146        #[allow(unused)]
1147        impl $world {
1148            fn get_cached_tables(&mut self, mask: u64) -> &[usize] {
1149                if !self.query_cache.contains_key(&mask) {
1150                    let matching_tables: Vec<usize> = self.tables
1151                        .iter()
1152                        .enumerate()
1153                        .filter(|(_, table)| table.mask & mask == mask)
1154                        .map(|(idx, _)| idx)
1155                        .collect();
1156                    self.query_cache.insert(mask, matching_tables);
1157                }
1158                &self.query_cache[&mask]
1159            }
1160
1161            fn invalidate_query_cache_for_table(&mut self, new_table_mask: u64, new_table_index: usize) {
1162                self.query_cache.retain(|query_mask, cached_tables| {
1163                    if new_table_mask & query_mask == *query_mask {
1164                        cached_tables.push(new_table_index);
1165                        true
1166                    } else {
1167                        true
1168                    }
1169                });
1170            }
1171
1172            $(
1173                $(#[$comp_attr])*
1174                $crate::paste::paste! {
1175                    #[inline]
1176                    pub fn [<get_ $name>](&self, entity: $crate::Entity) -> Option<&$type> {
1177                        let (table_index, array_index) = get_location(&self.entity_locations, entity)?;
1178                        let table = &self.tables[table_index];
1179
1180                        if table.mask & $mask == 0 {
1181                            return None;
1182                        }
1183
1184                        Some(&table.$name[array_index])
1185                    }
1186
1187                    $crate::paste::paste! {
1188                        #[inline]
1189                        pub fn [<get_ $name _mut>](&mut self, entity: $crate::Entity) -> Option<&mut $type> {
1190                            let (table_index, array_index) = get_location(&self.entity_locations, entity)?;
1191                            let current_tick = self.current_tick;
1192                            let table = &mut self.tables[table_index];
1193
1194                            if table.mask & $mask == 0 {
1195                                return None;
1196                            }
1197
1198                            table.[<$name _changed>][array_index] = current_tick;
1199                            Some(&mut table.$name[array_index])
1200                        }
1201                    }
1202
1203                    $crate::paste::paste! {
1204                        #[inline]
1205                        pub fn [<modify_ $name>]<R>(&mut self, entity: $crate::Entity, f: impl FnOnce(&mut $type) -> R) -> Option<R> {
1206                            let (table_index, array_index) = get_location(&self.entity_locations, entity)?;
1207                            let current_tick = self.current_tick;
1208                            let table = &mut self.tables[table_index];
1209
1210                            if table.mask & $mask == 0 {
1211                                return None;
1212                            }
1213
1214                            table.[<$name _changed>][array_index] = current_tick;
1215                            Some(f(&mut table.$name[array_index]))
1216                        }
1217                    }
1218
1219                    #[inline]
1220                    pub fn [<entity_has_ $name>](&self, entity: $crate::Entity) -> bool {
1221                        self.entity_has_components(entity, $mask)
1222                    }
1223
1224                    #[inline]
1225                    pub fn [<set_ $name>](&mut self, entity: $crate::Entity, value: $type) {
1226                        if let Some((table_index, array_index)) = get_location(&self.entity_locations, entity) {
1227                            if self.tables[table_index].mask & $mask != 0 {
1228                                self.tables[table_index].$name[array_index] = value;
1229                                return;
1230                            }
1231                            self.add_components_at(entity, $mask, table_index, array_index);
1232                            if let Some((new_table_index, new_array_index)) = get_location(&self.entity_locations, entity) {
1233                                self.tables[new_table_index].$name[new_array_index] = value;
1234                            }
1235                        }
1236                    }
1237
1238                    #[inline]
1239                    pub fn [<add_ $name>](&mut self, entity: $crate::Entity) {
1240                        self.add_components(entity, $mask);
1241                    }
1242
1243                    #[inline]
1244                    pub fn [<remove_ $name>](&mut self, entity: $crate::Entity) -> bool {
1245                        self.remove_components(entity, $mask)
1246                    }
1247
1248                    #[inline]
1249                    pub fn [<query_ $name>](&self) -> [<$mask:camel QueryIter>]<'_> {
1250                        [<$mask:camel QueryIter>] {
1251                            tables: &self.tables,
1252                            table_index: 0,
1253                            array_index: 0,
1254                        }
1255                    }
1256
1257                    pub fn [<for_each_ $name _mut>]<F>(&mut self, mut f: F)
1258                    where
1259                        F: FnMut(&mut $type),
1260                    {
1261                        let table_indices: Vec<usize> = self.get_cached_tables($mask).to_vec();
1262
1263                        for table_index in table_indices {
1264                            for component in &mut self.tables[table_index].$name {
1265                                f(component);
1266                            }
1267                        }
1268                    }
1269
1270                    #[cfg(not(target_family = "wasm"))]
1271                    pub fn [<par_for_each_ $name _mut>]<F>(&mut self, f: F)
1272                    where
1273                        F: Fn(&mut $type) + Send + Sync,
1274                    {
1275                        use $crate::rayon::prelude::*;
1276
1277                        self.tables
1278                            .par_iter_mut()
1279                            .filter(|table| table.mask & $mask != 0)
1280                            .for_each(|table| {
1281                                table.$name.par_iter_mut().for_each(|component| f(component));
1282                            });
1283                    }
1284
1285                    pub fn [<iter_ $name _slices>](&self) -> impl Iterator<Item = &[$type]> {
1286                        self.tables
1287                            .iter()
1288                            .filter(|table| table.mask & $mask != 0)
1289                            .map(|table| table.$name.as_slice())
1290                    }
1291
1292                    pub fn [<iter_ $name _slices_mut>](&mut self) -> impl Iterator<Item = &mut [$type]> {
1293                        self.tables
1294                            .iter_mut()
1295                            .filter(|table| table.mask & $mask != 0)
1296                            .map(|table| table.$name.as_mut_slice())
1297                    }
1298                }
1299            )*
1300
1301            pub fn spawn_entities(&mut self, mask: u64, count: usize) -> Vec<$crate::Entity> {
1302                let mut entities = Vec::with_capacity(count);
1303                let table_index = get_or_create_table(self, mask);
1304                let start_index = self.tables[table_index].entity_indices.len();
1305
1306                self.tables[table_index].entity_indices.reserve(count);
1307                $(
1308                    $(#[$comp_attr])*
1309                    $crate::paste::paste! {
1310                        if mask & $mask != 0 {
1311                            self.tables[table_index].$name.reserve(count);
1312                            self.tables[table_index].[<$name _changed>].reserve(count);
1313                        }
1314                    }
1315                )*
1316
1317                for i in 0..count {
1318                    let entity = create_entity(self);
1319                    entities.push(entity);
1320
1321                    self.tables[table_index].entity_indices.push(entity);
1322                    $(
1323                        $(#[$comp_attr])*
1324                        $crate::paste::paste! {
1325                            if mask & $mask != 0 {
1326                                self.tables[table_index].$name.push(<$type>::default());
1327                                self.tables[table_index].[<$name _changed>].push(self.current_tick);
1328                            }
1329                        }
1330                    )*
1331
1332                    insert_location(
1333                        &mut self.entity_locations,
1334                        entity,
1335                        (table_index, start_index + i),
1336                    );
1337                }
1338
1339                entities
1340            }
1341
1342            pub fn spawn_batch<F>(&mut self, mask: u64, count: usize, mut init: F) -> Vec<$crate::Entity>
1343            where
1344                F: FnMut(&mut ComponentArrays, usize),
1345            {
1346                let table_index = get_or_create_table(self, mask);
1347                let start_index = self.tables[table_index].entity_indices.len();
1348
1349                self.tables[table_index].entity_indices.reserve(count);
1350                $(
1351                    $(#[$comp_attr])*
1352                    $crate::paste::paste! {
1353                        if mask & $mask != 0 {
1354                            self.tables[table_index].$name.reserve(count);
1355                            self.tables[table_index].[<$name _changed>].reserve(count);
1356                        }
1357                    }
1358                )*
1359
1360                let mut entities = Vec::with_capacity(count);
1361
1362                for i in 0..count {
1363                    let entity = create_entity(self);
1364                    entities.push(entity);
1365
1366                    self.tables[table_index].entity_indices.push(entity);
1367                    $(
1368                        $(#[$comp_attr])*
1369                        $crate::paste::paste! {
1370                            if mask & $mask != 0 {
1371                                self.tables[table_index].$name.push(<$type>::default());
1372                                self.tables[table_index].[<$name _changed>].push(self.current_tick);
1373                            }
1374                        }
1375                    )*
1376
1377                    insert_location(
1378                        &mut self.entity_locations,
1379                        entity,
1380                        (table_index, start_index + i),
1381                    );
1382
1383                    init(&mut self.tables[table_index], start_index + i);
1384                }
1385
1386                entities
1387            }
1388
1389            pub fn query_entities(&self, mask: u64) -> EntityQueryIter<'_> {
1390                EntityQueryIter {
1391                    tables: &self.tables,
1392                    mask,
1393                    table_index: 0,
1394                    array_index: 0,
1395                }
1396            }
1397
1398            pub fn query_entities_changed(&self, mask: u64) -> ChangedEntityQueryIter<'_> {
1399                ChangedEntityQueryIter {
1400                    tables: &self.tables,
1401                    mask,
1402                    since_tick: self.last_tick,
1403                    table_index: 0,
1404                    array_index: 0,
1405                }
1406            }
1407
1408            pub fn query_first_entity(&self, mask: u64) -> Option<$crate::Entity> {
1409                for table in &self.tables {
1410                    if table.mask & mask != mask {
1411                        continue;
1412                    }
1413                    if let Some(&entity) = table.entity_indices.first() {
1414                        return Some(entity);
1415                    }
1416                }
1417                None
1418            }
1419
1420            pub fn despawn_entities(&mut self, entities: &[$crate::Entity]) -> Vec<$crate::Entity> {
1421                let mut despawned = Vec::with_capacity(entities.len());
1422                let mut tables_to_update = Vec::new();
1423
1424                for &entity in entities {
1425                    if let Some(loc) = self.entity_locations.get_mut(entity.id) {
1426                        if loc.allocated && loc.generation == entity.generation {
1427                            let table_idx = loc.table_index as usize;
1428                            let array_idx = loc.array_index as usize;
1429
1430                            let next_gen = loc.generation.wrapping_add(1);
1431                            self.entity_locations.mark_deallocated(entity.id);
1432                            if let Some(loc) = self.entity_locations.get_mut(entity.id) {
1433                                loc.generation = next_gen;
1434                            }
1435                            self.allocator.free_ids.push((entity.id, next_gen));
1436
1437                            tables_to_update.push((table_idx, array_idx));
1438                            despawned.push(entity);
1439                        }
1440                    }
1441                }
1442
1443                tables_to_update.sort_by(|a, b| b.cmp(a));
1444
1445                for (table_idx, array_idx) in tables_to_update {
1446                    if table_idx >= self.tables.len() {
1447                        continue;
1448                    }
1449
1450                    let table = &mut self.tables[table_idx];
1451                    if table.entity_indices.is_empty() {
1452                        continue;
1453                    }
1454                    let last_idx = table.entity_indices.len() - 1;
1455
1456                    if array_idx < last_idx {
1457                        let moved_entity = table.entity_indices[last_idx];
1458                        if let Some(loc) = self.entity_locations.get_mut(moved_entity.id) {
1459                            if loc.allocated {
1460                                loc.array_index = array_idx as u32;
1461                            }
1462                        }
1463                    }
1464
1465                    $(
1466                        $(#[$comp_attr])*
1467                        $crate::paste::paste! {
1468                            if table.mask & $mask != 0 {
1469                                table.$name.swap_remove(array_idx);
1470                                table.[<$name _changed>].swap_remove(array_idx);
1471                            }
1472                        }
1473                    )*
1474                    table.entity_indices.swap_remove(array_idx);
1475                }
1476
1477                $(
1478                    for &entity in &despawned {
1479                        self.$tag_name.remove(&entity);
1480                    }
1481                )*
1482
1483                despawned
1484            }
1485
1486            fn add_components_at(&mut self, entity: $crate::Entity, mask: u64, table_index: usize, array_index: usize) {
1487                let current_mask = self.tables[table_index].mask;
1488                if current_mask & mask == mask {
1489                    return;
1490                }
1491
1492                let target_table = if mask.count_ones() == 1 {
1493                    get_component_index(mask).and_then(|idx| self.table_edges[table_index].add_edges[idx])
1494                } else {
1495                    self.table_edges[table_index].multi_add_cache.get(&mask).copied()
1496                };
1497
1498                let new_table_index = target_table.unwrap_or_else(|| {
1499                    let new_idx = get_or_create_table(self, current_mask | mask);
1500                    self.table_edges[table_index].multi_add_cache.insert(mask, new_idx);
1501                    new_idx
1502                });
1503
1504                move_entity(self, entity, table_index, array_index, new_table_index);
1505            }
1506
1507            pub fn add_components(&mut self, entity: $crate::Entity, mask: u64) -> bool {
1508                if let Some((table_index, array_index)) = get_location(&self.entity_locations, entity) {
1509                    self.add_components_at(entity, mask, table_index, array_index);
1510                    true
1511                } else {
1512                    false
1513                }
1514            }
1515
1516            pub fn remove_components(&mut self, entity: $crate::Entity, mask: u64) -> bool {
1517                if let Some((table_index, array_index)) = get_location(&self.entity_locations, entity) {
1518                    let current_mask = self.tables[table_index].mask;
1519                    if current_mask & mask == 0 {
1520                        return true;
1521                    }
1522
1523                    let target_table = if mask.count_ones() == 1 {
1524                        get_component_index(mask)
1525                            .and_then(|idx| self.table_edges[table_index].remove_edges[idx])
1526                    } else {
1527                        self.table_edges[table_index].multi_remove_cache.get(&mask).copied()
1528                    };
1529
1530                    let new_table_index = target_table.unwrap_or_else(|| {
1531                        let new_idx = get_or_create_table(self, current_mask & !mask);
1532                        self.table_edges[table_index].multi_remove_cache.insert(mask, new_idx);
1533                        new_idx
1534                    });
1535
1536                    move_entity(self, entity, table_index, array_index, new_table_index);
1537                    true
1538                } else {
1539                    false
1540                }
1541            }
1542
1543            pub fn component_mask(&self, entity: $crate::Entity) -> Option<u64> {
1544                get_location(&self.entity_locations, entity)
1545                    .map(|(table_index, _)| self.tables[table_index].mask)
1546            }
1547
1548            pub fn get_all_entities(&self) -> Vec<$crate::Entity> {
1549                let mut result = Vec::with_capacity(self.entity_count());
1550                for table in &self.tables {
1551                    result.extend(table.entity_indices.iter().copied());
1552                }
1553                result
1554            }
1555
1556            pub fn entity_count(&self) -> usize {
1557                self.tables.iter().map(|table| table.entity_indices.len()).sum()
1558            }
1559
1560            pub fn entity_has_components(&self, entity: $crate::Entity, components: u64) -> bool {
1561                self.component_mask(entity).unwrap_or(0) & components == components
1562            }
1563
1564            pub fn increment_tick(&mut self) {
1565                self.last_tick = self.current_tick;
1566                self.current_tick = self.current_tick.wrapping_add(1);
1567            }
1568
1569            pub fn current_tick(&self) -> u32 {
1570                self.current_tick
1571            }
1572
1573            pub fn last_tick(&self) -> u32 {
1574                self.last_tick
1575            }
1576
1577            $(
1578                $crate::paste::paste! {
1579                    pub fn [<add_ $tag_name>](&mut self, entity: $crate::Entity) {
1580                        if get_location(&self.entity_locations, entity).is_some() {
1581                            self.$tag_name.insert(entity);
1582                        }
1583                    }
1584
1585                    pub fn [<remove_ $tag_name>](&mut self, entity: $crate::Entity) -> bool {
1586                        self.$tag_name.remove(&entity)
1587                    }
1588
1589                    pub fn [<has_ $tag_name>](&self, entity: $crate::Entity) -> bool {
1590                        self.$tag_name.contains(&entity)
1591                    }
1592
1593                    pub fn [<query_ $tag_name>](&self) -> impl Iterator<Item = $crate::Entity> + '_ {
1594                        self.$tag_name.iter().copied()
1595                    }
1596                }
1597            )*
1598
1599            fn entity_matches_tags(&self, entity: $crate::Entity, include_tags: u64, exclude_tags: u64) -> bool {
1600                $(
1601                    if include_tags & $tag_mask != 0 && !self.$tag_name.contains(&entity) {
1602                        return false;
1603                    }
1604                    if exclude_tags & $tag_mask != 0 && self.$tag_name.contains(&entity) {
1605                        return false;
1606                    }
1607                )*
1608                true
1609            }
1610
1611            $(
1612                $crate::paste::paste! {
1613                    pub fn [<send_ $event_name>](&mut self, event: $event_type) {
1614                        self.$event_name.send(event);
1615                    }
1616
1617                    pub fn [<read_ $event_name>](&self) -> impl Iterator<Item = &$event_type> {
1618                        self.$event_name.read()
1619                    }
1620
1621                    pub fn [<drain_ $event_name>](&mut self) -> impl Iterator<Item = $event_type> + '_ {
1622                        self.$event_name.drain()
1623                    }
1624
1625                    pub fn [<clear_ $event_name>](&mut self) {
1626                        self.$event_name.clear();
1627                    }
1628
1629                    pub fn [<update_ $event_name>](&mut self) {
1630                        self.$event_name.update();
1631                    }
1632
1633                    pub fn [<len_ $event_name>](&self) -> usize {
1634                        self.$event_name.len()
1635                    }
1636
1637                    pub fn [<is_empty_ $event_name>](&self) -> bool {
1638                        self.$event_name.is_empty()
1639                    }
1640
1641                    pub fn [<peek_ $event_name>](&self) -> Option<&$event_type> {
1642                        self.$event_name.peek()
1643                    }
1644
1645                    pub fn [<collect_ $event_name>](&self) -> Vec<$event_type>
1646                    where
1647                        $event_type: Clone,
1648                    {
1649                        self.$event_name.read().cloned().collect()
1650                    }
1651                }
1652            )*
1653
1654            fn update_events(&mut self) {
1655                $(
1656                    self.$event_name.update();
1657                )*
1658            }
1659
1660            pub fn step(&mut self) {
1661                self.update_events();
1662                self.last_tick = self.current_tick;
1663                self.current_tick += 1;
1664            }
1665
1666            pub fn queue_spawn_entities(&mut self, mask: u64, count: usize) {
1667                self.command_buffer.push(Command::SpawnEntities { mask, count });
1668            }
1669
1670            pub fn queue_despawn_entities(&mut self, entities: Vec<$crate::Entity>) {
1671                self.command_buffer.push(Command::DespawnEntities { entities });
1672            }
1673
1674            pub fn queue_despawn_entity(&mut self, entity: $crate::Entity) {
1675                self.command_buffer.push(Command::DespawnEntities { entities: vec![entity] });
1676            }
1677
1678            pub fn queue_add_components(&mut self, entity: $crate::Entity, mask: u64) {
1679                self.command_buffer.push(Command::AddComponents { entity, mask });
1680            }
1681
1682            pub fn queue_remove_components(&mut self, entity: $crate::Entity, mask: u64) {
1683                self.command_buffer.push(Command::RemoveComponents { entity, mask });
1684            }
1685
1686            $(
1687                $crate::paste::paste! {
1688                    pub fn [<queue_set_ $name>](&mut self, entity: $crate::Entity, value: $type) {
1689                        self.command_buffer.push(Command::[<Set $mask:camel>] { entity, value });
1690                    }
1691                }
1692            )*
1693
1694            $(
1695                $crate::paste::paste! {
1696                    pub fn [<queue_add_ $tag_name>](&mut self, entity: $crate::Entity) {
1697                        self.command_buffer.push(Command::[<Add $tag_mask:camel>] { entity });
1698                    }
1699
1700                    pub fn [<queue_remove_ $tag_name>](&mut self, entity: $crate::Entity) {
1701                        self.command_buffer.push(Command::[<Remove $tag_mask:camel>] { entity });
1702                    }
1703                }
1704            )*
1705
1706            pub fn apply_commands(&mut self) {
1707                let commands = std::mem::take(&mut self.command_buffer);
1708
1709                $crate::paste::paste! {
1710                    for command in commands {
1711                        match command {
1712                            Command::SpawnEntities { mask, count } => {
1713                                self.spawn_entities(mask, count);
1714                            }
1715                            Command::DespawnEntities { entities } => {
1716                                self.despawn_entities(&entities);
1717                            }
1718                            Command::AddComponents { entity, mask } => {
1719                                self.add_components(entity, mask);
1720                            }
1721                            Command::RemoveComponents { entity, mask } => {
1722                                self.remove_components(entity, mask);
1723                            }
1724                            $(
1725                                Command::[<Set $mask:camel>] { entity, value } => {
1726                                    self.[<set_ $name>](entity, value);
1727                                }
1728                            )*
1729                            $(
1730                                Command::[<Add $tag_mask:camel>] { entity } => {
1731                                    self.[<add_ $tag_name>](entity);
1732                                }
1733                                Command::[<Remove $tag_mask:camel>] { entity } => {
1734                                    self.[<remove_ $tag_name>](entity);
1735                                }
1736                            )*
1737                        }
1738                    }
1739                }
1740            }
1741
1742            pub fn command_count(&self) -> usize {
1743                self.command_buffer.len()
1744            }
1745
1746            pub fn clear_commands(&mut self) {
1747                self.command_buffer.clear();
1748            }
1749
1750
1751            $(
1752                $crate::paste::paste! {
1753                    pub fn [<query_ $name _mut>]<F>(&mut self, mask: u64, mut f: F)
1754                    where
1755                        F: FnMut($crate::Entity, &mut $type),
1756                    {
1757                        let table_indices: Vec<usize> = self.get_cached_tables(mask).to_vec();
1758
1759                        for &table_index in &table_indices {
1760                            let table = &mut self.tables[table_index];
1761                            if table.mask & $mask == 0 {
1762                                continue;
1763                            }
1764
1765                            for idx in 0..table.entity_indices.len() {
1766                                let entity = table.entity_indices[idx];
1767                                f(entity, &mut table.$name[idx]);
1768                            }
1769                        }
1770                    }
1771                }
1772            )*
1773        }
1774
1775        #[allow(unused)]
1776        impl $world {
1777            #[inline]
1778            pub fn for_each<F>(&self, include: u64, exclude: u64, mut f: F)
1779            where
1780                F: FnMut($crate::Entity, &ComponentArrays, usize),
1781            {
1782                let component_include = include & !ALL_TAGS_MASK;
1783                let component_exclude = exclude & !ALL_TAGS_MASK;
1784                let tag_include = include & ALL_TAGS_MASK;
1785                let tag_exclude = exclude & ALL_TAGS_MASK;
1786
1787                if let Some(cached) = self.query_cache.get(&component_include) {
1788                    for &table_index in cached {
1789                        let table = &self.tables[table_index];
1790                        if table.mask & component_exclude != 0 {
1791                            continue;
1792                        }
1793                        if tag_include == 0 && tag_exclude == 0 {
1794                            for (idx, &entity) in table.entity_indices.iter().enumerate() {
1795                                f(entity, table, idx);
1796                            }
1797                        } else {
1798                            for (idx, &entity) in table.entity_indices.iter().enumerate() {
1799                                if self.entity_matches_tags(entity, tag_include, tag_exclude) {
1800                                    f(entity, table, idx);
1801                                }
1802                            }
1803                        }
1804                    }
1805                    return;
1806                }
1807
1808                for table in &self.tables {
1809                    if table.mask & component_include != component_include || table.mask & component_exclude != 0 {
1810                        continue;
1811                    }
1812
1813                    if tag_include == 0 && tag_exclude == 0 {
1814                        for (idx, &entity) in table.entity_indices.iter().enumerate() {
1815                            f(entity, table, idx);
1816                        }
1817                    } else {
1818                        for (idx, &entity) in table.entity_indices.iter().enumerate() {
1819                            if self.entity_matches_tags(entity, tag_include, tag_exclude) {
1820                                f(entity, table, idx);
1821                            }
1822                        }
1823                    }
1824                }
1825            }
1826
1827            #[inline]
1828            pub fn for_each_mut<F>(&mut self, include: u64, exclude: u64, mut f: F)
1829            where
1830                F: FnMut($crate::Entity, &mut ComponentArrays, usize),
1831            {
1832                let component_include = include & !ALL_TAGS_MASK;
1833                let component_exclude = exclude & !ALL_TAGS_MASK;
1834                let tag_include = include & ALL_TAGS_MASK;
1835                let tag_exclude = exclude & ALL_TAGS_MASK;
1836
1837                let table_indices: Vec<usize> = self.get_cached_tables(component_include).to_vec();
1838
1839                if tag_include == 0 && tag_exclude == 0 {
1840                    for &table_index in &table_indices {
1841                        let table = &mut self.tables[table_index];
1842                        if table.mask & component_exclude != 0 {
1843                            continue;
1844                        }
1845
1846                        for idx in 0..table.entity_indices.len() {
1847                            let entity = table.entity_indices[idx];
1848                            f(entity, table, idx);
1849                        }
1850                    }
1851                } else {
1852                    for &table_index in &table_indices {
1853                        let table = &mut self.tables[table_index];
1854                        if table.mask & component_exclude != 0 {
1855                            continue;
1856                        }
1857
1858                        for idx in 0..table.entity_indices.len() {
1859                            let entity = table.entity_indices[idx];
1860                            let mut tag_match = true;
1861                            $(
1862                                if tag_include & $tag_mask != 0 && !self.$tag_name.contains(&entity) {
1863                                    tag_match = false;
1864                                }
1865                                if tag_match && tag_exclude & $tag_mask != 0 && self.$tag_name.contains(&entity) {
1866                                    tag_match = false;
1867                                }
1868                            )*
1869                            if tag_match {
1870                                f(entity, table, idx);
1871                            }
1872                        }
1873                    }
1874                }
1875            }
1876
1877            #[cfg(not(target_family = "wasm"))]
1878            #[inline]
1879            pub fn par_for_each_mut<F>(&mut self, include: u64, exclude: u64, f: F)
1880            where
1881                F: Fn($crate::Entity, &mut ComponentArrays, usize) + Send + Sync,
1882            {
1883                use $crate::rayon::prelude::*;
1884
1885                let component_include = include & !ALL_TAGS_MASK;
1886                let component_exclude = exclude & !ALL_TAGS_MASK;
1887                let tag_include = include & ALL_TAGS_MASK;
1888                let tag_exclude = exclude & ALL_TAGS_MASK;
1889
1890                if tag_include == 0 && tag_exclude == 0 {
1891                    self.tables
1892                        .par_iter_mut()
1893                        .filter(|table| table.mask & component_include == component_include && table.mask & component_exclude == 0)
1894                        .for_each(|table| {
1895                            for idx in 0..table.entity_indices.len() {
1896                                let entity = table.entity_indices[idx];
1897                                f(entity, table, idx);
1898                            }
1899                        });
1900                } else {
1901                    $(let $tag_name = &self.$tag_name;)*
1902                    self.tables
1903                        .par_iter_mut()
1904                        .filter(|table| table.mask & component_include == component_include && table.mask & component_exclude == 0)
1905                        .for_each(|table| {
1906                            for idx in 0..table.entity_indices.len() {
1907                                let entity = table.entity_indices[idx];
1908                                let mut tag_match = true;
1909                                $(
1910                                    if tag_include & $tag_mask != 0 && !$tag_name.contains(&entity) {
1911                                        tag_match = false;
1912                                    }
1913                                    if tag_match && tag_exclude & $tag_mask != 0 && $tag_name.contains(&entity) {
1914                                        tag_match = false;
1915                                    }
1916                                )*
1917                                if tag_match {
1918                                    f(entity, table, idx);
1919                                }
1920                            }
1921                        });
1922                }
1923            }
1924
1925            #[inline]
1926            pub fn for_each_mut_changed<F>(&mut self, include: u64, exclude: u64, mut f: F)
1927            where
1928                F: FnMut($crate::Entity, &mut ComponentArrays, usize),
1929            {
1930                let component_include = include & !ALL_TAGS_MASK;
1931                let component_exclude = exclude & !ALL_TAGS_MASK;
1932                let tag_include = include & ALL_TAGS_MASK;
1933                let tag_exclude = exclude & ALL_TAGS_MASK;
1934
1935                let table_indices: Vec<usize> = self.get_cached_tables(component_include).to_vec();
1936                let since_tick = self.last_tick;
1937
1938                if tag_include == 0 && tag_exclude == 0 {
1939                    for &table_index in &table_indices {
1940                        let table = &mut self.tables[table_index];
1941                        if table.mask & component_exclude != 0 {
1942                            continue;
1943                        }
1944
1945                        for idx in 0..table.entity_indices.len() {
1946                            let entity = table.entity_indices[idx];
1947
1948                            let mut changed = false;
1949                            $(
1950                                $crate::paste::paste! {
1951                                    if component_include & $mask != 0 && table.mask & $mask != 0 && table.[<$name _changed>][idx] > since_tick {
1952                                        changed = true;
1953                                    }
1954                                }
1955                            )*
1956
1957                            if changed {
1958                                f(entity, table, idx);
1959                            }
1960                        }
1961                    }
1962                } else {
1963                    for &table_index in &table_indices {
1964                        let table = &mut self.tables[table_index];
1965                        if table.mask & component_exclude != 0 {
1966                            continue;
1967                        }
1968
1969                        for idx in 0..table.entity_indices.len() {
1970                            let entity = table.entity_indices[idx];
1971                            let mut tag_match = true;
1972                            $(
1973                                if tag_include & $tag_mask != 0 && !self.$tag_name.contains(&entity) {
1974                                    tag_match = false;
1975                                }
1976                                if tag_match && tag_exclude & $tag_mask != 0 && self.$tag_name.contains(&entity) {
1977                                    tag_match = false;
1978                                }
1979                            )*
1980                            if !tag_match {
1981                                continue;
1982                            }
1983
1984                            let mut changed = false;
1985                            $(
1986                                $crate::paste::paste! {
1987                                    if component_include & $mask != 0 && table.mask & $mask != 0 && table.[<$name _changed>][idx] > since_tick {
1988                                        changed = true;
1989                                    }
1990                                }
1991                            )*
1992
1993                            if changed {
1994                                f(entity, table, idx);
1995                            }
1996                        }
1997                    }
1998                }
1999            }
2000
2001        }
2002
2003
2004        #[derive(Default)]
2005        pub struct $resources {
2006            $($(#[$attr])* pub $resource_name: $resource_type,)*
2007        }
2008
2009        $crate::paste::paste! {
2010            #[derive(Default)]
2011            pub struct ComponentArrays {
2012                $($(#[$comp_attr])* pub $name: Vec<$type>,)*
2013                $($(#[$comp_attr])* pub [<$name _changed>]: Vec<u32>,)*
2014                pub entity_indices: Vec<$crate::Entity>,
2015                pub mask: u64,
2016            }
2017        }
2018
2019
2020        pub struct QueryBuilder<'a> {
2021            world: &'a $world,
2022            include: u64,
2023            exclude: u64,
2024        }
2025
2026        impl<'a> QueryBuilder<'a> {
2027            pub fn new(world: &'a $world) -> Self {
2028                Self {
2029                    world,
2030                    include: 0,
2031                    exclude: 0,
2032                }
2033            }
2034
2035            pub fn with(mut self, mask: u64) -> Self {
2036                self.include |= mask;
2037                self
2038            }
2039
2040            pub fn without(mut self, mask: u64) -> Self {
2041                self.exclude |= mask;
2042                self
2043            }
2044
2045            pub fn iter<F>(self, f: F)
2046            where
2047                F: FnMut($crate::Entity, &ComponentArrays, usize),
2048            {
2049                self.world.for_each(self.include, self.exclude, f);
2050            }
2051        }
2052
2053        pub struct QueryBuilderMut<'a> {
2054            world: &'a mut $world,
2055            include: u64,
2056            exclude: u64,
2057        }
2058
2059        impl<'a> QueryBuilderMut<'a> {
2060            pub fn new(world: &'a mut $world) -> Self {
2061                Self {
2062                    world,
2063                    include: 0,
2064                    exclude: 0,
2065                }
2066            }
2067
2068            pub fn with(mut self, mask: u64) -> Self {
2069                self.include |= mask;
2070                self
2071            }
2072
2073            pub fn without(mut self, mask: u64) -> Self {
2074                self.exclude |= mask;
2075                self
2076            }
2077
2078            pub fn iter<F>(self, f: F)
2079            where
2080                F: FnMut($crate::Entity, &mut ComponentArrays, usize),
2081            {
2082                self.world.for_each_mut(self.include, self.exclude, f);
2083            }
2084        }
2085
2086        impl $world {
2087            pub fn query(&self) -> QueryBuilder<'_> {
2088                QueryBuilder::new(self)
2089            }
2090
2091            pub fn query_mut(&mut self) -> QueryBuilderMut<'_> {
2092                QueryBuilderMut::new(self)
2093            }
2094
2095            $(
2096                $(#[$comp_attr])*
2097                $crate::paste::paste! {
2098                    pub fn [<iter_ $name>]<F>(&self, mut f: F)
2099                    where
2100                        F: FnMut($crate::Entity, &$type),
2101                    {
2102                        self.for_each($mask, 0, |entity, table, idx| {
2103                            f(entity, &table.$name[idx]);
2104                        });
2105                    }
2106
2107                    pub fn [<iter_ $name _mut>]<F>(&mut self, mut f: F)
2108                    where
2109                        F: FnMut($crate::Entity, &mut $type),
2110                    {
2111                        self.for_each_mut($mask, 0, |entity, table, idx| {
2112                            f(entity, &mut table.$name[idx]);
2113                        });
2114                    }
2115                }
2116            )*
2117        }
2118
2119        pub struct EntityQueryIter<'a> {
2120            tables: &'a [ComponentArrays],
2121            mask: u64,
2122            table_index: usize,
2123            array_index: usize,
2124        }
2125
2126        impl<'a> Iterator for EntityQueryIter<'a> {
2127            type Item = $crate::Entity;
2128
2129            fn next(&mut self) -> Option<Self::Item> {
2130                loop {
2131                    if self.table_index >= self.tables.len() {
2132                        return None;
2133                    }
2134
2135                    let table = &self.tables[self.table_index];
2136
2137                    if table.mask & self.mask != self.mask {
2138                        self.table_index += 1;
2139                        self.array_index = 0;
2140                        continue;
2141                    }
2142
2143                    if self.array_index >= table.entity_indices.len() {
2144                        self.table_index += 1;
2145                        self.array_index = 0;
2146                        continue;
2147                    }
2148
2149                    let entity = table.entity_indices[self.array_index];
2150                    self.array_index += 1;
2151                    return Some(entity);
2152                }
2153            }
2154
2155            fn size_hint(&self) -> (usize, Option<usize>) {
2156                let mut remaining = 0;
2157                for table_idx in self.table_index..self.tables.len() {
2158                    let table = &self.tables[table_idx];
2159                    if table.mask & self.mask != self.mask {
2160                        continue;
2161                    }
2162                    if table_idx == self.table_index {
2163                        remaining += table.entity_indices.len().saturating_sub(self.array_index);
2164                    } else {
2165                        remaining += table.entity_indices.len();
2166                    }
2167                }
2168                (remaining, Some(remaining))
2169            }
2170        }
2171
2172        pub struct ChangedEntityQueryIter<'a> {
2173            tables: &'a [ComponentArrays],
2174            mask: u64,
2175            since_tick: u32,
2176            table_index: usize,
2177            array_index: usize,
2178        }
2179
2180        impl<'a> Iterator for ChangedEntityQueryIter<'a> {
2181            type Item = $crate::Entity;
2182
2183            fn next(&mut self) -> Option<Self::Item> {
2184                loop {
2185                    if self.table_index >= self.tables.len() {
2186                        return None;
2187                    }
2188
2189                    let table = &self.tables[self.table_index];
2190
2191                    if table.mask & self.mask != self.mask {
2192                        self.table_index += 1;
2193                        self.array_index = 0;
2194                        continue;
2195                    }
2196
2197                    if self.array_index >= table.entity_indices.len() {
2198                        self.table_index += 1;
2199                        self.array_index = 0;
2200                        continue;
2201                    }
2202
2203                    let idx = self.array_index;
2204                    self.array_index += 1;
2205
2206                    let mut changed = false;
2207                    $(
2208                        $crate::paste::paste! {
2209                            if self.mask & $mask != 0 && table.mask & $mask != 0 && table.[<$name _changed>][idx] > self.since_tick {
2210                                changed = true;
2211                            }
2212                        }
2213                    )*
2214
2215                    if changed {
2216                        return Some(table.entity_indices[idx]);
2217                    }
2218                }
2219            }
2220        }
2221
2222        $(
2223            $(#[$comp_attr])*
2224            $crate::paste::paste! {
2225                pub struct [<$mask:camel QueryIter>]<'a> {
2226                    tables: &'a [ComponentArrays],
2227                    table_index: usize,
2228                    array_index: usize,
2229                }
2230
2231                impl<'a> Iterator for [<$mask:camel QueryIter>]<'a> {
2232                    type Item = &'a $type;
2233
2234                    fn next(&mut self) -> Option<Self::Item> {
2235                        loop {
2236                            if self.table_index >= self.tables.len() {
2237                                return None;
2238                            }
2239
2240                            let table = &self.tables[self.table_index];
2241
2242                            if table.mask & $mask == 0 {
2243                                self.table_index += 1;
2244                                self.array_index = 0;
2245                                continue;
2246                            }
2247
2248                            if self.array_index >= table.$name.len() {
2249                                self.table_index += 1;
2250                                self.array_index = 0;
2251                                continue;
2252                            }
2253
2254                            let component = &table.$name[self.array_index];
2255                            self.array_index += 1;
2256                            return Some(component);
2257                        }
2258                    }
2259
2260                    fn size_hint(&self) -> (usize, Option<usize>) {
2261                        let mut remaining = 0;
2262                        for table_idx in self.table_index..self.tables.len() {
2263                            let table = &self.tables[table_idx];
2264                            if table.mask & $mask == 0 {
2265                                continue;
2266                            }
2267                            if table_idx == self.table_index {
2268                                remaining += table.$name.len().saturating_sub(self.array_index);
2269                            } else {
2270                                remaining += table.$name.len();
2271                            }
2272                        }
2273                        (remaining, Some(remaining))
2274                    }
2275                }
2276
2277            }
2278        )*
2279
2280        #[derive(Clone)]
2281        struct TableEdges {
2282            add_edges: [Option<usize>; COMPONENT_COUNT],
2283            remove_edges: [Option<usize>; COMPONENT_COUNT],
2284            multi_add_cache: std::collections::HashMap<u64, usize>,
2285            multi_remove_cache: std::collections::HashMap<u64, usize>,
2286        }
2287
2288        impl Default for TableEdges {
2289            fn default() -> Self {
2290                Self {
2291                    add_edges: [None; COMPONENT_COUNT],
2292                    remove_edges: [None; COMPONENT_COUNT],
2293                    multi_add_cache: std::collections::HashMap::default(),
2294                    multi_remove_cache: std::collections::HashMap::default(),
2295                }
2296            }
2297        }
2298
2299        fn get_component_index(mask: u64) -> Option<usize> {
2300            match mask {
2301                $($(#[$comp_attr])* $mask => Some(Component::$mask as _),)*
2302                _ => None,
2303            }
2304        }
2305
2306        fn remove_from_table(arrays: &mut ComponentArrays, index: usize) -> Option<$crate::Entity> {
2307            let last_index = arrays.entity_indices.len() - 1;
2308            let mut swapped_entity = None;
2309
2310            if index < last_index {
2311                swapped_entity = Some(arrays.entity_indices[last_index]);
2312            }
2313
2314            $(
2315                $(#[$comp_attr])*
2316                $crate::paste::paste! {
2317                    if arrays.mask & $mask != 0 {
2318                        arrays.$name.swap_remove(index);
2319                        arrays.[<$name _changed>].swap_remove(index);
2320                    }
2321                }
2322            )*
2323            arrays.entity_indices.swap_remove(index);
2324
2325            swapped_entity
2326        }
2327
2328        fn move_entity(
2329            world: &mut $world,
2330            entity: $crate::Entity,
2331            from_table: usize,
2332            from_index: usize,
2333            to_table: usize,
2334        ) {
2335            let tick = world.current_tick;
2336            let components = {
2337                let from_table_ref = &mut world.tables[from_table];
2338                (
2339                    $(
2340                        $(#[$comp_attr])*
2341                        {
2342                            if from_table_ref.mask & $mask != 0 {
2343                                Some(std::mem::take(&mut from_table_ref.$name[from_index]))
2344                            } else {
2345                                None
2346                            }
2347                        },
2348                    )*
2349                )
2350            };
2351
2352            add_to_table(&mut world.tables[to_table], entity, components, tick);
2353            let new_index = world.tables[to_table].entity_indices.len() - 1;
2354            insert_location(&mut world.entity_locations, entity, (to_table, new_index));
2355
2356            if let Some(swapped) = remove_from_table(&mut world.tables[from_table], from_index) {
2357                insert_location(
2358                    &mut world.entity_locations,
2359                    swapped,
2360                    (from_table, from_index),
2361                );
2362            }
2363        }
2364
2365        fn get_location(locations: &$crate::EntityLocations, entity: $crate::Entity) -> Option<(usize, usize)> {
2366            let location = locations.get(entity.id)?;
2367            if !location.allocated || location.generation != entity.generation {
2368                return None;
2369            }
2370
2371            Some((location.table_index as usize, location.array_index as usize))
2372        }
2373
2374        fn insert_location(
2375            locations: &mut $crate::EntityLocations,
2376            entity: $crate::Entity,
2377            location: (usize, usize),
2378        ) {
2379            locations.insert(entity.id, $crate::EntityLocation {
2380                generation: entity.generation,
2381                table_index: location.0 as u32,
2382                array_index: location.1 as u32,
2383                allocated: true,
2384            });
2385        }
2386
2387        fn create_entity(world: &mut $world) -> $crate::Entity {
2388            let entity = world.allocator.allocate();
2389            world.entity_locations.ensure_slot(entity.id, entity.generation);
2390            entity
2391        }
2392
2393        fn add_to_table(
2394            arrays: &mut ComponentArrays,
2395            entity: $crate::Entity,
2396            components: ( $(Option<$type>,)* ),
2397            tick: u32,
2398        ) {
2399            let ($($name,)*) = components;
2400            $(
2401                $(#[$comp_attr])*
2402                $crate::paste::paste! {
2403                    if arrays.mask & $mask != 0 {
2404                        if let Some(component) = $name {
2405                            arrays.$name.push(component);
2406                        } else {
2407                            arrays.$name.push(<$type>::default());
2408                        }
2409                        arrays.[<$name _changed>].push(tick);
2410                    }
2411                }
2412            )*
2413            arrays.entity_indices.push(entity);
2414        }
2415
2416        fn get_or_create_table(world: &mut $world, mask: u64) -> usize {
2417            if let Some(&index) = world.table_lookup.get(&mask) {
2418                return index;
2419            }
2420
2421            let table_index = world.tables.len();
2422            world.tables.push(ComponentArrays {
2423                mask,
2424                ..Default::default()
2425            });
2426            world.table_edges.push(TableEdges::default());
2427            world.table_lookup.insert(mask, table_index);
2428
2429            world.invalidate_query_cache_for_table(mask, table_index);
2430
2431            for comp_mask in [$($(#[$comp_attr])* $mask,)*] {
2432                if let Some(comp_idx) = get_component_index(comp_mask) {
2433                    for (idx, table) in world.tables.iter().enumerate() {
2434                        if table.mask | comp_mask == mask {
2435                            world.table_edges[idx].add_edges[comp_idx] = Some(table_index);
2436                        }
2437                        if table.mask & !comp_mask == mask {
2438                            world.table_edges[idx].remove_edges[comp_idx] = Some(table_index);
2439                        }
2440                    }
2441                }
2442            }
2443
2444            table_index
2445        }
2446    };
2447}
2448
2449#[macro_export]
2450macro_rules! ecs_world_impl {
2451    (
2452        $world:ident {
2453            $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
2454        }
2455    ) => {
2456        $crate::paste::paste! {
2457            #[repr(u64)]
2458            #[allow(clippy::upper_case_acronyms)]
2459            #[allow(non_camel_case_types)]
2460            pub enum [<$world Component>] {
2461                $($(#[$comp_attr])* $mask,)*
2462            }
2463
2464            $($(#[$comp_attr])* pub const $mask: u64 = 1 << ([<$world Component>]::$mask as u64);)*
2465
2466            pub const [<$world:snake:upper _COMPONENT_COUNT>]: usize = {
2467                let mut count = 0;
2468                $($(#[$comp_attr])* { count += 1; let _ = [<$world Component>]::$mask; })*
2469                count
2470            };
2471
2472            #[derive(Default)]
2473            pub struct [<$world ComponentArrays>] {
2474                $($(#[$comp_attr])* pub $name: Vec<$type>,)*
2475                $($(#[$comp_attr])* pub [<$name _changed>]: Vec<u32>,)*
2476                pub entity_indices: Vec<$crate::Entity>,
2477                pub mask: u64,
2478            }
2479
2480            #[derive(Clone)]
2481            struct [<$world TableEdges>] {
2482                add_edges: [Option<usize>; [<$world:snake:upper _COMPONENT_COUNT>]],
2483                remove_edges: [Option<usize>; [<$world:snake:upper _COMPONENT_COUNT>]],
2484                multi_add_cache: std::collections::HashMap<u64, usize>,
2485                multi_remove_cache: std::collections::HashMap<u64, usize>,
2486            }
2487
2488            impl Default for [<$world TableEdges>] {
2489                fn default() -> Self {
2490                    Self {
2491                        add_edges: [None; [<$world:snake:upper _COMPONENT_COUNT>]],
2492                        remove_edges: [None; [<$world:snake:upper _COMPONENT_COUNT>]],
2493                        multi_add_cache: std::collections::HashMap::default(),
2494                        multi_remove_cache: std::collections::HashMap::default(),
2495                    }
2496                }
2497            }
2498
2499            #[allow(unused)]
2500            pub struct $world {
2501                pub entity_locations: $crate::EntityLocations,
2502                tables: Vec<[<$world ComponentArrays>]>,
2503                table_edges: Vec<[<$world TableEdges>]>,
2504                table_lookup: std::collections::HashMap<u64, usize>,
2505                query_cache: std::collections::HashMap<u64, Vec<usize>>,
2506                current_tick: u32,
2507                last_tick: u32,
2508            }
2509
2510            impl Default for $world {
2511                fn default() -> Self {
2512                    Self {
2513                        entity_locations: $crate::EntityLocations::default(),
2514                        tables: Vec::default(),
2515                        table_edges: Vec::default(),
2516                        table_lookup: std::collections::HashMap::default(),
2517                        query_cache: std::collections::HashMap::default(),
2518                        current_tick: 0,
2519                        last_tick: 0,
2520                    }
2521                }
2522            }
2523
2524            #[allow(unused)]
2525            impl $world {
2526                fn get_cached_tables(&mut self, mask: u64) -> &[usize] {
2527                    if !self.query_cache.contains_key(&mask) {
2528                        let matching_tables: Vec<usize> = self.tables
2529                            .iter()
2530                            .enumerate()
2531                            .filter(|(_, table)| table.mask & mask == mask)
2532                            .map(|(idx, _)| idx)
2533                            .collect();
2534                        self.query_cache.insert(mask, matching_tables);
2535                    }
2536                    &self.query_cache[&mask]
2537                }
2538
2539                fn invalidate_query_cache_for_table(&mut self, new_table_mask: u64, new_table_index: usize) {
2540                    self.query_cache.retain(|query_mask, cached_tables| {
2541                        if new_table_mask & query_mask == *query_mask {
2542                            cached_tables.push(new_table_index);
2543                            true
2544                        } else {
2545                            true
2546                        }
2547                    });
2548                }
2549
2550                $(
2551                    $(#[$comp_attr])*
2552                    $crate::paste::paste! {
2553                        #[inline]
2554                        pub fn [<get_ $name>](&self, entity: $crate::Entity) -> Option<&$type> {
2555                            let (table_index, array_index) = [<get_location_ $world:snake>](&self.entity_locations, entity)?;
2556                            let table = &self.tables[table_index];
2557                            if table.mask & $mask == 0 {
2558                                return None;
2559                            }
2560                            Some(&table.$name[array_index])
2561                        }
2562
2563                        #[inline]
2564                        pub fn [<get_ $name _mut>](&mut self, entity: $crate::Entity) -> Option<&mut $type> {
2565                            let (table_index, array_index) = [<get_location_ $world:snake>](&self.entity_locations, entity)?;
2566                            let current_tick = self.current_tick;
2567                            let table = &mut self.tables[table_index];
2568                            if table.mask & $mask == 0 {
2569                                return None;
2570                            }
2571                            table.[<$name _changed>][array_index] = current_tick;
2572                            Some(&mut table.$name[array_index])
2573                        }
2574
2575                        #[inline]
2576                        pub fn [<modify_ $name>]<R>(&mut self, entity: $crate::Entity, f: impl FnOnce(&mut $type) -> R) -> Option<R> {
2577                            let (table_index, array_index) = [<get_location_ $world:snake>](&self.entity_locations, entity)?;
2578                            let current_tick = self.current_tick;
2579                            let table = &mut self.tables[table_index];
2580                            if table.mask & $mask == 0 {
2581                                return None;
2582                            }
2583                            table.[<$name _changed>][array_index] = current_tick;
2584                            Some(f(&mut table.$name[array_index]))
2585                        }
2586
2587                        #[inline]
2588                        pub fn [<entity_has_ $name>](&self, entity: $crate::Entity) -> bool {
2589                            self.entity_has_components(entity, $mask)
2590                        }
2591
2592                        #[inline]
2593                        pub fn [<set_ $name>](&mut self, entity: $crate::Entity, value: $type) {
2594                            if let Some((table_index, array_index)) = [<get_location_ $world:snake>](&self.entity_locations, entity) {
2595                                let table = &mut self.tables[table_index];
2596                                if table.mask & $mask != 0 {
2597                                    table.$name[array_index] = value;
2598                                    return;
2599                                }
2600                            }
2601                            self.add_components(entity, $mask);
2602                            if let Some((table_index, array_index)) = [<get_location_ $world:snake>](&self.entity_locations, entity) {
2603                                self.tables[table_index].$name[array_index] = value;
2604                            }
2605                        }
2606
2607                        #[inline]
2608                        pub fn [<add_ $name>](&mut self, entity: $crate::Entity) {
2609                            self.add_components(entity, $mask);
2610                        }
2611
2612                        #[inline]
2613                        pub fn [<remove_ $name>](&mut self, entity: $crate::Entity) -> bool {
2614                            self.remove_components(entity, $mask)
2615                        }
2616
2617                        #[inline]
2618                        pub fn [<query_ $name>](&self) -> [<$mask:camel QueryIter>]<'_> {
2619                            [<$mask:camel QueryIter>] {
2620                                tables: &self.tables,
2621                                table_index: 0,
2622                                array_index: 0,
2623                            }
2624                        }
2625
2626                        pub fn [<for_each_ $name _mut>]<F>(&mut self, mut f: F)
2627                        where
2628                            F: FnMut(&mut $type),
2629                        {
2630                            let table_indices: Vec<usize> = self.get_cached_tables($mask).to_vec();
2631                            for table_index in table_indices {
2632                                for component in &mut self.tables[table_index].$name {
2633                                    f(component);
2634                                }
2635                            }
2636                        }
2637
2638                        #[cfg(not(target_family = "wasm"))]
2639                        pub fn [<par_for_each_ $name _mut>]<F>(&mut self, f: F)
2640                        where
2641                            F: Fn(&mut $type) + Send + Sync,
2642                        {
2643                            use $crate::rayon::prelude::*;
2644                            self.tables
2645                                .par_iter_mut()
2646                                .filter(|table| table.mask & $mask != 0)
2647                                .for_each(|table| {
2648                                    table.$name.par_iter_mut().for_each(|component| f(component));
2649                                });
2650                        }
2651
2652                        pub fn [<iter_ $name _slices>](&self) -> impl Iterator<Item = &[$type]> {
2653                            self.tables
2654                                .iter()
2655                                .filter(|table| table.mask & $mask != 0)
2656                                .map(|table| table.$name.as_slice())
2657                        }
2658
2659                        pub fn [<iter_ $name _slices_mut>](&mut self) -> impl Iterator<Item = &mut [$type]> {
2660                            self.tables
2661                                .iter_mut()
2662                                .filter(|table| table.mask & $mask != 0)
2663                                .map(|table| table.$name.as_mut_slice())
2664                        }
2665                    }
2666                )*
2667
2668                pub fn spawn_entities(&mut self, allocator: &mut $crate::EntityAllocator, mask: u64, count: usize) -> Vec<$crate::Entity> {
2669                    let mut entities = Vec::with_capacity(count);
2670                    let table_index = [<get_or_create_table_ $world:snake>](self, mask);
2671                    let start_index = self.tables[table_index].entity_indices.len();
2672
2673                    self.tables[table_index].entity_indices.reserve(count);
2674                    $(
2675                        $(#[$comp_attr])*
2676                        {
2677                            if mask & $mask != 0 {
2678                                self.tables[table_index].$name.reserve(count);
2679                                self.tables[table_index].[<$name _changed>].reserve(count);
2680                            }
2681                        }
2682                    )*
2683
2684                    for local_index in 0..count {
2685                        let entity = allocator.allocate();
2686                        self.entity_locations.ensure_slot(entity.id, entity.generation);
2687                        entities.push(entity);
2688
2689                        self.tables[table_index].entity_indices.push(entity);
2690                        $(
2691                            $(#[$comp_attr])*
2692                            {
2693                                if mask & $mask != 0 {
2694                                    self.tables[table_index].$name.push(<$type>::default());
2695                                    self.tables[table_index].[<$name _changed>].push(self.current_tick);
2696                                }
2697                            }
2698                        )*
2699
2700                        [<insert_location_ $world:snake>](
2701                            &mut self.entity_locations,
2702                            entity,
2703                            (table_index, start_index + local_index),
2704                        );
2705                    }
2706
2707                    entities
2708                }
2709
2710                pub fn spawn_batch<F>(&mut self, allocator: &mut $crate::EntityAllocator, mask: u64, count: usize, mut init: F) -> Vec<$crate::Entity>
2711                where
2712                    F: FnMut(&mut [<$world ComponentArrays>], usize),
2713                {
2714                    let table_index = [<get_or_create_table_ $world:snake>](self, mask);
2715                    let start_index = self.tables[table_index].entity_indices.len();
2716
2717                    self.tables[table_index].entity_indices.reserve(count);
2718                    $(
2719                        $(#[$comp_attr])*
2720                        {
2721                            if mask & $mask != 0 {
2722                                self.tables[table_index].$name.reserve(count);
2723                                self.tables[table_index].[<$name _changed>].reserve(count);
2724                            }
2725                        }
2726                    )*
2727
2728                    let mut entities = Vec::with_capacity(count);
2729                    for local_index in 0..count {
2730                        let entity = allocator.allocate();
2731                        self.entity_locations.ensure_slot(entity.id, entity.generation);
2732                        entities.push(entity);
2733
2734                        self.tables[table_index].entity_indices.push(entity);
2735                        $(
2736                            $(#[$comp_attr])*
2737                            {
2738                                if mask & $mask != 0 {
2739                                    self.tables[table_index].$name.push(<$type>::default());
2740                                    self.tables[table_index].[<$name _changed>].push(self.current_tick);
2741                                }
2742                            }
2743                        )*
2744
2745                        [<insert_location_ $world:snake>](
2746                            &mut self.entity_locations,
2747                            entity,
2748                            (table_index, start_index + local_index),
2749                        );
2750
2751                        init(&mut self.tables[table_index], start_index + local_index);
2752                    }
2753
2754                    entities
2755                }
2756
2757                pub fn query_entities(&self, mask: u64) -> [<$world EntityQueryIter>]<'_> {
2758                    [<$world EntityQueryIter>] {
2759                        tables: &self.tables,
2760                        mask,
2761                        table_index: 0,
2762                        array_index: 0,
2763                    }
2764                }
2765
2766                pub fn query_entities_changed(&self, mask: u64) -> [<$world ChangedEntityQueryIter>]<'_> {
2767                    [<$world ChangedEntityQueryIter>] {
2768                        tables: &self.tables,
2769                        mask,
2770                        since_tick: self.last_tick,
2771                        table_index: 0,
2772                        array_index: 0,
2773                    }
2774                }
2775
2776                pub fn query_first_entity(&self, mask: u64) -> Option<$crate::Entity> {
2777                    for table in &self.tables {
2778                        if table.mask & mask != mask {
2779                            continue;
2780                        }
2781                        if let Some(&entity) = table.entity_indices.first() {
2782                            return Some(entity);
2783                        }
2784                    }
2785                    None
2786                }
2787
2788                pub fn remove_entity(&mut self, entity: $crate::Entity) -> bool {
2789                    if let Some(loc) = self.entity_locations.get_mut(entity.id) {
2790                        if loc.allocated && loc.generation == entity.generation {
2791                            let table_idx = loc.table_index as usize;
2792                            let array_idx = loc.array_index as usize;
2793
2794                            self.entity_locations.mark_deallocated(entity.id);
2795
2796                            if table_idx < self.tables.len() {
2797                                let table = &mut self.tables[table_idx];
2798                                if !table.entity_indices.is_empty() {
2799                                    let last_idx = table.entity_indices.len() - 1;
2800
2801                                    if array_idx < last_idx {
2802                                        let moved_entity = table.entity_indices[last_idx];
2803                                        if let Some(moved_loc) = self.entity_locations.get_mut(moved_entity.id) {
2804                                            if moved_loc.allocated {
2805                                                moved_loc.array_index = array_idx as u32;
2806                                            }
2807                                        }
2808                                    }
2809
2810                                    $(
2811                                        $(#[$comp_attr])*
2812                                        {
2813                                            if table.mask & $mask != 0 {
2814                                                table.$name.swap_remove(array_idx);
2815                                                table.[<$name _changed>].swap_remove(array_idx);
2816                                            }
2817                                        }
2818                                    )*
2819                                    table.entity_indices.swap_remove(array_idx);
2820                                }
2821                            }
2822                            return true;
2823                        }
2824                    }
2825                    false
2826                }
2827
2828                pub fn add_components(&mut self, entity: $crate::Entity, mask: u64) -> bool {
2829                    if let Some((table_index, array_index)) = [<get_location_ $world:snake>](&self.entity_locations, entity) {
2830                        let current_mask = self.tables[table_index].mask;
2831                        if current_mask & mask == mask {
2832                            return true;
2833                        }
2834
2835                        let target_table = if mask.count_ones() == 1 {
2836                            [<get_component_index_ $world:snake>](mask).and_then(|idx| self.table_edges[table_index].add_edges[idx])
2837                        } else {
2838                            self.table_edges[table_index].multi_add_cache.get(&mask).copied()
2839                        };
2840
2841                        let new_table_index = target_table.unwrap_or_else(|| {
2842                            let new_idx = [<get_or_create_table_ $world:snake>](self, current_mask | mask);
2843                            self.table_edges[table_index].multi_add_cache.insert(mask, new_idx);
2844                            new_idx
2845                        });
2846
2847                        [<move_entity_ $world:snake>](self, entity, table_index, array_index, new_table_index);
2848                        true
2849                    } else {
2850                        if let Some(loc) = self.entity_locations.get(entity.id) {
2851                            if loc.allocated {
2852                                return false;
2853                            }
2854                        }
2855
2856                        let table_index = [<get_or_create_table_ $world:snake>](self, mask);
2857                        let start_index = self.tables[table_index].entity_indices.len();
2858
2859                        self.tables[table_index].entity_indices.push(entity);
2860                        $(
2861                            $(#[$comp_attr])*
2862                            {
2863                                if mask & $mask != 0 {
2864                                    self.tables[table_index].$name.push(<$type>::default());
2865                                    self.tables[table_index].[<$name _changed>].push(self.current_tick);
2866                                }
2867                            }
2868                        )*
2869
2870                        self.entity_locations.insert(entity.id, $crate::EntityLocation {
2871                            generation: entity.generation,
2872                            table_index: table_index as u32,
2873                            array_index: start_index as u32,
2874                            allocated: true,
2875                        });
2876                        true
2877                    }
2878                }
2879
2880                pub fn remove_components(&mut self, entity: $crate::Entity, mask: u64) -> bool {
2881                    if let Some((table_index, array_index)) = [<get_location_ $world:snake>](&self.entity_locations, entity) {
2882                        let current_mask = self.tables[table_index].mask;
2883                        if current_mask & mask == 0 {
2884                            return true;
2885                        }
2886
2887                        let target_table = if mask.count_ones() == 1 {
2888                            [<get_component_index_ $world:snake>](mask)
2889                                .and_then(|idx| self.table_edges[table_index].remove_edges[idx])
2890                        } else {
2891                            self.table_edges[table_index].multi_remove_cache.get(&mask).copied()
2892                        };
2893
2894                        let new_table_index = target_table.unwrap_or_else(|| {
2895                            let new_idx = [<get_or_create_table_ $world:snake>](self, current_mask & !mask);
2896                            self.table_edges[table_index].multi_remove_cache.insert(mask, new_idx);
2897                            new_idx
2898                        });
2899
2900                        [<move_entity_ $world:snake>](self, entity, table_index, array_index, new_table_index);
2901                        true
2902                    } else {
2903                        false
2904                    }
2905                }
2906
2907                pub fn component_mask(&self, entity: $crate::Entity) -> Option<u64> {
2908                    [<get_location_ $world:snake>](&self.entity_locations, entity)
2909                        .map(|(table_index, _)| self.tables[table_index].mask)
2910                }
2911
2912                pub fn get_all_entities(&self) -> Vec<$crate::Entity> {
2913                    let mut result = Vec::with_capacity(self.entity_count());
2914                    for table in &self.tables {
2915                        result.extend(table.entity_indices.iter().copied());
2916                    }
2917                    result
2918                }
2919
2920                pub fn entity_count(&self) -> usize {
2921                    self.tables.iter().map(|table| table.entity_indices.len()).sum()
2922                }
2923
2924                pub fn entity_has_components(&self, entity: $crate::Entity, components: u64) -> bool {
2925                    self.component_mask(entity).unwrap_or(0) & components == components
2926                }
2927
2928                pub fn increment_tick(&mut self) {
2929                    self.last_tick = self.current_tick;
2930                    self.current_tick = self.current_tick.wrapping_add(1);
2931                }
2932
2933                pub fn current_tick(&self) -> u32 {
2934                    self.current_tick
2935                }
2936
2937                pub fn last_tick(&self) -> u32 {
2938                    self.last_tick
2939                }
2940
2941                $(
2942                    $(#[$comp_attr])*
2943                    $crate::paste::paste! {
2944                        pub fn [<query_ $name _mut>]<F>(&mut self, mask: u64, mut f: F)
2945                        where
2946                            F: FnMut($crate::Entity, &mut $type),
2947                        {
2948                            let table_indices: Vec<usize> = self.get_cached_tables(mask).to_vec();
2949                            for &table_index in &table_indices {
2950                                let table = &mut self.tables[table_index];
2951                                if table.mask & $mask == 0 {
2952                                    continue;
2953                                }
2954                                for idx in 0..table.entity_indices.len() {
2955                                    let entity = table.entity_indices[idx];
2956                                    f(entity, &mut table.$name[idx]);
2957                                }
2958                            }
2959                        }
2960                    }
2961                )*
2962            }
2963
2964            #[allow(unused)]
2965            impl $world {
2966                #[inline]
2967                pub fn for_each<F>(&self, include: u64, exclude: u64, mut f: F)
2968                where
2969                    F: FnMut($crate::Entity, &[<$world ComponentArrays>], usize),
2970                {
2971                    for table in &self.tables {
2972                        if table.mask & include != include || table.mask & exclude != 0 {
2973                            continue;
2974                        }
2975                        for (idx, &entity) in table.entity_indices.iter().enumerate() {
2976                            f(entity, table, idx);
2977                        }
2978                    }
2979                }
2980
2981                #[inline]
2982                pub fn for_each_mut<F>(&mut self, include: u64, exclude: u64, mut f: F)
2983                where
2984                    F: FnMut($crate::Entity, &mut [<$world ComponentArrays>], usize),
2985                {
2986                    let table_indices: Vec<usize> = self.get_cached_tables(include).to_vec();
2987
2988                    for &table_index in &table_indices {
2989                        let table = &mut self.tables[table_index];
2990                        if table.mask & exclude != 0 {
2991                            continue;
2992                        }
2993                        for idx in 0..table.entity_indices.len() {
2994                            let entity = table.entity_indices[idx];
2995                            f(entity, table, idx);
2996                        }
2997                    }
2998                }
2999
3000                #[cfg(not(target_family = "wasm"))]
3001                #[inline]
3002                pub fn par_for_each_mut<F>(&mut self, include: u64, exclude: u64, f: F)
3003                where
3004                    F: Fn($crate::Entity, &mut [<$world ComponentArrays>], usize) + Send + Sync,
3005                {
3006                    use $crate::rayon::prelude::*;
3007
3008                    self.tables
3009                        .par_iter_mut()
3010                        .filter(|table| table.mask & include == include && table.mask & exclude == 0)
3011                        .for_each(|table| {
3012                            for idx in 0..table.entity_indices.len() {
3013                                let entity = table.entity_indices[idx];
3014                                f(entity, table, idx);
3015                            }
3016                        });
3017                }
3018
3019                #[inline]
3020                pub fn for_each_mut_changed<F>(&mut self, include: u64, exclude: u64, mut f: F)
3021                where
3022                    F: FnMut($crate::Entity, &mut [<$world ComponentArrays>], usize),
3023                {
3024                    let table_indices: Vec<usize> = self.get_cached_tables(include).to_vec();
3025                    let since_tick = self.last_tick;
3026
3027                    for &table_index in &table_indices {
3028                        let table = &mut self.tables[table_index];
3029                        if table.mask & exclude != 0 {
3030                            continue;
3031                        }
3032
3033                        for idx in 0..table.entity_indices.len() {
3034                            let entity = table.entity_indices[idx];
3035
3036                            let mut changed = false;
3037                            $(
3038                                $(#[$comp_attr])*
3039                                {
3040                                    if include & $mask != 0 && table.mask & $mask != 0 && table.[<$name _changed>][idx] > since_tick {
3041                                        changed = true;
3042                                    }
3043                                }
3044                            )*
3045
3046                            if changed {
3047                                f(entity, table, idx);
3048                            }
3049                        }
3050                    }
3051                }
3052            }
3053
3054            #[allow(unused)]
3055            impl $world {
3056                #[inline]
3057                pub fn for_each_with_tags<F>(
3058                    &self,
3059                    include: u64,
3060                    exclude: u64,
3061                    include_tags: &[&std::collections::HashSet<$crate::Entity>],
3062                    exclude_tags: &[&std::collections::HashSet<$crate::Entity>],
3063                    mut f: F,
3064                )
3065                where
3066                    F: FnMut($crate::Entity, &[<$world ComponentArrays>], usize),
3067                {
3068                    let has_tag_filter = !include_tags.is_empty() || !exclude_tags.is_empty();
3069
3070                    for table in &self.tables {
3071                        if table.mask & include != include || table.mask & exclude != 0 {
3072                            continue;
3073                        }
3074
3075                        if has_tag_filter {
3076                            for (idx, &entity) in table.entity_indices.iter().enumerate() {
3077                                if include_tags.iter().all(|tag_set| tag_set.contains(&entity))
3078                                    && !exclude_tags.iter().any(|tag_set| tag_set.contains(&entity))
3079                                {
3080                                    f(entity, table, idx);
3081                                }
3082                            }
3083                        } else {
3084                            for (idx, &entity) in table.entity_indices.iter().enumerate() {
3085                                f(entity, table, idx);
3086                            }
3087                        }
3088                    }
3089                }
3090
3091                #[inline]
3092                pub fn for_each_mut_with_tags<F>(
3093                    &mut self,
3094                    include: u64,
3095                    exclude: u64,
3096                    include_tags: &[&std::collections::HashSet<$crate::Entity>],
3097                    exclude_tags: &[&std::collections::HashSet<$crate::Entity>],
3098                    mut f: F,
3099                )
3100                where
3101                    F: FnMut($crate::Entity, &mut [<$world ComponentArrays>], usize),
3102                {
3103                    let has_tag_filter = !include_tags.is_empty() || !exclude_tags.is_empty();
3104                    let table_indices: Vec<usize> = self.get_cached_tables(include).to_vec();
3105
3106                    if has_tag_filter {
3107                        let matching_entities: std::collections::HashSet<$crate::Entity> = table_indices
3108                            .iter()
3109                            .filter_map(|&idx| self.tables.get(idx))
3110                            .filter(|table| table.mask & exclude == 0)
3111                            .flat_map(|table| table.entity_indices.iter().copied())
3112                            .filter(|entity| {
3113                                include_tags.iter().all(|tag_set| tag_set.contains(entity))
3114                                    && !exclude_tags.iter().any(|tag_set| tag_set.contains(entity))
3115                            })
3116                            .collect();
3117
3118                        for &table_index in &table_indices {
3119                            let table = &mut self.tables[table_index];
3120                            if table.mask & exclude != 0 {
3121                                continue;
3122                            }
3123                            for idx in 0..table.entity_indices.len() {
3124                                let entity = table.entity_indices[idx];
3125                                if matching_entities.contains(&entity) {
3126                                    f(entity, table, idx);
3127                                }
3128                            }
3129                        }
3130                    } else {
3131                        for &table_index in &table_indices {
3132                            let table = &mut self.tables[table_index];
3133                            if table.mask & exclude != 0 {
3134                                continue;
3135                            }
3136                            for idx in 0..table.entity_indices.len() {
3137                                let entity = table.entity_indices[idx];
3138                                f(entity, table, idx);
3139                            }
3140                        }
3141                    }
3142                }
3143
3144                #[cfg(not(target_family = "wasm"))]
3145                #[inline]
3146                pub fn par_for_each_mut_with_tags<F>(
3147                    &mut self,
3148                    include: u64,
3149                    exclude: u64,
3150                    include_tags: &[&std::collections::HashSet<$crate::Entity>],
3151                    exclude_tags: &[&std::collections::HashSet<$crate::Entity>],
3152                    f: F,
3153                )
3154                where
3155                    F: Fn($crate::Entity, &mut [<$world ComponentArrays>], usize) + Send + Sync,
3156                {
3157                    use $crate::rayon::prelude::*;
3158
3159                    let table_indices: Vec<usize> = self.get_cached_tables(include).to_vec();
3160                    let table_index_set: std::collections::HashSet<usize> = table_indices.iter().copied().collect();
3161                    let has_tag_filter = !include_tags.is_empty() || !exclude_tags.is_empty();
3162
3163                    if has_tag_filter {
3164                        let matching_entities: std::collections::HashSet<$crate::Entity> = table_indices
3165                            .iter()
3166                            .filter_map(|&idx| self.tables.get(idx))
3167                            .filter(|table| table.mask & exclude == 0)
3168                            .flat_map(|table| table.entity_indices.iter().copied())
3169                            .filter(|entity| {
3170                                include_tags.iter().all(|tag_set| tag_set.contains(entity))
3171                                    && !exclude_tags.iter().any(|tag_set| tag_set.contains(entity))
3172                            })
3173                            .collect();
3174
3175                        self.tables
3176                            .par_iter_mut()
3177                            .enumerate()
3178                            .filter(|(idx, table)| table_index_set.contains(idx) && table.mask & exclude == 0)
3179                            .for_each(|(_, table)| {
3180                                for idx in 0..table.entity_indices.len() {
3181                                    let entity = table.entity_indices[idx];
3182                                    if matching_entities.contains(&entity) {
3183                                        f(entity, table, idx);
3184                                    }
3185                                }
3186                            });
3187                    } else {
3188                        self.tables
3189                            .par_iter_mut()
3190                            .enumerate()
3191                            .filter(|(idx, table)| table_index_set.contains(idx) && table.mask & exclude == 0)
3192                            .for_each(|(_, table)| {
3193                                for idx in 0..table.entity_indices.len() {
3194                                    let entity = table.entity_indices[idx];
3195                                    f(entity, table, idx);
3196                                }
3197                            });
3198                    }
3199                }
3200            }
3201
3202            #[allow(unused)]
3203            pub struct [<$world QueryBuilder>]<'a> {
3204                world: &'a $world,
3205                include: u64,
3206                exclude: u64,
3207            }
3208
3209            #[allow(unused)]
3210            impl<'a> [<$world QueryBuilder>]<'a> {
3211                pub fn new(world: &'a $world) -> Self {
3212                    Self {
3213                        world,
3214                        include: 0,
3215                        exclude: 0,
3216                    }
3217                }
3218
3219                pub fn with(mut self, mask: u64) -> Self {
3220                    self.include |= mask;
3221                    self
3222                }
3223
3224                pub fn without(mut self, mask: u64) -> Self {
3225                    self.exclude |= mask;
3226                    self
3227                }
3228
3229                pub fn iter<F>(self, f: F)
3230                where
3231                    F: FnMut($crate::Entity, &[<$world ComponentArrays>], usize),
3232                {
3233                    self.world.for_each(self.include, self.exclude, f);
3234                }
3235            }
3236
3237            #[allow(unused)]
3238            pub struct [<$world QueryBuilderMut>]<'a> {
3239                world: &'a mut $world,
3240                include: u64,
3241                exclude: u64,
3242            }
3243
3244            #[allow(unused)]
3245            impl<'a> [<$world QueryBuilderMut>]<'a> {
3246                pub fn new(world: &'a mut $world) -> Self {
3247                    Self {
3248                        world,
3249                        include: 0,
3250                        exclude: 0,
3251                    }
3252                }
3253
3254                pub fn with(mut self, mask: u64) -> Self {
3255                    self.include |= mask;
3256                    self
3257                }
3258
3259                pub fn without(mut self, mask: u64) -> Self {
3260                    self.exclude |= mask;
3261                    self
3262                }
3263
3264                pub fn iter<F>(self, f: F)
3265                where
3266                    F: FnMut($crate::Entity, &mut [<$world ComponentArrays>], usize),
3267                {
3268                    self.world.for_each_mut(self.include, self.exclude, f);
3269                }
3270            }
3271
3272            #[allow(unused)]
3273            impl $world {
3274                pub fn query(&self) -> [<$world QueryBuilder>]<'_> {
3275                    [<$world QueryBuilder>]::new(self)
3276                }
3277
3278                pub fn query_mut(&mut self) -> [<$world QueryBuilderMut>]<'_> {
3279                    [<$world QueryBuilderMut>]::new(self)
3280                }
3281
3282                $(
3283                    $(#[$comp_attr])*
3284                    $crate::paste::paste! {
3285                        pub fn [<iter_ $name>]<F>(&self, mut f: F)
3286                        where
3287                            F: FnMut($crate::Entity, &$type),
3288                        {
3289                            self.for_each($mask, 0, |entity, table, idx| {
3290                                f(entity, &table.$name[idx]);
3291                            });
3292                        }
3293
3294                        pub fn [<iter_ $name _mut>]<F>(&mut self, mut f: F)
3295                        where
3296                            F: FnMut($crate::Entity, &mut $type),
3297                        {
3298                            self.for_each_mut($mask, 0, |entity, table, idx| {
3299                                f(entity, &mut table.$name[idx]);
3300                            });
3301                        }
3302                    }
3303                )*
3304            }
3305
3306            pub struct [<$world EntityQueryIter>]<'a> {
3307                tables: &'a [[<$world ComponentArrays>]],
3308                mask: u64,
3309                table_index: usize,
3310                array_index: usize,
3311            }
3312
3313            impl<'a> Iterator for [<$world EntityQueryIter>]<'a> {
3314                type Item = $crate::Entity;
3315
3316                fn next(&mut self) -> Option<Self::Item> {
3317                    loop {
3318                        if self.table_index >= self.tables.len() {
3319                            return None;
3320                        }
3321                        let table = &self.tables[self.table_index];
3322                        if table.mask & self.mask != self.mask {
3323                            self.table_index += 1;
3324                            self.array_index = 0;
3325                            continue;
3326                        }
3327                        if self.array_index >= table.entity_indices.len() {
3328                            self.table_index += 1;
3329                            self.array_index = 0;
3330                            continue;
3331                        }
3332                        let entity = table.entity_indices[self.array_index];
3333                        self.array_index += 1;
3334                        return Some(entity);
3335                    }
3336                }
3337
3338                fn size_hint(&self) -> (usize, Option<usize>) {
3339                    let mut remaining = 0;
3340                    for table_idx in self.table_index..self.tables.len() {
3341                        let table = &self.tables[table_idx];
3342                        if table.mask & self.mask != self.mask {
3343                            continue;
3344                        }
3345                        if table_idx == self.table_index {
3346                            remaining += table.entity_indices.len().saturating_sub(self.array_index);
3347                        } else {
3348                            remaining += table.entity_indices.len();
3349                        }
3350                    }
3351                    (remaining, Some(remaining))
3352                }
3353            }
3354
3355            pub struct [<$world ChangedEntityQueryIter>]<'a> {
3356                tables: &'a [[<$world ComponentArrays>]],
3357                mask: u64,
3358                since_tick: u32,
3359                table_index: usize,
3360                array_index: usize,
3361            }
3362
3363            impl<'a> Iterator for [<$world ChangedEntityQueryIter>]<'a> {
3364                type Item = $crate::Entity;
3365
3366                fn next(&mut self) -> Option<Self::Item> {
3367                    loop {
3368                        if self.table_index >= self.tables.len() {
3369                            return None;
3370                        }
3371                        let table = &self.tables[self.table_index];
3372                        if table.mask & self.mask != self.mask {
3373                            self.table_index += 1;
3374                            self.array_index = 0;
3375                            continue;
3376                        }
3377                        if self.array_index >= table.entity_indices.len() {
3378                            self.table_index += 1;
3379                            self.array_index = 0;
3380                            continue;
3381                        }
3382                        let idx = self.array_index;
3383                        self.array_index += 1;
3384
3385                        let mut changed = false;
3386                        $(
3387                            $(#[$comp_attr])*
3388                            {
3389                                if self.mask & $mask != 0 && table.mask & $mask != 0 && table.[<$name _changed>][idx] > self.since_tick {
3390                                    changed = true;
3391                                }
3392                            }
3393                        )*
3394
3395                        if changed {
3396                            return Some(table.entity_indices[idx]);
3397                        }
3398                    }
3399                }
3400            }
3401
3402            $(
3403                $(#[$comp_attr])*
3404                $crate::paste::paste! {
3405                    pub struct [<$mask:camel QueryIter>]<'a> {
3406                        tables: &'a [[<$world ComponentArrays>]],
3407                        table_index: usize,
3408                        array_index: usize,
3409                    }
3410
3411                    impl<'a> Iterator for [<$mask:camel QueryIter>]<'a> {
3412                        type Item = &'a $type;
3413
3414                        fn next(&mut self) -> Option<Self::Item> {
3415                            loop {
3416                                if self.table_index >= self.tables.len() {
3417                                    return None;
3418                                }
3419                                let table = &self.tables[self.table_index];
3420                                if table.mask & $mask == 0 {
3421                                    self.table_index += 1;
3422                                    self.array_index = 0;
3423                                    continue;
3424                                }
3425                                if self.array_index >= table.$name.len() {
3426                                    self.table_index += 1;
3427                                    self.array_index = 0;
3428                                    continue;
3429                                }
3430                                let component = &table.$name[self.array_index];
3431                                self.array_index += 1;
3432                                return Some(component);
3433                            }
3434                        }
3435
3436                        fn size_hint(&self) -> (usize, Option<usize>) {
3437                            let mut remaining = 0;
3438                            for table_idx in self.table_index..self.tables.len() {
3439                                let table = &self.tables[table_idx];
3440                                if table.mask & $mask == 0 {
3441                                    continue;
3442                                }
3443                                if table_idx == self.table_index {
3444                                    remaining += table.$name.len().saturating_sub(self.array_index);
3445                                } else {
3446                                    remaining += table.$name.len();
3447                                }
3448                            }
3449                            (remaining, Some(remaining))
3450                        }
3451                    }
3452                }
3453            )*
3454
3455            fn [<get_component_index_ $world:snake>](mask: u64) -> Option<usize> {
3456                match mask {
3457                    $($(#[$comp_attr])* $mask => Some([<$world Component>]::$mask as _),)*
3458                    _ => None,
3459                }
3460            }
3461
3462            fn [<remove_from_table_ $world:snake>](arrays: &mut [<$world ComponentArrays>], index: usize) -> Option<$crate::Entity> {
3463                let last_index = arrays.entity_indices.len() - 1;
3464                let mut swapped_entity = None;
3465                if index < last_index {
3466                    swapped_entity = Some(arrays.entity_indices[last_index]);
3467                }
3468                $(
3469                    $(#[$comp_attr])*
3470                    {
3471                        if arrays.mask & $mask != 0 {
3472                            arrays.$name.swap_remove(index);
3473                            arrays.[<$name _changed>].swap_remove(index);
3474                        }
3475                    }
3476                )*
3477                arrays.entity_indices.swap_remove(index);
3478                swapped_entity
3479            }
3480
3481            fn [<move_entity_ $world:snake>](
3482                world: &mut $world,
3483                entity: $crate::Entity,
3484                from_table: usize,
3485                from_index: usize,
3486                to_table: usize,
3487            ) {
3488                let tick = world.current_tick;
3489                let components = {
3490                    let from_table_ref = &mut world.tables[from_table];
3491                    (
3492                        $(
3493                            $(#[$comp_attr])*
3494                            {
3495                                if from_table_ref.mask & $mask != 0 {
3496                                    Some(std::mem::take(&mut from_table_ref.$name[from_index]))
3497                                } else {
3498                                    None
3499                                }
3500                            },
3501                        )*
3502                    )
3503                };
3504
3505                [<add_to_table_ $world:snake>](&mut world.tables[to_table], entity, components, tick);
3506                let new_index = world.tables[to_table].entity_indices.len() - 1;
3507                [<insert_location_ $world:snake>](&mut world.entity_locations, entity, (to_table, new_index));
3508
3509                if let Some(swapped) = [<remove_from_table_ $world:snake>](&mut world.tables[from_table], from_index) {
3510                    [<insert_location_ $world:snake>](
3511                        &mut world.entity_locations,
3512                        swapped,
3513                        (from_table, from_index),
3514                    );
3515                }
3516            }
3517
3518            fn [<get_location_ $world:snake>](locations: &$crate::EntityLocations, entity: $crate::Entity) -> Option<(usize, usize)> {
3519                let location = locations.get(entity.id)?;
3520                if !location.allocated || location.generation != entity.generation {
3521                    return None;
3522                }
3523                Some((location.table_index as usize, location.array_index as usize))
3524            }
3525
3526            fn [<insert_location_ $world:snake>](
3527                locations: &mut $crate::EntityLocations,
3528                entity: $crate::Entity,
3529                location: (usize, usize),
3530            ) {
3531                locations.insert(entity.id, $crate::EntityLocation {
3532                    generation: entity.generation,
3533                    table_index: location.0 as u32,
3534                    array_index: location.1 as u32,
3535                    allocated: true,
3536                });
3537            }
3538
3539            fn [<add_to_table_ $world:snake>](
3540                arrays: &mut [<$world ComponentArrays>],
3541                entity: $crate::Entity,
3542                components: ( $($(#[$comp_attr])* Option<$type>,)* ),
3543                tick: u32,
3544            ) {
3545                let ($($name,)*) = components;
3546                $(
3547                    $(#[$comp_attr])*
3548                    {
3549                        if arrays.mask & $mask != 0 {
3550                            if let Some(component) = $name {
3551                                arrays.$name.push(component);
3552                            } else {
3553                                arrays.$name.push(<$type>::default());
3554                            }
3555                            arrays.[<$name _changed>].push(tick);
3556                        }
3557                    }
3558                )*
3559                arrays.entity_indices.push(entity);
3560            }
3561
3562            fn [<get_or_create_table_ $world:snake>](world: &mut $world, mask: u64) -> usize {
3563                if let Some(&index) = world.table_lookup.get(&mask) {
3564                    return index;
3565                }
3566
3567                let table_index = world.tables.len();
3568                world.tables.push([<$world ComponentArrays>] {
3569                    mask,
3570                    ..Default::default()
3571                });
3572                world.table_edges.push([<$world TableEdges>]::default());
3573                world.table_lookup.insert(mask, table_index);
3574
3575                world.invalidate_query_cache_for_table(mask, table_index);
3576
3577                for comp_mask in [$($(#[$comp_attr])* $mask,)*] {
3578                    if let Some(comp_idx) = [<get_component_index_ $world:snake>](comp_mask) {
3579                        for (idx, table) in world.tables.iter().enumerate() {
3580                            if table.mask | comp_mask == mask {
3581                                world.table_edges[idx].add_edges[comp_idx] = Some(table_index);
3582                            }
3583                            if table.mask & !comp_mask == mask {
3584                                world.table_edges[idx].remove_edges[comp_idx] = Some(table_index);
3585                            }
3586                        }
3587                    }
3588                }
3589
3590                table_index
3591            }
3592        }
3593    };
3594}
3595
3596#[macro_export]
3597macro_rules! ecs_multi_impl {
3598    (
3599        $ecs:ident {
3600            $($world_name:ident {
3601                $($(#[$comp_attr:meta])* $name:ident: $type:ty => $mask:ident),* $(,)?
3602            })+
3603        }
3604        Tags {
3605            $($tag_name:ident => $tag_mask:ident),* $(,)?
3606        }
3607        Events {
3608            $($event_name:ident: $event_type:ty),* $(,)?
3609        }
3610        $resources:ident {
3611            $($(#[$attr:meta])* $resource_name:ident: $resource_type:ty),* $(,)?
3612        }
3613    ) => {
3614        $(
3615            $crate::ecs_world_impl! {
3616                $world_name {
3617                    $($(#[$comp_attr])* $name: $type => $mask),*
3618                }
3619            }
3620        )+
3621
3622        #[derive(Default)]
3623        pub struct $resources {
3624            $($(#[$attr])* pub $resource_name: $resource_type,)*
3625        }
3626
3627        $crate::paste::paste! {
3628            #[allow(unused)]
3629            #[derive(Default, Debug, Clone)]
3630            pub struct EntityBuilder {
3631                $($(
3632                    $(#[$comp_attr])* $name: Option<$type>,
3633                )*)+
3634            }
3635
3636            #[allow(unused)]
3637            impl EntityBuilder {
3638                pub fn new() -> Self {
3639                    Self::default()
3640                }
3641
3642                $($(
3643                    $(#[$comp_attr])*
3644                    pub fn [<with_ $name>](mut self, value: $type) -> Self {
3645                        self.$name = Some(value);
3646                        self
3647                    }
3648                )*)+
3649
3650                pub fn spawn(self, ecs: &mut $ecs, instances: usize) -> Vec<$crate::Entity> {
3651                    let mut entities = Vec::with_capacity(instances);
3652                    for _ in 0..instances {
3653                        entities.push(ecs.allocator.allocate());
3654                    }
3655
3656                    $(
3657                        {
3658                            let mut mask: u64 = 0;
3659                            $(
3660                                $(#[$comp_attr])*
3661                                if self.$name.is_some() {
3662                                    mask |= $mask;
3663                                }
3664                            )*
3665                            if mask != 0 {
3666                                let last_entity_index = entities.len().saturating_sub(1);
3667                                for (entity_index, entity) in entities.iter().enumerate() {
3668                                    ecs.[<$world_name:snake>].add_components(*entity, mask);
3669                                    if entity_index == last_entity_index {
3670                                        $(
3671                                            $(#[$comp_attr])*
3672                                            if let Some(component) = self.$name {
3673                                                ecs.[<$world_name:snake>].[<set_ $name>](*entity, component);
3674                                            }
3675                                        )*
3676                                        break;
3677                                    } else {
3678                                        $(
3679                                            $(#[$comp_attr])*
3680                                            if let Some(ref component) = self.$name {
3681                                                ecs.[<$world_name:snake>].[<set_ $name>](*entity, component.clone());
3682                                            }
3683                                        )*
3684                                    }
3685                                }
3686                            }
3687                        }
3688                    )+
3689
3690                    entities
3691                }
3692            }
3693
3694            pub enum Command {
3695                Spawn { count: usize },
3696                Despawn { entities: Vec<$crate::Entity> },
3697                $($(
3698                    $(#[$comp_attr])*
3699                    [<$world_name Set $mask:camel>] { entity: $crate::Entity, value: $type },
3700                    $(#[$comp_attr])*
3701                    [<$world_name AddComponents $mask:camel>] { entity: $crate::Entity },
3702                    $(#[$comp_attr])*
3703                    [<$world_name RemoveComponents $mask:camel>] { entity: $crate::Entity },
3704                )*)+
3705                $(
3706                    [<Add $tag_mask:camel>] { entity: $crate::Entity },
3707                    [<Remove $tag_mask:camel>] { entity: $crate::Entity },
3708                )*
3709            }
3710
3711            #[allow(unused)]
3712            pub struct $ecs {
3713                $(pub [<$world_name:snake>]: $world_name,)+
3714                allocator: $crate::EntityAllocator,
3715                pub resources: $resources,
3716                $(pub $tag_name: std::collections::HashSet<$crate::Entity>,)*
3717                command_buffer: Vec<Command>,
3718                $($event_name: $crate::EventQueue<$event_type>,)*
3719            }
3720
3721            impl Default for $ecs {
3722                fn default() -> Self {
3723                    Self {
3724                        $([<$world_name:snake>]: $world_name::default(),)+
3725                        allocator: $crate::EntityAllocator::default(),
3726                        resources: $resources::default(),
3727                        $(
3728                            $tag_name: std::collections::HashSet::default(),
3729                        )*
3730                        command_buffer: Vec::default(),
3731                        $(
3732                            $event_name: $crate::EventQueue::new(),
3733                        )*
3734                    }
3735                }
3736            }
3737
3738            #[allow(unused)]
3739            impl $ecs {
3740                pub fn spawn(&mut self) -> $crate::Entity {
3741                    self.allocator.allocate()
3742                }
3743
3744                pub fn spawn_count(&mut self, count: usize) -> Vec<$crate::Entity> {
3745                    let mut entities = Vec::with_capacity(count);
3746                    for _ in 0..count {
3747                        entities.push(self.allocator.allocate());
3748                    }
3749                    entities
3750                }
3751
3752                pub fn despawn(&mut self, entity: $crate::Entity) {
3753                    $(self.[<$world_name:snake>].remove_entity(entity);)+
3754                    $(self.$tag_name.remove(&entity);)*
3755                    self.allocator.deallocate(entity);
3756                }
3757
3758                pub fn despawn_entities(&mut self, entities: &[$crate::Entity]) {
3759                    for &entity in entities {
3760                        self.despawn(entity);
3761                    }
3762                }
3763
3764                $(
3765                    pub fn [<add_ $tag_name>](&mut self, entity: $crate::Entity) {
3766                        self.$tag_name.insert(entity);
3767                    }
3768
3769                    pub fn [<remove_ $tag_name>](&mut self, entity: $crate::Entity) -> bool {
3770                        self.$tag_name.remove(&entity)
3771                    }
3772
3773                    pub fn [<has_ $tag_name>](&self, entity: $crate::Entity) -> bool {
3774                        self.$tag_name.contains(&entity)
3775                    }
3776
3777                    pub fn [<query_ $tag_name>](&self) -> impl Iterator<Item = $crate::Entity> + '_ {
3778                        self.$tag_name.iter().copied()
3779                    }
3780                )*
3781
3782                $(
3783                    pub fn [<send_ $event_name>](&mut self, event: $event_type) {
3784                        self.$event_name.send(event);
3785                    }
3786
3787                    pub fn [<read_ $event_name>](&self) -> impl Iterator<Item = &$event_type> {
3788                        self.$event_name.read()
3789                    }
3790
3791                    pub fn [<drain_ $event_name>](&mut self) -> impl Iterator<Item = $event_type> + '_ {
3792                        self.$event_name.drain()
3793                    }
3794
3795                    pub fn [<clear_ $event_name>](&mut self) {
3796                        self.$event_name.clear();
3797                    }
3798
3799                    pub fn [<update_ $event_name>](&mut self) {
3800                        self.$event_name.update();
3801                    }
3802
3803                    pub fn [<len_ $event_name>](&self) -> usize {
3804                        self.$event_name.len()
3805                    }
3806
3807                    pub fn [<is_empty_ $event_name>](&self) -> bool {
3808                        self.$event_name.is_empty()
3809                    }
3810
3811                    pub fn [<peek_ $event_name>](&self) -> Option<&$event_type> {
3812                        self.$event_name.peek()
3813                    }
3814
3815                    pub fn [<collect_ $event_name>](&self) -> Vec<$event_type>
3816                    where
3817                        $event_type: Clone,
3818                    {
3819                        self.$event_name.read().cloned().collect()
3820                    }
3821                )*
3822
3823                fn update_events(&mut self) {
3824                    $(
3825                        self.$event_name.update();
3826                    )*
3827                }
3828
3829                pub fn step(&mut self) {
3830                    self.update_events();
3831                    $(
3832                        self.[<$world_name:snake>].last_tick = self.[<$world_name:snake>].current_tick;
3833                        self.[<$world_name:snake>].current_tick += 1;
3834                    )+
3835                }
3836
3837                pub fn queue_spawn(&mut self, count: usize) {
3838                    self.command_buffer.push(Command::Spawn { count });
3839                }
3840
3841                pub fn queue_despawn_entity(&mut self, entity: $crate::Entity) {
3842                    self.command_buffer.push(Command::Despawn { entities: vec![entity] });
3843                }
3844
3845                pub fn queue_despawn_entities(&mut self, entities: Vec<$crate::Entity>) {
3846                    self.command_buffer.push(Command::Despawn { entities });
3847                }
3848
3849                $($(
3850                    $(#[$comp_attr])*
3851                    pub fn [<queue_set_ $name>](&mut self, entity: $crate::Entity, value: $type) {
3852                        self.command_buffer.push(Command::[<$world_name Set $mask:camel>] { entity, value });
3853                    }
3854
3855                    $(#[$comp_attr])*
3856                    pub fn [<queue_add_ $name>](&mut self, entity: $crate::Entity) {
3857                        self.command_buffer.push(Command::[<$world_name AddComponents $mask:camel>] { entity });
3858                    }
3859
3860                    $(#[$comp_attr])*
3861                    pub fn [<queue_remove_ $name>](&mut self, entity: $crate::Entity) {
3862                        self.command_buffer.push(Command::[<$world_name RemoveComponents $mask:camel>] { entity });
3863                    }
3864                )*)+
3865
3866                $(
3867                    pub fn [<queue_add_ $tag_name>](&mut self, entity: $crate::Entity) {
3868                        self.command_buffer.push(Command::[<Add $tag_mask:camel>] { entity });
3869                    }
3870
3871                    pub fn [<queue_remove_ $tag_name>](&mut self, entity: $crate::Entity) {
3872                        self.command_buffer.push(Command::[<Remove $tag_mask:camel>] { entity });
3873                    }
3874                )*
3875
3876                pub fn apply_commands(&mut self) {
3877                    let commands = std::mem::take(&mut self.command_buffer);
3878                    for command in commands {
3879                        match command {
3880                            Command::Spawn { count } => {
3881                                self.spawn_count(count);
3882                            }
3883                            Command::Despawn { entities } => {
3884                                self.despawn_entities(&entities);
3885                            }
3886                            $($(
3887                                $(#[$comp_attr])*
3888                                Command::[<$world_name Set $mask:camel>] { entity, value } => {
3889                                    self.[<$world_name:snake>].[<set_ $name>](entity, value);
3890                                }
3891                                $(#[$comp_attr])*
3892                                Command::[<$world_name AddComponents $mask:camel>] { entity } => {
3893                                    self.[<$world_name:snake>].add_components(entity, $mask);
3894                                }
3895                                $(#[$comp_attr])*
3896                                Command::[<$world_name RemoveComponents $mask:camel>] { entity } => {
3897                                    self.[<$world_name:snake>].remove_components(entity, $mask);
3898                                }
3899                            )*)+
3900                            $(
3901                                Command::[<Add $tag_mask:camel>] { entity } => {
3902                                    self.[<add_ $tag_name>](entity);
3903                                }
3904                                Command::[<Remove $tag_mask:camel>] { entity } => {
3905                                    self.[<remove_ $tag_name>](entity);
3906                                }
3907                            )*
3908                        }
3909                    }
3910                }
3911
3912                pub fn command_count(&self) -> usize {
3913                    self.command_buffer.len()
3914                }
3915
3916                pub fn clear_commands(&mut self) {
3917                    self.command_buffer.clear();
3918                }
3919            }
3920        }
3921    };
3922}
3923
3924#[macro_export]
3925macro_rules! table_has_components {
3926    ($table:expr, $mask:expr) => {
3927        $table.mask & $mask == $mask
3928    };
3929}
3930
3931#[cfg(test)]
3932mod tests {
3933    use super::*;
3934    use std::collections::HashSet;
3935
3936    ecs! {
3937        World {
3938            position: Position => POSITION,
3939            velocity: Velocity => VELOCITY,
3940            health: Health => HEALTH,
3941            parent: Parent => PARENT,
3942            node: Node => NODE,
3943        }
3944        Tags {
3945            player => PLAYER,
3946            enemy => ENEMY,
3947            active => ACTIVE,
3948        }
3949        Resources {
3950            _delta_time: f32,
3951        }
3952    }
3953
3954    use components::*;
3955    mod components {
3956        use super::*;
3957
3958        #[derive(Default, Debug, Copy, Clone, PartialEq)]
3959        pub struct Parent(pub Entity);
3960
3961        #[derive(Default, Debug, Clone, PartialEq)]
3962        pub struct Node {
3963            pub id: Entity,
3964            pub parent: Option<Entity>,
3965            pub children: Vec<Entity>,
3966        }
3967
3968        #[derive(Default, Debug, Clone)]
3969        pub struct Position {
3970            pub x: f32,
3971            pub y: f32,
3972        }
3973
3974        #[derive(Default, Debug, Clone)]
3975        pub struct Velocity {
3976            pub x: f32,
3977            pub y: f32,
3978        }
3979
3980        #[derive(Default, Debug, Clone)]
3981        pub struct Health {
3982            pub value: f32,
3983        }
3984    }
3985
3986    mod systems {
3987        use super::*;
3988
3989        pub fn run_systems(world: &mut World, dt: f32) {
3990            world.tables.iter_mut().for_each(|table| {
3991                if super::table_has_components!(table, POSITION | VELOCITY | HEALTH) {
3992                    update_positions_system(&mut table.position, &table.velocity, dt);
3993                }
3994                if super::table_has_components!(table, HEALTH) {
3995                    health_system(&mut table.health);
3996                }
3997            });
3998        }
3999
4000        #[inline]
4001        pub fn update_positions_system(
4002            positions: &mut [Position],
4003            velocities: &[Velocity],
4004            dt: f32,
4005        ) {
4006            positions
4007                .iter_mut()
4008                .zip(velocities.iter())
4009                .for_each(|(pos, vel)| {
4010                    pos.x += vel.x * dt;
4011                    pos.y += vel.y * dt;
4012                });
4013        }
4014
4015        #[inline]
4016        pub fn health_system(health: &mut [Health]) {
4017            health.iter_mut().for_each(|health| {
4018                health.value *= 0.98;
4019            });
4020        }
4021    }
4022
4023    fn setup_test_world() -> (World, Entity) {
4024        let mut world = World::default();
4025        let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4026
4027        if let Some(pos) = world.get_position_mut(entity) {
4028            pos.x = 1.0;
4029            pos.y = 2.0;
4030        }
4031        if let Some(vel) = world.get_velocity_mut(entity) {
4032            vel.x = 3.0;
4033            vel.y = 4.0;
4034        }
4035
4036        (world, entity)
4037    }
4038
4039    #[test]
4040    fn test_spawn_entities() {
4041        let mut world = World::default();
4042        let entities = world.spawn_entities(POSITION | VELOCITY, 3);
4043
4044        assert_eq!(entities.len(), 3);
4045        assert_eq!(world.get_all_entities().len(), 3);
4046
4047        for entity in entities {
4048            assert!(world.get_position(entity).is_some());
4049            assert!(world.get_velocity(entity).is_some());
4050            assert!(world.get_health(entity).is_none());
4051        }
4052    }
4053
4054    #[test]
4055    fn test_component_access() {
4056        let (mut world, entity) = setup_test_world();
4057
4058        let pos = world.get_position(entity).unwrap();
4059        assert_eq!(pos.x, 1.0);
4060        assert_eq!(pos.y, 2.0);
4061
4062        if let Some(pos) = world.get_position_mut(entity) {
4063            pos.x = 5.0;
4064        }
4065
4066        let pos = world.get_position(entity).unwrap();
4067        assert_eq!(pos.x, 5.0);
4068    }
4069
4070    #[test]
4071    fn test_modify_component() {
4072        let (mut world, entity) = setup_test_world();
4073
4074        let pos = world.get_position(entity).unwrap();
4075        assert_eq!(pos.x, 1.0);
4076        assert_eq!(pos.y, 2.0);
4077
4078        let old_x = world.modify_position(entity, |pos| {
4079            let old = pos.x;
4080            pos.x = 10.0;
4081            pos.y = 20.0;
4082            old
4083        });
4084        assert_eq!(old_x, Some(1.0));
4085
4086        let pos = world.get_position(entity).unwrap();
4087        assert_eq!(pos.x, 10.0);
4088        assert_eq!(pos.y, 20.0);
4089
4090        let invalid_entity = Entity {
4091            id: 9999,
4092            generation: 0,
4093        };
4094        let result = world.modify_position(invalid_entity, |pos| pos.x = 100.0);
4095        assert!(result.is_none());
4096
4097        let entity_no_health = world.spawn_entities(POSITION, 1)[0];
4098        let result = world.modify_health(entity_no_health, |h| h.value = 50.0);
4099        assert!(result.is_none());
4100    }
4101
4102    #[test]
4103    fn test_add_remove_components() {
4104        let (mut world, entity) = setup_test_world();
4105
4106        assert!(world.get_health(entity).is_none());
4107
4108        world.add_components(entity, HEALTH);
4109        assert!(world.get_health(entity).is_some());
4110
4111        world.remove_components(entity, HEALTH);
4112        assert!(world.get_health(entity).is_none());
4113    }
4114
4115    #[test]
4116    fn test_component_mask() {
4117        let (mut world, entity) = setup_test_world();
4118
4119        let mask = world.component_mask(entity).unwrap();
4120        assert_eq!(mask, POSITION | VELOCITY);
4121
4122        world.add_components(entity, HEALTH);
4123        let mask = world.component_mask(entity).unwrap();
4124        assert_eq!(mask, POSITION | VELOCITY | HEALTH);
4125    }
4126
4127    #[test]
4128    fn test_query_entities() {
4129        let mut world = World::default();
4130
4131        let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4132        let _e2 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4133        let e3 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4134
4135        let pos_vel: Vec<_> = world.query_entities(POSITION | VELOCITY).collect();
4136        let pos_health: Vec<_> = world.query_entities(POSITION | HEALTH).collect();
4137        let all: Vec<_> = world.query_entities(POSITION | VELOCITY | HEALTH).collect();
4138
4139        assert_eq!(pos_vel.len(), 2);
4140        assert_eq!(pos_health.len(), 2);
4141        assert_eq!(all.len(), 1);
4142
4143        let pos_vel: HashSet<_> = pos_vel.into_iter().collect();
4144        assert!(pos_vel.contains(&e1));
4145        assert!(pos_vel.contains(&e3));
4146
4147        assert_eq!(all[0], e3);
4148    }
4149
4150    #[test]
4151    fn test_query_first_entity() {
4152        let mut world = World::default();
4153
4154        let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4155        let e2 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4156
4157        let first = world.query_first_entity(POSITION | VELOCITY).unwrap();
4158        assert!(first == e1 || first == e2);
4159
4160        assert!(world.query_first_entity(HEALTH).is_some());
4161        assert!(
4162            world
4163                .query_first_entity(POSITION | VELOCITY | HEALTH)
4164                .is_some()
4165        );
4166    }
4167
4168    #[test]
4169    fn test_despawn_entities() {
4170        let mut world = World::default();
4171
4172        let entities = world.spawn_entities(POSITION | VELOCITY, 3);
4173        assert_eq!(world.get_all_entities().len(), 3);
4174
4175        let despawned = world.despawn_entities(&[entities[1]]);
4176        assert_eq!(despawned.len(), 1);
4177        assert_eq!(world.get_all_entities().len(), 2);
4178
4179        assert!(world.get_position(entities[1]).is_none());
4180
4181        assert!(world.get_position(entities[0]).is_some());
4182        assert!(world.get_position(entities[2]).is_some());
4183    }
4184
4185    #[test]
4186    fn test_parallel_systems() {
4187        let mut world = World::default();
4188
4189        let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4190
4191        if let Some(pos) = world.get_position_mut(entity) {
4192            pos.x = 0.0;
4193            pos.y = 0.0;
4194        }
4195        if let Some(vel) = world.get_velocity_mut(entity) {
4196            vel.x = 1.0;
4197            vel.y = 1.0;
4198        }
4199        if let Some(health) = world.get_health_mut(entity) {
4200            health.value = 100.0;
4201        }
4202
4203        systems::run_systems(&mut world, 1.0);
4204
4205        let pos = world.get_position(entity).unwrap();
4206        let health = world.get_health(entity).unwrap();
4207
4208        assert_eq!(pos.x, 1.0);
4209        assert_eq!(pos.y, 1.0);
4210        assert!(health.value < 100.0);
4211    }
4212
4213    #[test]
4214    fn test_add_components() {
4215        let (mut world, entity) = setup_test_world();
4216
4217        assert!(world.get_health(entity).is_none());
4218
4219        world.add_components(entity, HEALTH);
4220        assert!(world.get_health(entity).is_some());
4221
4222        world.remove_components(entity, HEALTH);
4223        assert!(world.get_health(entity).is_none());
4224    }
4225
4226    #[test]
4227    fn test_multiple_component_addition() {
4228        let mut world = World::default();
4229        let entity = world.spawn_entities(POSITION, 1)[0];
4230
4231        world.add_components(entity, VELOCITY | HEALTH);
4232
4233        assert!(world.get_position(entity).is_some());
4234        assert!(world.get_velocity(entity).is_some());
4235        assert!(world.get_health(entity).is_some());
4236
4237        if let Some(pos) = world.get_position_mut(entity) {
4238            pos.x = 1.0;
4239        }
4240        world.add_components(entity, VELOCITY);
4241        assert_eq!(world.get_position(entity).unwrap().x, 1.0);
4242    }
4243
4244    #[test]
4245    fn test_component_chain_addition() {
4246        let mut world = World::default();
4247        let entity = world.spawn_entities(POSITION, 1)[0];
4248
4249        if let Some(pos) = world.get_position_mut(entity) {
4250            pos.x = 1.0;
4251        }
4252
4253        world.add_components(entity, VELOCITY);
4254        world.add_components(entity, HEALTH);
4255
4256        assert_eq!(world.get_position(entity).unwrap().x, 1.0);
4257    }
4258
4259    #[test]
4260    fn test_component_removal_order() {
4261        let mut world = World::default();
4262        let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4263
4264        world.remove_components(entity, VELOCITY);
4265        world.remove_components(entity, HEALTH);
4266        assert!(world.get_position(entity).is_some());
4267        assert!(world.get_velocity(entity).is_none());
4268        assert!(world.get_health(entity).is_none());
4269    }
4270
4271    #[test]
4272    fn test_edge_cases() {
4273        let mut world = World::default();
4274
4275        let empty = world.spawn_entities(0, 1)[0];
4276
4277        world.add_components(empty, POSITION);
4278        assert!(world.get_position(empty).is_some());
4279
4280        world.add_components(empty, POSITION);
4281        world.add_components(empty, POSITION);
4282
4283        world.remove_components(empty, VELOCITY);
4284
4285        world.remove_components(empty, POSITION);
4286        assert_eq!(world.component_mask(empty).unwrap(), 0);
4287
4288        let invalid = Entity {
4289            id: 9999,
4290            generation: 0,
4291        };
4292        assert!(!world.add_components(invalid, POSITION));
4293    }
4294
4295    #[test]
4296    fn test_component_data_integrity() {
4297        let mut world = World::default();
4298        let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4299
4300        {
4301            let pos = world.get_position_mut(entity).unwrap();
4302            pos.x = 1.0;
4303            pos.y = 2.0;
4304            let vel = world.get_velocity_mut(entity).unwrap();
4305            vel.x = 3.0;
4306            vel.y = 4.0;
4307        }
4308
4309        world.add_components(entity, HEALTH);
4310        world.remove_components(entity, HEALTH);
4311        world.add_components(entity, HEALTH);
4312
4313        let pos = world.get_position(entity).unwrap();
4314        let vel = world.get_velocity(entity).unwrap();
4315        assert_eq!(pos.x, 1.0);
4316        assert_eq!(pos.y, 2.0);
4317        assert_eq!(vel.x, 3.0);
4318        assert_eq!(vel.y, 4.0);
4319    }
4320
4321    #[test]
4322    fn test_entity_references_through_moves() {
4323        let mut world = World::default();
4324
4325        let entity1 = world.spawn_entities(POSITION, 1)[0];
4326        let entity2 = world.spawn_entities(POSITION, 1)[0];
4327
4328        world.add_components(entity1, VELOCITY);
4329        if let Some(vel) = world.get_velocity_mut(entity1) {
4330            vel.x = entity2.id as f32;
4331        }
4332
4333        world.add_components(entity2, VELOCITY | HEALTH);
4334
4335        let stored_id = world.get_velocity(entity1).unwrap().x as u32;
4336        let entity2_loc = get_location(&world.entity_locations, entity2);
4337        assert!(entity2_loc.is_some());
4338        assert_eq!(stored_id, entity2.id);
4339    }
4340
4341    #[test]
4342    fn test_table_cleanup_after_despawn() {
4343        let mut world = World::default();
4344
4345        let e1 = world.spawn_entities(POSITION, 1)[0];
4346        let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4347
4348        let initial_tables = world.tables.len();
4349        assert_eq!(initial_tables, 2, "Should have two tables initially");
4350
4351        world.despawn_entities(&[e2]);
4352
4353        assert!(world.get_position(e2).is_none());
4354        assert!(world.get_velocity(e2).is_none());
4355
4356        assert!(world.get_position(e1).is_some());
4357
4358        let remaining: Vec<_> = world.query_entities(POSITION).collect();
4359        assert_eq!(remaining.len(), 1);
4360        assert!(remaining.contains(&e1));
4361
4362        assert!(
4363            world.tables.len() <= initial_tables,
4364            "Should not have more tables than initial state"
4365        );
4366
4367        for table in &world.tables {
4368            for &entity in &table.entity_indices {
4369                assert!(
4370                    get_location(&world.entity_locations, entity).is_some(),
4371                    "Entity location should be valid for remaining entities"
4372                );
4373            }
4374        }
4375    }
4376
4377    #[test]
4378    fn test_concurrent_entity_references() {
4379        let mut world = World::default();
4380
4381        let entity1 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4382        let entity2 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4383
4384        if let Some(pos) = world.get_position_mut(entity1) {
4385            pos.x = 1.0;
4386        }
4387        if let Some(health) = world.get_health_mut(entity1) {
4388            health.value = 100.0;
4389        }
4390
4391        let id1 = entity1.id;
4392
4393        world.despawn_entities(&[entity1]);
4394
4395        let entity3 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4396        assert_eq!(entity3.id, id1, "Should reuse entity1's ID");
4397        assert_eq!(
4398            entity3.generation,
4399            entity1.generation + 1,
4400            "Should have incremented generation"
4401        );
4402
4403        if let Some(pos) = world.get_position_mut(entity3) {
4404            pos.x = 3.0;
4405        }
4406        if let Some(health) = world.get_health_mut(entity3) {
4407            health.value = 50.0;
4408        }
4409
4410        if let Some(pos) = world.get_position(entity2) {
4411            assert_eq!(pos.x, 0.0, "Entity2's data should be unchanged");
4412        }
4413
4414        if let Some(pos) = world.get_position(entity3) {
4415            assert_eq!(pos.x, 3.0, "Should get entity3's data, not entity1's");
4416        }
4417        assert!(
4418            world.get_position(entity1).is_none(),
4419            "Should not be able to access entity1's old data"
4420        );
4421    }
4422
4423    #[test]
4424    fn test_generational_indices_aba() {
4425        let mut world = World::default();
4426
4427        let entity_a1 = world.spawn_entities(POSITION, 1)[0];
4428        assert_eq!(
4429            entity_a1.generation, 0,
4430            "First use of ID should have generation 0"
4431        );
4432
4433        if let Some(pos) = world.get_position_mut(entity_a1) {
4434            pos.x = 1.0;
4435            pos.y = 1.0;
4436        }
4437
4438        let id = entity_a1.id;
4439
4440        world.despawn_entities(&[entity_a1]);
4441
4442        let entity_a2 = world.spawn_entities(POSITION, 1)[0];
4443        assert_eq!(entity_a2.id, id, "Should reuse the same ID");
4444        assert_eq!(
4445            entity_a2.generation, 1,
4446            "Second use of ID should have generation 1"
4447        );
4448
4449        if let Some(pos) = world.get_position_mut(entity_a2) {
4450            pos.x = 2.0;
4451            pos.y = 2.0;
4452        }
4453
4454        assert!(
4455            world.get_position(entity_a1).is_none(),
4456            "Old reference to entity should be invalid"
4457        );
4458
4459        world.despawn_entities(&[entity_a2]);
4460
4461        let entity_a3 = world.spawn_entities(POSITION, 1)[0];
4462        assert_eq!(entity_a3.id, id, "Should reuse the same ID again");
4463        assert_eq!(
4464            entity_a3.generation, 2,
4465            "Third use of ID should have generation 2"
4466        );
4467
4468        if let Some(pos) = world.get_position_mut(entity_a3) {
4469            pos.x = 3.0;
4470            pos.y = 3.0;
4471        }
4472
4473        assert!(
4474            world.get_position(entity_a1).is_none(),
4475            "First generation reference should be invalid"
4476        );
4477        assert!(
4478            world.get_position(entity_a2).is_none(),
4479            "Second generation reference should be invalid"
4480        );
4481
4482        let pos = world.get_position(entity_a3);
4483        assert!(
4484            pos.is_some(),
4485            "Current generation reference should be valid"
4486        );
4487        let pos = pos.unwrap();
4488        assert_eq!(pos.x, 3.0, "Should have the current generation's data");
4489        assert_eq!(pos.y, 3.0, "Should have the current generation's data");
4490    }
4491
4492    #[test]
4493    fn test_all_entities() {
4494        let mut world = World::default();
4495
4496        let e1 = world.spawn_entities(POSITION, 1)[0];
4497        let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4498        let e3 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4499        let e4 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4500
4501        let all = world.get_all_entities();
4502
4503        assert_eq!(all.len(), 4, "Should have 4 total entities");
4504
4505        assert!(all.contains(&e1), "Missing entity 1");
4506        assert!(all.contains(&e2), "Missing entity 2");
4507        assert!(all.contains(&e3), "Missing entity 3");
4508        assert!(all.contains(&e4), "Missing entity 4");
4509
4510        world.despawn_entities(&[e2, e3]);
4511        let remaining = world.get_all_entities();
4512
4513        assert_eq!(remaining.len(), 2, "Should have 2 entities after despawn");
4514
4515        assert!(remaining.contains(&e1), "Missing entity 1 after despawn");
4516        assert!(remaining.contains(&e4), "Missing entity 4 after despawn");
4517        assert!(!remaining.contains(&e2), "Entity 2 should be despawned");
4518        assert!(!remaining.contains(&e3), "Entity 3 should be despawned");
4519    }
4520
4521    #[test]
4522    fn test_all_entities_empty_world() {
4523        assert!(
4524            World::default().get_all_entities().is_empty(),
4525            "Empty world should return empty vector"
4526        );
4527    }
4528
4529    #[test]
4530    fn test_all_entities_after_table_merges() {
4531        let mut world = World::default();
4532
4533        let e1 = world.spawn_entities(POSITION, 1)[0];
4534        let e2 = world.spawn_entities(VELOCITY, 1)[0];
4535
4536        world.add_components(e1, VELOCITY);
4537        world.add_components(e2, POSITION);
4538
4539        let all = world.get_all_entities();
4540        assert_eq!(
4541            all.len(),
4542            2,
4543            "Should maintain all entities through table merges"
4544        );
4545        assert!(all.contains(&e1), "Should contain first entity after merge");
4546        assert!(
4547            all.contains(&e2),
4548            "Should contain second entity after merge"
4549        );
4550    }
4551
4552    #[test]
4553    fn test_table_transitions() {
4554        let mut world = World::default();
4555
4556        let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4557
4558        println!("Initial mask: {:b}", world.component_mask(entity).unwrap());
4559
4560        let (old_table_idx, _) = get_location(&world.entity_locations, entity).unwrap();
4561
4562        world.add_components(entity, POSITION);
4563
4564        let final_mask = world.component_mask(entity).unwrap();
4565        println!("Final mask: {:b}", final_mask);
4566        let (new_table_idx, _) = get_location(&world.entity_locations, entity).unwrap();
4567
4568        println!(
4569            "Old table index: {}, New table index: {}",
4570            old_table_idx, new_table_idx
4571        );
4572        println!("Tables after operation:");
4573        for (index, table) in world.tables.iter().enumerate() {
4574            println!("Table {}: mask={:b}", index, table.mask);
4575        }
4576
4577        assert_eq!(
4578            final_mask & (POSITION | VELOCITY | HEALTH),
4579            POSITION | VELOCITY | HEALTH,
4580            "Entity should still have all original components"
4581        );
4582    }
4583
4584    #[test]
4585    fn test_real_camera_scenario() {
4586        let mut world = World::default();
4587
4588        let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4589
4590        let query_results: Vec<_> = world.query_entities(POSITION | VELOCITY).collect();
4591        assert!(
4592            query_results.contains(&entity),
4593            "Initial query should match\n\
4594                Entity mask: {:b}\n\
4595                Query mask: {:b}",
4596            world.component_mask(entity).unwrap(),
4597            POSITION | VELOCITY
4598        );
4599
4600        world.add_components(entity, HEALTH);
4601
4602        let query_results: Vec<_> = world.query_entities(POSITION | VELOCITY).collect();
4603        assert!(
4604            query_results.contains(&entity),
4605            "Query should still match after adding component\n\
4606                Entity mask: {:b}\n\
4607                Query mask: {:b}",
4608            world.component_mask(entity).unwrap(),
4609            POSITION | VELOCITY
4610        );
4611    }
4612
4613    #[test]
4614    fn test_query_consistency() {
4615        let mut world = World::default();
4616
4617        let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4618
4619        let query_mask = POSITION | VELOCITY;
4620
4621        let query_results: Vec<_> = world.query_entities(query_mask).collect();
4622        assert!(
4623            query_results.contains(&entity),
4624            "query_entities should find entity with mask {:b} when querying for {:b}",
4625            world.component_mask(entity).unwrap(),
4626            query_mask
4627        );
4628
4629        let first_result = world.query_first_entity(query_mask);
4630        assert!(
4631            first_result.is_some(),
4632            "query_first_entity should find entity with mask {:b} when querying for {:b}",
4633            world.component_mask(entity).unwrap(),
4634            query_mask
4635        );
4636        assert_eq!(
4637            first_result.unwrap(),
4638            entity,
4639            "query_first_entity should find same entity as query_entities"
4640        );
4641
4642        world.add_components(entity, HEALTH);
4643
4644        let query_results: Vec<_> = world.query_entities(query_mask).collect();
4645        assert!(
4646            query_results.contains(&entity),
4647            "query_entities should still find entity after adding component\n\
4648            Entity mask: {:b}\n\
4649            Query mask: {:b}",
4650            world.component_mask(entity).unwrap(),
4651            query_mask
4652        );
4653
4654        let first_result = world.query_first_entity(query_mask);
4655        assert!(
4656            first_result.is_some(),
4657            "query_first_entity should still find entity after adding component\n\
4658            Entity mask: {:b}\n\
4659            Query mask: {:b}",
4660            world.component_mask(entity).unwrap(),
4661            query_mask
4662        );
4663    }
4664
4665    #[test]
4666    fn entity_has_components_test() {
4667        let mut world = World::default();
4668        let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4669        assert!(world.entity_has_components(entity, POSITION | VELOCITY));
4670        assert!(!world.entity_has_components(entity, HEALTH));
4671    }
4672
4673    #[test]
4674    fn test_set_component() {
4675        let mut world = World::default();
4676        let entity = world.spawn_entities(POSITION, 1)[0];
4677        world.set_position(entity, Position { x: 1.0, y: 2.0 });
4678        assert_eq!(world.get_position(entity).unwrap().x, 1.0);
4679        assert_eq!(world.get_position(entity).unwrap().y, 2.0);
4680
4681        world.set_position(entity, Position { x: 3.0, y: 4.0 });
4682        assert_eq!(world.get_position(entity).unwrap().x, 3.0);
4683        assert_eq!(world.get_position(entity).unwrap().y, 4.0);
4684    }
4685
4686    #[test]
4687    fn test_entity_builder() {
4688        let mut world = World::default();
4689        let entities = EntityBuilder::new()
4690            .with_position(Position { x: 1.0, y: 2.0 })
4691            .spawn(&mut world, 2);
4692        assert_eq!(world.get_position(entities[0]).unwrap().x, 1.0);
4693        assert_eq!(world.get_position(entities[1]).unwrap().y, 2.0);
4694    }
4695
4696    #[test]
4697    fn test_query_composition_exclude() {
4698        let mut world = World::default();
4699
4700        let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4701        let e2 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4702        let e3 = world.spawn_entities(POSITION, 1)[0];
4703
4704        let mut count = 0;
4705        world.for_each_mut(POSITION | VELOCITY, HEALTH, |_entity, _table, _idx| {
4706            count += 1;
4707        });
4708
4709        assert_eq!(count, 1);
4710
4711        let mut found_entities = Vec::new();
4712        world.for_each_mut(POSITION | VELOCITY, HEALTH, |entity, _table, _idx| {
4713            found_entities.push(entity);
4714        });
4715
4716        assert!(found_entities.contains(&e1));
4717        assert!(!found_entities.contains(&e2));
4718        assert!(!found_entities.contains(&e3));
4719    }
4720
4721    #[test]
4722    fn test_query_composition_include_only() {
4723        let mut world = World::default();
4724
4725        let e1 = world.spawn_entities(POSITION, 1)[0];
4726        let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4727        let e3 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
4728
4729        let mut count = 0;
4730        world.for_each_mut(POSITION | VELOCITY, 0, |_entity, _table, _idx| {
4731            count += 1;
4732        });
4733
4734        assert_eq!(count, 2);
4735
4736        let mut found_entities = Vec::new();
4737        world.for_each_mut(POSITION | VELOCITY, 0, |entity, _table, _idx| {
4738            found_entities.push(entity);
4739        });
4740
4741        assert!(!found_entities.contains(&e1));
4742        assert!(found_entities.contains(&e2));
4743        assert!(found_entities.contains(&e3));
4744    }
4745
4746    #[test]
4747    fn test_change_detection_basic() {
4748        let mut world = World::default();
4749        let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4750
4751        world.step();
4752
4753        world.get_position_mut(entity).unwrap().x = 10.0;
4754
4755        let mut changed_count = 0;
4756        world.for_each_mut_changed(POSITION, 0, |_entity, _table, _idx| {
4757            changed_count += 1;
4758        });
4759
4760        assert_eq!(changed_count, 1);
4761    }
4762
4763    #[test]
4764    fn test_change_detection_unchanged() {
4765        let mut world = World::default();
4766        let e1 = world.spawn_entities(POSITION, 1)[0];
4767        let e2 = world.spawn_entities(POSITION, 1)[0];
4768
4769        world.step();
4770
4771        world.get_position_mut(e1).unwrap().x = 5.0;
4772
4773        let mut changed_entities = Vec::new();
4774        world.for_each_mut_changed(POSITION, 0, |entity, _table, _idx| {
4775            changed_entities.push(entity);
4776        });
4777
4778        assert_eq!(changed_entities.len(), 1);
4779        assert!(changed_entities.contains(&e1));
4780        assert!(!changed_entities.contains(&e2));
4781    }
4782
4783    #[test]
4784    fn test_change_detection_tick_tracking() {
4785        let mut world = World::default();
4786        let entity = world.spawn_entities(POSITION, 1)[0];
4787
4788        assert_eq!(world.current_tick(), 0);
4789        assert_eq!(world.last_tick(), 0);
4790
4791        world.step();
4792        assert_eq!(world.current_tick(), 1);
4793        assert_eq!(world.last_tick(), 0);
4794
4795        world.step();
4796        assert_eq!(world.current_tick(), 2);
4797        assert_eq!(world.last_tick(), 1);
4798
4799        world.step();
4800        world.get_position_mut(entity).unwrap().x = 10.0;
4801
4802        let mut count = 0;
4803        world.for_each_mut_changed(POSITION, 0, |_entity, _table, _idx| {
4804            count += 1;
4805        });
4806        assert_eq!(count, 1);
4807
4808        world.step();
4809        count = 0;
4810        world.for_each_mut_changed(POSITION, 0, |_entity, _table, _idx| {
4811            count += 1;
4812        });
4813        assert_eq!(count, 0);
4814    }
4815
4816    #[test]
4817    fn test_change_detection_multiple_components() {
4818        let mut world = World::default();
4819        let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4820        let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
4821
4822        world.step();
4823
4824        world.get_position_mut(e1).unwrap().x = 5.0;
4825        world.get_velocity_mut(e2).unwrap().x = 10.0;
4826
4827        let mut changed_entities = Vec::new();
4828        world.for_each_mut_changed(POSITION | VELOCITY, 0, |entity, _table, _idx| {
4829            changed_entities.push(entity);
4830        });
4831
4832        assert_eq!(changed_entities.len(), 2);
4833        assert!(changed_entities.contains(&e1));
4834        assert!(changed_entities.contains(&e2));
4835    }
4836
4837    #[test]
4838    fn test_change_detection_with_exclude() {
4839        let mut world = World::default();
4840        let e1 = world.spawn_entities(POSITION, 1)[0];
4841        let e2 = world.spawn_entities(POSITION | HEALTH, 1)[0];
4842
4843        world.step();
4844
4845        world.get_position_mut(e1).unwrap().x = 5.0;
4846        world.get_position_mut(e2).unwrap().x = 10.0;
4847
4848        let mut changed_entities = Vec::new();
4849        world.for_each_mut_changed(POSITION, HEALTH, |entity, _table, _idx| {
4850            changed_entities.push(entity);
4851        });
4852
4853        assert_eq!(changed_entities.len(), 1);
4854        assert!(changed_entities.contains(&e1));
4855        assert!(!changed_entities.contains(&e2));
4856    }
4857
4858    #[test]
4859    fn test_simd_slice_iteration_read() {
4860        let mut world = World::default();
4861        world.spawn_entities(POSITION, 3);
4862
4863        let mut total_count = 0;
4864        for slice in world.iter_position_slices() {
4865            total_count += slice.len();
4866            for pos in slice {
4867                assert_eq!(pos.x, 0.0);
4868                assert_eq!(pos.y, 0.0);
4869            }
4870        }
4871
4872        assert_eq!(total_count, 3);
4873    }
4874
4875    #[test]
4876    fn test_simd_slice_iteration_write() {
4877        let mut world = World::default();
4878        let entities = world.spawn_entities(POSITION, 5);
4879
4880        for slice in world.iter_position_slices_mut() {
4881            for pos in slice {
4882                pos.x = 10.0;
4883                pos.y = 20.0;
4884            }
4885        }
4886
4887        for entity in entities {
4888            let pos = world.get_position(entity).unwrap();
4889            assert_eq!(pos.x, 10.0);
4890            assert_eq!(pos.y, 20.0);
4891        }
4892    }
4893
4894    #[test]
4895    fn test_simd_slice_iteration_multiple_archetypes() {
4896        let mut world = World::default();
4897        world.spawn_entities(POSITION, 2);
4898        world.spawn_entities(POSITION | VELOCITY, 3);
4899        world.spawn_entities(POSITION | HEALTH, 4);
4900
4901        let mut slice_count = 0;
4902        let mut total_entities = 0;
4903
4904        for slice in world.iter_position_slices() {
4905            slice_count += 1;
4906            total_entities += slice.len();
4907        }
4908
4909        assert_eq!(slice_count, 3);
4910        assert_eq!(total_entities, 9);
4911    }
4912
4913    #[test]
4914    fn test_simd_slice_vectorizable_operation() {
4915        let mut world = World::default();
4916        world.spawn_entities(POSITION | VELOCITY, 1000);
4917
4918        for pos in world.iter_position_slices_mut() {
4919            for p in pos {
4920                p.x += 1.0;
4921                p.y += 2.0;
4922            }
4923        }
4924
4925        for vel in world.iter_velocity_slices_mut() {
4926            for v in vel {
4927                v.x *= 0.99;
4928                v.y *= 0.99;
4929            }
4930        }
4931
4932        let mut checked = 0;
4933        for slice in world.iter_position_slices() {
4934            for pos in slice {
4935                assert_eq!(pos.x, 1.0);
4936                assert_eq!(pos.y, 2.0);
4937                checked += 1;
4938            }
4939        }
4940        assert_eq!(checked, 1000);
4941    }
4942
4943    #[test]
4944    fn test_simd_slice_empty_world() {
4945        let world = World::default();
4946
4947        let mut count = 0;
4948        for _ in world.iter_position_slices() {
4949            count += 1;
4950        }
4951        assert_eq!(count, 0);
4952    }
4953
4954    #[test]
4955    fn test_simd_slice_no_matching_archetype() {
4956        let mut world = World::default();
4957        world.spawn_entities(VELOCITY, 5);
4958
4959        let mut count = 0;
4960        for _ in world.iter_position_slices() {
4961            count += 1;
4962        }
4963        assert_eq!(count, 0);
4964    }
4965
4966    #[test]
4967    fn test_sparse_set_add_remove_has() {
4968        let mut world = World::default();
4969        let entity = world.spawn_entities(POSITION, 1)[0];
4970
4971        assert!(!world.has_player(entity));
4972
4973        world.add_player(entity);
4974        assert!(world.has_player(entity));
4975
4976        world.remove_player(entity);
4977        assert!(!world.has_player(entity));
4978    }
4979
4980    #[test]
4981    fn test_sparse_set_query() {
4982        let mut world = World::default();
4983        let e1 = world.spawn_entities(POSITION, 1)[0];
4984        let e2 = world.spawn_entities(POSITION, 1)[0];
4985        let e3 = world.spawn_entities(POSITION, 1)[0];
4986
4987        world.add_player(e1);
4988        world.add_player(e3);
4989
4990        let players: Vec<Entity> = world.query_player().collect();
4991        assert_eq!(players.len(), 2);
4992        assert!(players.contains(&e1));
4993        assert!(players.contains(&e3));
4994        assert!(!players.contains(&e2));
4995    }
4996
4997    #[test]
4998    fn test_sparse_set_no_archetype_fragmentation() {
4999        let mut world = World::default();
5000        let e1 = world.spawn_entities(POSITION, 1)[0];
5001        let e2 = world.spawn_entities(POSITION, 1)[0];
5002        let e3 = world.spawn_entities(POSITION, 1)[0];
5003
5004        world.add_player(e1);
5005        world.add_enemy(e2);
5006
5007        let mut count = 0;
5008        world.for_each(POSITION, 0, |_, _, _| {
5009            count += 1;
5010        });
5011
5012        assert_eq!(count, 3);
5013
5014        let mask1 = world.component_mask(e1).unwrap();
5015        let mask2 = world.component_mask(e2).unwrap();
5016        let mask3 = world.component_mask(e3).unwrap();
5017
5018        assert_eq!(mask1, mask2);
5019        assert_eq!(mask2, mask3);
5020    }
5021
5022    #[test]
5023    fn test_sparse_set_tag_component_query() {
5024        let mut world = World::default();
5025        let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5026        let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5027        let e3 = world.spawn_entities(POSITION, 1)[0];
5028        let e4 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5029
5030        world.add_player(e1);
5031        world.add_player(e2);
5032
5033        let mut count = 0;
5034        world.for_each(POSITION | VELOCITY | PLAYER, 0, |_, _, _| {
5035            count += 1;
5036        });
5037        assert_eq!(count, 2);
5038
5039        let mut found = Vec::new();
5040        world.for_each(POSITION | VELOCITY | PLAYER, 0, |entity, _, _| {
5041            found.push(entity);
5042        });
5043        assert!(found.contains(&e1));
5044        assert!(found.contains(&e2));
5045        assert!(!found.contains(&e3));
5046        assert!(!found.contains(&e4));
5047    }
5048
5049    #[test]
5050    fn test_sparse_set_exclude_tag() {
5051        let mut world = World::default();
5052        let e1 = world.spawn_entities(POSITION, 1)[0];
5053        let e2 = world.spawn_entities(POSITION, 1)[0];
5054        let e3 = world.spawn_entities(POSITION, 1)[0];
5055
5056        world.add_player(e1);
5057        world.add_player(e2);
5058
5059        let mut count = 0;
5060        world.for_each(POSITION, PLAYER, |_, _, _| {
5061            count += 1;
5062        });
5063        assert_eq!(count, 1);
5064
5065        let mut found = Vec::new();
5066        world.for_each(POSITION, PLAYER, |entity, _, _| {
5067            found.push(entity);
5068        });
5069        assert_eq!(found.len(), 1);
5070        assert!(found.contains(&e3));
5071    }
5072
5073    #[test]
5074    fn test_sparse_set_cleanup_on_despawn() {
5075        let mut world = World::default();
5076        let e1 = world.spawn_entities(POSITION, 1)[0];
5077        let e2 = world.spawn_entities(POSITION, 1)[0];
5078
5079        world.add_player(e1);
5080        world.add_enemy(e2);
5081
5082        assert_eq!(world.query_player().count(), 1);
5083        assert_eq!(world.query_enemy().count(), 1);
5084
5085        world.despawn_entities(&[e1]);
5086
5087        assert_eq!(world.query_player().count(), 0);
5088        assert_eq!(world.query_enemy().count(), 1);
5089
5090        world.despawn_entities(&[e2]);
5091        assert_eq!(world.query_enemy().count(), 0);
5092    }
5093
5094    #[test]
5095    fn test_sparse_set_multiple_tags() {
5096        let mut world = World::default();
5097        let entity = world.spawn_entities(POSITION, 1)[0];
5098
5099        world.add_player(entity);
5100        world.add_active(entity);
5101
5102        assert!(world.has_player(entity));
5103        assert!(world.has_active(entity));
5104        assert!(!world.has_enemy(entity));
5105
5106        let mut count = 0;
5107        world.for_each(POSITION | PLAYER | ACTIVE, 0, |_, _, _| {
5108            count += 1;
5109        });
5110        assert_eq!(count, 1);
5111
5112        world.remove_active(entity);
5113        assert!(world.has_player(entity));
5114        assert!(!world.has_active(entity));
5115
5116        count = 0;
5117        world.for_each(POSITION | PLAYER | ACTIVE, 0, |_, _, _| {
5118            count += 1;
5119        });
5120        assert_eq!(count, 0);
5121    }
5122
5123    #[test]
5124    fn test_sparse_set_for_each_mut() {
5125        let mut world = World::default();
5126        let e1 = world.spawn_entities(POSITION, 1)[0];
5127        let e2 = world.spawn_entities(POSITION, 1)[0];
5128        let e3 = world.spawn_entities(POSITION, 1)[0];
5129
5130        world.add_player(e1);
5131        world.add_player(e3);
5132
5133        world.for_each_mut(POSITION | PLAYER, 0, |_, table, idx| {
5134            table.position[idx].x = 100.0;
5135        });
5136
5137        assert_eq!(world.get_position(e1).unwrap().x, 100.0);
5138        assert_ne!(world.get_position(e2).unwrap().x, 100.0);
5139        assert_eq!(world.get_position(e3).unwrap().x, 100.0);
5140    }
5141
5142    #[test]
5143    #[cfg(not(target_family = "wasm"))]
5144    fn test_sparse_set_par_for_each_mut() {
5145        let mut world = World::default();
5146        let entities = world.spawn_entities(POSITION, 100);
5147
5148        for &entity in &entities[0..50] {
5149            world.add_player(entity);
5150        }
5151
5152        world.par_for_each_mut(POSITION | PLAYER, 0, |_, table, idx| {
5153            table.position[idx].x = 200.0;
5154        });
5155
5156        for &entity in entities.iter().take(50) {
5157            assert_eq!(world.get_position(entity).unwrap().x, 200.0);
5158        }
5159        for &entity in entities.iter().skip(50).take(50) {
5160            assert_ne!(world.get_position(entity).unwrap().x, 200.0);
5161        }
5162    }
5163
5164    #[test]
5165    fn test_sparse_set_complex_query() {
5166        let mut world = World::default();
5167        let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5168        let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5169        let e3 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5170        let _e4 = world.spawn_entities(POSITION, 1)[0];
5171
5172        world.add_player(e1);
5173        world.add_enemy(e2);
5174        world.add_enemy(e3);
5175        world.add_active(e1);
5176        world.add_active(e2);
5177
5178        let mut count = 0;
5179        world.for_each(POSITION | VELOCITY | ENEMY | ACTIVE, 0, |_, _, _| {
5180            count += 1;
5181        });
5182        assert_eq!(count, 1);
5183
5184        let mut found = Vec::new();
5185        world.for_each(POSITION | VELOCITY | ENEMY | ACTIVE, 0, |entity, _, _| {
5186            found.push(entity);
5187        });
5188        assert_eq!(found.len(), 1);
5189        assert!(found.contains(&e2));
5190    }
5191
5192    #[test]
5193    fn test_command_buffer_spawn() {
5194        let mut world = World::default();
5195
5196        world.queue_spawn_entities(POSITION, 3);
5197        world.queue_spawn_entities(VELOCITY, 2);
5198
5199        assert_eq!(world.command_count(), 2);
5200        assert_eq!(world.get_all_entities().len(), 0);
5201
5202        world.apply_commands();
5203
5204        assert_eq!(world.command_count(), 0);
5205        assert_eq!(world.get_all_entities().len(), 5);
5206
5207        let mut pos_count = 0;
5208        world.for_each(POSITION, 0, |_, _, _| pos_count += 1);
5209        assert_eq!(pos_count, 3);
5210
5211        let mut vel_count = 0;
5212        world.for_each(VELOCITY, 0, |_, _, _| vel_count += 1);
5213        assert_eq!(vel_count, 2);
5214    }
5215
5216    #[test]
5217    fn test_command_buffer_despawn() {
5218        let mut world = World::default();
5219        let entities = world.spawn_entities(POSITION, 5);
5220
5221        world.queue_despawn_entity(entities[0]);
5222        world.queue_despawn_entity(entities[2]);
5223        world.queue_despawn_entity(entities[4]);
5224
5225        assert_eq!(world.command_count(), 3);
5226        assert_eq!(world.get_all_entities().len(), 5);
5227
5228        world.apply_commands();
5229
5230        assert_eq!(world.command_count(), 0);
5231        assert_eq!(world.get_all_entities().len(), 2);
5232
5233        let remaining = world.get_all_entities();
5234        assert!(remaining.contains(&entities[1]));
5235        assert!(remaining.contains(&entities[3]));
5236    }
5237
5238    #[test]
5239    fn test_command_buffer_add_remove_components() {
5240        let mut world = World::default();
5241        let entity = world.spawn_entities(POSITION, 1)[0];
5242
5243        world.queue_add_components(entity, VELOCITY);
5244        assert_eq!(world.command_count(), 1);
5245        assert!(world.get_velocity(entity).is_none());
5246
5247        world.apply_commands();
5248        assert!(world.get_velocity(entity).is_some());
5249
5250        world.queue_remove_components(entity, VELOCITY);
5251        world.apply_commands();
5252        assert!(world.get_velocity(entity).is_none());
5253    }
5254
5255    #[test]
5256    fn test_command_buffer_set_component() {
5257        let mut world = World::default();
5258        let entity = world.spawn_entities(POSITION, 1)[0];
5259
5260        world.queue_set_position(entity, Position { x: 100.0, y: 200.0 });
5261        assert_eq!(world.command_count(), 1);
5262        assert_ne!(world.get_position(entity).unwrap().x, 100.0);
5263
5264        world.apply_commands();
5265
5266        assert_eq!(world.command_count(), 0);
5267        let pos = world.get_position(entity).unwrap();
5268        assert_eq!(pos.x, 100.0);
5269        assert_eq!(pos.y, 200.0);
5270    }
5271
5272    #[test]
5273    fn test_command_buffer_tags() {
5274        let mut world = World::default();
5275        let entity = world.spawn_entities(POSITION, 1)[0];
5276
5277        world.queue_add_player(entity);
5278        world.queue_add_active(entity);
5279
5280        assert_eq!(world.command_count(), 2);
5281        assert!(!world.has_player(entity));
5282        assert!(!world.has_active(entity));
5283
5284        world.apply_commands();
5285
5286        assert!(world.has_player(entity));
5287        assert!(world.has_active(entity));
5288
5289        world.queue_remove_player(entity);
5290        world.apply_commands();
5291
5292        assert!(!world.has_player(entity));
5293        assert!(world.has_active(entity));
5294    }
5295
5296    #[test]
5297    fn test_command_buffer_mixed_operations() {
5298        let mut world = World::default();
5299        let e1 = world.spawn_entities(POSITION, 1)[0];
5300
5301        world.queue_spawn_entities(VELOCITY, 2);
5302        world.queue_add_components(e1, VELOCITY);
5303        world.queue_set_position(e1, Position { x: 50.0, y: 75.0 });
5304        world.queue_add_player(e1);
5305
5306        assert_eq!(world.command_count(), 4);
5307
5308        world.apply_commands();
5309
5310        assert_eq!(world.command_count(), 0);
5311        assert_eq!(world.get_all_entities().len(), 3);
5312        assert!(world.get_velocity(e1).is_some());
5313        assert_eq!(world.get_position(e1).unwrap().x, 50.0);
5314        assert!(world.has_player(e1));
5315    }
5316
5317    #[test]
5318    fn test_command_buffer_clear() {
5319        let mut world = World::default();
5320
5321        world.queue_spawn_entities(POSITION, 5);
5322        world.queue_spawn_entities(VELOCITY, 3);
5323
5324        assert_eq!(world.command_count(), 2);
5325
5326        world.clear_commands();
5327
5328        assert_eq!(world.command_count(), 0);
5329        assert_eq!(world.get_all_entities().len(), 0);
5330    }
5331
5332    #[test]
5333    fn test_command_buffer_multiple_batches() {
5334        let mut world = World::default();
5335
5336        world.queue_spawn_entities(POSITION, 2);
5337        world.apply_commands();
5338        assert_eq!(world.get_all_entities().len(), 2);
5339
5340        world.queue_spawn_entities(VELOCITY, 3);
5341        world.apply_commands();
5342        assert_eq!(world.get_all_entities().len(), 5);
5343
5344        world.queue_spawn_entities(POSITION | VELOCITY, 1);
5345        world.apply_commands();
5346        assert_eq!(world.get_all_entities().len(), 6);
5347    }
5348
5349    #[test]
5350    fn test_command_buffer_despawn_batch() {
5351        let mut world = World::default();
5352        let entities = world.spawn_entities(POSITION, 10);
5353
5354        let to_despawn = vec![entities[0], entities[3], entities[5], entities[9]];
5355        world.queue_despawn_entities(to_despawn.clone());
5356
5357        assert_eq!(world.command_count(), 1);
5358        world.apply_commands();
5359
5360        assert_eq!(world.get_all_entities().len(), 6);
5361
5362        let remaining = world.get_all_entities();
5363        for &entity in &to_despawn {
5364            assert!(!remaining.contains(&entity));
5365        }
5366    }
5367
5368    #[test]
5369    fn test_command_buffer_parallel_safety() {
5370        let mut world = World::default();
5371        let _ = world.spawn_entities(POSITION | VELOCITY, 100);
5372
5373        let mut to_despawn = Vec::new();
5374        world.for_each(POSITION | VELOCITY, 0, |entity, table, idx| {
5375            if table.position[idx].x < 0.0 {
5376                to_despawn.push(entity);
5377            }
5378        });
5379
5380        for entity in to_despawn {
5381            world.queue_despawn_entity(entity);
5382        }
5383
5384        let command_count_before = world.command_count();
5385        world.apply_commands();
5386
5387        assert_eq!(world.get_all_entities().len(), 100 - command_count_before);
5388    }
5389
5390    #[test]
5391    fn test_ergonomic_query_builder() {
5392        let mut world = World::default();
5393        world.spawn_entities(POSITION | VELOCITY, 3);
5394        world.spawn_entities(POSITION, 2);
5395
5396        let mut count = 0;
5397        world
5398            .query()
5399            .with(POSITION)
5400            .with(VELOCITY)
5401            .iter(|_entity, _table, _idx| {
5402                count += 1;
5403            });
5404        assert_eq!(count, 3);
5405
5406        count = 0;
5407        world
5408            .query()
5409            .with(POSITION)
5410            .without(VELOCITY)
5411            .iter(|_entity, _table, _idx| {
5412                count += 1;
5413            });
5414        assert_eq!(count, 2);
5415    }
5416
5417    #[test]
5418    fn test_ergonomic_query_builder_mut() {
5419        let mut world = World::default();
5420        let entities = world.spawn_entities(POSITION, 3);
5421
5422        world
5423            .query_mut()
5424            .with(POSITION)
5425            .iter(|_entity, table, idx| {
5426                table.position[idx].x = 100.0;
5427            });
5428
5429        for &entity in &entities {
5430            assert_eq!(world.get_position(entity).unwrap().x, 100.0);
5431        }
5432    }
5433
5434    #[test]
5435    fn test_ergonomic_single_component_iter() {
5436        let mut world = World::default();
5437        world.spawn_entities(POSITION, 5);
5438
5439        let mut count = 0;
5440        world.iter_position(|_entity, pos| {
5441            assert_eq!(pos.x, 0.0);
5442            count += 1;
5443        });
5444        assert_eq!(count, 5);
5445    }
5446
5447    #[test]
5448    fn test_ergonomic_single_component_iter_mut() {
5449        let mut world = World::default();
5450        let entities = world.spawn_entities(POSITION, 5);
5451
5452        world.iter_position_mut(|_entity, pos| {
5453            pos.x = 42.0;
5454        });
5455
5456        for &entity in &entities {
5457            assert_eq!(world.get_position(entity).unwrap().x, 42.0);
5458        }
5459    }
5460
5461    #[test]
5462    fn test_ergonomic_iter_with_entity() {
5463        let mut world = World::default();
5464        let e1 = world.spawn_entities(POSITION, 1)[0];
5465        let e2 = world.spawn_entities(POSITION, 1)[0];
5466
5467        let mut found = vec![];
5468        world.iter_position(|entity, _pos| {
5469            found.push(entity);
5470        });
5471
5472        assert_eq!(found.len(), 2);
5473        assert!(found.contains(&e1));
5474        assert!(found.contains(&e2));
5475    }
5476
5477    #[test]
5478    fn test_ergonomic_batch_spawn() {
5479        let mut world = World::default();
5480
5481        let entities = world.spawn_batch(POSITION | VELOCITY, 100, |table, idx| {
5482            table.position[idx] = Position {
5483                x: idx as f32,
5484                y: idx as f32 * 2.0,
5485            };
5486            table.velocity[idx] = Velocity { x: 1.0, y: -1.0 };
5487        });
5488
5489        assert_eq!(entities.len(), 100);
5490
5491        for (i, &entity) in entities.iter().enumerate() {
5492            let pos = world.get_position(entity).unwrap();
5493            assert_eq!(pos.x, i as f32);
5494            assert_eq!(pos.y, i as f32 * 2.0);
5495
5496            let vel = world.get_velocity(entity).unwrap();
5497            assert_eq!(vel.x, 1.0);
5498            assert_eq!(vel.y, -1.0);
5499        }
5500    }
5501
5502    #[test]
5503    fn test_ergonomic_query_with_tags() {
5504        let mut world = World::default();
5505        let e1 = world.spawn_entities(POSITION, 1)[0];
5506        let e2 = world.spawn_entities(POSITION, 1)[0];
5507        let _e3 = world.spawn_entities(POSITION, 1)[0];
5508
5509        world.add_player(e1);
5510        world.add_player(e2);
5511
5512        let mut count = 0;
5513        world
5514            .query()
5515            .with(POSITION | PLAYER)
5516            .iter(|_entity, _table, _idx| {
5517                count += 1;
5518            });
5519        assert_eq!(count, 2);
5520
5521        count = 0;
5522        world
5523            .query()
5524            .with(POSITION)
5525            .without(PLAYER)
5526            .iter(|_entity, _table, _idx| {
5527                count += 1;
5528            });
5529        assert_eq!(count, 1);
5530    }
5531
5532    #[test]
5533    fn test_query_builder_mut_without() {
5534        let mut world = World::default();
5535        let e1 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5536        let e2 = world.spawn_entities(POSITION, 1)[0];
5537
5538        world.set_position(e1, Position { x: 1.0, y: 2.0 });
5539        world.set_position(e2, Position { x: 3.0, y: 4.0 });
5540
5541        let mut count = 0;
5542        world
5543            .query_mut()
5544            .with(POSITION)
5545            .without(VELOCITY)
5546            .iter(|_entity, _table, _idx| {
5547                count += 1;
5548            });
5549        assert_eq!(count, 1);
5550    }
5551
5552    #[test]
5553    fn test_iter_methods() {
5554        let mut world = World::default();
5555        let e1 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
5556        let e2 = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5557
5558        world.set_position(e1, Position { x: 1.0, y: 2.0 });
5559        world.set_position(e2, Position { x: 3.0, y: 4.0 });
5560        world.set_velocity(e1, Velocity { x: 1.0, y: 1.0 });
5561        world.set_velocity(e2, Velocity { x: 2.0, y: 2.0 });
5562        world.set_health(e1, Health { value: 100.0 });
5563
5564        let mut sum_x = 0.0;
5565        world.iter_position(|_entity, pos| {
5566            sum_x += pos.x;
5567        });
5568        assert_eq!(sum_x, 4.0);
5569
5570        world.iter_position_mut(|_entity, pos| {
5571            pos.x *= 2.0;
5572        });
5573
5574        let mut count = 0;
5575        world.iter_velocity(|_entity, _vel| {
5576            count += 1;
5577        });
5578        assert_eq!(count, 2);
5579
5580        world.iter_velocity_mut(|_entity, vel| {
5581            vel.x *= 2.0;
5582        });
5583
5584        world.iter_health_mut(|_entity, health| {
5585            health.value += 10.0;
5586        });
5587
5588        let mut health_sum = 0.0;
5589        world.iter_health(|_entity, health| {
5590            health_sum += health.value;
5591        });
5592        assert_eq!(health_sum, 110.0);
5593
5594        let e3 = world.spawn_entities(PARENT | NODE, 1)[0];
5595        world.set_parent(e3, Parent(e1));
5596        world.set_node(
5597            e3,
5598            Node {
5599                id: e3,
5600                parent: Some(e1),
5601                children: Vec::new(),
5602            },
5603        );
5604
5605        let mut parent_count = 0;
5606        world.iter_parent(|_entity, _parent| {
5607            parent_count += 1;
5608        });
5609        assert_eq!(parent_count, 1);
5610
5611        let mut node_count = 0;
5612        world.iter_node(|_entity, _node| {
5613            node_count += 1;
5614        });
5615        assert_eq!(node_count, 1);
5616
5617        world.iter_parent_mut(|_entity, parent| {
5618            parent.0 = e2;
5619        });
5620
5621        world.iter_node_mut(|_entity, _node| {});
5622    }
5623
5624    #[test]
5625    fn test_schedule_basic() {
5626        let mut world = World::default();
5627        world.resources._delta_time = 0.016;
5628
5629        let mut schedule = Schedule::new();
5630        schedule.push("tick", |world: &mut World| {
5631            world.resources._delta_time += 0.016;
5632        });
5633
5634        schedule.run(&mut world);
5635        assert_eq!(world.resources._delta_time, 0.032);
5636
5637        schedule.run(&mut world);
5638        assert_eq!(world.resources._delta_time, 0.048);
5639    }
5640
5641    #[test]
5642    fn test_schedule_multiple_systems() {
5643        let mut world = World::default();
5644        let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
5645        world.set_position(entity, Position { x: 0.0, y: 0.0 });
5646        world.set_velocity(entity, Velocity { x: 10.0, y: 5.0 });
5647        world.resources._delta_time = 0.1;
5648
5649        let mut schedule = Schedule::new();
5650
5651        schedule.push("physics", |world: &mut World| {
5652            let dt = world.resources._delta_time;
5653            let updates: Vec<(Entity, Velocity)> = world
5654                .query_entities(POSITION | VELOCITY)
5655                .filter_map(|entity| world.get_velocity(entity).map(|vel| (entity, vel.clone())))
5656                .collect();
5657
5658            for (entity, vel) in updates {
5659                if let Some(pos) = world.get_position_mut(entity) {
5660                    pos.x += vel.x * dt;
5661                    pos.y += vel.y * dt;
5662                }
5663            }
5664        });
5665
5666        schedule.push("double_dt", |world: &mut World| {
5667            world.resources._delta_time *= 2.0;
5668        });
5669
5670        schedule.run(&mut world);
5671
5672        let pos = world.get_position(entity).unwrap();
5673        assert_eq!(pos.x, 1.0);
5674        assert_eq!(pos.y, 0.5);
5675        assert_eq!(world.resources._delta_time, 0.2);
5676    }
5677
5678    #[test]
5679    fn test_schedule_builder_pattern() {
5680        let mut world = World::default();
5681        world.resources._delta_time = 1.0;
5682
5683        let mut schedule = Schedule::new();
5684        schedule
5685            .push("add", |world: &mut World| {
5686                world.resources._delta_time += 1.0;
5687            })
5688            .push("multiply", |world: &mut World| {
5689                world.resources._delta_time *= 2.0;
5690            });
5691
5692        schedule.run(&mut world);
5693        assert_eq!(world.resources._delta_time, 4.0);
5694    }
5695
5696    #[test]
5697    fn test_schedule_system_order() {
5698        let mut world = World::default();
5699        let entity = world.spawn_entities(POSITION, 1)[0];
5700        world.set_position(entity, Position { x: 0.0, y: 0.0 });
5701
5702        let mut schedule = Schedule::new();
5703
5704        schedule.push("set_x", |world: &mut World| {
5705            let entities: Vec<Entity> = world.query_entities(POSITION).collect();
5706            if let Some(pos) = world.get_position_mut(entities[0]) {
5707                pos.x = 10.0;
5708            }
5709        });
5710
5711        schedule.push("double_x", |world: &mut World| {
5712            let entities: Vec<Entity> = world.query_entities(POSITION).collect();
5713            if let Some(pos) = world.get_position_mut(entities[0]) {
5714                pos.x *= 2.0;
5715            }
5716        });
5717
5718        schedule.run(&mut world);
5719
5720        let pos = world.get_position(entity).unwrap();
5721        assert_eq!(pos.x, 20.0);
5722    }
5723
5724    #[test]
5725    fn test_schedule_with_components() {
5726        let mut world = World::default();
5727        let entity = world.spawn_entities(HEALTH, 1)[0];
5728        world.set_health(entity, Health { value: 100.0 });
5729
5730        let mut schedule = Schedule::new();
5731
5732        schedule.push("damage", |world: &mut World| {
5733            let entities: Vec<Entity> = world.query_entities(HEALTH).collect();
5734            for entity in entities {
5735                if let Some(health) = world.get_health_mut(entity) {
5736                    health.value -= 10.0;
5737                }
5738            }
5739        });
5740
5741        schedule.push("decay", |world: &mut World| {
5742            let entities: Vec<Entity> = world.query_entities(HEALTH).collect();
5743            for entity in entities {
5744                if let Some(health) = world.get_health_mut(entity) {
5745                    health.value *= 0.9;
5746                }
5747            }
5748        });
5749
5750        schedule.run(&mut world);
5751
5752        let health = world.get_health(entity).unwrap();
5753        assert_eq!(health.value, 81.0);
5754    }
5755
5756    #[test]
5757    fn test_schedule_insert_before() {
5758        let mut world = World::default();
5759        world.resources._delta_time = 1.0;
5760
5761        let mut schedule = Schedule::new();
5762        schedule.push("first", |world: &mut World| {
5763            world.resources._delta_time += 10.0;
5764        });
5765        schedule.push("third", |world: &mut World| {
5766            world.resources._delta_time *= 3.0;
5767        });
5768        schedule.insert_before("third", "second", |world: &mut World| {
5769            world.resources._delta_time *= 2.0;
5770        });
5771
5772        schedule.run(&mut world);
5773        assert_eq!(world.resources._delta_time, 66.0);
5774    }
5775
5776    #[test]
5777    fn test_schedule_insert_after() {
5778        let mut world = World::default();
5779        world.resources._delta_time = 1.0;
5780
5781        let mut schedule = Schedule::new();
5782        schedule.push("first", |world: &mut World| {
5783            world.resources._delta_time += 10.0;
5784        });
5785        schedule.push("third", |world: &mut World| {
5786            world.resources._delta_time *= 3.0;
5787        });
5788        schedule.insert_after("first", "second", |world: &mut World| {
5789            world.resources._delta_time *= 2.0;
5790        });
5791
5792        schedule.run(&mut world);
5793        assert_eq!(world.resources._delta_time, 66.0);
5794    }
5795
5796    #[test]
5797    fn test_schedule_remove() {
5798        let mut world = World::default();
5799        world.resources._delta_time = 1.0;
5800
5801        let mut schedule = Schedule::new();
5802        schedule.push("add", |world: &mut World| {
5803            world.resources._delta_time += 10.0;
5804        });
5805        schedule.push("multiply", |world: &mut World| {
5806            world.resources._delta_time *= 5.0;
5807        });
5808
5809        schedule.remove("multiply");
5810        schedule.run(&mut world);
5811        assert_eq!(world.resources._delta_time, 11.0);
5812    }
5813
5814    #[test]
5815    fn test_schedule_remove_nonexistent() {
5816        let mut schedule: Schedule<World> = Schedule::new();
5817        schedule.push("a", |_world: &mut World| {});
5818        schedule.remove("nonexistent");
5819        assert!(schedule.contains("a"));
5820    }
5821
5822    #[test]
5823    fn test_schedule_contains() {
5824        let mut schedule: Schedule<World> = Schedule::new();
5825        assert!(!schedule.contains("physics"));
5826
5827        schedule.push("physics", |_world: &mut World| {});
5828        assert!(schedule.contains("physics"));
5829        assert!(!schedule.contains("render"));
5830
5831        schedule.remove("physics");
5832        assert!(!schedule.contains("physics"));
5833    }
5834
5835    #[test]
5836    #[should_panic(expected = "system \"nonexistent\" not found")]
5837    fn test_schedule_insert_before_panics() {
5838        let mut schedule: Schedule<World> = Schedule::new();
5839        schedule.insert_before("nonexistent", "new", |_world: &mut World| {});
5840    }
5841
5842    #[test]
5843    #[should_panic(expected = "system \"nonexistent\" not found")]
5844    fn test_schedule_insert_after_panics() {
5845        let mut schedule: Schedule<World> = Schedule::new();
5846        schedule.insert_after("nonexistent", "new", |_world: &mut World| {});
5847    }
5848
5849    #[test]
5850    fn test_schedule_ordering_verification() {
5851        let order = std::sync::Arc::new(std::sync::Mutex::new(Vec::<&'static str>::new()));
5852
5853        let mut schedule: Schedule<World> = Schedule::new();
5854        let order_clone = order.clone();
5855        schedule.push("a", move |_world: &mut World| {
5856            order_clone.lock().unwrap().push("a");
5857        });
5858        let order_clone = order.clone();
5859        schedule.push("c", move |_world: &mut World| {
5860            order_clone.lock().unwrap().push("c");
5861        });
5862        let order_clone = order.clone();
5863        schedule.insert_before("c", "b", move |_world: &mut World| {
5864            order_clone.lock().unwrap().push("b");
5865        });
5866        let order_clone = order.clone();
5867        schedule.insert_after("c", "d", move |_world: &mut World| {
5868            order_clone.lock().unwrap().push("d");
5869        });
5870
5871        let mut world = World::default();
5872        schedule.run(&mut world);
5873        assert_eq!(*order.lock().unwrap(), vec!["a", "b", "c", "d"]);
5874    }
5875
5876    #[test]
5877    fn test_schedule_readonly_wrapper() {
5878        let mut world = World::default();
5879        world.resources._delta_time = 42.0;
5880
5881        fn read_system(world: &World) -> f32 {
5882            world.resources._delta_time
5883        }
5884
5885        let observed = std::sync::Arc::new(std::sync::Mutex::new(0.0_f32));
5886        let observed_clone = observed.clone();
5887
5888        let mut schedule = Schedule::new();
5889        schedule.push("reader", move |w: &mut World| {
5890            *observed_clone.lock().unwrap() = read_system(w);
5891        });
5892
5893        schedule.run(&mut world);
5894        assert_eq!(*observed.lock().unwrap(), 42.0);
5895    }
5896
5897    #[test]
5898    #[should_panic(expected = "already exists")]
5899    fn test_schedule_push_duplicate_panics() {
5900        let mut schedule: Schedule<World> = Schedule::new();
5901        schedule.push("a", |_w: &mut World| {});
5902        schedule.push("a", |_w: &mut World| {});
5903    }
5904
5905    #[test]
5906    #[should_panic(expected = "already exists")]
5907    fn test_schedule_insert_before_duplicate_panics() {
5908        let mut schedule: Schedule<World> = Schedule::new();
5909        schedule.push("a", |_w: &mut World| {});
5910        schedule.push("b", |_w: &mut World| {});
5911        schedule.insert_before("b", "a", |_w: &mut World| {});
5912    }
5913
5914    #[test]
5915    #[should_panic(expected = "already exists")]
5916    fn test_schedule_insert_after_duplicate_panics() {
5917        let mut schedule: Schedule<World> = Schedule::new();
5918        schedule.push("a", |_w: &mut World| {});
5919        schedule.insert_after("a", "a", |_w: &mut World| {});
5920    }
5921
5922    #[test]
5923    fn test_schedule_replace() {
5924        let mut world = World::default();
5925        world.resources._delta_time = 1.0;
5926
5927        let mut schedule = Schedule::new();
5928        schedule.push("add", |world: &mut World| {
5929            world.resources._delta_time += 10.0;
5930        });
5931
5932        schedule.run(&mut world);
5933        assert_eq!(world.resources._delta_time, 11.0);
5934
5935        schedule.replace("add", |world: &mut World| {
5936            world.resources._delta_time += 100.0;
5937        });
5938
5939        schedule.run(&mut world);
5940        assert_eq!(world.resources._delta_time, 111.0);
5941    }
5942
5943    #[test]
5944    #[should_panic(expected = "system \"nonexistent\" not found")]
5945    fn test_schedule_replace_panics_on_missing() {
5946        let mut schedule: Schedule<World> = Schedule::new();
5947        schedule.replace("nonexistent", |_w: &mut World| {});
5948    }
5949
5950    #[test]
5951    fn test_schedule_replace_preserves_order() {
5952        let mut world = World::default();
5953        world.resources._delta_time = 0.0;
5954
5955        let mut schedule = Schedule::new();
5956        schedule.push("first", |world: &mut World| {
5957            world.resources._delta_time += 1.0;
5958        });
5959        schedule.push("second", |world: &mut World| {
5960            world.resources._delta_time *= 10.0;
5961        });
5962        schedule.push("third", |world: &mut World| {
5963            world.resources._delta_time += 5.0;
5964        });
5965
5966        schedule.replace("second", |world: &mut World| {
5967            world.resources._delta_time *= 100.0;
5968        });
5969
5970        schedule.run(&mut world);
5971        assert_eq!(world.resources._delta_time, 105.0);
5972    }
5973
5974    #[test]
5975    fn test_schedule_names() {
5976        let mut schedule: Schedule<World> = Schedule::new();
5977        schedule.push("a", |_w: &mut World| {});
5978        schedule.push("b", |_w: &mut World| {});
5979        schedule.push("c", |_w: &mut World| {});
5980
5981        let names: Vec<&str> = schedule.names().collect();
5982        assert_eq!(names, vec!["a", "b", "c"]);
5983    }
5984
5985    #[test]
5986    fn test_schedule_len_and_is_empty() {
5987        let mut schedule: Schedule<World> = Schedule::new();
5988        assert!(schedule.is_empty());
5989        assert_eq!(schedule.len(), 0);
5990
5991        schedule.push("a", |_w: &mut World| {});
5992        assert!(!schedule.is_empty());
5993        assert_eq!(schedule.len(), 1);
5994
5995        schedule.push("b", |_w: &mut World| {});
5996        assert_eq!(schedule.len(), 2);
5997
5998        schedule.remove("a");
5999        assert_eq!(schedule.len(), 1);
6000
6001        schedule.remove("b");
6002        assert!(schedule.is_empty());
6003    }
6004
6005    #[test]
6006    fn test_schedule_remove_returns_bool() {
6007        let mut schedule: Schedule<World> = Schedule::new();
6008        schedule.push("a", |_w: &mut World| {});
6009
6010        assert!(schedule.remove("a"));
6011        assert!(!schedule.remove("a"));
6012        assert!(!schedule.remove("nonexistent"));
6013    }
6014
6015    #[test]
6016    fn test_schedule_remove_then_push_reuses_name() {
6017        let mut world = World::default();
6018        world.resources._delta_time = 0.0;
6019
6020        let mut schedule = Schedule::new();
6021        schedule.push("sys", |world: &mut World| {
6022            world.resources._delta_time += 1.0;
6023        });
6024
6025        schedule.remove("sys");
6026        schedule.push("sys", |world: &mut World| {
6027            world.resources._delta_time += 100.0;
6028        });
6029
6030        schedule.run(&mut world);
6031        assert_eq!(world.resources._delta_time, 100.0);
6032    }
6033
6034    #[test]
6035    fn test_schedule_insert_before_first() {
6036        let mut schedule: Schedule<World> = Schedule::new();
6037        schedule.push("b", |_w: &mut World| {});
6038        schedule.insert_before("b", "a", |_w: &mut World| {});
6039
6040        let names: Vec<&str> = schedule.names().collect();
6041        assert_eq!(names, vec!["a", "b"]);
6042    }
6043
6044    #[test]
6045    fn test_schedule_insert_after_last() {
6046        let mut schedule: Schedule<World> = Schedule::new();
6047        schedule.push("a", |_w: &mut World| {});
6048        schedule.insert_after("a", "b", |_w: &mut World| {});
6049
6050        let names: Vec<&str> = schedule.names().collect();
6051        assert_eq!(names, vec!["a", "b"]);
6052    }
6053
6054    #[test]
6055    fn test_schedule_push_readonly() {
6056        let mut world = World::default();
6057        world.resources._delta_time = 42.0;
6058
6059        let observed = std::sync::Arc::new(std::sync::Mutex::new(0.0_f32));
6060        let obs = observed.clone();
6061
6062        let mut schedule = Schedule::new();
6063        schedule.push_readonly("reader", move |world: &World| {
6064            *obs.lock().unwrap() = world.resources._delta_time;
6065        });
6066
6067        schedule.run(&mut world);
6068        assert_eq!(*observed.lock().unwrap(), 42.0);
6069    }
6070
6071    #[test]
6072    fn test_query_cache_persistence_on_new_archetype() {
6073        let mut world = World::default();
6074
6075        world.spawn_entities(POSITION, 5);
6076
6077        let mut count1 = 0;
6078        world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6079            count1 += 1;
6080        });
6081        assert_eq!(count1, 5);
6082
6083        let cache_size_before = world.query_cache.len();
6084        assert_eq!(cache_size_before, 1);
6085
6086        world.spawn_entities(POSITION | VELOCITY, 3);
6087
6088        let cache_size_after = world.query_cache.len();
6089        assert_eq!(cache_size_after, 1);
6090
6091        let mut count2 = 0;
6092        world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6093            count2 += 1;
6094        });
6095        assert_eq!(count2, 8);
6096
6097        world.spawn_entities(POSITION | HEALTH, 2);
6098
6099        let mut count3 = 0;
6100        world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6101            count3 += 1;
6102        });
6103        assert_eq!(count3, 10);
6104    }
6105
6106    #[test]
6107    fn test_multi_component_add_performance() {
6108        let mut world = World::default();
6109        let entity = world.spawn_entities(POSITION, 1)[0];
6110        let source_table_index =
6111            world.entity_locations.get(entity.id).unwrap().table_index as usize;
6112
6113        world.add_components(entity, VELOCITY | HEALTH);
6114
6115        assert!(world.get_position(entity).is_some());
6116        assert!(world.get_velocity(entity).is_some());
6117        assert!(world.get_health(entity).is_some());
6118
6119        let cache_entry_in_source = world.table_edges[source_table_index]
6120            .multi_add_cache
6121            .get(&(VELOCITY | HEALTH))
6122            .copied();
6123        assert!(cache_entry_in_source.is_some());
6124
6125        let entity2 = world.spawn_entities(POSITION, 1)[0];
6126        let source_table_index2 =
6127            world.entity_locations.get(entity2.id).unwrap().table_index as usize;
6128
6129        assert_eq!(source_table_index, source_table_index2);
6130
6131        world.add_components(entity2, VELOCITY | HEALTH);
6132
6133        let cache_hit = world.table_edges[source_table_index2]
6134            .multi_add_cache
6135            .get(&(VELOCITY | HEALTH))
6136            .copied();
6137        assert_eq!(cache_hit, cache_entry_in_source);
6138    }
6139
6140    #[test]
6141    fn test_multi_component_remove_performance() {
6142        let mut world = World::default();
6143        let entity = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
6144        let source_table_index =
6145            world.entity_locations.get(entity.id).unwrap().table_index as usize;
6146
6147        world.remove_components(entity, VELOCITY | HEALTH);
6148
6149        assert!(world.get_position(entity).is_some());
6150        assert!(world.get_velocity(entity).is_none());
6151        assert!(world.get_health(entity).is_none());
6152
6153        let cache_entry_in_source = world.table_edges[source_table_index]
6154            .multi_remove_cache
6155            .get(&(VELOCITY | HEALTH))
6156            .copied();
6157        assert!(cache_entry_in_source.is_some());
6158
6159        let entity2 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
6160        let source_table_index2 =
6161            world.entity_locations.get(entity2.id).unwrap().table_index as usize;
6162
6163        assert_eq!(source_table_index, source_table_index2);
6164
6165        world.remove_components(entity2, VELOCITY | HEALTH);
6166
6167        let cache_hit = world.table_edges[source_table_index2]
6168            .multi_remove_cache
6169            .get(&(VELOCITY | HEALTH))
6170            .copied();
6171        assert_eq!(cache_hit, cache_entry_in_source);
6172    }
6173
6174    #[test]
6175    fn test_query_cache_invalidation_selective() {
6176        let mut world = World::default();
6177
6178        world.spawn_entities(POSITION, 10);
6179        world.spawn_entities(VELOCITY, 5);
6180
6181        let mut pos_count1 = 0;
6182        world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6183            pos_count1 += 1;
6184        });
6185        let mut vel_count1 = 0;
6186        world.for_each_mut(VELOCITY, 0, |_entity, _table, _idx| {
6187            vel_count1 += 1;
6188        });
6189        assert_eq!(pos_count1, 10);
6190        assert_eq!(vel_count1, 5);
6191
6192        let cache_size_before = world.query_cache.len();
6193        assert_eq!(cache_size_before, 2);
6194
6195        world.spawn_entities(POSITION | VELOCITY, 3);
6196
6197        let cache_size_after_archetype = world.query_cache.len();
6198        assert_eq!(cache_size_after_archetype, 2);
6199
6200        let mut pos_count2 = 0;
6201        world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6202            pos_count2 += 1;
6203        });
6204        let mut vel_count2 = 0;
6205        world.for_each_mut(VELOCITY, 0, |_entity, _table, _idx| {
6206            vel_count2 += 1;
6207        });
6208        assert_eq!(pos_count2, 13);
6209        assert_eq!(vel_count2, 8);
6210
6211        world.spawn_entities(HEALTH, 2);
6212
6213        let cache_size_after_health = world.query_cache.len();
6214        assert_eq!(cache_size_after_health, 2);
6215
6216        let mut pos_count3 = 0;
6217        world.for_each_mut(POSITION, 0, |_entity, _table, _idx| {
6218            pos_count3 += 1;
6219        });
6220        let mut vel_count3 = 0;
6221        world.for_each_mut(VELOCITY, 0, |_entity, _table, _idx| {
6222            vel_count3 += 1;
6223        });
6224        let mut health_count = 0;
6225        world.for_each_mut(HEALTH, 0, |_entity, _table, _idx| {
6226            health_count += 1;
6227        });
6228        assert_eq!(pos_count3, 13);
6229        assert_eq!(vel_count3, 8);
6230        assert_eq!(health_count, 2);
6231
6232        let final_cache_size = world.query_cache.len();
6233        assert_eq!(final_cache_size, 3);
6234    }
6235
6236    #[test]
6237    fn test_entity_has_components_requires_all() {
6238        let mut world = World::default();
6239        let entity = world.spawn_entities(POSITION, 1)[0];
6240
6241        assert!(
6242            !world.entity_has_components(entity, POSITION | VELOCITY),
6243            "Should return false when entity only has POSITION but query asks for POSITION | VELOCITY"
6244        );
6245
6246        assert!(
6247            world.entity_has_components(entity, POSITION),
6248            "Should return true when entity has the single queried component"
6249        );
6250
6251        world.add_components(entity, VELOCITY);
6252        assert!(
6253            world.entity_has_components(entity, POSITION | VELOCITY),
6254            "Should return true when entity has all queried components"
6255        );
6256
6257        assert!(
6258            !world.entity_has_components(entity, POSITION | VELOCITY | HEALTH),
6259            "Should return false when entity is missing one of three queried components"
6260        );
6261    }
6262
6263    #[test]
6264    fn test_entity_has_generated_methods_consistency() {
6265        let mut world = World::default();
6266        let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
6267
6268        assert!(world.entity_has_position(entity));
6269        assert!(world.entity_has_velocity(entity));
6270        assert!(!world.entity_has_health(entity));
6271
6272        assert!(world.entity_has_components(entity, POSITION));
6273        assert!(world.entity_has_components(entity, VELOCITY));
6274        assert!(world.entity_has_components(entity, POSITION | VELOCITY));
6275        assert!(!world.entity_has_components(entity, HEALTH));
6276        assert!(!world.entity_has_components(entity, POSITION | HEALTH));
6277    }
6278
6279    #[test]
6280    fn test_despawn_preserves_change_vec_consistency() {
6281        let mut world = World::default();
6282        let entities = world.spawn_entities(POSITION, 5);
6283
6284        world.step();
6285
6286        world.get_position_mut(entities[0]).unwrap().x = 10.0;
6287        world.get_position_mut(entities[4]).unwrap().x = 40.0;
6288
6289        world.despawn_entities(&[entities[2]]);
6290
6291        for table in &world.tables {
6292            if table.mask & POSITION != 0 {
6293                let entity_count = table.entity_indices.len();
6294                assert_eq!(
6295                    table.position.len(),
6296                    entity_count,
6297                    "Position vec length should match entity count after despawn"
6298                );
6299                assert_eq!(
6300                    table.position_changed.len(),
6301                    entity_count,
6302                    "Position changed vec length should match entity count after despawn"
6303                );
6304            }
6305        }
6306    }
6307
6308    #[test]
6309    fn test_change_detection_after_despawn() {
6310        let mut world = World::default();
6311        let e1 = world.spawn_entities(POSITION, 1)[0];
6312        let e2 = world.spawn_entities(POSITION, 1)[0];
6313        let e3 = world.spawn_entities(POSITION, 1)[0];
6314
6315        world.set_position(e1, Position { x: 1.0, y: 0.0 });
6316        world.set_position(e2, Position { x: 2.0, y: 0.0 });
6317        world.set_position(e3, Position { x: 3.0, y: 0.0 });
6318
6319        world.step();
6320
6321        world.get_position_mut(e3).unwrap().x = 30.0;
6322
6323        world.despawn_entities(&[e2]);
6324
6325        let mut changed_entities = Vec::new();
6326        world.for_each_mut_changed(POSITION, 0, |entity, _table, _idx| {
6327            changed_entities.push(entity);
6328        });
6329
6330        assert!(
6331            changed_entities.contains(&e3),
6332            "e3 was modified and should be detected as changed"
6333        );
6334        assert!(
6335            !changed_entities.contains(&e1),
6336            "e1 was not modified and should not be detected as changed"
6337        );
6338    }
6339
6340    #[test]
6341    fn test_change_detection_only_checks_queried_components() {
6342        let mut world = World::default();
6343        let entity = world.spawn_entities(POSITION | VELOCITY, 1)[0];
6344
6345        world.step();
6346
6347        world.get_velocity_mut(entity).unwrap().x = 99.0;
6348
6349        let mut pos_changed_count = 0;
6350        world.for_each_mut_changed(POSITION, 0, |_entity, _table, _idx| {
6351            pos_changed_count += 1;
6352        });
6353        assert_eq!(
6354            pos_changed_count, 0,
6355            "Changing velocity should not trigger position change detection"
6356        );
6357
6358        let mut vel_changed_count = 0;
6359        world.for_each_mut_changed(VELOCITY, 0, |_entity, _table, _idx| {
6360            vel_changed_count += 1;
6361        });
6362        assert_eq!(
6363            vel_changed_count, 1,
6364            "Changing velocity should trigger velocity change detection"
6365        );
6366    }
6367
6368    #[test]
6369    fn test_change_detection_multi_component_query_only_relevant() {
6370        let mut world = World::default();
6371        let e1 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
6372        let e2 = world.spawn_entities(POSITION | VELOCITY | HEALTH, 1)[0];
6373
6374        world.step();
6375
6376        world.get_position_mut(e1).unwrap().x = 5.0;
6377        world.get_health_mut(e2).unwrap().value = 50.0;
6378
6379        let mut changed_entities = Vec::new();
6380        world.for_each_mut_changed(POSITION | VELOCITY, 0, |entity, _table, _idx| {
6381            changed_entities.push(entity);
6382        });
6383
6384        assert!(
6385            changed_entities.contains(&e1),
6386            "e1 had position changed, which is in the query"
6387        );
6388        assert!(
6389            !changed_entities.contains(&e2),
6390            "e2 only had health changed, which is NOT in the query"
6391        );
6392    }
6393
6394    #[test]
6395    fn test_entity_count() {
6396        let mut world = World::default();
6397        assert_eq!(world.entity_count(), 0);
6398
6399        world.spawn_entities(POSITION, 5);
6400        assert_eq!(world.entity_count(), 5);
6401
6402        world.spawn_entities(VELOCITY, 3);
6403        assert_eq!(world.entity_count(), 8);
6404
6405        let entities = world.spawn_entities(POSITION | VELOCITY, 2);
6406        assert_eq!(world.entity_count(), 10);
6407
6408        world.despawn_entities(&[entities[0]]);
6409        assert_eq!(world.entity_count(), 9);
6410    }
6411
6412    #[test]
6413    fn test_entity_count_matches_get_all_entities() {
6414        let mut world = World::default();
6415
6416        world.spawn_entities(POSITION, 10);
6417        world.spawn_entities(VELOCITY, 5);
6418        world.spawn_entities(POSITION | VELOCITY | HEALTH, 3);
6419
6420        assert_eq!(world.entity_count(), world.get_all_entities().len());
6421    }
6422
6423    #[test]
6424    fn test_entity_query_iter_size_hint() {
6425        let mut world = World::default();
6426
6427        world.spawn_entities(POSITION | VELOCITY, 5);
6428        world.spawn_entities(POSITION, 3);
6429        world.spawn_entities(VELOCITY | HEALTH, 2);
6430
6431        let iter = world.query_entities(POSITION);
6432        let (lower, upper) = iter.size_hint();
6433        assert_eq!(lower, 8);
6434        assert_eq!(upper, Some(8));
6435
6436        let count = world.query_entities(POSITION).count();
6437        assert_eq!(count, 8);
6438
6439        let iter = world.query_entities(POSITION | VELOCITY);
6440        let (lower, upper) = iter.size_hint();
6441        assert_eq!(lower, 5);
6442        assert_eq!(upper, Some(5));
6443    }
6444
6445    #[test]
6446    fn test_entity_query_iter_size_hint_decreases() {
6447        let mut world = World::default();
6448        world.spawn_entities(POSITION, 3);
6449
6450        let mut iter = world.query_entities(POSITION);
6451        assert_eq!(iter.size_hint(), (3, Some(3)));
6452
6453        iter.next();
6454        assert_eq!(iter.size_hint(), (2, Some(2)));
6455
6456        iter.next();
6457        assert_eq!(iter.size_hint(), (1, Some(1)));
6458
6459        iter.next();
6460        assert_eq!(iter.size_hint(), (0, Some(0)));
6461    }
6462
6463    #[test]
6464    fn test_component_query_iter_size_hint() {
6465        let mut world = World::default();
6466        world.spawn_entities(POSITION, 4);
6467        world.spawn_entities(POSITION | VELOCITY, 3);
6468
6469        let iter = world.query_position();
6470        let (lower, upper) = iter.size_hint();
6471        assert_eq!(lower, 7);
6472        assert_eq!(upper, Some(7));
6473
6474        let count = world.query_position().count();
6475        assert_eq!(count, 7);
6476    }
6477
6478    #[test]
6479    fn test_query_entities_collect_preallocates() {
6480        let mut world = World::default();
6481        world.spawn_entities(POSITION | VELOCITY, 100);
6482
6483        let entities: Vec<Entity> = world.query_entities(POSITION | VELOCITY).collect();
6484        assert_eq!(entities.len(), 100);
6485    }
6486
6487    #[test]
6488    fn test_despawn_multiple_same_table_change_vecs() {
6489        let mut world = World::default();
6490        let entities = world.spawn_entities(POSITION | VELOCITY, 6);
6491
6492        world.step();
6493
6494        world.get_position_mut(entities[0]).unwrap().x = 10.0;
6495        world.get_position_mut(entities[5]).unwrap().x = 50.0;
6496
6497        world.despawn_entities(&[entities[1], entities[3]]);
6498
6499        for table in &world.tables {
6500            if table.mask & POSITION != 0 {
6501                let entity_count = table.entity_indices.len();
6502                assert_eq!(table.position.len(), entity_count);
6503                assert_eq!(table.position_changed.len(), entity_count);
6504            }
6505            if table.mask & VELOCITY != 0 {
6506                let entity_count = table.entity_indices.len();
6507                assert_eq!(table.velocity.len(), entity_count);
6508                assert_eq!(table.velocity_changed.len(), entity_count);
6509            }
6510        }
6511
6512        let remaining = world.get_all_entities();
6513        assert_eq!(remaining.len(), 4);
6514        assert!(world.get_position(entities[0]).is_some());
6515        assert!(world.get_position(entities[5]).is_some());
6516    }
6517
6518    mod cfg_test {
6519        #[derive(Default, Debug, Clone)]
6520        pub struct BaseComponent {
6521            pub value: i32,
6522        }
6523
6524        #[derive(Default, Debug, Clone)]
6525        pub struct DebugComponent {
6526            pub debug_value: i32,
6527        }
6528
6529        crate::ecs! {
6530            CfgWorld {
6531                base: BaseComponent => BASE,
6532                #[cfg(debug_assertions)]
6533                debug_only: DebugComponent => DEBUG_ONLY,
6534            }
6535            CfgResources {
6536                counter: i32,
6537            }
6538        }
6539
6540        #[test]
6541        fn test_cfg_attribute_on_components() {
6542            let mut world = CfgWorld::default();
6543
6544            let entities = world.spawn_entities(BASE, 3);
6545            assert_eq!(entities.len(), 3);
6546
6547            world.set_base(entities[0], BaseComponent { value: 10 });
6548            assert_eq!(world.get_base(entities[0]).unwrap().value, 10);
6549
6550            world.resources.counter = 42;
6551            assert_eq!(world.resources.counter, 42);
6552
6553            let mut count = 0;
6554            world.query().with(BASE).iter(|_entity, _table, _idx| {
6555                count += 1;
6556            });
6557            assert_eq!(count, 3);
6558
6559            world.query_mut().with(BASE).iter(|_entity, table, idx| {
6560                table.base[idx].value += 1;
6561            });
6562            assert_eq!(world.get_base(entities[0]).unwrap().value, 11);
6563
6564            let mut without_count = 0;
6565            world
6566                .query()
6567                .with(BASE)
6568                .without(0)
6569                .iter(|_entity, _table, _idx| {
6570                    without_count += 1;
6571                });
6572            assert_eq!(without_count, 3);
6573
6574            let mut without_mut_count = 0;
6575            world
6576                .query_mut()
6577                .with(BASE)
6578                .without(0)
6579                .iter(|_entity, _table, _idx| {
6580                    without_mut_count += 1;
6581                });
6582            assert_eq!(without_mut_count, 3);
6583
6584            let mut iter_count = 0;
6585            world.iter_base(|_entity, _base| {
6586                iter_count += 1;
6587            });
6588            assert_eq!(iter_count, 3);
6589
6590            world.iter_base_mut(|_entity, base| {
6591                base.value *= 2;
6592            });
6593            assert_eq!(world.get_base(entities[0]).unwrap().value, 22);
6594
6595            #[cfg(debug_assertions)]
6596            {
6597                world.set_debug_only(entities[0], DebugComponent { debug_value: 42 });
6598                assert_eq!(world.get_debug_only(entities[0]).unwrap().debug_value, 42);
6599
6600                let debug_entities = world.spawn_entities(DEBUG_ONLY, 2);
6601                assert_eq!(debug_entities.len(), 2);
6602
6603                let mut debug_count = 0;
6604                world.iter_debug_only(|_entity, _debug| {
6605                    debug_count += 1;
6606                });
6607                assert_eq!(debug_count, 3);
6608
6609                world.iter_debug_only_mut(|_entity, debug| {
6610                    debug.debug_value += 1;
6611                });
6612                assert_eq!(world.get_debug_only(entities[0]).unwrap().debug_value, 43);
6613            }
6614        }
6615    }
6616
6617    mod multi_world_test {
6618        use super::*;
6619
6620        #[derive(Default, Debug, Clone, PartialEq)]
6621        pub struct Position {
6622            pub x: f32,
6623            pub y: f32,
6624        }
6625
6626        #[derive(Default, Debug, Clone, PartialEq)]
6627        pub struct Velocity {
6628            pub x: f32,
6629            pub y: f32,
6630        }
6631
6632        #[derive(Default, Debug, Clone, PartialEq)]
6633        pub struct Sprite {
6634            pub id: u32,
6635        }
6636
6637        #[derive(Default, Debug, Clone, PartialEq)]
6638        pub struct Color {
6639            pub r: f32,
6640            pub g: f32,
6641            pub b: f32,
6642        }
6643
6644        #[derive(Debug, Clone)]
6645        #[allow(dead_code)]
6646        pub struct CollisionEvent {
6647            pub entity_a: Entity,
6648            pub entity_b: Entity,
6649        }
6650
6651        crate::ecs! {
6652            GameEcs {
6653                CoreWorld {
6654                    position: Position => MW_POSITION,
6655                    velocity: Velocity => MW_VELOCITY,
6656                }
6657                RenderWorld {
6658                    sprite: Sprite => MW_SPRITE,
6659                    color: Color => MW_COLOR,
6660                }
6661            }
6662            Tags {
6663                player => MW_PLAYER,
6664            }
6665            Events {
6666                collision: CollisionEvent,
6667            }
6668            GameResources {
6669                delta_time: f32,
6670            }
6671        }
6672
6673        #[test]
6674        fn test_multi_world_spawn() {
6675            let mut ecs = GameEcs::default();
6676            let entity = ecs.spawn();
6677            assert_eq!(entity.id, 0);
6678            assert_eq!(entity.generation, 0);
6679        }
6680
6681        #[test]
6682        fn test_multi_world_per_world_components() {
6683            let mut ecs = GameEcs::default();
6684            let entity = ecs.spawn();
6685
6686            ecs.core_world
6687                .add_components(entity, MW_POSITION | MW_VELOCITY);
6688            ecs.core_world
6689                .set_position(entity, Position { x: 1.0, y: 2.0 });
6690
6691            assert_eq!(ecs.core_world.get_position(entity).unwrap().x, 1.0);
6692            assert_eq!(ecs.core_world.get_position(entity).unwrap().y, 2.0);
6693            assert!(ecs.core_world.get_velocity(entity).is_some());
6694        }
6695
6696        #[test]
6697        fn test_multi_world_cross_world_access() {
6698            let mut ecs = GameEcs::default();
6699            let entity = ecs.spawn();
6700
6701            ecs.core_world
6702                .set_position(entity, Position { x: 5.0, y: 10.0 });
6703            ecs.render_world.set_sprite(entity, Sprite { id: 42 });
6704
6705            assert_eq!(ecs.core_world.get_position(entity).unwrap().x, 5.0);
6706            assert_eq!(ecs.render_world.get_sprite(entity).unwrap().id, 42);
6707        }
6708
6709        #[test]
6710        fn test_multi_world_split_borrow() {
6711            let mut ecs = GameEcs::default();
6712            let entity = ecs.spawn();
6713
6714            ecs.core_world
6715                .set_position(entity, Position { x: 1.0, y: 2.0 });
6716            ecs.render_world.set_sprite(entity, Sprite { id: 1 });
6717
6718            let GameEcs {
6719                core_world,
6720                render_world,
6721                ..
6722            } = &mut ecs;
6723            core_world.for_each_mut(MW_POSITION, 0, |entity, table, idx| {
6724                if let Some(sprite) = render_world.get_sprite(entity) {
6725                    table.position[idx].x += sprite.id as f32;
6726                }
6727            });
6728
6729            assert_eq!(ecs.core_world.get_position(entity).unwrap().x, 2.0);
6730        }
6731
6732        #[test]
6733        fn test_multi_world_despawn_cascades() {
6734            let mut ecs = GameEcs::default();
6735            let entity = ecs.spawn();
6736
6737            ecs.core_world
6738                .set_position(entity, Position { x: 1.0, y: 2.0 });
6739            ecs.render_world.set_sprite(entity, Sprite { id: 1 });
6740            ecs.add_player(entity);
6741
6742            assert!(ecs.core_world.get_position(entity).is_some());
6743            assert!(ecs.render_world.get_sprite(entity).is_some());
6744            assert!(ecs.has_player(entity));
6745
6746            ecs.despawn(entity);
6747
6748            assert!(ecs.core_world.get_position(entity).is_none());
6749            assert!(ecs.render_world.get_sprite(entity).is_none());
6750            assert!(!ecs.has_player(entity));
6751        }
6752
6753        #[test]
6754        fn test_multi_world_entity_in_one_world_only() {
6755            let mut ecs = GameEcs::default();
6756            let entity = ecs.spawn();
6757
6758            ecs.core_world
6759                .set_position(entity, Position { x: 1.0, y: 2.0 });
6760
6761            assert!(ecs.core_world.get_position(entity).is_some());
6762            assert!(ecs.render_world.get_sprite(entity).is_none());
6763            assert!(ecs.render_world.get_color(entity).is_none());
6764        }
6765
6766        #[test]
6767        fn test_multi_world_entity_in_no_world() {
6768            let mut ecs = GameEcs::default();
6769            let entity = ecs.spawn();
6770
6771            assert!(ecs.core_world.get_position(entity).is_none());
6772            assert!(ecs.render_world.get_sprite(entity).is_none());
6773        }
6774
6775        #[test]
6776        fn test_multi_world_entity_builder() {
6777            let mut ecs = GameEcs::default();
6778            let entities = EntityBuilder::new()
6779                .with_position(Position { x: 1.0, y: 2.0 })
6780                .with_sprite(Sprite { id: 42 })
6781                .spawn(&mut ecs, 2);
6782
6783            assert_eq!(entities.len(), 2);
6784            assert_eq!(ecs.core_world.get_position(entities[0]).unwrap().x, 1.0);
6785            assert_eq!(ecs.core_world.get_position(entities[1]).unwrap().x, 1.0);
6786            assert_eq!(ecs.render_world.get_sprite(entities[0]).unwrap().id, 42);
6787            assert_eq!(ecs.render_world.get_sprite(entities[1]).unwrap().id, 42);
6788        }
6789
6790        #[test]
6791        fn test_multi_world_tags() {
6792            let mut ecs = GameEcs::default();
6793            let entity = ecs.spawn();
6794
6795            ecs.add_player(entity);
6796            assert!(ecs.has_player(entity));
6797
6798            let players: Vec<Entity> = ecs.query_player().collect();
6799            assert_eq!(players.len(), 1);
6800
6801            ecs.remove_player(entity);
6802            assert!(!ecs.has_player(entity));
6803        }
6804
6805        #[test]
6806        fn test_multi_world_tag_split_borrow() {
6807            let mut ecs = GameEcs::default();
6808            let e1 = ecs.spawn();
6809            let e2 = ecs.spawn();
6810
6811            ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
6812            ecs.core_world.set_position(e2, Position { x: 2.0, y: 0.0 });
6813            ecs.add_player(e1);
6814
6815            let GameEcs {
6816                core_world, player, ..
6817            } = &mut ecs;
6818            let mut player_positions = Vec::new();
6819            core_world.for_each_mut(MW_POSITION, 0, |entity, table, idx| {
6820                if player.contains(&entity) {
6821                    player_positions.push(table.position[idx].clone());
6822                }
6823            });
6824
6825            assert_eq!(player_positions.len(), 1);
6826            assert_eq!(player_positions[0].x, 1.0);
6827        }
6828
6829        #[test]
6830        fn test_multi_world_events() {
6831            let mut ecs = GameEcs::default();
6832            let e1 = ecs.spawn();
6833            let e2 = ecs.spawn();
6834
6835            ecs.send_collision(CollisionEvent {
6836                entity_a: e1,
6837                entity_b: e2,
6838            });
6839
6840            let events: Vec<_> = ecs.collect_collision();
6841            assert_eq!(events.len(), 1);
6842            assert_eq!(events[0].entity_a, e1);
6843        }
6844
6845        #[test]
6846        fn test_multi_world_resources() {
6847            let mut ecs = GameEcs::default();
6848            ecs.resources.delta_time = 0.016;
6849            assert_eq!(ecs.resources.delta_time, 0.016);
6850        }
6851
6852        #[test]
6853        fn test_multi_world_schedule() {
6854            let mut ecs = GameEcs::default();
6855            let entity = ecs.spawn();
6856            ecs.core_world
6857                .set_position(entity, Position { x: 0.0, y: 0.0 });
6858            ecs.core_world
6859                .set_velocity(entity, Velocity { x: 1.0, y: 2.0 });
6860            ecs.resources.delta_time = 0.1;
6861
6862            let mut schedule: Schedule<GameEcs> = Schedule::new();
6863            schedule.push("physics", |ecs: &mut GameEcs| {
6864                let dt = ecs.resources.delta_time;
6865                ecs.core_world
6866                    .for_each_mut(MW_POSITION | MW_VELOCITY, 0, |_entity, table, idx| {
6867                        table.position[idx].x += table.velocity[idx].x * dt;
6868                        table.position[idx].y += table.velocity[idx].y * dt;
6869                    });
6870            });
6871
6872            schedule.run(&mut ecs);
6873
6874            let pos = ecs.core_world.get_position(entity).unwrap();
6875            assert!((pos.x - 0.1).abs() < f32::EPSILON);
6876            assert!((pos.y - 0.2).abs() < f32::EPSILON);
6877        }
6878
6879        #[test]
6880        fn test_multi_world_mask_independence() {
6881            assert_eq!(MW_POSITION, 1 << 0);
6882            assert_eq!(MW_VELOCITY, 1 << 1);
6883            assert_eq!(MW_SPRITE, 1 << 0);
6884            assert_eq!(MW_COLOR, 1 << 1);
6885        }
6886
6887        #[test]
6888        fn test_multi_world_command_buffer() {
6889            let mut ecs = GameEcs::default();
6890            let entity = ecs.spawn();
6891
6892            ecs.core_world
6893                .set_position(entity, Position { x: 0.0, y: 0.0 });
6894
6895            ecs.queue_set_position(entity, Position { x: 10.0, y: 20.0 });
6896            ecs.queue_add_player(entity);
6897
6898            assert_eq!(ecs.core_world.get_position(entity).unwrap().x, 0.0);
6899            assert!(!ecs.has_player(entity));
6900
6901            ecs.apply_commands();
6902
6903            assert_eq!(ecs.core_world.get_position(entity).unwrap().x, 10.0);
6904            assert!(ecs.has_player(entity));
6905        }
6906
6907        #[test]
6908        fn test_multi_world_step() {
6909            let mut ecs = GameEcs::default();
6910
6911            assert_eq!(ecs.core_world.current_tick(), 0);
6912            assert_eq!(ecs.render_world.current_tick(), 0);
6913
6914            ecs.step();
6915
6916            assert_eq!(ecs.core_world.current_tick(), 1);
6917            assert_eq!(ecs.render_world.current_tick(), 1);
6918        }
6919
6920        #[test]
6921        fn test_multi_world_for_each_within_world() {
6922            let mut ecs = GameEcs::default();
6923            let e1 = ecs.spawn();
6924            let e2 = ecs.spawn();
6925
6926            ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
6927            ecs.core_world.set_position(e2, Position { x: 2.0, y: 0.0 });
6928            ecs.core_world
6929                .set_velocity(e2, Velocity { x: 10.0, y: 0.0 });
6930
6931            let mut count = 0;
6932            ecs.core_world
6933                .for_each(MW_POSITION, 0, |_entity, _table, _idx| {
6934                    count += 1;
6935                });
6936            assert_eq!(count, 2);
6937
6938            count = 0;
6939            ecs.core_world
6940                .for_each(MW_POSITION | MW_VELOCITY, 0, |_entity, _table, _idx| {
6941                    count += 1;
6942                });
6943            assert_eq!(count, 1);
6944        }
6945
6946        #[test]
6947        fn test_multi_world_query_builder() {
6948            let mut ecs = GameEcs::default();
6949            let e1 = ecs.spawn();
6950            let e2 = ecs.spawn();
6951
6952            ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
6953            ecs.core_world.set_position(e2, Position { x: 2.0, y: 0.0 });
6954            ecs.core_world
6955                .set_velocity(e2, Velocity { x: 10.0, y: 0.0 });
6956
6957            let mut count = 0;
6958            ecs.core_world
6959                .query()
6960                .with(MW_POSITION)
6961                .without(MW_VELOCITY)
6962                .iter(|_entity, _table, _idx| {
6963                    count += 1;
6964                });
6965            assert_eq!(count, 1);
6966        }
6967
6968        #[test]
6969        fn test_multi_world_generational_reuse() {
6970            let mut ecs = GameEcs::default();
6971
6972            let e1 = ecs.spawn();
6973            ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
6974
6975            ecs.despawn(e1);
6976            assert!(ecs.core_world.get_position(e1).is_none());
6977
6978            let e2 = ecs.spawn();
6979            assert_eq!(e2.id, e1.id);
6980            assert_eq!(e2.generation, e1.generation + 1);
6981
6982            assert!(ecs.core_world.get_position(e1).is_none());
6983        }
6984
6985        #[test]
6986        fn test_multi_world_ghost_entity_guard() {
6987            let mut ecs = GameEcs::default();
6988            let e1 = ecs.spawn();
6989            ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
6990            ecs.despawn(e1);
6991
6992            let e2 = ecs.spawn();
6993            assert_eq!(e2.id, e1.id);
6994
6995            ecs.core_world.set_position(e2, Position { x: 5.0, y: 5.0 });
6996            assert_eq!(ecs.core_world.get_position(e2).unwrap().x, 5.0);
6997
6998            assert!(ecs.core_world.get_position(e1).is_none());
6999        }
7000
7001        #[test]
7002        fn test_multi_world_for_each_with_tags() {
7003            let mut ecs = GameEcs::default();
7004            let e1 = ecs.spawn();
7005            let e2 = ecs.spawn();
7006            let e3 = ecs.spawn();
7007
7008            ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
7009            ecs.core_world.set_position(e2, Position { x: 2.0, y: 0.0 });
7010            ecs.core_world.set_position(e3, Position { x: 3.0, y: 0.0 });
7011
7012            ecs.add_player(e1);
7013            ecs.add_player(e3);
7014
7015            let GameEcs {
7016                core_world, player, ..
7017            } = &ecs;
7018
7019            let mut count = 0;
7020            core_world.for_each_with_tags(
7021                MW_POSITION,
7022                0,
7023                &[player],
7024                &[],
7025                |_entity, _table, _idx| {
7026                    count += 1;
7027                },
7028            );
7029            assert_eq!(count, 2);
7030
7031            count = 0;
7032            core_world.for_each_with_tags(
7033                MW_POSITION,
7034                0,
7035                &[],
7036                &[player],
7037                |_entity, _table, _idx| {
7038                    count += 1;
7039                },
7040            );
7041            assert_eq!(count, 1);
7042        }
7043
7044        #[test]
7045        fn test_multi_world_for_each_mut_with_tags() {
7046            let mut ecs = GameEcs::default();
7047            let e1 = ecs.spawn();
7048            let e2 = ecs.spawn();
7049
7050            ecs.core_world.set_position(e1, Position { x: 1.0, y: 0.0 });
7051            ecs.core_world.set_position(e2, Position { x: 2.0, y: 0.0 });
7052
7053            ecs.add_player(e1);
7054
7055            let player_set = ecs.player.clone();
7056            ecs.core_world.for_each_mut_with_tags(
7057                MW_POSITION,
7058                0,
7059                &[&player_set],
7060                &[],
7061                |_entity, table, idx| {
7062                    table.position[idx].x += 100.0;
7063                },
7064            );
7065
7066            assert_eq!(ecs.core_world.get_position(e1).unwrap().x, 101.0);
7067            assert_eq!(ecs.core_world.get_position(e2).unwrap().x, 2.0);
7068        }
7069    }
7070}