galeon_engine/
commands.rs1use std::any::TypeId;
4
5use crate::component::Component;
6use crate::deadline::{DeadlineId, Timestamp};
7use crate::entity::Entity;
8use crate::system_param::Access;
9use crate::world::{Bundle, UnsafeWorldCell, World};
10
11type BoxedCommand = Box<dyn FnOnce(&mut World) + Send>;
13
14pub struct CommandBuffer {
23 queue: Vec<BoxedCommand>,
24}
25
26impl CommandBuffer {
27 pub fn new() -> Self {
28 Self { queue: Vec::new() }
29 }
30
31 fn push(&mut self, cmd: impl FnOnce(&mut World) + Send + 'static) {
33 self.queue.push(Box::new(cmd));
34 }
35
36 pub fn len(&self) -> usize {
38 self.queue.len()
39 }
40
41 pub fn is_empty(&self) -> bool {
43 self.queue.is_empty()
44 }
45
46 pub(crate) fn take(&mut self) -> Vec<BoxedCommand> {
51 std::mem::take(&mut self.queue)
52 }
53}
54
55impl Default for CommandBuffer {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61pub struct Commands<'w> {
80 buffer: &'w mut CommandBuffer,
81}
82
83impl<'w> Commands<'w> {
84 pub fn spawn<B: Bundle + Send + 'static>(&mut self, bundle: B) {
86 self.buffer.push(move |world: &mut World| {
87 world.spawn(bundle);
88 });
89 }
90
91 pub fn despawn(&mut self, entity: Entity) {
93 self.buffer.push(move |world: &mut World| {
94 world.despawn(entity);
95 });
96 }
97
98 pub fn insert<C: Component>(&mut self, entity: Entity, component: C) {
102 self.buffer.push(move |world: &mut World| {
103 world.insert(entity, component);
104 });
105 }
106
107 pub fn remove<C: Component>(&mut self, entity: Entity) {
109 self.buffer.push(move |world: &mut World| {
110 world.remove::<C>(entity);
111 });
112 }
113
114 pub fn schedule_deadline<T: Send + 'static>(&mut self, deadline: Timestamp, event: T) {
119 self.buffer.push(move |world: &mut World| {
120 world.schedule_deadline(deadline, event);
121 });
122 }
123
124 pub fn cancel_deadline<T: Send + 'static>(&mut self, id: DeadlineId) {
126 self.buffer.push(move |world: &mut World| {
127 world.cancel_deadline::<T>(id);
128 });
129 }
130
131 pub fn len(&self) -> usize {
133 self.buffer.len()
134 }
135
136 pub fn is_empty(&self) -> bool {
138 self.buffer.is_empty()
139 }
140}
141
142unsafe impl crate::system_param::SystemParam for Commands<'_> {
150 type Item<'w> = Commands<'w>;
151
152 fn access() -> Vec<Access> {
153 vec![Access::ResWrite(TypeId::of::<CommandBuffer>())]
157 }
158
159 unsafe fn fetch<'w>(world: UnsafeWorldCell) -> Commands<'w> {
160 Commands {
161 buffer: unsafe { world.commands_mut() },
162 }
163 }
164}
165
166#[cfg(test)]
171mod tests {
172 use super::*;
173 use crate::component::Component;
174 use crate::system_param::SystemParam;
175
176 #[derive(Debug, Clone, PartialEq)]
177 struct Pos {
178 x: f32,
179 y: f32,
180 }
181 impl Component for Pos {}
182
183 #[derive(Debug, Clone, PartialEq)]
184 struct Vel {
185 x: f32,
186 y: f32,
187 }
188 impl Component for Vel {}
189
190 #[allow(dead_code)]
191 #[derive(Debug, Clone, PartialEq)]
192 struct Health(i32);
193 impl Component for Health {}
194
195 #[test]
198 fn command_buffer_starts_empty() {
199 let buf = CommandBuffer::new();
200 assert!(buf.is_empty());
201 assert_eq!(buf.len(), 0);
202 }
203
204 #[test]
205 fn command_buffer_tracks_length() {
206 let mut buf = CommandBuffer::new();
207 buf.push(|_| {});
208 buf.push(|_| {});
209 assert_eq!(buf.len(), 2);
210 assert!(!buf.is_empty());
211 }
212
213 #[test]
214 fn command_buffer_take_drains_all() {
215 let mut buf = CommandBuffer::new();
216 buf.push(|world: &mut World| {
217 world.spawn((Pos { x: 1.0, y: 2.0 },));
218 });
219 buf.push(|world: &mut World| {
220 world.spawn((Pos { x: 3.0, y: 4.0 },));
221 });
222
223 let mut world = World::new();
224 let commands = buf.take();
225 assert!(buf.is_empty());
226 for cmd in commands {
227 cmd(&mut world);
228 }
229 assert_eq!(world.entity_count(), 2);
230 }
231
232 #[test]
235 fn commands_spawn_deferred() {
236 let mut world = World::new();
237 assert_eq!(world.entity_count(), 0);
238
239 {
241 let buf = world.command_buffer_mut();
242 let mut cmds = Commands { buffer: buf };
243 cmds.spawn((Pos { x: 1.0, y: 2.0 },));
244 }
245
246 assert_eq!(world.entity_count(), 0);
248
249 world.apply_commands();
251 assert_eq!(world.entity_count(), 1);
252
253 let xs: Vec<f32> = world.query::<&Pos>().map(|(_, p)| p.x).collect();
254 assert_eq!(xs, vec![1.0]);
255 }
256
257 #[test]
258 fn commands_despawn_deferred() {
259 let mut world = World::new();
260 let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
261
262 {
263 let buf = world.command_buffer_mut();
264 let mut cmds = Commands { buffer: buf };
265 cmds.despawn(e);
266 }
267
268 assert!(world.is_alive(e));
270
271 world.apply_commands();
272 assert!(!world.is_alive(e));
273 }
274
275 #[test]
276 fn commands_insert_deferred() {
277 let mut world = World::new();
278 let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
279
280 {
281 let buf = world.command_buffer_mut();
282 let mut cmds = Commands { buffer: buf };
283 cmds.insert(e, Vel { x: 3.0, y: 4.0 });
284 }
285
286 assert!(world.get::<Vel>(e).is_none());
288
289 world.apply_commands();
290 assert_eq!(world.get::<Vel>(e).unwrap().x, 3.0);
291 assert_eq!(world.get::<Pos>(e).unwrap().x, 1.0);
293 }
294
295 #[test]
296 fn commands_remove_deferred() {
297 let mut world = World::new();
298 let e = world.spawn((Pos { x: 1.0, y: 2.0 }, Vel { x: 3.0, y: 4.0 }));
299
300 {
301 let buf = world.command_buffer_mut();
302 let mut cmds = Commands { buffer: buf };
303 cmds.remove::<Vel>(e);
304 }
305
306 assert!(world.get::<Vel>(e).is_some());
308
309 world.apply_commands();
310 assert!(world.get::<Vel>(e).is_none());
311 assert_eq!(world.get::<Pos>(e).unwrap().x, 1.0);
312 }
313
314 #[test]
315 fn commands_multiple_ops_applied_in_order() {
316 let mut world = World::new();
317 let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
318
319 {
320 let buf = world.command_buffer_mut();
321 let mut cmds = Commands { buffer: buf };
322 cmds.insert(e, Vel { x: 10.0, y: 20.0 });
324 cmds.remove::<Vel>(e);
325 }
326
327 world.apply_commands();
328 assert!(world.get::<Vel>(e).is_none());
329 }
330
331 #[test]
332 fn commands_on_dead_entity_is_safe() {
333 let mut world = World::new();
334 let e = world.spawn((Pos { x: 1.0, y: 2.0 },));
335 world.despawn(e);
336
337 {
338 let buf = world.command_buffer_mut();
339 let mut cmds = Commands { buffer: buf };
340 cmds.insert(e, Vel { x: 1.0, y: 1.0 });
341 cmds.despawn(e);
342 cmds.remove::<Pos>(e);
343 }
344
345 world.apply_commands();
347 }
348
349 #[test]
350 fn commands_spawn_multi_component() {
351 let mut world = World::new();
352
353 {
354 let buf = world.command_buffer_mut();
355 let mut cmds = Commands { buffer: buf };
356 cmds.spawn((Pos { x: 1.0, y: 2.0 }, Vel { x: 3.0, y: 4.0 }));
357 }
358
359 world.apply_commands();
360 assert_eq!(world.entity_count(), 1);
361
362 let results: Vec<_> = world.query::<(&Pos, &Vel)>().collect();
363 assert_eq!(results.len(), 1);
364 assert_eq!(results[0].1.0.x, 1.0);
365 assert_eq!(results[0].1.1.x, 3.0);
366 }
367
368 #[test]
371 fn commands_access_uses_command_buffer_marker() {
372 let access = <Commands<'_> as SystemParam>::access();
373 assert_eq!(access.len(), 1);
374 assert_eq!(access[0], Access::ResWrite(TypeId::of::<CommandBuffer>()));
375 }
376
377 #[test]
378 fn commands_does_not_conflict_with_res() {
379 use crate::system_param::{Res, has_conflicts};
380 let a = <Commands<'_> as SystemParam>::access();
381 let b = <Res<'_, i32> as SystemParam>::access();
382 assert!(!has_conflicts(&a, &b));
383 }
384
385 #[test]
386 fn commands_does_not_conflict_with_query() {
387 use crate::system_param::{Query, has_conflicts};
388 let a = <Commands<'_> as SystemParam>::access();
389 let b = <Query<'_, Pos> as SystemParam>::access();
390 assert!(!has_conflicts(&a, &b));
391 }
392
393 #[test]
394 fn commands_fetch_via_unsafe_world_cell() {
395 let mut world = World::new();
396 let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
397 unsafe {
398 let mut cmds: Commands<'_> = <Commands<'_> as SystemParam>::fetch(cell);
399 cmds.spawn((Pos { x: 42.0, y: 0.0 },));
400 }
401 world.apply_commands();
402 assert_eq!(world.entity_count(), 1);
403 }
404}