pub mod command_queue;
pub mod component;
pub mod rx;
pub mod system;
pub mod world_query_adapter;
#[cfg(test)]
mod world_graph_tests;
use crate::engine::graphics::{RenderAssets, VisualWorld};
use slotmap::{SlotMap, new_key_type};
use std::cell::RefCell;
use std::collections::HashMap;
new_key_type! {
pub struct ComponentId;
}
#[allow(unused_imports)]
pub use crate::engine::graphics::primitives::{Renderable, Transform, TransformMatrix};
pub use command_queue::CommandQueue;
pub use rx::{
EventSignal, IntentSignal, IntentValue, PointerActivationSource, PoseApplyMode, RxWorld,
Signal, SignalEmitter, SignalHandler, SignalKind, SignalWhen,
};
pub use system::{System, SystemWorld};
pub use world_query_adapter::WorldQueryAdapter;
pub struct WorldContext<'a> {
pub world: &'a mut World,
pub systems: &'a mut SystemWorld,
pub visuals: &'a mut VisualWorld,
pub render_assets: &'a mut RenderAssets,
}
impl<'a> WorldContext<'a> {
pub fn new(
world: &'a mut World,
systems: &'a mut SystemWorld,
visuals: &'a mut VisualWorld,
render_assets: &'a mut RenderAssets,
) -> Self {
Self {
world,
systems,
visuals,
render_assets,
}
}
}
#[derive(Default)]
pub struct World {
components: SlotMap<ComponentId, crate::engine::ecs::component::ComponentNode>,
guid_index: HashMap<uuid::Uuid, ComponentId>,
mmq_parser: RefCell<mittens_query::mmq::MmqQuerySyntax>,
}
impl World {
pub fn component_id_by_guid(&self, guid: uuid::Uuid) -> Option<ComponentId> {
self.guid_index.get(&guid).copied()
}
pub fn set_component_guid(
&mut self,
id: ComponentId,
new_guid: uuid::Uuid,
) -> Result<(), String> {
let Some(node) = self.get_component_record(id) else {
return Err(format!("set_component_guid: component {id:?} missing"));
};
let old_guid = node.guid;
if old_guid == new_guid {
return Ok(());
}
if let Some(&existing) = self.guid_index.get(&new_guid) {
if existing != id {
return Err(format!(
"set_component_guid: guid {new_guid} already in use by {existing:?}"
));
}
}
self.guid_index.remove(&old_guid);
self.guid_index.insert(new_guid, id);
if let Some(node) = self.get_component_record_mut(id) {
node.guid = new_guid;
}
Ok(())
}
pub fn add_component<T: crate::engine::ecs::component::Component>(
&mut self,
c: T,
) -> ComponentId {
let id = self.add_component_boxed(Box::new(c));
if let Some(node) = self.get_component_record_mut(id) {
node.component.set_id(id);
}
id
}
pub fn register<T: crate::engine::ecs::component::Component>(&mut self, c: T) -> ComponentId {
self.add_component(c)
}
pub fn is_initialized(&self, c: ComponentId) -> bool {
self.get_component_record(c)
.map(|n| n.initialized)
.unwrap_or(false)
}
pub fn add_component_boxed(
&mut self,
c: Box<dyn crate::engine::ecs::component::Component>,
) -> ComponentId {
let node = crate::engine::ecs::component::ComponentNode::new(c);
let guid = node.guid;
let id = self.components.insert(node);
let _old = self.guid_index.insert(guid, id);
if let Some(node) = self.get_component_record_mut(id) {
node.component.set_id(id);
}
id
}
pub fn add_component_boxed_named(
&mut self,
name: impl Into<String>,
c: Box<dyn crate::engine::ecs::component::Component>,
) -> ComponentId {
let node = crate::engine::ecs::component::ComponentNode::new_named(name, c);
let guid = node.guid;
let id = self.components.insert(node);
let _old = self.guid_index.insert(guid, id);
if let Some(node) = self.get_component_record_mut(id) {
node.component.set_id(id);
}
id
}
pub fn add_component_boxed_with_guid_named(
&mut self,
guid: uuid::Uuid,
name: impl Into<String>,
c: Box<dyn crate::engine::ecs::component::Component>,
) -> ComponentId {
if self.guid_index.contains_key(&guid) {
panic!("duplicate component guid inserted into World: {}", guid);
}
let node = crate::engine::ecs::component::ComponentNode::new_with_guid_named(guid, name, c);
let guid = node.guid;
let id = self.components.insert(node);
self.guid_index.insert(guid, id);
if let Some(node) = self.get_component_record_mut(id) {
node.component.set_id(id);
}
id
}
pub fn spawn_component_boxed(
&mut self,
c: Box<dyn crate::engine::ecs::component::Component>,
) -> ComponentId {
self.add_component_boxed(c)
}
pub fn get_component_record(
&self,
id: ComponentId,
) -> Option<&crate::engine::ecs::component::ComponentNode> {
self.components.get(id)
}
pub fn get_component_node(
&self,
id: ComponentId,
) -> Option<&crate::engine::ecs::component::ComponentNode> {
self.get_component_record(id)
}
pub fn get_component_record_mut(
&mut self,
id: ComponentId,
) -> Option<&mut crate::engine::ecs::component::ComponentNode> {
self.components.get_mut(id)
}
pub fn component_name(&self, id: ComponentId) -> Option<&str> {
self.get_component_record(id)
.map(|node| node.component_type.as_str())
}
pub fn component_label(&self, id: ComponentId) -> Option<&str> {
self.get_component_record(id).map(|node| node.name.as_str())
}
pub fn parent_of(&self, c: ComponentId) -> Option<ComponentId> {
self.get_component_record(c)?.parent
}
pub fn all_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.components.keys()
}
pub fn component_count(&self) -> usize {
self.components.len()
}
pub fn children_of(&self, c: ComponentId) -> &[ComponentId] {
static EMPTY: [ComponentId; 0] = [];
self.get_component_record(c)
.map(|n| n.children.as_slice())
.unwrap_or(&EMPTY)
}
pub fn get_component_by_id_as<T: 'static>(&self, c: ComponentId) -> Option<&T> {
let node = self.get_component_record(c)?;
node.component.as_any().downcast_ref::<T>()
}
pub fn get_component_by_id_as_mut<T: 'static>(&mut self, c: ComponentId) -> Option<&mut T> {
let node = self.get_component_record_mut(c)?;
node.component.as_any_mut().downcast_mut::<T>()
}
pub fn find_component(&self, root: ComponentId, selector: &str) -> Option<ComponentId> {
let matches = self.run_query(root, selector);
matches.into_iter().next()
}
pub fn find_all_components(&self, root: ComponentId, selector: &str) -> Vec<ComponentId> {
self.run_query(root, selector)
}
pub fn scripting_query_roots(&self, component: ComponentId) -> Vec<ComponentId> {
let Some(gltf) =
self.get_component_by_id_as::<crate::engine::ecs::component::GLTFComponent>(component)
else {
return vec![component];
};
let spawned: std::collections::HashSet<_> =
gltf.spawned_node_transforms.iter().copied().collect();
let mut roots = vec![component];
roots.extend(gltf.spawned_node_transforms.iter().copied().filter(|node| {
self.parent_of(*node)
.is_none_or(|parent| !spawned.contains(&parent))
}));
roots
}
pub fn component_matches_selector(&self, component: ComponentId, selector: &str) -> bool {
self.run_query(component, selector).first().copied() == Some(component)
}
pub fn world_roots(&self) -> Vec<ComponentId> {
self.all_components()
.filter(|&cid| self.parent_of(cid).is_none())
.collect()
}
fn run_query(&self, root: ComponentId, selector: &str) -> Vec<ComponentId> {
use crate::engine::ecs::world_query_adapter::WorldQueryAdapter;
use mittens_query::QueryEvaluator;
use mittens_query::QuerySyntax;
if self.get_component_record(root).is_none() {
return Vec::new();
}
let Ok(ast) = self.mmq_parser.borrow_mut().parse(selector) else {
return Vec::new();
};
let adapter = WorldQueryAdapter::new(self);
QueryEvaluator::evaluate(&adapter, root, &ast)
}
pub fn get_parent_as<T: 'static>(&self, c: ComponentId) -> Option<(ComponentId, &T)> {
let parent = self.parent_of(c)?;
let typed = self.get_component_by_id_as::<T>(parent)?;
Some((parent, typed))
}
pub fn get_parent_as_mut<T: 'static>(
&mut self,
c: ComponentId,
) -> Option<(ComponentId, &mut T)> {
let parent = self.parent_of(c)?;
let node = self.get_component_record_mut(parent)?;
let typed = node.component.as_any_mut().downcast_mut::<T>()?;
Some((parent, typed))
}
fn is_ancestor_of(&self, maybe_ancestor: ComponentId, mut node: ComponentId) -> bool {
while let Some(p) = self.parent_of(node) {
if p == maybe_ancestor {
return true;
}
node = p;
}
false
}
pub fn add_child(
&mut self,
parent: ComponentId,
child: ComponentId,
) -> Result<(), &'static str> {
if self.get_component_record(parent).is_none() {
return Err("parent does not exist");
}
if self.get_component_record(child).is_none() {
return Err("child does not exist");
}
if parent == child {
return Err("cannot parent component to itself");
}
if self.is_ancestor_of(child, parent) {
return Err("cycle detected");
}
self.detach_from_parent(child);
{
let child_node = self
.get_component_record_mut(child)
.ok_or("child missing")?;
child_node.parent = Some(parent);
}
{
let parent_node = self
.get_component_record_mut(parent)
.ok_or("parent missing")?;
if !parent_node.children.contains(&child) {
parent_node.children.push(child);
}
}
Ok(())
}
pub fn set_parent(
&mut self,
child: ComponentId,
new_parent: Option<ComponentId>,
) -> Result<(), &'static str> {
match new_parent {
None => {
self.detach_from_parent(child);
Ok(())
}
Some(parent) => self.add_child(parent, child),
}
}
pub fn detach_from_parent(&mut self, child: ComponentId) {
let Some(old_parent) = self.parent_of(child) else {
return;
};
if let Some(node) = self.get_component_record_mut(child) {
node.parent = None;
}
if let Some(parent_node) = self.get_component_record_mut(old_parent) {
parent_node.children.retain(|&c| c != child);
}
}
pub fn remove_component_leaf(&mut self, c: ComponentId) -> Result<(), &'static str> {
let guid = {
let Some(node) = self.get_component_record(c) else {
return Err("component does not exist");
};
if !node.children.is_empty() {
return Err(
"component has children; use remove_component_subtree or detach children first",
);
}
node.guid
};
self.guid_index.remove(&guid);
self.detach_from_parent(c);
self.components.remove(c);
Ok(())
}
pub fn remove_component_subtree(&mut self, root: ComponentId) -> Result<(), &'static str> {
if self.get_component_record(root).is_none() {
return Err("component does not exist");
}
self.detach_from_parent(root);
let mut stack = vec![root];
let mut order: Vec<ComponentId> = Vec::new();
while let Some(c) = stack.pop() {
order.push(c);
let children: Vec<ComponentId> = self.children_of(c).to_vec();
for ch in children {
stack.push(ch);
}
}
for c in order.into_iter().rev() {
let guid = self.get_component_record(c).map(|n| n.guid);
if let Some(guid) = guid {
self.guid_index.remove(&guid);
}
if let Some(node) = self.get_component_record_mut(c) {
node.parent = None;
node.children.clear();
}
self.components.remove(c);
}
Ok(())
}
pub fn init_component_tree(
&mut self,
root: ComponentId,
emit: &mut dyn crate::engine::ecs::SignalEmitter,
) {
use std::collections::HashSet;
let mut stack: Vec<ComponentId> = vec![root];
let mut visited: HashSet<ComponentId> = HashSet::new();
let mut cycle_logs_left: usize = 8;
while let Some(node_id) = stack.pop() {
if !visited.insert(node_id) {
if cycle_logs_left > 0 {
cycle_logs_left -= 1;
println!(
"[World::init_component_tree] cycle/revisit detected at component={:?}",
node_id
);
}
continue;
}
if let Some(node) = self.get_component_record_mut(node_id) {
if !node.initialized {
node.component.init(emit, node_id);
node.initialized = true;
}
}
let children: Vec<ComponentId> = self.children_of(node_id).to_vec();
for child in children.into_iter().rev() {
stack.push(child);
}
}
}
}