use legion::prelude::*; use std::collections::HashSet; #[derive(Clone, Copy, Debug, PartialEq)] struct Pos(f32, f32, f32); #[derive(Clone, Copy, Debug, PartialEq)] struct Rot(f32, f32, f32); #[derive(Clone, Copy, Debug, PartialEq)] struct Scale(f32, f32, f32); #[derive(Clone, Copy, Debug, PartialEq)] struct Vel(f32, f32, f32); #[derive(Clone, Copy, Debug, PartialEq)] struct Accel(f32, f32, f32); #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] struct Model(u32); #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] struct Static; #[test] fn insert() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (1usize, 2f32, 3u16); let components = vec![(4f32, 5u64, 6u16), (4f32, 5u64, 6u16)]; let entities = world.insert(shared, components); assert_eq!(2, entities.len()); } #[test] fn get_component() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Static, Model(5)); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let mut entities: Vec = Vec::new(); for e in world.insert(shared, components.clone()) { entities.push(*e); } for (i, e) in entities.iter().enumerate() { match world.get_component(*e) { Some(x) => assert_eq!(components.get(i).map(|(x, _)| x), Some(&x as &Pos)), None => assert_eq!(components.get(i).map(|(x, _)| x), None), } match world.get_component(*e) { Some(x) => assert_eq!(components.get(i).map(|(_, x)| x), Some(&x as &Rot)), None => assert_eq!(components.get(i).map(|(_, x)| x), None), } } } #[test] fn get_component_wrong_type() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let entity = *world.insert((), vec![(0f64,)]).get(0).unwrap(); assert!(world.get_component::(entity).is_none()); } #[test] fn get_shared() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Static, Model(5)); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let mut entities: Vec = Vec::new(); for e in world.insert(shared, components) { entities.push(*e); } for e in entities.iter() { assert_eq!(Some(&Static), world.get_tag(*e)); assert_eq!(Some(&Model(5)), world.get_tag(*e)); } } #[test] fn get_shared_wrong_type() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let entity = *world.insert((Static,), vec![(0f64,)]).get(0).unwrap(); assert!(world.get_tag::(entity).is_none()); } #[test] fn delete() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Static, Model(5)); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let mut entities: Vec = Vec::new(); for e in world.insert(shared, components) { entities.push(*e); } for e in entities.iter() { assert_eq!(true, world.is_alive(*e)); } for e in entities.iter() { world.delete(*e); assert_eq!(false, world.is_alive(*e)); } } #[test] fn delete_all() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Static, Model(5)); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let mut entities: Vec = Vec::new(); for e in world.insert(shared, components) { entities.push(*e); } // Check that the entity allocator knows about the entities for e in entities.iter() { assert_eq!(true, world.is_alive(*e)); } // Check that the entities are in storage let query = <(Read, Read)>::query(); assert_eq!(2, query.iter(&world).count()); world.delete_all(); // Check that the entity allocator no longer knows about the entities for e in entities.iter() { assert_eq!(false, world.is_alive(*e)); } // Check that the entities are removed from storage let query = <(Read, Read)>::query(); assert_eq!(0, query.iter(&world).count()); } #[test] fn delete_last() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Static, Model(5)); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let mut entities: Vec = Vec::new(); for e in world.insert(shared, components.clone()) { entities.push(*e); } let last = *entities.last().unwrap(); world.delete(last); assert_eq!(false, world.is_alive(last)); for (i, e) in entities.iter().take(entities.len() - 1).enumerate() { assert_eq!(true, world.is_alive(*e)); match world.get_component(*e) { Some(x) => assert_eq!(components.get(i).map(|(x, _)| x), Some(&x as &Pos)), None => assert_eq!(components.get(i).map(|(x, _)| x), None), } match world.get_component(*e) { Some(x) => assert_eq!(components.get(i).map(|(_, x)| x), Some(&x as &Rot)), None => assert_eq!(components.get(i).map(|(_, x)| x), None), } } } #[test] fn delete_first() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Static, Model(5)); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let mut entities: Vec = Vec::new(); for e in world.insert(shared, components.clone()) { entities.push(*e); } let first = *entities.first().unwrap(); world.delete(first); assert_eq!(false, world.is_alive(first)); for (i, e) in entities.iter().skip(1).enumerate() { assert_eq!(true, world.is_alive(*e)); match world.get_component(*e) { Some(x) => assert_eq!(components.get(i + 1).map(|(x, _)| x), Some(&x as &Pos)), None => assert_eq!(components.get(i + 1).map(|(x, _)| x), None), } match world.get_component(*e) { Some(x) => assert_eq!(components.get(i + 1).map(|(_, x)| x), Some(&x as &Rot)), None => assert_eq!(components.get(i + 1).map(|(_, x)| x), None), } } } #[test] fn merge() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world_1 = universe.create_world(); let mut world_2 = universe.create_world(); let shared = (Static, Model(5)); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let mut world_1_entities: Vec = Vec::new(); for e in world_1.insert(shared, components.clone()) { world_1_entities.push(*e); } let mut world_2_entities: Vec = Vec::new(); for e in world_2.insert(shared, components.clone()) { world_2_entities.push(*e); } world_1.merge(world_2); for (i, e) in world_2_entities.iter().enumerate() { assert!(world_1.is_alive(*e)); let (pos, rot) = components.get(i).unwrap(); assert_eq!(pos, &world_1.get_component(*e).unwrap() as &Pos); assert_eq!(rot, &world_1.get_component(*e).unwrap() as &Rot); } } #[test] fn mutate_add_component() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Static, Model(5)); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let entities = world.insert(shared, components).to_vec(); let query_without_scale = <(Read, Read)>::query(); let query_with_scale = <(Read, Read, Read)>::query(); assert_eq!(3, query_without_scale.iter(&world).count()); assert_eq!(0, query_with_scale.iter(&world).count()); world .add_component(*entities.get(1).unwrap(), Scale(0.5, 0.5, 0.5)) .unwrap(); assert_eq!(3, query_without_scale.iter(&world).count()); assert_eq!(1, query_with_scale.iter(&world).count()); } #[test] fn mutate_remove_component() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Static, Model(5)); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let entities = world.insert(shared, components).to_vec(); let query_without_rot = Read::::query().filter(!component::()); let query_with_rot = <(Read, Read)>::query(); assert_eq!(0, query_without_rot.iter(&world).count()); assert_eq!(3, query_with_rot.iter(&world).count()); world .remove_component::(*entities.get(1).unwrap()) .unwrap(); assert_eq!(1, query_without_rot.iter(&world).count()); assert_eq!(2, query_with_rot.iter(&world).count()); } #[test] fn mutate_add_tag() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Model(5),); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let entities = world.insert(shared, components).to_vec(); let query_without_static = <(Read, Read)>::query(); let query_with_static = <(Read, Read, Tagged)>::query(); assert_eq!(3, query_without_static.iter(&world).count()); assert_eq!(0, query_with_static.iter(&world).count()); world.add_tag(*entities.get(1).unwrap(), Static).unwrap(); assert_eq!(3, query_without_static.iter(&world).count()); assert_eq!(1, query_with_static.iter(&world).count()); } #[test] fn mutate_remove_tag() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Model(5), Static); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let entities = world.insert(shared, components).to_vec(); let query_without_static = <(Read, Read)>::query().filter(!tag::()); let query_with_static = <(Read, Read, Tagged)>::query(); assert_eq!(0, query_without_static.iter(&world).count()); assert_eq!(3, query_with_static.iter(&world).count()); world .remove_tag::(*entities.get(1).unwrap()) .unwrap(); assert_eq!(1, query_without_static.iter(&world).count()); assert_eq!(2, query_with_static.iter(&world).count()); } #[test] fn mutate_change_tag_minimum_test() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Model(5),); let components = vec![(Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3))]; let entities = world.insert(shared, components).to_vec(); tracing::trace!("STARTING CHANGE"); world.add_tag(entities[0], Model(3)).unwrap(); tracing::trace!("CHANGED\n"); assert_eq!(*world.get_tag::(entities[0]).unwrap(), Model(3)); } #[test] fn delete_entities_on_drop() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let (tx, rx) = crossbeam_channel::unbounded::(); let shared = (Model(5),); let components = vec![(Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3))]; // Insert the data and store resulting entities in a HashSet let mut entities = HashSet::new(); for entity in world.insert(shared, components) { entities.insert(*entity); } world.subscribe(tx, legion::filter::filter_fns::any()); //ManuallyDrop::drop(&mut world); std::mem::drop(world); for e in rx.try_recv() { match e { legion::event::Event::EntityRemoved(entity, _chunk_id) => { assert!(entities.remove(&entity)); } _ => {} } } // Verify that no extra entities are included assert!(entities.is_empty()); } #[test] #[allow(clippy::suspicious_map)] fn mutate_change_tag() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Model(5),); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let entities = world.insert(shared, components).to_vec(); let query_model_3 = <(Read, Read)>::query().filter(tag_value(&Model(3))); let query_model_5 = <(Read, Read)>::query().filter(tag_value(&Model(5))); assert_eq!(3, query_model_5.iter(&world).count()); assert_eq!(0, query_model_3.iter(&world).count()); tracing::trace!("STARTING CHANGE"); world.add_tag(*entities.get(1).unwrap(), Model(3)).unwrap(); tracing::trace!("CHANGED\n"); assert_eq!( 1, query_model_3 .iter_entities_mut(&mut world) .map(|e| { tracing::trace!("iter: {:?}", e); e }) .count() ); assert_eq!( *world.get_tag::(*entities.get(1).unwrap()).unwrap(), Model(3) ); assert_eq!(2, query_model_5.iter(&world).count()); } // This test repeatedly creates a world with new entities and drops it, reproducing // https://github.com/TomGillen/legion/issues/92 #[test] fn lots_of_deletes() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); for _ in 0..10000 { let shared = (Model(5),); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; let mut world = universe.create_world(); world.insert(shared, components).to_vec(); } } #[test] fn iter_entities() { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let shared = (Model(5),); let components = vec![ (Pos(1., 2., 3.), Rot(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), (Pos(4., 5., 6.), Rot(0.4, 0.5, 0.6)), ]; // Insert the data and store resulting entities in a HashSet let mut entities = HashSet::new(); for entity in world.insert(shared, components) { entities.insert(*entity); } // Verify that all entities in iter_entities() are included for entity in world.iter_entities() { assert!(entities.remove(&entity)); } // Verify that no extra entities are included assert!(entities.is_empty()); }