1use crate::component::ComponentStorage;
2use crate::simple_world::{EntityId, SimpleWorld};
3use alloc::vec::Vec;
4use soroban_sdk::{Bytes, Symbol};
5
6enum CommandKind {
8 Spawn,
9 Despawn,
10 AddComponent,
11 RemoveComponent,
12}
13
14struct CommandEntry {
16 kind: CommandKind,
17 entity_id: Option<EntityId>,
18 component_type: Option<Symbol>,
19 data: Option<Bytes>,
20 storage: ComponentStorage,
21}
22
23pub struct CommandQueue {
47 commands: Vec<CommandEntry>,
48}
49
50impl CommandQueue {
51 pub fn new() -> Self {
53 Self {
54 commands: Vec::new(),
55 }
56 }
57
58 pub fn spawn(&mut self) {
60 self.commands.push(CommandEntry {
61 kind: CommandKind::Spawn,
62 entity_id: None,
63 component_type: None,
64 data: None,
65 storage: ComponentStorage::Table,
66 });
67 }
68
69 pub fn despawn(&mut self, entity_id: EntityId) {
71 self.commands.push(CommandEntry {
72 kind: CommandKind::Despawn,
73 entity_id: Some(entity_id),
74 component_type: None,
75 data: None,
76 storage: ComponentStorage::Table,
77 });
78 }
79
80 pub fn add_component(&mut self, entity_id: EntityId, component_type: Symbol, data: Bytes) {
82 self.commands.push(CommandEntry {
83 kind: CommandKind::AddComponent,
84 entity_id: Some(entity_id),
85 component_type: Some(component_type),
86 data: Some(data),
87 storage: ComponentStorage::Table,
88 });
89 }
90
91 pub fn add_sparse_component(
93 &mut self,
94 entity_id: EntityId,
95 component_type: Symbol,
96 data: Bytes,
97 ) {
98 self.commands.push(CommandEntry {
99 kind: CommandKind::AddComponent,
100 entity_id: Some(entity_id),
101 component_type: Some(component_type),
102 data: Some(data),
103 storage: ComponentStorage::Sparse,
104 });
105 }
106
107 pub fn remove_component(&mut self, entity_id: EntityId, component_type: Symbol) {
109 self.commands.push(CommandEntry {
110 kind: CommandKind::RemoveComponent,
111 entity_id: Some(entity_id),
112 component_type: Some(component_type),
113 data: None,
114 storage: ComponentStorage::Table,
115 });
116 }
117
118 pub fn apply(self, world: &mut SimpleWorld) -> Vec<EntityId> {
123 let mut spawned_ids = Vec::new();
124
125 for entry in self.commands {
126 match entry.kind {
127 CommandKind::Spawn => {
128 let id = world.spawn_entity();
129 spawned_ids.push(id);
130 }
131 CommandKind::Despawn => {
132 if let Some(entity_id) = entry.entity_id {
133 world.despawn_entity(entity_id);
134 }
135 }
136 CommandKind::AddComponent => {
137 if let (Some(entity_id), Some(component_type), Some(data)) =
138 (entry.entity_id, entry.component_type, entry.data)
139 {
140 world.add_component_with_storage(
141 entity_id,
142 component_type,
143 data,
144 entry.storage,
145 );
146 }
147 }
148 CommandKind::RemoveComponent => {
149 if let (Some(entity_id), Some(component_type)) =
150 (entry.entity_id, entry.component_type)
151 {
152 world.remove_component(entity_id, &component_type);
153 }
154 }
155 }
156 }
157
158 spawned_ids
159 }
160
161 pub fn is_empty(&self) -> bool {
163 self.commands.is_empty()
164 }
165
166 pub fn len(&self) -> usize {
168 self.commands.len()
169 }
170}
171
172impl Default for CommandQueue {
173 fn default() -> Self {
174 Self::new()
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use soroban_sdk::{symbol_short, Env};
182
183 #[test]
184 fn test_empty_queue() {
185 let queue = CommandQueue::new();
186 assert!(queue.is_empty());
187 assert_eq!(queue.len(), 0);
188
189 let env = Env::default();
190 let mut world = SimpleWorld::new(&env);
191 let spawned = queue.apply(&mut world);
192 assert!(spawned.is_empty());
193 }
194
195 #[test]
196 fn test_spawn_via_queue() {
197 let env = Env::default();
198 let mut world = SimpleWorld::new(&env);
199 let mut queue = CommandQueue::new();
200
201 queue.spawn();
202 queue.spawn();
203 assert_eq!(queue.len(), 2);
204
205 let spawned = queue.apply(&mut world);
206 assert_eq!(spawned.len(), 2);
207 assert_eq!(spawned[0], 1);
208 assert_eq!(spawned[1], 2);
209 }
210
211 #[test]
212 fn test_despawn_via_queue() {
213 let env = Env::default();
214 let mut world = SimpleWorld::new(&env);
215 let e1 = world.spawn_entity();
216 let data = Bytes::from_array(&env, &[1]);
217 world.add_component(e1, symbol_short!("pos"), data);
218
219 let mut queue = CommandQueue::new();
220 queue.despawn(e1);
221 queue.apply(&mut world);
222
223 assert!(!world.has_component(e1, &symbol_short!("pos")));
224 }
225
226 #[test]
227 fn test_add_component_via_queue() {
228 let env = Env::default();
229 let mut world = SimpleWorld::new(&env);
230 let e1 = world.spawn_entity();
231
232 let mut queue = CommandQueue::new();
233 let data = Bytes::from_array(&env, &[1, 2, 3]);
234 queue.add_component(e1, symbol_short!("pos"), data.clone());
235 queue.apply(&mut world);
236
237 assert!(world.has_component(e1, &symbol_short!("pos")));
238 assert_eq!(world.get_component(e1, &symbol_short!("pos")), Some(data));
239 }
240
241 #[test]
242 fn test_add_sparse_component_via_queue() {
243 let env = Env::default();
244 let mut world = SimpleWorld::new(&env);
245 let e1 = world.spawn_entity();
246
247 let mut queue = CommandQueue::new();
248 let data = Bytes::from_array(&env, &[0xAA]);
249 queue.add_sparse_component(e1, symbol_short!("tag"), data.clone());
250 queue.apply(&mut world);
251
252 assert!(world.has_component(e1, &symbol_short!("tag")));
253 assert_eq!(world.table_component_count(&symbol_short!("tag")), 0);
254 assert_eq!(world.component_count(&symbol_short!("tag")), 1);
255 }
256
257 #[test]
258 fn test_remove_component_via_queue() {
259 let env = Env::default();
260 let mut world = SimpleWorld::new(&env);
261 let e1 = world.spawn_entity();
262 let data = Bytes::from_array(&env, &[1]);
263 world.add_component(e1, symbol_short!("pos"), data);
264
265 let mut queue = CommandQueue::new();
266 queue.remove_component(e1, symbol_short!("pos"));
267 queue.apply(&mut world);
268
269 assert!(!world.has_component(e1, &symbol_short!("pos")));
270 }
271
272 #[test]
273 fn test_mixed_operations() {
274 let env = Env::default();
275 let mut world = SimpleWorld::new(&env);
276
277 let e1 = world.spawn_entity();
279 let data = Bytes::from_array(&env, &[1]);
280 world.add_component(e1, symbol_short!("old"), data);
281
282 let mut queue = CommandQueue::new();
283 queue.spawn();
285 let new_data = Bytes::from_array(&env, &[2, 3]);
286 queue.add_component(e1, symbol_short!("new"), new_data.clone());
287 queue.remove_component(e1, symbol_short!("old"));
288
289 let spawned = queue.apply(&mut world);
290 assert_eq!(spawned.len(), 1);
291 assert_eq!(spawned[0], 2); assert!(world.has_component(e1, &symbol_short!("new")));
293 assert!(!world.has_component(e1, &symbol_short!("old")));
294 }
295
296 #[test]
297 fn test_queue_len_tracking() {
298 let env = Env::default();
299 let mut queue = CommandQueue::new();
300 assert_eq!(queue.len(), 0);
301 assert!(queue.is_empty());
302
303 queue.spawn();
304 assert_eq!(queue.len(), 1);
305 assert!(!queue.is_empty());
306
307 let data = Bytes::from_array(&env, &[1]);
308 queue.add_component(1, symbol_short!("test"), data);
309 assert_eq!(queue.len(), 2);
310
311 queue.remove_component(1, symbol_short!("test"));
312 assert_eq!(queue.len(), 3);
313
314 queue.despawn(1);
315 assert_eq!(queue.len(), 4);
316 }
317}