nightshade 0.57.0

A cross-platform data-oriented game engine.
Documentation
//! The physics plugin's own ECS member world. Physics components live here
//! rather than in the engine CORE schema, so the core stays capability
//! agnostic and an uncomposed physics plugin adds nothing to it. The world
//! is created on first physics use, which happens after startup, so it
//! appends after the app's `GAME` world without disturbing its index.

use crate::ecs::world::{GAME, World};
use nightshade_ecs::dynamic::ComponentRegistry;

/// Builds the physics member world's component registry, with serde codecs so
/// physics state is captured in world snapshots. The registration order is
/// this world's schema; restoring a snapshot rebuilds it through the same
/// function.
pub(crate) fn register_physics_components() -> ComponentRegistry {
    let mut registry = ComponentRegistry::new();
    registry.register_serde::<crate::plugins::physics::components::RigidBodyComponent>();
    registry.register_serde::<crate::plugins::physics::components::ColliderComponent>();
    registry.register_serde::<crate::plugins::physics::components::CharacterControllerComponent>();
    registry.register_serde::<crate::plugins::physics::components::CollisionListener>();
    registry.register_serde::<crate::plugins::physics::components::PhysicsInterpolation>();
    registry
}

/// Returns the physics member world's index, creating it on first use. Any
/// path that writes a physics component routes through here first so the
/// component's row lands in the physics world rather than lazily polluting the
/// app's `GAME` world. Call it before a `world.set` of a physics component from
/// outside the engine, since the plain [`World::set`](crate::ecs::world::World)
/// fallback would otherwise register the type into the app's `GAME` world.
///
/// The [`GAME`] slot is reserved (padded with an empty registry, exactly as
/// [`World::set`] does) before the physics world is appended, so physics can
/// never take `GAME`'s index. Were it to, a later `world.set` of an unrouted
/// app component would resolve `GAME` to the physics world and land app state
/// there.
pub fn physics_component_world(world: &mut World) -> usize {
    if let Some(index) = world
        .plugin_resource::<crate::plugins::physics::resources::PhysicsWorld>()
        .component_world
    {
        return index;
    }
    if world.ecs.worlds.len() <= GAME {
        world.ecs.add_world_at(GAME, ComponentRegistry::default());
    }
    let index = world.ecs.add_world(register_physics_components());
    world
        .plugin_resource_mut::<crate::plugins::physics::resources::PhysicsWorld>()
        .component_world = Some(index);
    index
}