Skip to main content

mittens_engine/engine/ecs/
mod.rs

1pub 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    /// Global component identity (dense arena key).
17    pub struct ComponentId;
18}
19
20// Re-export these so other modules can use `crate::engine::ecs::Transform`
21// and `crate::engine::ecs::Renderable` consistently.
22#[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
33/// Bundle of mutable engine state passed to component mutation APIs.
34///
35/// This exists to avoid threading `&mut World`, `&mut SystemWorld`, and `&mut VisualWorld`
36/// through every component call.
37pub 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/// World: owns all global components.
61#[derive(Default)]
62pub struct World {
63    components: SlotMap<ComponentId, crate::engine::ecs::component::ComponentNode>,
64    guid_index: HashMap<uuid::Uuid, ComponentId>,
65    /// MMQ parser instance with per-instance AST cache. Behind a RefCell so
66    /// `find_component(&self, ...)` can mutate the cache without forcing
67    /// every caller to thread `&mut World`. Single-threaded — World lives
68    /// on the main thread.
69    mmq_parser: RefCell<mittens_query::mmq::MmqQuerySyntax>,
70}
71
72impl World {
73    /// Fast GUID -> ComponentId lookup.
74    pub fn component_id_by_guid(&self, guid: uuid::Uuid) -> Option<ComponentId> {
75        self.guid_index.get(&guid).copied()
76    }
77
78    /// Replace the GUID of an already-inserted component, keeping its
79    /// ComponentId stable. Used by spawn paths that restore an authored
80    /// `guid = "..."` property after `create_component` has already minted
81    /// a fresh GUID.
82    ///
83    /// Returns Err if the target id is missing, or if the new guid is
84    /// already taken by another component (would silently overwrite the
85    /// reverse index otherwise).
86    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    /// Add a new component to the world (no parent) and return its id.
114    ///
115    /// Note: this currently does *not* call `Component::init`. That should happen via a
116    /// higher-level API that has access to `SystemWorld` and `VisualWorld`.
117    pub fn add_component<T: crate::engine::ecs::component::Component>(
118        &mut self,
119        c: T,
120    ) -> ComponentId {
121        // We set the id after insertion so components that cache their id can do so.
122        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    /// Register a new component in the world and return its id.
130    ///
131    /// This is intentionally a storage/identity operation only: it does *not* call
132    /// `Component::init`.
133    pub fn register<T: crate::engine::ecs::component::Component>(&mut self, c: T) -> ComponentId {
134        self.add_component(c)
135    }
136
137    /// Whether this component has had `Component::init` invoked.
138    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    /// Add a new component to the world (no parent). Returns its global id.
145    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    /// Add a new boxed component with an explicit stored name.
160    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    /// Add a new boxed component with a restored GUID and explicit stored name.
176    ///
177    /// This is intended for deserialization.
178    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    /// Temporary alias during migration.
199    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    /// Alias for `get_component_record`.
214    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    /// Returns the engine type identifier for this component (e.g. `"transform"`).
229    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    /// Returns the user-assigned label for this component (empty string if unset).
235    pub fn component_label(&self, id: ComponentId) -> Option<&str> {
236        self.get_component_record(id).map(|node| node.name.as_str())
237    }
238
239    // --- Topology helpers (component-graph) ---
240    pub fn parent_of(&self, c: ComponentId) -> Option<ComponentId> {
241        self.get_component_record(c)?.parent
242    }
243
244    /// Iterator over all component IDs in the world.
245    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    // --- Typed component access ---
261    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    /// Find the first component under `root` matching `selector`.
272    ///
273    /// `selector` is parsed as MMQ — `#name`, `Type`, `Type#name`, `[name='...']`, etc.
274    /// See `crates/mittens-query/src/mmq/parser.rs`.
275    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    /// Find all components under `root` matching `selector` (DFS pre-order).
281    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        // Avoid borrowing self twice by doing the downcast via the node.
322        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    // --- Graph mutation ---
328    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    /// Attach `child` under `parent`.
339    ///
340    /// This is a lower-level graph mutation API.
341    ///
342    /// It updates only the parent/child links in the world graph. It does not
343    /// emit `ParentChanged`, does not refresh topology-dependent systems, and
344    /// does not route through runtime helpers like routers/scroll ownership.
345    ///
346    /// Prefer `IntentValue::Attach` or `Universe::attach(...)` when attaching a
347    /// subtree into an already-live parent and you expect normal runtime side
348    /// effects.
349    ///
350    /// `add_child(...)` is appropriate for offline subtree assembly before init,
351    /// tests, and internal structural building where the caller intentionally
352    /// manages any needed follow-up work.
353    ///
354    /// Safety rules:
355    /// - Both ids must exist.
356    /// - `child` is detached from its current parent first.
357    /// - Cycles are rejected.
358    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        // Set child's parent.
379        {
380            let child_node = self
381                .get_component_record_mut(child)
382                .ok_or("child missing")?;
383            child_node.parent = Some(parent);
384        }
385        // Push into parent's children list.
386        {
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    /// Change a component's parent.
399    ///
400    /// Equivalent to `detach_from_parent(child)` when `new_parent` is None.
401    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    /// Detach `child` from its current parent.
416    ///
417    /// This does *not* delete anything.
418    pub fn detach_from_parent(&mut self, child: ComponentId) {
419        let Some(old_parent) = self.parent_of(child) else {
420            return;
421        };
422
423        // Clear child's parent.
424        if let Some(node) = self.get_component_record_mut(child) {
425            node.parent = None;
426        }
427
428        // Remove from old parent's children list.
429        if let Some(parent_node) = self.get_component_record_mut(old_parent) {
430            parent_node.children.retain(|&c| c != child);
431        }
432    }
433
434    /// Remove a component from the world.
435    ///
436    /// This is a *leaf-only* removal: it fails if the component still has children.
437    /// Use `remove_component_subtree` when you want to delete a whole branch.
438    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    /// Remove a component and all its descendants.
459    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        // Detach root first so parent doesn't retain dead child.
465        self.detach_from_parent(root);
466
467        // Post-order delete: collect subtree ids, then delete leaves upward.
468        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        // Delete in reverse (children first).
479        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            // Clear parent/children links if node still exists.
485            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    /// Initialize a component tree starting from the given root component.
496    ///
497    /// This recursively initializes the root component and all its descendants by calling
498    /// `Component::init` on each component in the tree.
499    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        // Iterative traversal prevents stack overflows on large init expansions.
507        let mut stack: Vec<ComponentId> = vec![root];
508        let mut visited: HashSet<ComponentId> = HashSet::new();
509
510        // Log only a small number of cycle detections to avoid spam.
511        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            // Initialize the component (idempotent).
526            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            // Push children (reverse so first child is processed first in LIFO order).
534            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}