Relations
A Relation is always between two entities.
You can think of Entities being nodes on a directed graph with Relations being the edges.
Relations are distinguished with a Rust type via its TypeId
.
To prevent accidentally adding a Relation as a Component it is recommended to use uninhabited types for them.
For example an enum with no variants.
A Relation always has an origin and a target.
Registration
Like for Components, it is recommended to register Relations before use.
use froql::world::World;
enum MyRelation {}
fn create_world() -> World {
let mut world = World::new();
world.register_relation::<MyRelation>();
world
}
Adding and removing relations between entities
use froql::world::World;
enum MyRelation {}
let mut world = World::new();
world.register_relation::<MyRelation>();
let a = world.create_entity();
let b = world.create_entity();
world.add_relation::<MyRelation>(a,b);
assert!(world.has_relation::<MyRelation>(a,b));
world.remove_relation::<MyRelation>(a,b);
assert!(!world.has_relation::<MyRelation>(a,b));
In the EntityView the vocubulary is relate
and unrelate
.
use froql::world::World;
enum MyRelation {}
let mut world = World::new();
world.register_relation::<MyRelation>();
let b = world.create_entity();
let a = world.create().relate_to::<MyRelation>(b).entity;
assert!(world.has_relation::<MyRelation>(a,b));
world.view_mut(a).unrelate_to::<MyRelation>(b);
assert!(!world.has_relation::<MyRelation>(a,b));
Relation Flags
By default relations are directed, many-to-many and non-transitive. But this behavior can be changed when registering the relation using flags.
Exclusive Relations
A -> B
implies there is no A->C
use froql::world::World;
use froql::component::EXCLUSIVE;
enum ChildOf {}
let mut world = World::new();
world.register_relation_flags::<ChildOf>(EXCLUSIVE);
let a = world.create_entity();
let b = world.create().relate_from::<ChildOf>(a).entity;
assert!(world.has_relation::<ChildOf>(a,b));
let c = world.create().relate_from::<ChildOf>(a).entity;
// a is not in a ChildOf relation to b anymore
assert!(!world.has_relation::<ChildOf>(a,b));
assert!(world.has_relation::<ChildOf>(a,c));
Transitive Relations
A -> B -> C
implies A->C
use froql::world::World;
use froql::component::TRANSITIVE;
enum InsideOf {}
let mut world = World::new();
world.register_relation_flags::<InsideOf>(TRANSITIVE);
let house = world.create_entity();
let room = world.create().relate_to::<InsideOf>(house).entity;
let guy = world.create().relate_to::<InsideOf>(room).entity;
assert!(world.has_relation::<InsideOf>(guy, room));
assert!(world.has_relation::<InsideOf>(guy, house));
Symmetric Relations
A -> B
implies B->A
use froql::world::World;
use froql::component::SYMMETRIC;
enum Friends {}
let mut world = World::new();
world.register_relation_flags::<Friends>(SYMMETRIC);
let anna = world.create_entity();
let otto = world.create().relate_to::<Friends>(anna).entity;
assert!(world.has_relation::<Friends>(anna, otto));
assert!(world.has_relation::<Friends>(otto, anna));
Cascading deletion
When A
in A->B
gets destroyed, B
also gets destroyed
use froql::world::World;
use froql::component::CASCADING_DESTRUCT;
enum Cleanup {}
let mut world = World::new();
world.register_relation_flags::<Cleanup>(CASCADING_DESTRUCT);
let resource = world.create_entity();
let container = world.create().relate_to::<Cleanup>(resource).entity;
let outer_container = world.create().relate_to::<Cleanup>(container).entity;
// destruction is propagated
world.destroy(outer_container);
assert!(!world.is_alive(outer_container));
assert!(!world.is_alive(container));
assert!(!world.is_alive(resource));
Multiple Flags
You can pass multiple flags when registering a relation by xoring them together.
use froql::world::World;
use froql::component::SYMMETRIC;
use froql::component::EXCLUSIVE;
enum BestFriends {}
let mut world = World::new();
world.register_relation_flags::<BestFriends>(SYMMETRIC | EXCLUSIVE);
let mustadir = world.create_entity();
let asif = world.create_entity();
world.add_relation::<BestFriends>(asif, mustadir);
let salman = world.create_entity();
// friendship ended with mustadir, now salman is my best friend
world.add_relation::<BestFriends>(asif, salman);
assert!(!world.has_relation::<BestFriends>(asif, mustadir));
assert!(world.has_relation::<BestFriends>(salman, asif));