examples/systems/system_immediate.rs
1use crate::z_ignore_test_common::*;
2
3use flecs_ecs::prelude::*;
4
5// When an application calls world.progress(), the world is put in readonly mode.
6// This ensures that systems (on multiple threads) can safely iterate
7// components, without having to worry about components moving around while
8// they're being read. This has as side effect that any operations (like adding
9// or removing components) are not visible until the end of the frame (see the
10// sync_point example for more details).
11// Sometimes this is not what you want, and you need a change to be visible
12// immediately. For these use cases, applications can use a no_readonly system.
13// This temporarily takes the world out of readonly mode, so a system can make
14// changes that are directly visible.
15// Because they mutate the world directly, no_readonly systems are never ran on
16// more than one thread, and no other systems are ran at the same time.
17
18#[derive(Component)]
19struct Waiter;
20
21#[derive(Component)]
22struct Plate;
23
24fn main() {
25 let world = World::new();
26
27 // Create query to find all waiters without a plate
28 let mut q_waiter = world
29 .query::<()>()
30 .with(Waiter)
31 .without((Plate, id::<flecs::Wildcard>()))
32 .build();
33
34 // System that assigns plates to waiter. By making this system no_readonly
35 // plate assignments are assigned directly (not deferred) to waiters, which
36 // ensures that we won't assign plates to the same waiter more than once.
37 world
38 .system_named::<()>("AssignPlate")
39 .with(Plate)
40 .without((Waiter, id::<flecs::Wildcard>()))
41 .immediate(true)
42 .each_iter(move |mut it, index, plate| {
43 let world = it.world();
44 let plate = it.entity(index);
45
46 // Find an available waiter
47 if let Some(waiter) = q_waiter.try_first_entity() {
48 // An available waiter was found, assign a plate to it so
49 // that the next plate will no longer find it.
50 // The defer_suspend function temporarily suspends deferring
51 // operations, which ensures that our plate is assigned
52 // immediately. Even though this is a no_readonly system,
53 // deferring is still enabled by default as adding/removing
54 // components to the entities being iterated would interfere
55 // with the system iterator.
56 it.world().defer_suspend();
57 waiter.add((Plate, plate));
58 it.world().defer_resume();
59
60 // Now that deferring is resumed, we can safely also add the
61 // waiter to the plate. We can't do this while deferring is
62 // suspended, because the plate is the entity we're
63 // currently iterating, and we don't want to move it to a
64 // different table while we're iterating it.
65
66 plate.add((Waiter, waiter));
67
68 println!("Assigned {} to {}!", waiter.name(), plate.name());
69 }
70 });
71
72 let waiter_1 = world.entity_named("waiter_1").add(Waiter);
73 world.entity_named("waiter_2").add(Waiter);
74 world.entity_named("waiter_3").add(Waiter);
75
76 world.entity_named("plate_1").add(Plate);
77 let plate_2 = world.entity_named("plate_2").add(Plate);
78 world.entity_named("plate_3").add(Plate);
79
80 waiter_1.add((Plate, plate_2));
81 plate_2.add((Waiter, waiter_1));
82
83 // run systems
84 world.progress();
85
86 // Output:
87 // Assigned waiter_3 to plate_1!
88 // Assigned waiter_2 to plate_3!
89}
90
91#[cfg(feature = "flecs_nightly_tests")]
92#[test]
93#[ignore = "todo fix"]
94fn test() {
95 let output_capture = OutputCapture::capture().unwrap();
96 main();
97 output_capture.test("system_no_readonly".to_string());
98}