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
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        // Apply öncesi entity yok
154        assert_eq!(world.entity_count(), 0);
155
156        queue.apply(&mut world);
157
158        // Apply sonrası 1 entity var ve componenti eklenmiş
159        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        // Use a standard (&World, f32) system to access query and manually fetch Commands
209        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}