1use crate::component::Component;
2use crate::entity::Entity;
3use crate::system::{Res, SystemParam};
4use crate::world::World;
5use std::sync::Arc;
6
7use crossbeam_queue::SegQueue;
8
9type BoxedCommand = Box<dyn FnOnce(&mut World) + Send + Sync>;
10
11#[derive(Default, Clone)]
14pub struct CommandQueue {
15 queue: Arc<SegQueue<BoxedCommand>>,
16}
17
18impl CommandQueue {
19 pub fn new() -> Self {
20 Self::default()
21 }
22
23 pub fn push<F>(&self, command: F)
24 where
25 F: FnOnce(&mut World) + Send + Sync + 'static,
26 {
27 self.queue.push(Box::new(command));
28 }
29
30 pub fn is_empty(&self) -> bool {
31 self.queue.is_empty()
32 }
33
34 pub fn apply(&self, world: &mut World) {
35 while let Some(command) = self.queue.pop() {
36 command(world);
37 }
38 }
39}
40
41pub struct Commands<'w> {
43 pub queue: Res<'w, CommandQueue>,
44 pub entities: Res<'w, crate::entity::allocator::Entities>,
45}
46
47impl SystemParam for Commands<'static> {
48 type Item<'w> = Commands<'w>;
49
50 fn fetch<'w>(
51 world: &'w World,
52 dt: f32,
53 ) -> Result<Self::Item<'w>, crate::system::SystemParamFetchError> {
54 let queue = <Res<'static, CommandQueue> as SystemParam>::fetch(world, dt)?;
55 let entities =
56 <Res<'static, crate::entity::allocator::Entities> as SystemParam>::fetch(world, dt)?;
57 Ok(Commands { queue, entities })
58 }
59
60 fn get_access_info(info: &mut crate::system::AccessInfo) {
61 <Res<'static, CommandQueue> as SystemParam>::get_access_info(info);
62 <Res<'static, crate::entity::allocator::Entities> as SystemParam>::get_access_info(info);
63 }
64}
65
66impl<'w> Commands<'w> {
67 pub fn spawn(&mut self) -> EntityCommands<'_, 'w> {
69 let entity = self.entities.reserve_entity();
70
71 self.queue.push(move |world| {
72 world.flush_spawn(entity);
73 });
74
75 EntityCommands {
76 entity,
77 commands: self,
78 }
79 }
80
81 pub fn entity(&mut self, entity: Entity) -> EntityCommands<'_, 'w> {
83 EntityCommands {
84 entity,
85 commands: self,
86 }
87 }
88}
89
90pub struct EntityCommands<'a, 'w> {
91 entity: Entity,
92 commands: &'a mut Commands<'w>,
93}
94
95impl<'a, 'w> EntityCommands<'a, 'w> {
96 pub fn id(&self) -> Entity {
98 self.entity
99 }
100
101 pub fn insert<T: Component>(&mut self, component: T) -> &mut Self {
103 let e = self.entity;
104 self.commands.queue.push(move |world| {
105 world.add_component(e, component);
106 });
107 self
108 }
109
110 pub fn remove<T: Component>(&mut self) -> &mut Self {
112 let e = self.entity;
113 self.commands.queue.push(move |world| {
114 world.remove_component::<T>(e);
115 });
116 self
117 }
118
119 pub fn despawn(&mut self) {
121 let e = self.entity;
122 self.commands.queue.push(move |world| {
123 world.despawn(e);
124 });
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::system::Schedule;
132
133 use crate::world::World;
134
135 #[derive(Clone, PartialEq, Debug)]
136 struct ComponentA(i32);
137 impl Component for ComponentA {}
138
139 #[derive(Clone, PartialEq, Debug)]
140 struct ComponentB(f32);
141 impl Component for ComponentB {}
142
143 #[test]
144 fn test_command_queue_push_and_apply() {
145 let mut world = World::new();
146 let queue = CommandQueue::new();
147
148 queue.push(|w| {
149 let e = w.spawn();
150 w.add_component(e, ComponentA(42));
151 });
152
153 assert_eq!(world.entity_count(), 0);
155
156 queue.apply(&mut world);
157
158 assert_eq!(world.entity_count(), 1);
160
161 let mut count = 0;
162 if let Some(q) = world.query::<&ComponentA>() {
163 for (_, c) in q.iter() {
164 assert_eq!(c.0, 42);
165 count += 1;
166 }
167 }
168 assert_eq!(count, 1);
169 }
170
171 #[test]
172 fn test_commands_system_spawn_and_insert() {
173 let mut world = World::new();
174 let mut schedule = Schedule::new();
175
176 schedule.add_di_system::<(Commands<'static>,), _>(|mut commands: Commands| {
177 commands
178 .spawn()
179 .insert(ComponentA(100))
180 .insert(ComponentB(3.14));
181 });
182
183 schedule.run(&mut world, 0.1);
184
185 let mut count = 0;
186 if let Some(q) = world.query::<(&ComponentA, &ComponentB)>() {
187 for (_, (ca, cb)) in q.iter() {
188 assert_eq!(ca.0, 100);
189 assert_eq!(cb.0, 3.14);
190 count += 1;
191 }
192 }
193 assert_eq!(count, 1);
194 }
195
196 #[test]
197 fn test_commands_system_despawn() {
198 let mut world = World::new();
199
200 let e1 = world.spawn();
201 world.add_component(e1, ComponentA(10));
202
203 let e2 = world.spawn();
204 world.add_component(e2, ComponentA(20));
205
206 let mut schedule = Schedule::new();
207
208 schedule.add_system(|world: &World, dt: f32| {
210 let mut commands = Commands::fetch(world, dt).unwrap();
211 if let Some(q) = world.query::<&ComponentA>() {
212 for (id, c) in q.iter() {
213 if c.0 == 10 {
214 commands.entity(Entity::new(id, 0)).despawn();
215 }
216 }
217 }
218 });
219
220 schedule.run(&mut world, 0.1);
221
222 assert_eq!(world.entity_count(), 1);
223 if let Some(q) = world.query::<&ComponentA>() {
224 for (_, c) in q.iter() {
225 assert_eq!(c.0, 20);
226 }
227 }
228 }
229
230 #[test]
231 fn test_commands_system_remove_component() {
232 let mut world = World::new();
233
234 let e = world.spawn();
235 world.add_component(e, ComponentA(1));
236 world.add_component(e, ComponentB(2.0));
237
238 let mut schedule = Schedule::new();
239
240 schedule.add_system(|world: &World, dt: f32| {
241 let mut commands = Commands::fetch(world, dt).unwrap();
242 if let Some(q) = world.query::<&ComponentA>() {
243 for (id, _) in q.iter() {
244 commands.entity(Entity::new(id, 0)).remove::<ComponentA>();
245 }
246 }
247 });
248
249 schedule.run(&mut world, 0.1);
250
251 assert_eq!(world.entity_count(), 1);
252
253 let mut has_a = false;
254 if let Some(q) = world.query::<&ComponentA>() {
255 has_a = q.iter().count() > 0;
256 }
257 assert!(!has_a, "ComponentA still exists!");
258
259 let mut has_b = false;
260 if let Some(q) = world.query::<&ComponentB>() {
261 has_b = q.iter().count() > 0;
262 }
263 assert!(has_b, "ComponentB was unexpectedly removed!");
264 }
265}