Skip to main content

gizmo_core/
commands.rs

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/// Otonom iterasyonlar ve sistemler içerisinden güvenli bir şekilde `World`e
12/// müdahale etmeyi (spawn, despawn, bileşen ekleme/çıkarma) sağlayan kilitsiz komut kuyruğu.
13#[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
41/// Sistem imzasında kullanılabilecek `Commands` parametresi.
42pub struct Commands<'w> {
43    pub queue: Res<'w, CommandQueue>,
44    pub entities: Res<'w, crate::entity::allocator::Entities>,
45}
46
47impl crate::system::sealed::Sealed for Commands<'static> {}
48impl SystemParam for Commands<'static> {
49    type Item<'w> = Commands<'w>;
50
51    fn fetch<'w>(
52        world: &'w World,
53        dt: f32,
54    ) -> Result<Self::Item<'w>, crate::system::SystemParamFetchError> {
55        let queue = <Res<'static, CommandQueue> as SystemParam>::fetch(world, dt)?;
56        let entities =
57            <Res<'static, crate::entity::allocator::Entities> as SystemParam>::fetch(world, dt)?;
58        Ok(Commands { queue, entities })
59    }
60
61    fn get_access_info(info: &mut crate::system::AccessInfo) {
62        <Res<'static, CommandQueue> as SystemParam>::get_access_info(info);
63        <Res<'static, crate::entity::allocator::Entities> as SystemParam>::get_access_info(info);
64    }
65}
66
67impl<'w> Commands<'w> {
68    /// Yeni bir entity oluşturur ve onun üzerine eklentiler yapmak için `EntityCommands` döndürür.
69    pub fn spawn(&mut self) -> EntityCommands<'_, 'w> {
70        let entity = self.entities.reserve_entity();
71
72        self.queue.push(move |world| {
73            world.flush_spawn(entity);
74        });
75
76        EntityCommands {
77            entity,
78            commands: self,
79        }
80    }
81
82    /// Var olan bir entity üzerinde işlemler yapmak için `EntityCommands` alır.
83    pub fn entity(&mut self, entity: Entity) -> EntityCommands<'_, 'w> {
84        EntityCommands {
85            entity,
86            commands: self,
87        }
88    }
89}
90
91pub struct EntityCommands<'a, 'w> {
92    entity: Entity,
93    commands: &'a mut Commands<'w>,
94}
95
96impl<'a, 'w> EntityCommands<'a, 'w> {
97    /// Bu komut tamponunun hedeflendiği native Entity ID'sini döndürür.
98    pub fn id(&self) -> Entity {
99        self.entity
100    }
101
102    /// Entity'ye yeni bir bileşen ekler (Entity o an veya sonradan oluşur olsun fark etmez)
103    pub fn insert<T: Component>(&mut self, component: T) -> &mut Self {
104        let e = self.entity;
105        self.commands.queue.push(move |world| {
106            world.add_component(e, component);
107        });
108        self
109    }
110
111    /// Entity'den bir bileşen çıkarır
112    pub fn remove<T: Component>(&mut self) -> &mut Self {
113        let e = self.entity;
114        self.commands.queue.push(move |world| {
115            world.remove_component::<T>(e);
116        });
117        self
118    }
119
120    /// Entity'yi tamamen yok eder
121    pub fn despawn(&mut self) {
122        let e = self.entity;
123        self.commands.queue.push(move |world| {
124            world.despawn(e);
125        });
126    }
127
128    /// Entity'yi ve altındaki tüm çocukları yok eder (recursive)
129    pub fn despawn_recursive(&mut self) {
130        use crate::hierarchy::HierarchyExt;
131        let e = self.entity;
132        self.commands.queue.push(move |world| {
133            world.despawn_recursive(e);
134        });
135    }
136
137    /// Bu entity'ye bir çocuk ekler
138    pub fn add_child(&mut self, child: Entity) -> &mut Self {
139        use crate::hierarchy::HierarchyExt;
140        let p = self.entity;
141        self.commands.queue.push(move |world| {
142            world.add_child(p, child);
143        });
144        self
145    }
146
147    /// Bu entity'den bir çocuğu koparır
148    pub fn remove_child(&mut self, child: Entity) -> &mut Self {
149        use crate::hierarchy::HierarchyExt;
150        let p = self.entity;
151        self.commands.queue.push(move |world| {
152            world.remove_child(p, child);
153        });
154        self
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    use crate::system::Schedule;
162
163    use crate::world::World;
164
165    #[derive(Clone, PartialEq, Debug)]
166    struct ComponentA(i32);
167    impl Component for ComponentA {}
168
169    #[derive(Clone, PartialEq, Debug)]
170    struct ComponentB(f32);
171    impl Component for ComponentB {}
172
173    #[test]
174    fn test_command_queue_push_and_apply() {
175        let mut world = World::new();
176        let queue = CommandQueue::new();
177
178        queue.push(|w| {
179            let e = w.spawn();
180            w.add_component(e, ComponentA(42));
181        });
182
183        // Apply öncesi entity yok
184        assert_eq!(world.entity_count(), 0);
185
186        queue.apply(&mut world);
187
188        // Apply sonrası 1 entity var ve componenti eklenmiş
189        assert_eq!(world.entity_count(), 1);
190
191        let mut count = 0;
192        if let Some(q) = world.query::<&ComponentA>() {
193            for (_, c) in q.iter() {
194                assert_eq!(c.0, 42);
195                count += 1;
196            }
197        }
198        assert_eq!(count, 1);
199    }
200
201    #[test]
202    fn test_commands_system_spawn_and_insert() {
203        let mut world = World::new();
204        let mut schedule = Schedule::new();
205
206        schedule.add_di_system::<(Commands<'static>,), _>(|mut commands: Commands| {
207            commands
208                .spawn()
209                .insert(ComponentA(100))
210                .insert(ComponentB(2.5));
211        });
212
213        schedule.run(&mut world, 0.1);
214
215        let mut count = 0;
216        if let Some(q) = world.query::<(&ComponentA, &ComponentB)>() {
217            for (_, (ca, cb)) in q.iter() {
218                assert_eq!(ca.0, 100);
219                assert_eq!(cb.0, 2.5);
220                count += 1;
221            }
222        }
223        assert_eq!(count, 1);
224    }
225
226    #[test]
227    fn test_commands_system_despawn() {
228        let mut world = World::new();
229
230        let e1 = world.spawn();
231        world.add_component(e1, ComponentA(10));
232
233        let e2 = world.spawn();
234        world.add_component(e2, ComponentA(20));
235
236        let mut schedule = Schedule::new();
237
238        // Use a standard (&World, f32) system to access query and manually fetch Commands
239        schedule.add_system(|world: &World, dt: f32| {
240            let mut commands = Commands::fetch(world, dt).unwrap();
241            if let Some(q) = world.query::<&ComponentA>() {
242                for (id, c) in q.iter() {
243                    if c.0 == 10 {
244                        commands.entity(Entity::new(id, 0)).despawn();
245                    }
246                }
247            }
248        });
249
250        schedule.run(&mut world, 0.1);
251
252        assert_eq!(world.entity_count(), 1);
253        if let Some(q) = world.query::<&ComponentA>() {
254            for (_, c) in q.iter() {
255                assert_eq!(c.0, 20);
256            }
257        }
258    }
259
260    #[test]
261    fn test_commands_system_remove_component() {
262        let mut world = World::new();
263
264        let e = world.spawn();
265        world.add_component(e, ComponentA(1));
266        world.add_component(e, ComponentB(2.0));
267
268        let mut schedule = Schedule::new();
269
270        schedule.add_system(|world: &World, dt: f32| {
271            let mut commands = Commands::fetch(world, dt).unwrap();
272            if let Some(q) = world.query::<&ComponentA>() {
273                for (id, _) in q.iter() {
274                    commands.entity(Entity::new(id, 0)).remove::<ComponentA>();
275                }
276            }
277        });
278
279        schedule.run(&mut world, 0.1);
280
281        assert_eq!(world.entity_count(), 1);
282
283        let mut has_a = false;
284        if let Some(q) = world.query::<&ComponentA>() {
285            has_a = q.iter().count() > 0;
286        }
287        assert!(!has_a, "ComponentA still exists!");
288
289        let mut has_b = false;
290        if let Some(q) = world.query::<&ComponentB>() {
291            has_b = q.iter().count() > 0;
292        }
293        assert!(has_b, "ComponentB was unexpectedly removed!");
294    }
295}