use flax::{ component, components::{child_of, name}, entity_ids, metadata::debuggable, relations_like, Debuggable, Entity, EntityBuilder, FetchExt, Query, World, }; use itertools::Itertools; #[derive(Debug, Clone)] pub struct Countdown(usize); impl Countdown { pub fn new() -> Self { Self(0) } pub fn proceed(&mut self) -> bool { self.0 += 1; match self.0.cmp(&C) { std::cmp::Ordering::Less => false, std::cmp::Ordering::Equal => true, std::cmp::Ordering::Greater => { eprintln!("Sire!"); self.0 = C; true } } } } impl Default for Countdown { fn default() -> Self { Self::new() } } #[test] fn visitors() { use flax::Debuggable; component! { health: f32 => [Debuggable], // Then shalt count to three, no more no less count: Countdown<3> => [Debuggable], } let mut world = World::new(); EntityBuilder::new() .set(name(), "Holy Hand Grenade of Antioch".to_string()) .spawn(&mut world); let mut builder = EntityBuilder::new(); for i in 0..128 { let perm = ((i as f32 + 0.4) * (i as f32) * 6.0) % 100.0; builder .set(name(), format!("Clone#{i}")) .set(health(), perm) .spawn(&mut world); } eprintln!("World: {world:#?}"); } #[test] fn relations() { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] enum RelationKind { Mom, Dad, } component! { hobby: &'static str => [ Debuggable ], profession: &'static str => [ Debuggable ], child_of(e): RelationKind => [ Debuggable ], } let mut world = World::new(); let parent = EntityBuilder::new() .set(name(), "Jessica".to_string()) .set(hobby(), "Reading") .spawn(&mut world); let parent2 = EntityBuilder::new() .set(name(), "Jack".to_string()) .set(hobby(), "Crocheting") .spawn(&mut world); let child = EntityBuilder::new() .set(name(), "John".to_string()) .set(hobby(), "Studying") .set(child_of(parent), RelationKind::Mom) .spawn(&mut world); let child2 = EntityBuilder::new() .set(name(), "Sally".to_string()) .set(hobby(), "Hockey") .set(child_of(parent), RelationKind::Mom) .set(profession(), "Student") .spawn(&mut world); assert!(world.get(child_of(parent).id(), debuggable()).is_ok()); let child3 = EntityBuilder::new() .set(name(), "Reacher".to_string()) .set(hobby(), "Hockey") .set(child_of(parent2), RelationKind::Dad) .spawn(&mut world); let mut query = Query::new((name(), child_of(parent))); assert!(world.get(child_of(parent).id(), debuggable()).is_ok()); assert!(world.get(child_of(parent2).id(), debuggable()).is_ok()); let items = query .borrow(&world) .iter() .map(|(a, b)| (a.clone(), *b)) .sorted() .collect_vec(); assert_eq!( items, [ ("John".to_string(), RelationKind::Mom), ("Sally".to_string(), RelationKind::Mom) ] ); eprintln!("World: {world:#?}"); // Visit the first parent of the children let mut query = Query::new((name(), relations_like(child_of))); let mut query = query.borrow(&world); let items = query .iter() .map(|(name, relations)| (name, relations.collect_vec())) .sorted() .collect_vec(); assert_eq!( items, [ (&"Jack".to_string(), vec![]), (&"Jessica".to_string(), vec![]), (&"John".to_string(), vec![(parent, &RelationKind::Mom)]), (&"Reacher".to_string(), vec![(parent2, &RelationKind::Dad)]), (&"Sally".to_string(), vec![(parent, &RelationKind::Mom)]) ] ); drop(query); eprintln!("Detaching parent: {parent}"); // If we remove a parent, the children will be detached. world.detach(parent); assert!(world.get(child, child_of(parent)).is_err()); assert!(world.get(child2, child_of(parent)).is_err()); assert!(world.get(child3, child_of(parent2)).is_ok()); eprintln!("Before: {world:#?}"); world.clear(parent2).unwrap(); world.detach(parent2); eprintln!("After: {world:#?}"); assert!(world.is_alive(parent2)); assert!(world.get(parent2, name()).is_err()); assert!(world.get(child3, child_of(parent2)).is_err()); } #[test] fn build_hierarchy() { // tracing_subscriber::fmt::init(); #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] enum RelationKind { Dad, } component! { hobby: &'static str => [ Debuggable ], profession: &'static str => [ Debuggable ], child_of(e): RelationKind => [ Debuggable ], } let mut world = World::new(); let parent = Entity::builder() .set(name(), "Alex".into()) .set(hobby(), "Sewing") .attach_with( child_of, RelationKind::Dad, Entity::builder() .set(name(), "Eric".into()) .set(hobby(), "Gaming"), ) .attach_with( child_of, RelationKind::Dad, Entity::builder() .set(name(), "Vanessa".into()) .set(hobby(), "Climbing"), ) .spawn(&mut world); assert_eq!( world.get(parent, name()).as_deref(), Ok(&"Alex".to_string()) ); let mut query = Query::new((name(), child_of(parent))); let mut query = query.borrow(&world); let children = query.iter().sorted().collect_vec(); assert_eq!( children, [ (&"Eric".to_string(), &RelationKind::Dad), (&"Vanessa".to_string(), &RelationKind::Dad) ] ); } #[test] fn hierarchy_manipulation() { let mut world = World::new(); let a = Entity::builder() .set(name(), "a".into()) .attach(child_of, Entity::builder().set(name(), "a.a".into())) .spawn(&mut world); let b = Entity::builder() .set(name(), "b".into()) .attach(child_of, Entity::builder().set(name(), "b.a".into())) .attach(child_of, Entity::builder().set(name(), "b.b".into())) .spawn(&mut world); // Query all entities with no `child_of` relation let mut q = Query::new(entity_ids()).without_relation(child_of); let roots = q.borrow(&world).iter().sorted().collect_vec(); assert_eq!(roots, [a, b]); // Attach a under b world.set(a, child_of(b), ()).unwrap(); let roots = q.borrow(&world).iter().sorted().collect_vec(); assert_eq!(roots, [b]); world.detach(b); assert_eq!(world.get(b, name()).as_deref(), Ok(&"b".to_string())); let mut q = Query::new(name()).without_relation(child_of); let mut roots = q.borrow(&world); let roots = roots.iter().sorted().collect_vec(); assert_eq!(roots, ["a", "b", "b.a", "b.b"]); let children_of_a = Query::new(name().cloned()) .with(child_of(a)) .collect_vec(&world); let children_of_b = Query::new(name().cloned()) .with(child_of(b)) .collect_vec(&world); assert_eq!(children_of_a, ["a.a"]); assert_eq!(children_of_b, [""; 0]); }