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 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    /// Yeni bir entity oluşturur ve onun üzerine eklentiler yapmak için `EntityCommands` döndürür.
68    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    /// Var olan bir entity üzerinde işlemler yapmak için `EntityCommands` alır.
82    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    /// Bu komut tamponunun hedeflendiği native Entity ID'sini döndürür.
97    pub fn id(&self) -> Entity {
98        self.entity
99    }
100
101    /// Entity'ye yeni bir bileşen ekler (Entity o an veya sonradan oluşur olsun fark etmez)
102    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    /// Entity'den bir bileşen çıkarır
111    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    /// Entity'yi tamamen yok eder
120    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    /// Entity'yi ve altındaki tüm çocukları yok eder (recursive)
128    pub fn despawn_recursive(&mut self) {
129        use crate::hierarchy::HierarchyExt;
130        let e = self.entity;
131        self.commands.queue.push(move |world| {
132            world.despawn_recursive(e);
133        });
134    }
135
136    /// Bu entity'ye bir çocuk ekler
137    pub fn add_child(&mut self, child: Entity) -> &mut Self {
138        use crate::hierarchy::HierarchyExt;
139        let p = self.entity;
140        self.commands.queue.push(move |world| {
141            world.add_child(p, child);
142        });
143        self
144    }
145
146    /// Bu entity'den bir çocuğu koparır
147    pub fn remove_child(&mut self, child: Entity) -> &mut Self {
148        use crate::hierarchy::HierarchyExt;
149        let p = self.entity;
150        self.commands.queue.push(move |world| {
151            world.remove_child(p, child);
152        });
153        self
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160    use crate::system::Schedule;
161
162    use crate::world::World;
163
164    #[derive(Clone, PartialEq, Debug)]
165    struct ComponentA(i32);
166    impl Component for ComponentA {}
167
168    #[derive(Clone, PartialEq, Debug)]
169    struct ComponentB(f32);
170    impl Component for ComponentB {}
171
172    #[test]
173    fn test_command_queue_push_and_apply() {
174        let mut world = World::new();
175        let queue = CommandQueue::new();
176
177        queue.push(|w| {
178            let e = w.spawn();
179            w.add_component(e, ComponentA(42));
180        });
181
182        // Apply öncesi entity yok
183        assert_eq!(world.entity_count(), 0);
184
185        queue.apply(&mut world);
186
187        // Apply sonrası 1 entity var ve componenti eklenmiş
188        assert_eq!(world.entity_count(), 1);
189
190        let mut count = 0;
191        if let Some(q) = world.query::<&ComponentA>() {
192            for (_, c) in q.iter() {
193                assert_eq!(c.0, 42);
194                count += 1;
195            }
196        }
197        assert_eq!(count, 1);
198    }
199
200    #[test]
201    fn test_commands_system_spawn_and_insert() {
202        let mut world = World::new();
203        let mut schedule = Schedule::new();
204
205        schedule.add_di_system::<(Commands<'static>,), _>(|mut commands: Commands| {
206            commands
207                .spawn()
208                .insert(ComponentA(100))
209                .insert(ComponentB(3.14));
210        });
211
212        schedule.run(&mut world, 0.1);
213
214        let mut count = 0;
215        if let Some(q) = world.query::<(&ComponentA, &ComponentB)>() {
216            for (_, (ca, cb)) in q.iter() {
217                assert_eq!(ca.0, 100);
218                assert_eq!(cb.0, 3.14);
219                count += 1;
220            }
221        }
222        assert_eq!(count, 1);
223    }
224
225    #[test]
226    fn test_commands_system_despawn() {
227        let mut world = World::new();
228
229        let e1 = world.spawn();
230        world.add_component(e1, ComponentA(10));
231
232        let e2 = world.spawn();
233        world.add_component(e2, ComponentA(20));
234
235        let mut schedule = Schedule::new();
236
237        // Use a standard (&World, f32) system to access query and manually fetch Commands
238        schedule.add_system(|world: &World, dt: f32| {
239            let mut commands = Commands::fetch(world, dt).unwrap();
240            if let Some(q) = world.query::<&ComponentA>() {
241                for (id, c) in q.iter() {
242                    if c.0 == 10 {
243                        commands.entity(Entity::new(id, 0)).despawn();
244                    }
245                }
246            }
247        });
248
249        schedule.run(&mut world, 0.1);
250
251        assert_eq!(world.entity_count(), 1);
252        if let Some(q) = world.query::<&ComponentA>() {
253            for (_, c) in q.iter() {
254                assert_eq!(c.0, 20);
255            }
256        }
257    }
258
259    #[test]
260    fn test_commands_system_remove_component() {
261        let mut world = World::new();
262
263        let e = world.spawn();
264        world.add_component(e, ComponentA(1));
265        world.add_component(e, ComponentB(2.0));
266
267        let mut schedule = Schedule::new();
268
269        schedule.add_system(|world: &World, dt: f32| {
270            let mut commands = Commands::fetch(world, dt).unwrap();
271            if let Some(q) = world.query::<&ComponentA>() {
272                for (id, _) in q.iter() {
273                    commands.entity(Entity::new(id, 0)).remove::<ComponentA>();
274                }
275            }
276        });
277
278        schedule.run(&mut world, 0.1);
279
280        assert_eq!(world.entity_count(), 1);
281
282        let mut has_a = false;
283        if let Some(q) = world.query::<&ComponentA>() {
284            has_a = q.iter().count() > 0;
285        }
286        assert!(!has_a, "ComponentA still exists!");
287
288        let mut has_b = false;
289        if let Some(q) = world.query::<&ComponentB>() {
290            has_b = q.iter().count() > 0;
291        }
292        assert!(has_b, "ComponentB was unexpectedly removed!");
293    }
294}