#![warn(clippy::undocumented_unsafe_blocks)] #![doc = include_str!("../README.md")] #[cfg(target_pointer_width = "16")] compile_error!("bevy_ecs cannot safely compile for a 16-bit platform."); pub mod archetype; pub mod bundle; pub mod change_detection; pub mod component; pub mod entity; pub mod event; pub mod query; #[cfg(feature = "bevy_reflect")] pub mod reflect; pub mod removal_detection; pub mod schedule; pub mod storage; pub mod system; pub mod world; use std::any::TypeId; pub use bevy_ptr as ptr; /// Most commonly used re-exported types. pub mod prelude { #[doc(hidden)] #[cfg(feature = "bevy_reflect")] pub use crate::reflect::{ReflectComponent, ReflectResource}; #[doc(hidden)] pub use crate::{ bundle::Bundle, change_detection::{DetectChanges, DetectChangesMut, Mut, Ref}, component::Component, entity::Entity, event::{Event, EventReader, EventWriter, Events}, query::{Added, AnyOf, Changed, Or, QueryState, With, Without}, removal_detection::RemovedComponents, schedule::{ apply_state_transition, apply_system_buffers, common_conditions::*, Condition, IntoSystemConfig, IntoSystemConfigs, IntoSystemSet, IntoSystemSetConfig, IntoSystemSetConfigs, NextState, OnEnter, OnExit, OnUpdate, Schedule, Schedules, State, States, SystemSet, }, system::{ adapter as system_adapter, adapter::{dbg, error, ignore, info, unwrap, warn}, Commands, Deferred, In, IntoPipeSystem, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands, ParamSet, Query, Res, ResMut, Resource, System, SystemParamFunction, }, world::{FromWorld, World}, }; } pub use bevy_utils::all_tuples; /// A specialized hashmap type with Key of `TypeId` type TypeIdMap = rustc_hash::FxHashMap; #[cfg(test)] mod tests { use crate as bevy_ecs; use crate::prelude::Or; use crate::{ bundle::Bundle, change_detection::Ref, component::{Component, ComponentId}, entity::Entity, query::{Added, Changed, FilteredAccess, ReadOnlyWorldQuery, With, Without}, system::Resource, world::{Mut, World}, }; use bevy_tasks::{ComputeTaskPool, TaskPool}; use std::{ any::TypeId, marker::PhantomData, sync::{ atomic::{AtomicUsize, Ordering}, Arc, Mutex, }, }; #[derive(Component, Resource, Debug, PartialEq, Eq, Clone, Copy)] struct A(usize); #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)] struct B(usize); #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)] struct C; #[derive(Default)] struct NonSendA(usize, PhantomData<*mut ()>); #[derive(Component, Clone, Debug)] struct DropCk(Arc); impl DropCk { fn new_pair() -> (Self, Arc) { let atomic = Arc::new(AtomicUsize::new(0)); (DropCk(atomic.clone()), atomic) } } impl Drop for DropCk { fn drop(&mut self) { self.0.as_ref().fetch_add(1, Ordering::Relaxed); } } #[derive(Component, Clone, Debug)] #[component(storage = "SparseSet")] struct DropCkSparse(DropCk); #[derive(Component, Copy, Clone, PartialEq, Eq, Debug)] #[component(storage = "Table")] struct TableStored(&'static str); #[derive(Component, Copy, Clone, PartialEq, Eq, Debug)] #[component(storage = "SparseSet")] struct SparseStored(u32); #[test] fn random_access() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), SparseStored(123))).id(); let f = world .spawn((TableStored("def"), SparseStored(456), A(1))) .id(); assert_eq!(world.get::(e).unwrap().0, "abc"); assert_eq!(world.get::(e).unwrap().0, 123); assert_eq!(world.get::(f).unwrap().0, "def"); assert_eq!(world.get::(f).unwrap().0, 456); // test archetype get_mut() world.get_mut::(e).unwrap().0 = "xyz"; assert_eq!(world.get::(e).unwrap().0, "xyz"); // test sparse set get_mut() world.get_mut::(f).unwrap().0 = 42; assert_eq!(world.get::(f).unwrap().0, 42); } #[test] fn bundle_derive() { let mut world = World::new(); #[derive(Bundle, PartialEq, Debug)] struct FooBundle { x: TableStored, y: SparseStored, } let mut ids = Vec::new(); ::component_ids( &mut world.components, &mut world.storages, &mut |id| { ids.push(id); }, ); assert_eq!( ids, &[ world.init_component::(), world.init_component::(), ] ); let e1 = world .spawn(FooBundle { x: TableStored("abc"), y: SparseStored(123), }) .id(); let e2 = world .spawn((TableStored("def"), SparseStored(456), A(1))) .id(); assert_eq!(world.get::(e1).unwrap().0, "abc"); assert_eq!(world.get::(e1).unwrap().0, 123); assert_eq!(world.get::(e2).unwrap().0, "def"); assert_eq!(world.get::(e2).unwrap().0, 456); // test archetype get_mut() world.get_mut::(e1).unwrap().0 = "xyz"; assert_eq!(world.get::(e1).unwrap().0, "xyz"); // test sparse set get_mut() world.get_mut::(e2).unwrap().0 = 42; assert_eq!(world.get::(e2).unwrap().0, 42); assert_eq!( world.entity_mut(e1).take::().unwrap(), FooBundle { x: TableStored("xyz"), y: SparseStored(123), } ); #[derive(Bundle, PartialEq, Debug)] struct NestedBundle { a: A, foo: FooBundle, b: B, } let mut ids = Vec::new(); ::component_ids( &mut world.components, &mut world.storages, &mut |id| { ids.push(id); }, ); assert_eq!( ids, &[ world.init_component::(), world.init_component::(), world.init_component::(), world.init_component::(), ] ); let e3 = world .spawn(NestedBundle { a: A(1), foo: FooBundle { x: TableStored("ghi"), y: SparseStored(789), }, b: B(2), }) .id(); assert_eq!(world.get::(e3).unwrap().0, "ghi"); assert_eq!(world.get::(e3).unwrap().0, 789); assert_eq!(world.get::(e3).unwrap().0, 1); assert_eq!(world.get::(e3).unwrap().0, 2); assert_eq!( world.entity_mut(e3).take::().unwrap(), NestedBundle { a: A(1), foo: FooBundle { x: TableStored("ghi"), y: SparseStored(789), }, b: B(2), } ); #[derive(Default, Component, PartialEq, Debug)] struct Ignored; #[derive(Bundle, PartialEq, Debug)] struct BundleWithIgnored { c: C, #[bundle(ignore)] ignored: Ignored, } let mut ids = Vec::new(); ::component_ids( &mut world.components, &mut world.storages, &mut |id| { ids.push(id); }, ); assert_eq!(ids, &[world.init_component::(),]); let e4 = world .spawn(BundleWithIgnored { c: C, ignored: Ignored, }) .id(); assert_eq!(world.get::(e4).unwrap(), &C); assert_eq!(world.get::(e4), None); assert_eq!( world.entity_mut(e4).take::().unwrap(), BundleWithIgnored { c: C, ignored: Ignored, } ); } #[test] fn despawn_table_storage() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); let f = world.spawn((TableStored("def"), A(456))).id(); assert_eq!(world.entities.len(), 2); assert!(world.despawn(e)); assert_eq!(world.entities.len(), 1); assert!(world.get::(e).is_none()); assert!(world.get::(e).is_none()); assert_eq!(world.get::(f).unwrap().0, "def"); assert_eq!(world.get::(f).unwrap().0, 456); } #[test] fn despawn_mixed_storage() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), SparseStored(123))).id(); let f = world.spawn((TableStored("def"), SparseStored(456))).id(); assert_eq!(world.entities.len(), 2); assert!(world.despawn(e)); assert_eq!(world.entities.len(), 1); assert!(world.get::(e).is_none()); assert!(world.get::(e).is_none()); assert_eq!(world.get::(f).unwrap().0, "def"); assert_eq!(world.get::(f).unwrap().0, 456); } #[test] fn query_all() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); let f = world.spawn((TableStored("def"), A(456))).id(); let ents = world .query::<(Entity, &A, &TableStored)>() .iter(&world) .map(|(e, &i, &s)| (e, i, s)) .collect::>(); assert_eq!( ents, &[ (e, A(123), TableStored("abc")), (f, A(456), TableStored("def")) ] ); } #[test] fn query_all_for_each() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); let f = world.spawn((TableStored("def"), A(456))).id(); let mut results = Vec::new(); world .query::<(Entity, &A, &TableStored)>() .for_each(&world, |(e, &i, &s)| results.push((e, i, s))); assert_eq!( results, &[ (e, A(123), TableStored("abc")), (f, A(456), TableStored("def")) ] ); } #[test] fn query_single_component() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); let f = world.spawn((TableStored("def"), A(456), B(1))).id(); let ents = world .query::<(Entity, &A)>() .iter(&world) .map(|(e, &i)| (e, i)) .collect::>(); assert_eq!(ents, &[(e, A(123)), (f, A(456))]); } #[test] fn stateful_query_handles_new_archetype() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); let mut query = world.query::<(Entity, &A)>(); let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::>(); assert_eq!(ents, &[(e, A(123))]); let f = world.spawn((TableStored("def"), A(456), B(1))).id(); let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::>(); assert_eq!(ents, &[(e, A(123)), (f, A(456))]); } #[test] fn query_single_component_for_each() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); let f = world.spawn((TableStored("def"), A(456), B(1))).id(); let mut results = Vec::new(); world .query::<(Entity, &A)>() .for_each(&world, |(e, &i)| results.push((e, i))); assert_eq!(results, &[(e, A(123)), (f, A(456))]); } #[test] fn par_for_each_dense() { ComputeTaskPool::init(TaskPool::default); let mut world = World::new(); let e1 = world.spawn(A(1)).id(); let e2 = world.spawn(A(2)).id(); let e3 = world.spawn(A(3)).id(); let e4 = world.spawn((A(4), B(1))).id(); let e5 = world.spawn((A(5), B(1))).id(); let results = Arc::new(Mutex::new(Vec::new())); world .query::<(Entity, &A)>() .par_iter(&world) .for_each(|(e, &A(i))| { results.lock().unwrap().push((e, i)); }); results.lock().unwrap().sort(); assert_eq!( &*results.lock().unwrap(), &[(e1, 1), (e2, 2), (e3, 3), (e4, 4), (e5, 5)] ); } #[test] fn par_for_each_sparse() { ComputeTaskPool::init(TaskPool::default); let mut world = World::new(); let e1 = world.spawn(SparseStored(1)).id(); let e2 = world.spawn(SparseStored(2)).id(); let e3 = world.spawn(SparseStored(3)).id(); let e4 = world.spawn((SparseStored(4), A(1))).id(); let e5 = world.spawn((SparseStored(5), A(1))).id(); let results = Arc::new(Mutex::new(Vec::new())); world .query::<(Entity, &SparseStored)>() .par_iter(&world) .for_each(|(e, &SparseStored(i))| results.lock().unwrap().push((e, i))); results.lock().unwrap().sort(); assert_eq!( &*results.lock().unwrap(), &[(e1, 1), (e2, 2), (e3, 3), (e4, 4), (e5, 5)] ); } #[test] fn query_missing_component() { let mut world = World::new(); world.spawn((TableStored("abc"), A(123))); world.spawn((TableStored("def"), A(456))); assert!(world.query::<(&B, &A)>().iter(&world).next().is_none()); } #[test] fn query_sparse_component() { let mut world = World::new(); world.spawn((TableStored("abc"), A(123))); let f = world.spawn((TableStored("def"), A(456), B(1))).id(); let ents = world .query::<(Entity, &B)>() .iter(&world) .map(|(e, &b)| (e, b)) .collect::>(); assert_eq!(ents, &[(f, B(1))]); } #[test] fn query_filter_with() { let mut world = World::new(); world.spawn((A(123), B(1))); world.spawn(A(456)); let result = world .query_filtered::<&A, With>() .iter(&world) .cloned() .collect::>(); assert_eq!(result, vec![A(123)]); } #[test] fn query_filter_with_for_each() { let mut world = World::new(); world.spawn((A(123), B(1))); world.spawn(A(456)); let mut results = Vec::new(); world .query_filtered::<&A, With>() .for_each(&world, |i| results.push(*i)); assert_eq!(results, vec![A(123)]); } #[test] fn query_filter_with_sparse() { let mut world = World::new(); world.spawn((A(123), SparseStored(321))); world.spawn(A(456)); let result = world .query_filtered::<&A, With>() .iter(&world) .cloned() .collect::>(); assert_eq!(result, vec![A(123)]); } #[test] fn query_filter_with_sparse_for_each() { let mut world = World::new(); world.spawn((A(123), SparseStored(321))); world.spawn(A(456)); let mut results = Vec::new(); world .query_filtered::<&A, With>() .for_each(&world, |i| results.push(*i)); assert_eq!(results, vec![A(123)]); } #[test] fn query_filter_without() { let mut world = World::new(); world.spawn((A(123), B(321))); world.spawn(A(456)); let result = world .query_filtered::<&A, Without>() .iter(&world) .cloned() .collect::>(); assert_eq!(result, vec![A(456)]); } #[test] fn query_optional_component_table() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); let f = world.spawn((TableStored("def"), A(456), B(1))).id(); // this should be skipped world.spawn(TableStored("abc")); let ents = world .query::<(Entity, Option<&B>, &A)>() .iter(&world) .map(|(e, b, &i)| (e, b.copied(), i)) .collect::>(); assert_eq!(ents, &[(e, None, A(123)), (f, Some(B(1)), A(456))]); } #[test] fn query_optional_component_sparse() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); let f = world .spawn((TableStored("def"), A(456), SparseStored(1))) .id(); // this should be skipped // world.spawn(SparseStored(1)); let ents = world .query::<(Entity, Option<&SparseStored>, &A)>() .iter(&world) .map(|(e, b, &i)| (e, b.copied(), i)) .collect::>(); assert_eq!( ents, &[(e, None, A(123)), (f, Some(SparseStored(1)), A(456))] ); } #[test] fn query_optional_component_sparse_no_match() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); let f = world.spawn((TableStored("def"), A(456))).id(); // // this should be skipped world.spawn(TableStored("abc")); let ents = world .query::<(Entity, Option<&SparseStored>, &A)>() .iter(&world) .map(|(e, b, &i)| (e, b.copied(), i)) .collect::>(); assert_eq!(ents, &[(e, None, A(123)), (f, None, A(456))]); } #[test] fn add_remove_components() { let mut world = World::new(); let e1 = world.spawn((A(1), B(3), TableStored("abc"))).id(); let e2 = world.spawn((A(2), B(4), TableStored("xyz"))).id(); assert_eq!( world .query::<(Entity, &A, &B)>() .iter(&world) .map(|(e, &i, &b)| (e, i, b)) .collect::>(), &[(e1, A(1), B(3)), (e2, A(2), B(4))] ); assert_eq!(world.entity_mut(e1).take::(), Some(A(1))); assert_eq!( world .query::<(Entity, &A, &B)>() .iter(&world) .map(|(e, &i, &b)| (e, i, b)) .collect::>(), &[(e2, A(2), B(4))] ); assert_eq!( world .query::<(Entity, &B, &TableStored)>() .iter(&world) .map(|(e, &B(b), &TableStored(s))| (e, b, s)) .collect::>(), &[(e2, 4, "xyz"), (e1, 3, "abc")] ); world.entity_mut(e1).insert(A(43)); assert_eq!( world .query::<(Entity, &A, &B)>() .iter(&world) .map(|(e, &i, &b)| (e, i, b)) .collect::>(), &[(e2, A(2), B(4)), (e1, A(43), B(3))] ); world.entity_mut(e1).insert(C); assert_eq!( world .query::<(Entity, &C)>() .iter(&world) .map(|(e, &f)| (e, f)) .collect::>(), &[(e1, C)] ); } #[test] fn table_add_remove_many() { let mut world = World::default(); #[cfg(miri)] let (mut entities, to) = { let to = 10; (Vec::with_capacity(to), to) }; #[cfg(not(miri))] let (mut entities, to) = { let to = 10_000; (Vec::with_capacity(to), to) }; for _ in 0..to { entities.push(world.spawn(B(0)).id()); } for (i, entity) in entities.iter().cloned().enumerate() { world.entity_mut(entity).insert(A(i)); } for (i, entity) in entities.iter().cloned().enumerate() { assert_eq!(world.entity_mut(entity).take::(), Some(A(i))); } } #[test] fn sparse_set_add_remove_many() { let mut world = World::default(); let mut entities = Vec::with_capacity(1000); for _ in 0..4 { entities.push(world.spawn(A(2)).id()); } for (i, entity) in entities.iter().cloned().enumerate() { world.entity_mut(entity).insert(SparseStored(i as u32)); } for (i, entity) in entities.iter().cloned().enumerate() { assert_eq!( world.entity_mut(entity).take::(), Some(SparseStored(i as u32)) ); } } #[test] fn remove_missing() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); assert!(world.entity_mut(e).take::().is_none()); } #[test] fn spawn_batch() { let mut world = World::new(); world.spawn_batch((0..100).map(|x| (A(x), TableStored("abc")))); let values = world .query::<&A>() .iter(&world) .map(|v| v.0) .collect::>(); let expected = (0..100).collect::>(); assert_eq!(values, expected); } #[test] fn query_get() { let mut world = World::new(); let a = world.spawn((TableStored("abc"), A(123))).id(); let b = world.spawn((TableStored("def"), A(456))).id(); let c = world.spawn((TableStored("ghi"), A(789), B(1))).id(); let mut i32_query = world.query::<&A>(); assert_eq!(i32_query.get(&world, a).unwrap().0, 123); assert_eq!(i32_query.get(&world, b).unwrap().0, 456); let mut i32_bool_query = world.query::<(&A, &B)>(); assert!(i32_bool_query.get(&world, a).is_err()); assert_eq!(i32_bool_query.get(&world, c).unwrap(), (&A(789), &B(1))); assert!(world.despawn(a)); assert!(i32_query.get(&world, a).is_err()); } #[test] fn query_get_works_across_sparse_removal() { // Regression test for: https://github.com/bevyengine/bevy/issues/6623 let mut world = World::new(); let a = world.spawn((TableStored("abc"), SparseStored(123))).id(); let b = world.spawn((TableStored("def"), SparseStored(456))).id(); let c = world .spawn((TableStored("ghi"), SparseStored(789), B(1))) .id(); let mut query = world.query::<&TableStored>(); assert_eq!(query.get(&world, a).unwrap(), &TableStored("abc")); assert_eq!(query.get(&world, b).unwrap(), &TableStored("def")); assert_eq!(query.get(&world, c).unwrap(), &TableStored("ghi")); world.entity_mut(b).remove::(); world.entity_mut(c).remove::(); assert_eq!(query.get(&world, a).unwrap(), &TableStored("abc")); assert_eq!(query.get(&world, b).unwrap(), &TableStored("def")); assert_eq!(query.get(&world, c).unwrap(), &TableStored("ghi")); } #[test] fn remove_tracking() { let mut world = World::new(); let a = world.spawn((SparseStored(0), A(123))).id(); let b = world.spawn((SparseStored(1), A(123))).id(); world.entity_mut(a).despawn(); assert_eq!( world.removed::().collect::>(), &[a], "despawning results in 'removed component' state for table components" ); assert_eq!( world.removed::().collect::>(), &[a], "despawning results in 'removed component' state for sparse set components" ); world.entity_mut(b).insert(B(1)); assert_eq!( world.removed::().collect::>(), &[a], "archetype moves does not result in 'removed component' state" ); world.entity_mut(b).remove::(); assert_eq!( world.removed::().collect::>(), &[a, b], "removing a component results in a 'removed component' state" ); world.clear_trackers(); assert_eq!( world.removed::().collect::>(), &[], "clearing trackers clears removals" ); assert_eq!( world.removed::().collect::>(), &[], "clearing trackers clears removals" ); assert_eq!( world.removed::().collect::>(), &[], "clearing trackers clears removals" ); // TODO: uncomment when world.clear() is implemented // let c = world.spawn(("abc", 123)).id(); // let d = world.spawn(("abc", 123)).id(); // world.clear(); // assert_eq!( // world.removed::(), // &[c, d], // "world clears result in 'removed component' states" // ); // assert_eq!( // world.removed::<&'static str>(), // &[c, d, b], // "world clears result in 'removed component' states" // ); // assert_eq!( // world.removed::(), // &[b], // "world clears result in 'removed component' states" // ); } #[test] fn added_tracking() { let mut world = World::new(); let a = world.spawn(A(123)).id(); assert_eq!(world.query::<&A>().iter(&world).count(), 1); assert_eq!( world.query_filtered::<(), Added>().iter(&world).count(), 1 ); assert_eq!(world.query::<&A>().iter(&world).count(), 1); assert_eq!( world.query_filtered::<(), Added>().iter(&world).count(), 1 ); assert!(world.query::<&A>().get(&world, a).is_ok()); assert!(world .query_filtered::<(), Added>() .get(&world, a) .is_ok()); assert!(world.query::<&A>().get(&world, a).is_ok()); assert!(world .query_filtered::<(), Added>() .get(&world, a) .is_ok()); world.clear_trackers(); assert_eq!(world.query::<&A>().iter(&world).count(), 1); assert_eq!( world.query_filtered::<(), Added>().iter(&world).count(), 0 ); assert_eq!(world.query::<&A>().iter(&world).count(), 1); assert_eq!( world.query_filtered::<(), Added>().iter(&world).count(), 0 ); assert!(world.query::<&A>().get(&world, a).is_ok()); assert!(world .query_filtered::<(), Added>() .get(&world, a) .is_err()); assert!(world.query::<&A>().get(&world, a).is_ok()); assert!(world .query_filtered::<(), Added>() .get(&world, a) .is_err()); } #[test] fn added_queries() { let mut world = World::default(); let e1 = world.spawn(A(0)).id(); fn get_added(world: &mut World) -> Vec { world .query_filtered::>() .iter(world) .collect::>() } assert_eq!(get_added::(&mut world), vec![e1]); world.entity_mut(e1).insert(B(0)); assert_eq!(get_added::(&mut world), vec![e1]); assert_eq!(get_added::(&mut world), vec![e1]); world.clear_trackers(); assert!(get_added::(&mut world).is_empty()); let e2 = world.spawn((A(1), B(1))).id(); assert_eq!(get_added::(&mut world), vec![e2]); assert_eq!(get_added::(&mut world), vec![e2]); let added = world .query_filtered::, Added)>() .iter(&world) .collect::>(); assert_eq!(added, vec![e2]); } #[test] fn changed_trackers() { let mut world = World::default(); let e1 = world.spawn((A(0), B(0))).id(); let e2 = world.spawn((A(0), B(0))).id(); let e3 = world.spawn((A(0), B(0))).id(); world.spawn((A(0), B(0))); world.clear_trackers(); for (i, mut a) in world.query::<&mut A>().iter_mut(&mut world).enumerate() { if i % 2 == 0 { a.0 += 1; } } fn get_filtered(world: &mut World) -> Vec { world .query_filtered::() .iter(world) .collect::>() } assert_eq!(get_filtered::>(&mut world), vec![e1, e3]); // ensure changing an entity's archetypes also moves its changed state world.entity_mut(e1).insert(C); assert_eq!(get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); // spawning a new A entity should not change existing changed state world.entity_mut(e1).insert((A(0), B(0))); assert_eq!( get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change" ); // removing an unchanged entity should not change changed state assert!(world.despawn(e2)); assert_eq!( get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change" ); // removing a changed entity should remove it from enumeration assert!(world.despawn(e1)); assert_eq!( get_filtered::>(&mut world), vec![e3], "e1 should no longer be returned" ); world.clear_trackers(); assert!(get_filtered::>(&mut world).is_empty()); let e4 = world.spawn_empty().id(); world.entity_mut(e4).insert(A(0)); assert_eq!(get_filtered::>(&mut world), vec![e4]); assert_eq!(get_filtered::>(&mut world), vec![e4]); world.entity_mut(e4).insert(A(1)); assert_eq!(get_filtered::>(&mut world), vec![e4]); world.clear_trackers(); // ensure inserting multiple components set changed state for all components and set added // state for non existing components even when changing archetype. world.entity_mut(e4).insert((A(0), B(0))); assert!(get_filtered::>(&mut world).is_empty()); assert_eq!(get_filtered::>(&mut world), vec![e4]); assert_eq!(get_filtered::>(&mut world), vec![e4]); assert_eq!(get_filtered::>(&mut world), vec![e4]); } #[test] fn changed_trackers_sparse() { let mut world = World::default(); let e1 = world.spawn(SparseStored(0)).id(); let e2 = world.spawn(SparseStored(0)).id(); let e3 = world.spawn(SparseStored(0)).id(); world.spawn(SparseStored(0)); world.clear_trackers(); for (i, mut a) in world .query::<&mut SparseStored>() .iter_mut(&mut world) .enumerate() { if i % 2 == 0 { a.0 += 1; } } fn get_filtered(world: &mut World) -> Vec { world .query_filtered::() .iter(world) .collect::>() } assert_eq!( get_filtered::>(&mut world), vec![e1, e3] ); // ensure changing an entity's archetypes also moves its changed state world.entity_mut(e1).insert(C); assert_eq!(get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); // spawning a new SparseStored entity should not change existing changed state world.entity_mut(e1).insert(SparseStored(0)); assert_eq!( get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change" ); // removing an unchanged entity should not change changed state assert!(world.despawn(e2)); assert_eq!( get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change" ); // removing a changed entity should remove it from enumeration assert!(world.despawn(e1)); assert_eq!( get_filtered::>(&mut world), vec![e3], "e1 should no longer be returned" ); world.clear_trackers(); assert!(get_filtered::>(&mut world).is_empty()); let e4 = world.spawn_empty().id(); world.entity_mut(e4).insert(SparseStored(0)); assert_eq!(get_filtered::>(&mut world), vec![e4]); assert_eq!(get_filtered::>(&mut world), vec![e4]); world.entity_mut(e4).insert(A(1)); assert_eq!(get_filtered::>(&mut world), vec![e4]); world.clear_trackers(); // ensure inserting multiple components set changed state for all components and set added // state for non existing components even when changing archetype. world.entity_mut(e4).insert(SparseStored(0)); assert!(get_filtered::>(&mut world).is_empty()); assert_eq!(get_filtered::>(&mut world), vec![e4]); } #[test] fn empty_spawn() { let mut world = World::default(); let e = world.spawn_empty().id(); let mut e_mut = world.entity_mut(e); e_mut.insert(A(0)); assert_eq!(e_mut.get::().unwrap(), &A(0)); } #[test] fn reserve_and_spawn() { let mut world = World::default(); let e = world.entities().reserve_entity(); world.flush(); let mut e_mut = world.entity_mut(e); e_mut.insert(A(0)); assert_eq!(e_mut.get::().unwrap(), &A(0)); } #[test] fn changed_query() { let mut world = World::default(); let e1 = world.spawn((A(0), B(0))).id(); fn get_changed(world: &mut World) -> Vec { world .query_filtered::>() .iter(world) .collect::>() } assert_eq!(get_changed(&mut world), vec![e1]); world.clear_trackers(); assert_eq!(get_changed(&mut world), vec![]); *world.get_mut(e1).unwrap() = A(1); assert_eq!(get_changed(&mut world), vec![e1]); } #[test] fn resource() { use crate::system::Resource; #[derive(Resource, PartialEq, Debug)] struct Num(i32); #[derive(Resource, PartialEq, Debug)] struct BigNum(u64); let mut world = World::default(); assert!(world.get_resource::().is_none()); assert!(!world.contains_resource::()); assert!(!world.is_resource_added::()); assert!(!world.is_resource_changed::()); world.insert_resource(Num(123)); let resource_id = world .components() .get_resource_id(TypeId::of::()) .unwrap(); let archetype_component_id = world.storages().resources.get(resource_id).unwrap().id(); assert_eq!(world.resource::().0, 123); assert!(world.contains_resource::()); assert!(world.is_resource_added::()); assert!(world.is_resource_changed::()); world.insert_resource(BigNum(456)); assert_eq!(world.resource::().0, 456u64); world.insert_resource(BigNum(789)); assert_eq!(world.resource::().0, 789); { let mut value = world.resource_mut::(); assert_eq!(value.0, 789); value.0 = 10; } assert_eq!( world.resource::().0, 10, "resource changes are preserved" ); assert_eq!( world.remove_resource::(), Some(BigNum(10)), "removed resource has the correct value" ); assert_eq!( world.get_resource::(), None, "removed resource no longer exists" ); assert_eq!( world.remove_resource::(), None, "double remove returns nothing" ); world.insert_resource(BigNum(1)); assert_eq!( world.get_resource::(), Some(&BigNum(1)), "re-inserting resources works" ); assert_eq!( world.get_resource::(), Some(&Num(123)), "other resources are unaffected" ); let current_resource_id = world .components() .get_resource_id(TypeId::of::()) .unwrap(); assert_eq!( resource_id, current_resource_id, "resource id does not change after removing / re-adding" ); let current_archetype_component_id = world.storages().resources.get(resource_id).unwrap().id(); assert_eq!( archetype_component_id, current_archetype_component_id, "resource archetype component id does not change after removing / re-adding" ); } #[test] fn remove() { let mut world = World::default(); let e1 = world.spawn((A(1), B(1), TableStored("a"))).id(); let mut e = world.entity_mut(e1); assert_eq!(e.get::(), Some(&TableStored("a"))); assert_eq!(e.get::(), Some(&A(1))); assert_eq!(e.get::(), Some(&B(1))); assert_eq!( e.get::(), None, "C is not in the entity, so it should not exist" ); e.remove::<(A, B, C)>(); assert_eq!( e.get::(), Some(&TableStored("a")), "TableStored is not in the removed bundle, so it should exist" ); assert_eq!( e.get::(), None, "Num is in the removed bundle, so it should not exist" ); assert_eq!( e.get::(), None, "f64 is in the removed bundle, so it should not exist" ); assert_eq!( e.get::(), None, "usize is in the removed bundle, so it should not exist" ); } #[test] fn take() { let mut world = World::default(); world.spawn((A(1), B(1), TableStored("1"))); let e2 = world.spawn((A(2), B(2), TableStored("2"))).id(); world.spawn((A(3), B(3), TableStored("3"))); let mut query = world.query::<(&B, &TableStored)>(); let results = query .iter(&world) .map(|(a, b)| (a.0, b.0)) .collect::>(); assert_eq!(results, vec![(1, "1"), (2, "2"), (3, "3"),]); let removed_bundle = world.entity_mut(e2).take::<(B, TableStored)>().unwrap(); assert_eq!(removed_bundle, (B(2), TableStored("2"))); let results = query .iter(&world) .map(|(a, b)| (a.0, b.0)) .collect::>(); assert_eq!(results, vec![(1, "1"), (3, "3"),]); let mut a_query = world.query::<&A>(); let results = a_query.iter(&world).map(|a| a.0).collect::>(); assert_eq!(results, vec![1, 3, 2]); let entity_ref = world.entity(e2); assert_eq!( entity_ref.get::(), Some(&A(2)), "A is not in the removed bundle, so it should exist" ); assert_eq!( entity_ref.get::(), None, "B is in the removed bundle, so it should not exist" ); assert_eq!( entity_ref.get::(), None, "TableStored is in the removed bundle, so it should not exist" ); } #[test] fn non_send_resource() { let mut world = World::default(); world.insert_non_send_resource(123i32); world.insert_non_send_resource(456i64); assert_eq!(*world.non_send_resource::(), 123); assert_eq!(*world.non_send_resource_mut::(), 456); } #[test] fn non_send_resource_points_to_distinct_data() { let mut world = World::default(); world.insert_resource(A(123)); world.insert_non_send_resource(A(456)); assert_eq!(*world.resource::(), A(123)); assert_eq!(*world.non_send_resource::(), A(456)); } #[test] #[should_panic] fn non_send_resource_panic() { let mut world = World::default(); world.insert_non_send_resource(0i32); std::thread::spawn(move || { let _ = world.non_send_resource_mut::(); }) .join() .unwrap(); } #[test] fn exact_size_query() { let mut world = World::default(); world.spawn((A(0), B(0))); world.spawn((A(0), B(0))); world.spawn((A(0), B(0), C)); world.spawn(C); let mut query = world.query::<(&A, &B)>(); assert_eq!(query.iter(&world).len(), 3); } #[test] #[should_panic] fn duplicate_components_panic() { let mut world = World::new(); world.spawn((A(1), A(2))); } #[test] #[should_panic] fn ref_and_mut_query_panic() { let mut world = World::new(); world.query::<(&A, &mut A)>(); } #[test] #[should_panic] fn mut_and_ref_query_panic() { let mut world = World::new(); world.query::<(&mut A, &A)>(); } #[test] #[should_panic] fn mut_and_mut_query_panic() { let mut world = World::new(); world.query::<(&mut A, &mut A)>(); } #[test] #[should_panic] fn multiple_worlds_same_query_iter() { let mut world_a = World::new(); let world_b = World::new(); let mut query = world_a.query::<&A>(); query.iter(&world_a); query.iter(&world_b); } #[test] fn query_filters_dont_collide_with_fetches() { let mut world = World::new(); world.query_filtered::<&mut A, Changed>(); } #[test] fn filtered_query_access() { let mut world = World::new(); let query = world.query_filtered::<&mut A, Changed>(); let mut expected = FilteredAccess::::default(); let a_id = world.components.get_id(TypeId::of::()).unwrap(); let b_id = world.components.get_id(TypeId::of::()).unwrap(); expected.add_write(a_id); expected.add_read(b_id); assert!( query.component_access.eq(&expected), "ComponentId access from query fetch and query filter should be combined" ); } #[test] #[should_panic] fn multiple_worlds_same_query_get() { let mut world_a = World::new(); let world_b = World::new(); let mut query = world_a.query::<&A>(); let _ = query.get(&world_a, Entity::from_raw(0)); let _ = query.get(&world_b, Entity::from_raw(0)); } #[test] #[should_panic] fn multiple_worlds_same_query_for_each() { let mut world_a = World::new(); let world_b = World::new(); let mut query = world_a.query::<&A>(); query.for_each(&world_a, |_| {}); query.for_each(&world_b, |_| {}); } #[test] fn resource_scope() { let mut world = World::default(); world.insert_resource(A(0)); world.resource_scope(|world: &mut World, mut value: Mut| { value.0 += 1; assert!(!world.contains_resource::()); }); assert_eq!(world.resource::().0, 1); } #[test] #[should_panic( expected = "Attempted to access or drop non-send resource bevy_ecs::tests::NonSendA from thread" )] fn non_send_resource_drop_from_different_thread() { let mut world = World::default(); world.insert_non_send_resource(NonSendA::default()); let thread = std::thread::spawn(move || { // Dropping the non-send resource on a different thread // Should result in a panic drop(world); }); if let Err(err) = thread.join() { std::panic::resume_unwind(err); } } #[test] fn non_send_resource_drop_from_same_thread() { let mut world = World::default(); world.insert_non_send_resource(NonSendA::default()); drop(world); } #[test] fn insert_overwrite_drop() { let (dropck1, dropped1) = DropCk::new_pair(); let (dropck2, dropped2) = DropCk::new_pair(); let mut world = World::default(); world.spawn(dropck1).insert(dropck2); assert_eq!(dropped1.load(Ordering::Relaxed), 1); assert_eq!(dropped2.load(Ordering::Relaxed), 0); drop(world); assert_eq!(dropped1.load(Ordering::Relaxed), 1); assert_eq!(dropped2.load(Ordering::Relaxed), 1); } #[test] fn insert_overwrite_drop_sparse() { let (dropck1, dropped1) = DropCk::new_pair(); let (dropck2, dropped2) = DropCk::new_pair(); let mut world = World::default(); world .spawn(DropCkSparse(dropck1)) .insert(DropCkSparse(dropck2)); assert_eq!(dropped1.load(Ordering::Relaxed), 1); assert_eq!(dropped2.load(Ordering::Relaxed), 0); drop(world); assert_eq!(dropped1.load(Ordering::Relaxed), 1); assert_eq!(dropped2.load(Ordering::Relaxed), 1); } #[test] fn clear_entities() { let mut world = World::default(); world.insert_resource(A(0)); world.spawn(A(1)); world.spawn(SparseStored(1)); let mut q1 = world.query::<&A>(); let mut q2 = world.query::<&SparseStored>(); assert_eq!(q1.iter(&world).len(), 1); assert_eq!(q2.iter(&world).len(), 1); assert_eq!(world.entities().len(), 2); world.clear_entities(); assert_eq!( q1.iter(&world).len(), 0, "world should not contain table components" ); assert_eq!( q2.iter(&world).len(), 0, "world should not contain sparse set components" ); assert_eq!( world.entities().len(), 0, "world should not have any entities" ); assert_eq!( world.resource::().0, 0, "world should still contain resources" ); } #[test] fn test_is_archetypal_size_hints() { let mut world = World::default(); macro_rules! query_min_size { ($query:ty, $filter:ty) => { world .query_filtered::<$query, $filter>() .iter(&world) .size_hint() .0 }; } world.spawn((A(1), B(1), C)); world.spawn((A(1), C)); world.spawn((A(1), B(1))); world.spawn((B(1), C)); world.spawn(A(1)); world.spawn(C); assert_eq!(2, query_min_size![(), (With, Without)],); assert_eq!(3, query_min_size![&B, Or<(With, With)>],); assert_eq!(1, query_min_size![&B, (With, With)],); assert_eq!(1, query_min_size![(&A, &B), With],); assert_eq!(4, query_min_size![&A, ()], "Simple Archetypal"); assert_eq!(4, query_min_size![Ref, ()],); // All the following should set minimum size to 0, as it's impossible to predict // how many entities the filters will trim. assert_eq!(0, query_min_size![(), Added], "Simple Added"); assert_eq!(0, query_min_size![(), Changed], "Simple Changed"); assert_eq!(0, query_min_size![(&A, &B), Changed],); assert_eq!(0, query_min_size![&A, (Changed, With)],); assert_eq!(0, query_min_size![(&A, &B), Or<(Changed, Changed)>],); } #[test] fn reserve_entities_across_worlds() { let mut world_a = World::default(); let mut world_b = World::default(); let e1 = world_a.spawn(A(1)).id(); let e2 = world_a.spawn(A(2)).id(); let e3 = world_a.entities().reserve_entity(); world_a.flush(); let world_a_max_entities = world_a.entities().len(); world_b.entities.reserve_entities(world_a_max_entities); world_b.entities.flush_as_invalid(); let e4 = world_b.spawn(A(4)).id(); assert_eq!( e4, Entity::new(3, 0), "new entity is created immediately after world_a's max entity" ); assert!(world_b.get::(e1).is_none()); assert!(world_b.get_entity(e1).is_none()); assert!(world_b.get::(e2).is_none()); assert!(world_b.get_entity(e2).is_none()); assert!(world_b.get::(e3).is_none()); assert!(world_b.get_entity(e3).is_none()); world_b.get_or_spawn(e1).unwrap().insert(B(1)); assert_eq!( world_b.get::(e1), Some(&B(1)), "spawning into 'world_a' entities works" ); world_b.get_or_spawn(e4).unwrap().insert(B(4)); assert_eq!( world_b.get::(e4), Some(&B(4)), "spawning into existing `world_b` entities works" ); assert_eq!( world_b.get::(e4), Some(&A(4)), "spawning into existing `world_b` entities works" ); let e4_mismatched_generation = Entity::new(3, 1); assert!( world_b.get_or_spawn(e4_mismatched_generation).is_none(), "attempting to spawn on top of an entity with a mismatched entity generation fails" ); assert_eq!( world_b.get::(e4), Some(&B(4)), "failed mismatched spawn doesn't change existing entity" ); assert_eq!( world_b.get::(e4), Some(&A(4)), "failed mismatched spawn doesn't change existing entity" ); let high_non_existent_entity = Entity::new(6, 0); world_b .get_or_spawn(high_non_existent_entity) .unwrap() .insert(B(10)); assert_eq!( world_b.get::(high_non_existent_entity), Some(&B(10)), "inserting into newly allocated high / non-continuous entity id works" ); let high_non_existent_but_reserved_entity = Entity::new(5, 0); assert!( world_b.get_entity(high_non_existent_but_reserved_entity).is_none(), "entities between high-newly allocated entity and continuous block of existing entities don't exist" ); let reserved_entities = vec![ world_b.entities().reserve_entity(), world_b.entities().reserve_entity(), world_b.entities().reserve_entity(), world_b.entities().reserve_entity(), ]; assert_eq!( reserved_entities, vec![ Entity::new(5, 0), Entity::new(4, 0), Entity::new(7, 0), Entity::new(8, 0), ], "space between original entities and high entities is used for new entity ids" ); } #[test] fn insert_or_spawn_batch() { let mut world = World::default(); let e0 = world.spawn(A(0)).id(); let e1 = Entity::from_raw(1); let values = vec![(e0, (B(0), C)), (e1, (B(1), C))]; world.insert_or_spawn_batch(values).unwrap(); assert_eq!( world.get::(e0), Some(&A(0)), "existing component was preserved" ); assert_eq!( world.get::(e0), Some(&B(0)), "pre-existing entity received correct B component" ); assert_eq!( world.get::(e1), Some(&B(1)), "new entity was spawned and received correct B component" ); assert_eq!( world.get::(e0), Some(&C), "pre-existing entity received C component" ); assert_eq!( world.get::(e1), Some(&C), "new entity was spawned and received C component" ); } #[test] fn insert_or_spawn_batch_invalid() { let mut world = World::default(); let e0 = world.spawn(A(0)).id(); let e1 = Entity::from_raw(1); let e2 = world.spawn_empty().id(); let invalid_e2 = Entity::new(e2.index(), 1); let values = vec![(e0, (B(0), C)), (e1, (B(1), C)), (invalid_e2, (B(2), C))]; let result = world.insert_or_spawn_batch(values); assert_eq!( result, Err(vec![invalid_e2]), "e2 failed to be spawned or inserted into" ); assert_eq!( world.get::(e0), Some(&A(0)), "existing component was preserved" ); assert_eq!( world.get::(e0), Some(&B(0)), "pre-existing entity received correct B component" ); assert_eq!( world.get::(e1), Some(&B(1)), "new entity was spawned and received correct B component" ); assert_eq!( world.get::(e0), Some(&C), "pre-existing entity received C component" ); assert_eq!( world.get::(e1), Some(&C), "new entity was spawned and received C component" ); } }