mittens_engine/engine/ecs/
mod.rs1pub mod command_queue;
2pub mod component;
3pub mod rx;
4pub mod system;
5pub mod world_query_adapter;
6
7#[cfg(test)]
8mod world_graph_tests;
9
10use crate::engine::graphics::{RenderAssets, VisualWorld};
11use slotmap::{SlotMap, new_key_type};
12use std::cell::RefCell;
13use std::collections::HashMap;
14
15new_key_type! {
16 pub struct ComponentId;
18}
19
20#[allow(unused_imports)]
23pub use crate::engine::graphics::primitives::{Renderable, Transform, TransformMatrix};
24
25pub use command_queue::CommandQueue;
26pub use rx::{
27 EventSignal, IntentSignal, IntentValue, RxWorld, Signal, SignalEmitter, SignalHandler,
28 SignalKind, SignalWhen,
29};
30pub use system::{System, SystemWorld};
31pub use world_query_adapter::WorldQueryAdapter;
32
33pub struct WorldContext<'a> {
38 pub world: &'a mut World,
39 pub systems: &'a mut SystemWorld,
40 pub visuals: &'a mut VisualWorld,
41 pub render_assets: &'a mut RenderAssets,
42}
43
44impl<'a> WorldContext<'a> {
45 pub fn new(
46 world: &'a mut World,
47 systems: &'a mut SystemWorld,
48 visuals: &'a mut VisualWorld,
49 render_assets: &'a mut RenderAssets,
50 ) -> Self {
51 Self {
52 world,
53 systems,
54 visuals,
55 render_assets,
56 }
57 }
58}
59
60#[derive(Default)]
62pub struct World {
63 components: SlotMap<ComponentId, crate::engine::ecs::component::ComponentNode>,
64 guid_index: HashMap<uuid::Uuid, ComponentId>,
65 mmq_parser: RefCell<mittens_query::mmq::MmqQuerySyntax>,
70}
71
72impl World {
73 pub fn component_id_by_guid(&self, guid: uuid::Uuid) -> Option<ComponentId> {
75 self.guid_index.get(&guid).copied()
76 }
77
78 pub fn set_component_guid(
87 &mut self,
88 id: ComponentId,
89 new_guid: uuid::Uuid,
90 ) -> Result<(), String> {
91 let Some(node) = self.get_component_record(id) else {
92 return Err(format!("set_component_guid: component {id:?} missing"));
93 };
94 let old_guid = node.guid;
95 if old_guid == new_guid {
96 return Ok(());
97 }
98 if let Some(&existing) = self.guid_index.get(&new_guid) {
99 if existing != id {
100 return Err(format!(
101 "set_component_guid: guid {new_guid} already in use by {existing:?}"
102 ));
103 }
104 }
105 self.guid_index.remove(&old_guid);
106 self.guid_index.insert(new_guid, id);
107 if let Some(node) = self.get_component_record_mut(id) {
108 node.guid = new_guid;
109 }
110 Ok(())
111 }
112
113 pub fn add_component<T: crate::engine::ecs::component::Component>(
118 &mut self,
119 c: T,
120 ) -> ComponentId {
121 let id = self.add_component_boxed(Box::new(c));
123 if let Some(node) = self.get_component_record_mut(id) {
124 node.component.set_id(id);
125 }
126 id
127 }
128
129 pub fn register<T: crate::engine::ecs::component::Component>(&mut self, c: T) -> ComponentId {
134 self.add_component(c)
135 }
136
137 pub fn is_initialized(&self, c: ComponentId) -> bool {
139 self.get_component_record(c)
140 .map(|n| n.initialized)
141 .unwrap_or(false)
142 }
143
144 pub fn add_component_boxed(
146 &mut self,
147 c: Box<dyn crate::engine::ecs::component::Component>,
148 ) -> ComponentId {
149 let node = crate::engine::ecs::component::ComponentNode::new(c);
150 let guid = node.guid;
151 let id = self.components.insert(node);
152 let _old = self.guid_index.insert(guid, id);
153 if let Some(node) = self.get_component_record_mut(id) {
154 node.component.set_id(id);
155 }
156 id
157 }
158
159 pub fn add_component_boxed_named(
161 &mut self,
162 name: impl Into<String>,
163 c: Box<dyn crate::engine::ecs::component::Component>,
164 ) -> ComponentId {
165 let node = crate::engine::ecs::component::ComponentNode::new_named(name, c);
166 let guid = node.guid;
167 let id = self.components.insert(node);
168 let _old = self.guid_index.insert(guid, id);
169 if let Some(node) = self.get_component_record_mut(id) {
170 node.component.set_id(id);
171 }
172 id
173 }
174
175 pub fn add_component_boxed_with_guid_named(
179 &mut self,
180 guid: uuid::Uuid,
181 name: impl Into<String>,
182 c: Box<dyn crate::engine::ecs::component::Component>,
183 ) -> ComponentId {
184 if self.guid_index.contains_key(&guid) {
185 panic!("duplicate component guid inserted into World: {}", guid);
186 }
187
188 let node = crate::engine::ecs::component::ComponentNode::new_with_guid_named(guid, name, c);
189 let guid = node.guid;
190 let id = self.components.insert(node);
191 self.guid_index.insert(guid, id);
192 if let Some(node) = self.get_component_record_mut(id) {
193 node.component.set_id(id);
194 }
195 id
196 }
197
198 pub fn spawn_component_boxed(
200 &mut self,
201 c: Box<dyn crate::engine::ecs::component::Component>,
202 ) -> ComponentId {
203 self.add_component_boxed(c)
204 }
205
206 pub fn get_component_record(
207 &self,
208 id: ComponentId,
209 ) -> Option<&crate::engine::ecs::component::ComponentNode> {
210 self.components.get(id)
211 }
212
213 pub fn get_component_node(
215 &self,
216 id: ComponentId,
217 ) -> Option<&crate::engine::ecs::component::ComponentNode> {
218 self.get_component_record(id)
219 }
220
221 pub fn get_component_record_mut(
222 &mut self,
223 id: ComponentId,
224 ) -> Option<&mut crate::engine::ecs::component::ComponentNode> {
225 self.components.get_mut(id)
226 }
227
228 pub fn component_name(&self, id: ComponentId) -> Option<&str> {
230 self.get_component_record(id)
231 .map(|node| node.component_type.as_str())
232 }
233
234 pub fn component_label(&self, id: ComponentId) -> Option<&str> {
236 self.get_component_record(id).map(|node| node.name.as_str())
237 }
238
239 pub fn parent_of(&self, c: ComponentId) -> Option<ComponentId> {
241 self.get_component_record(c)?.parent
242 }
243
244 pub fn all_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
246 self.components.keys()
247 }
248
249 pub fn component_count(&self) -> usize {
250 self.components.len()
251 }
252
253 pub fn children_of(&self, c: ComponentId) -> &[ComponentId] {
254 static EMPTY: [ComponentId; 0] = [];
255 self.get_component_record(c)
256 .map(|n| n.children.as_slice())
257 .unwrap_or(&EMPTY)
258 }
259
260 pub fn get_component_by_id_as<T: 'static>(&self, c: ComponentId) -> Option<&T> {
262 let node = self.get_component_record(c)?;
263 node.component.as_any().downcast_ref::<T>()
264 }
265
266 pub fn get_component_by_id_as_mut<T: 'static>(&mut self, c: ComponentId) -> Option<&mut T> {
267 let node = self.get_component_record_mut(c)?;
268 node.component.as_any_mut().downcast_mut::<T>()
269 }
270
271 pub fn find_component(&self, root: ComponentId, selector: &str) -> Option<ComponentId> {
276 let matches = self.run_query(root, selector);
277 matches.into_iter().next()
278 }
279
280 pub fn find_all_components(&self, root: ComponentId, selector: &str) -> Vec<ComponentId> {
282 self.run_query(root, selector)
283 }
284
285 pub fn component_matches_selector(&self, component: ComponentId, selector: &str) -> bool {
286 self.run_query(component, selector).first().copied() == Some(component)
287 }
288
289 pub fn world_roots(&self) -> Vec<ComponentId> {
290 self.all_components()
291 .filter(|&cid| self.parent_of(cid).is_none())
292 .collect()
293 }
294
295 fn run_query(&self, root: ComponentId, selector: &str) -> Vec<ComponentId> {
296 use crate::engine::ecs::world_query_adapter::WorldQueryAdapter;
297 use mittens_query::QueryEvaluator;
298 use mittens_query::QuerySyntax;
299
300 if self.get_component_record(root).is_none() {
301 return Vec::new();
302 }
303 let Ok(ast) = self.mmq_parser.borrow_mut().parse(selector) else {
304 return Vec::new();
305 };
306 let adapter = WorldQueryAdapter::new(self);
307 QueryEvaluator::evaluate(&adapter, root, &ast)
308 }
309
310 pub fn get_parent_as<T: 'static>(&self, c: ComponentId) -> Option<(ComponentId, &T)> {
311 let parent = self.parent_of(c)?;
312 let typed = self.get_component_by_id_as::<T>(parent)?;
313 Some((parent, typed))
314 }
315
316 pub fn get_parent_as_mut<T: 'static>(
317 &mut self,
318 c: ComponentId,
319 ) -> Option<(ComponentId, &mut T)> {
320 let parent = self.parent_of(c)?;
321 let node = self.get_component_record_mut(parent)?;
323 let typed = node.component.as_any_mut().downcast_mut::<T>()?;
324 Some((parent, typed))
325 }
326
327 fn is_ancestor_of(&self, maybe_ancestor: ComponentId, mut node: ComponentId) -> bool {
329 while let Some(p) = self.parent_of(node) {
330 if p == maybe_ancestor {
331 return true;
332 }
333 node = p;
334 }
335 false
336 }
337
338 pub fn add_child(
359 &mut self,
360 parent: ComponentId,
361 child: ComponentId,
362 ) -> Result<(), &'static str> {
363 if self.get_component_record(parent).is_none() {
364 return Err("parent does not exist");
365 }
366 if self.get_component_record(child).is_none() {
367 return Err("child does not exist");
368 }
369 if parent == child {
370 return Err("cannot parent component to itself");
371 }
372 if self.is_ancestor_of(child, parent) {
373 return Err("cycle detected");
374 }
375
376 self.detach_from_parent(child);
377
378 {
380 let child_node = self
381 .get_component_record_mut(child)
382 .ok_or("child missing")?;
383 child_node.parent = Some(parent);
384 }
385 {
387 let parent_node = self
388 .get_component_record_mut(parent)
389 .ok_or("parent missing")?;
390 if !parent_node.children.contains(&child) {
391 parent_node.children.push(child);
392 }
393 }
394
395 Ok(())
396 }
397
398 pub fn set_parent(
402 &mut self,
403 child: ComponentId,
404 new_parent: Option<ComponentId>,
405 ) -> Result<(), &'static str> {
406 match new_parent {
407 None => {
408 self.detach_from_parent(child);
409 Ok(())
410 }
411 Some(parent) => self.add_child(parent, child),
412 }
413 }
414
415 pub fn detach_from_parent(&mut self, child: ComponentId) {
419 let Some(old_parent) = self.parent_of(child) else {
420 return;
421 };
422
423 if let Some(node) = self.get_component_record_mut(child) {
425 node.parent = None;
426 }
427
428 if let Some(parent_node) = self.get_component_record_mut(old_parent) {
430 parent_node.children.retain(|&c| c != child);
431 }
432 }
433
434 pub fn remove_component_leaf(&mut self, c: ComponentId) -> Result<(), &'static str> {
439 let guid = {
440 let Some(node) = self.get_component_record(c) else {
441 return Err("component does not exist");
442 };
443 if !node.children.is_empty() {
444 return Err(
445 "component has children; use remove_component_subtree or detach children first",
446 );
447 }
448 node.guid
449 };
450
451 self.guid_index.remove(&guid);
452
453 self.detach_from_parent(c);
454 self.components.remove(c);
455 Ok(())
456 }
457
458 pub fn remove_component_subtree(&mut self, root: ComponentId) -> Result<(), &'static str> {
460 if self.get_component_record(root).is_none() {
461 return Err("component does not exist");
462 }
463
464 self.detach_from_parent(root);
466
467 let mut stack = vec![root];
469 let mut order: Vec<ComponentId> = Vec::new();
470 while let Some(c) = stack.pop() {
471 order.push(c);
472 let children: Vec<ComponentId> = self.children_of(c).to_vec();
473 for ch in children {
474 stack.push(ch);
475 }
476 }
477
478 for c in order.into_iter().rev() {
480 let guid = self.get_component_record(c).map(|n| n.guid);
481 if let Some(guid) = guid {
482 self.guid_index.remove(&guid);
483 }
484 if let Some(node) = self.get_component_record_mut(c) {
486 node.parent = None;
487 node.children.clear();
488 }
489 self.components.remove(c);
490 }
491
492 Ok(())
493 }
494
495 pub fn init_component_tree(
500 &mut self,
501 root: ComponentId,
502 emit: &mut dyn crate::engine::ecs::SignalEmitter,
503 ) {
504 use std::collections::HashSet;
505
506 let mut stack: Vec<ComponentId> = vec![root];
508 let mut visited: HashSet<ComponentId> = HashSet::new();
509
510 let mut cycle_logs_left: usize = 8;
512
513 while let Some(node_id) = stack.pop() {
514 if !visited.insert(node_id) {
515 if cycle_logs_left > 0 {
516 cycle_logs_left -= 1;
517 println!(
518 "[World::init_component_tree] cycle/revisit detected at component={:?}",
519 node_id
520 );
521 }
522 continue;
523 }
524
525 if let Some(node) = self.get_component_record_mut(node_id) {
527 if !node.initialized {
528 node.component.init(emit, node_id);
529 node.initialized = true;
530 }
531 }
532
533 let children: Vec<ComponentId> = self.children_of(node_id).to_vec();
535 for child in children.into_iter().rev() {
536 stack.push(child);
537 }
538 }
539 }
540}