Skip to main content

pebble/ecs/
mod.rs

1/// A handle to a specific entity in the ECS world. Pebble's own re-export
2/// of `hecs::Entity` — since `hecs` is the ECS world itself, not a
3/// swappable backend, referencing it doesn't require adding `hecs` as your
4/// own dependency. Include it in a query tuple (`Query<(Entity, &Health)>`)
5/// to get entity IDs back out of iteration, or pass one to
6/// [`Commands::despawn`](crate::ecs::commands::Commands).
7pub use hecs::Entity;
8
9pub mod commands;
10pub mod events;
11pub mod local;
12pub mod observers;
13pub mod plugin;
14pub mod promise;
15pub mod query;
16pub mod resources;
17pub mod schedule;
18pub mod system;
19pub mod system_param;
20
21#[cfg(test)]
22mod tests {
23    use std::{cell::RefCell, rc::Rc};
24
25    use crate::ecs::{
26        commands::{Commands, ResourceCommandQueue, TriggerQueue},
27        local::Local,
28        resources::{Read, Resources, Write},
29        schedule::Schedule,
30    };
31
32    #[test]
33    fn resources_insert_and_get() {
34        let mut resources = Resources::default();
35        resources.insert(42i32);
36
37        assert_eq!(*resources.get::<i32>(), 42);
38    }
39
40    #[test]
41    fn resources_get_mut_updates_value() {
42        let mut resources = Resources::default();
43        resources.insert(1i32);
44
45        *resources.get_mut::<i32>() += 1;
46
47        assert_eq!(*resources.get::<i32>(), 2);
48    }
49
50    #[test]
51    fn resources_contains() {
52        let mut resources = Resources::default();
53        assert!(!resources.contains::<i32>());
54
55        resources.insert(1i32);
56        assert!(resources.contains::<i32>());
57    }
58
59    #[test]
60    fn resources_remove() {
61        let mut resources = Resources::default();
62        resources.insert(7i32);
63
64        let removed = resources.remove::<i32>();
65
66        assert_eq!(removed, Some(7));
67        assert!(!resources.contains::<i32>());
68    }
69
70    #[test]
71    #[should_panic(expected = "Resource not found")]
72    fn resources_get_missing_panics() {
73        let resources = Resources::default();
74        resources.get::<i32>();
75    }
76
77    #[test]
78    fn schedule_runs_systems_in_order() {
79        let mut world = hecs::World::default();
80        let mut resources = Resources::default();
81        resources.insert(0i32);
82        resources.insert(hecs::CommandBuffer::default());
83
84        let mut schedule = Schedule::default();
85        schedule.add_system(|mut counter: Write<i32>| {
86            *counter += 1;
87        });
88
89        schedule.run(&mut world, &mut resources);
90        schedule.run(&mut world, &mut resources);
91
92        assert_eq!(*resources.get::<i32>(), 2);
93    }
94
95    #[test]
96    fn commands_spawn_is_applied_on_sync() {
97        let mut world = hecs::World::default();
98        let mut resources = Resources::default();
99        resources.insert(hecs::CommandBuffer::default());
100        resources.insert(ResourceCommandQueue::default());
101        resources.insert(TriggerQueue::default());
102
103        let mut schedule = Schedule::default();
104        schedule.add_system(|mut commands: Commands| {
105            commands.spawn((1i32,));
106        });
107
108        assert_eq!(world.len(), 0);
109
110        schedule.run(&mut world, &mut resources);
111
112        assert_eq!(world.len(), 1);
113    }
114
115    #[test]
116    fn commands_insert_resource_is_applied_on_sync() {
117        let mut world = hecs::World::default();
118        let mut resources = Resources::default();
119        resources.insert(hecs::CommandBuffer::default());
120        resources.insert(ResourceCommandQueue::default());
121        resources.insert(TriggerQueue::default());
122
123        let mut schedule = Schedule::default();
124        schedule.add_system(|mut commands: Commands| {
125            commands.insert_resource(42i32);
126        });
127
128        assert!(!resources.contains::<i32>());
129
130        schedule.run(&mut world, &mut resources);
131
132        assert_eq!(*resources.get::<i32>(), 42);
133    }
134
135    #[test]
136    fn commands_remove_resource_is_applied_on_sync() {
137        let mut world = hecs::World::default();
138        let mut resources = Resources::default();
139        resources.insert(hecs::CommandBuffer::default());
140        resources.insert(ResourceCommandQueue::default());
141        resources.insert(TriggerQueue::default());
142        resources.insert(42i32);
143
144        let mut schedule = Schedule::default();
145        schedule.add_system(|mut commands: Commands| {
146            commands.remove_resource::<i32>();
147        });
148
149        schedule.run(&mut world, &mut resources);
150
151        assert!(!resources.contains::<i32>());
152    }
153
154    #[test]
155    fn option_read_is_some_when_resource_present() {
156        let mut world = hecs::World::default();
157        let mut resources = Resources::default();
158        resources.insert(hecs::CommandBuffer::default());
159        resources.insert(42i32);
160
161        let mut schedule = Schedule::default();
162        schedule.add_system(|value: Option<Read<i32>>| {
163            assert_eq!(*value.expect("resource should be present"), 42);
164        });
165
166        schedule.run(&mut world, &mut resources);
167    }
168
169    #[test]
170    fn option_read_is_none_when_resource_missing() {
171        let mut world = hecs::World::default();
172        let mut resources = Resources::default();
173        resources.insert(hecs::CommandBuffer::default());
174
175        let mut schedule = Schedule::default();
176        schedule.add_system(|value: Option<Read<i32>>| {
177            assert!(value.is_none());
178        });
179
180        schedule.run(&mut world, &mut resources);
181    }
182
183    #[test]
184    fn option_write_mutates_resource_when_present() {
185        let mut world = hecs::World::default();
186        let mut resources = Resources::default();
187        resources.insert(hecs::CommandBuffer::default());
188        resources.insert(1i32);
189
190        let mut schedule = Schedule::default();
191        schedule.add_system(|value: Option<Write<i32>>| {
192            *value.expect("resource should be present") += 1;
193        });
194
195        schedule.run(&mut world, &mut resources);
196
197        assert_eq!(*resources.get::<i32>(), 2);
198    }
199
200    #[test]
201    fn option_write_is_none_when_resource_missing() {
202        let mut world = hecs::World::default();
203        let mut resources = Resources::default();
204        resources.insert(hecs::CommandBuffer::default());
205
206        let mut schedule = Schedule::default();
207        schedule.add_system(|value: Option<Write<i32>>| {
208            assert!(value.is_none());
209        });
210
211        schedule.run(&mut world, &mut resources);
212    }
213
214    #[test]
215    fn local_defaults_to_type_default() {
216        let mut world = hecs::World::default();
217        let mut resources = Resources::default();
218        resources.insert(hecs::CommandBuffer::default());
219
220        let mut schedule = Schedule::default();
221        schedule.add_system(|counter: Local<i32>| {
222            assert_eq!(*counter, 0);
223        });
224
225        schedule.run(&mut world, &mut resources);
226    }
227
228    #[test]
229    fn local_persists_across_runs() {
230        let mut world = hecs::World::default();
231        let mut resources = Resources::default();
232        resources.insert(hecs::CommandBuffer::default());
233
234        let log = Rc::new(RefCell::new(Vec::new()));
235        let log_clone = log.clone();
236
237        let mut schedule = Schedule::default();
238        schedule.add_system(move |mut counter: Local<i32>| {
239            *counter += 1;
240            log_clone.borrow_mut().push(*counter);
241        });
242
243        schedule.run(&mut world, &mut resources);
244        schedule.run(&mut world, &mut resources);
245        schedule.run(&mut world, &mut resources);
246
247        assert_eq!(*log.borrow(), vec![1, 2, 3]);
248    }
249
250    #[test]
251    fn local_state_is_isolated_per_system() {
252        let mut world = hecs::World::default();
253        let mut resources = Resources::default();
254        resources.insert(hecs::CommandBuffer::default());
255
256        let log_a = Rc::new(RefCell::new(Vec::new()));
257        let log_b = Rc::new(RefCell::new(Vec::new()));
258        let (la, lb) = (log_a.clone(), log_b.clone());
259
260        let mut schedule = Schedule::default();
261        schedule
262            .add_system(move |mut counter: Local<i32>| {
263                *counter += 1;
264                la.borrow_mut().push(*counter);
265            })
266            .add_system(move |mut counter: Local<i32>| {
267                *counter += 10;
268                lb.borrow_mut().push(*counter);
269            });
270
271        schedule.run(&mut world, &mut resources);
272        schedule.run(&mut world, &mut resources);
273
274        assert_eq!(*log_a.borrow(), vec![1, 2]);
275        assert_eq!(*log_b.borrow(), vec![10, 20]);
276    }
277}