gizmo-core 0.10.0

A custom ECS and physics engine aimed for realistic simulations.
Documentation
use super::{AddHook, DespawnHook, RemoveHook, SetHook, World};
use crate::archetype::ComponentInfo;
use crate::component::Component;
use crate::entity::Entity;

use std::any::TypeId;
use std::collections::HashMap;

impl World {
    /// Registers the runtime metadata of a given component type.
    /// It is used to create columns during the archetype storage migration stages.
    #[inline]
    pub fn register_component_type<T: Component>(&mut self) {
        let type_id = TypeId::of::<T>();
        self.component_infos
            .entry(type_id)
            .or_insert_with(ComponentInfo::of::<T>);
    }

    /// Registers a Component hook (Observer) for `OnInsert`.
    pub fn add_observer<T: Component, F>(&mut self, mut system: F) -> &mut Self
    where
        F: FnMut(crate::observer::On<crate::observer::Insert, T>) + Send + Sync + 'static,
    {
        let type_id = TypeId::of::<T>();
        let mut hooks = self.component_hooks.remove(&type_id).unwrap_or_default();

        hooks.on_add.push(Box::new(move |_world, entity| {
            let event = crate::observer::On {
                event: crate::observer::Insert,
                entity,
                _marker: std::marker::PhantomData,
            };
            system(event);
        }));

        self.component_hooks.insert(type_id, hooks);
        self
    }

    /// Entity-based Observer registration for custom EntityEvents
    pub fn observe<E: crate::observer::EntityEvent, F>(&mut self, entity: Entity, listener: F) -> &mut Self
    where
        F: FnMut(crate::observer::On<E>) + Send + Sync + 'static,
    {
        let type_id = TypeId::of::<E>();
        let map_any = self.entity_observers.entry(type_id).or_insert_with(|| {
            Box::new(HashMap::<Entity, Vec<Box<dyn FnMut(crate::observer::On<E>) + Send + Sync + 'static>>>::new())
        });

        let map = map_any.downcast_mut::<HashMap<Entity, Vec<Box<dyn FnMut(crate::observer::On<E>) + Send + Sync + 'static>>>>().unwrap();
        map.entry(entity).or_default().push(Box::new(listener));
        self
    }

    /// Triggers an Event and propagates it upwards through the hierarchy (bubble-up)
    pub fn trigger<E: crate::observer::EntityEvent>(&mut self, event: E) {
        use crate::component::Parent;
        let mut current_entity = event.target();

        loop {
            // Observer'ları bu entity için bul ve çalıştır
            let mut hooks_to_run = Vec::new();

            if let Some(map_any) = self.entity_observers.get_mut(&TypeId::of::<E>()) {
                if let Some(map) = map_any.downcast_mut::<HashMap<Entity, Vec<Box<dyn FnMut(crate::observer::On<E>) + Send + Sync + 'static>>>>() {
                    if let Some(listeners) = map.remove(&current_entity) {
                        hooks_to_run = listeners;
                    }
                }
            }

            for mut listener in hooks_to_run.drain(..) {
                let e = crate::observer::On {
                    event: event.clone(),
                    entity: current_entity,
                    _marker: std::marker::PhantomData,
                };
                listener(e);

                // Geri koy
                if let Some(map_any) = self.entity_observers.get_mut(&TypeId::of::<E>()) {
                    if let Some(map) = map_any.downcast_mut::<HashMap<Entity, Vec<Box<dyn FnMut(crate::observer::On<E>) + Send + Sync + 'static>>>>() {
                        map.entry(current_entity).or_default().push(listener);
                    }
                }
            }

            if !event.can_propagate() {
                break;
            }

            // Propagate to parent
            if let Some(parent_ptr) = self.get_component_ptr(current_entity, TypeId::of::<Parent>()) {
                // `Parent` stores a bare id with no generation; a plain `despawn(parent)`
                // (not despawn_recursive) leaves children with a dangling `Parent(id)`.
                // Resolve it safely — a dead id stops propagation instead of panicking.
                let parent_id = unsafe { (*(parent_ptr as *const Parent)).0 };
                match self.entity(parent_id) {
                    Some(e) => current_entity = e,
                    None => break,
                }
            } else {
                break;
            }
        }
    }

    /// Is a given component type registered?
    #[inline]
    pub fn is_component_registered<T: Component>(&self) -> bool {
        self.component_infos.contains_key(&TypeId::of::<T>())
    }

    /// The number of registered component metadata entries.
    #[inline]
    pub fn registered_component_count(&self) -> usize {
        self.component_infos.len()
    }

    /// Appends a hook fired when `T` becomes newly present on an entity — not when an
    /// existing value is overwritten. See [`AddHook`] for exactly when in the insert it
    /// runs.
    ///
    /// Hooks accumulate: registering the same closure twice makes it fire twice, and there
    /// is no unregister. Within a type they run in registration order — except for one
    /// registered from inside that same type's dispatch, whose position afterwards is not
    /// guaranteed (see [`ComponentHooks`](crate::world::ComponentHooks)) — and all `on_add`
    /// hooks precede that insert's `on_set` hooks.
    pub fn register_on_add<T: Component>(&mut self, hook: AddHook) {
        self.component_hooks
            .entry(TypeId::of::<T>())
            .or_default()
            .on_add
            .push(hook);
    }

    /// Appends a hook fired when `T` is detached from an entity, whether explicitly or
    /// because the entity was despawned. Same accumulate-and-never-unregister rules as
    /// [`World::register_on_add`].
    ///
    /// Read [`RemoveHook`] before relying on it: whether the component is still readable
    /// when the hook runs depends on the removal path and on `T`'s storage type, and
    /// `World::remove_bundle` does not fire it for Table-storage components at all.
    pub fn register_on_remove<T: Component>(&mut self, hook: RemoveHook) {
        self.component_hooks
            .entry(TypeId::of::<T>())
            .or_default()
            .on_remove
            .push(hook);
    }

    /// Appends a hook fired on every write of `T`: the initial insert (right after the
    /// `on_add` hooks) and every later overwrite of the same entity's value. Same
    /// accumulate-and-never-unregister rules as [`World::register_on_add`].
    ///
    /// It is a *write* notification, not a change notification — the hook fires even when
    /// the new value equals the old one, and it cannot see either value except by reading
    /// the entity out of the `&mut World` it is handed.
    pub fn register_on_set<T: Component>(&mut self, hook: SetHook) {
        self.component_hooks
            .entry(TypeId::of::<T>())
            .or_default()
            .on_set
            .push(hook);
    }

    /// Appends a hook fired once for every entity [`World::despawn`] actually destroys,
    /// whatever components it carries — the place for teardown that no single component owns.
    /// Handles that are already dead when despawn reaches them are skipped, so a double
    /// despawn fires the hook once, not twice.
    ///
    /// Unlike the `on_*` hooks this one is global, not keyed by component type. It runs
    /// before any `on_remove` hook and before the id is freed, so the entity is still alive
    /// and fully readable. Hooks accumulate and cannot be unregistered; one registered from
    /// inside a despawn hook does not run for the entity currently being despawned.
    pub fn register_despawn_hook(&mut self, hook: DespawnHook) {
        self.despawn_hooks.push(hook);
    }
}