nightshade 0.57.0

A cross-platform data-oriented game engine.
Documentation
//! How a capability with its own member world joins whole-world save and load.
//!
//! A plugin appends a [`SnapshotCapability`] when it is composed and the
//! snapshot walks the list. A capability nobody composed registers nothing, its
//! key is absent from the snapshot, and a restore skips it.

use crate::ecs::world::World;
use nightshade_ecs::dynamic::ComponentRegistry;

/// One capability's stake in a world snapshot. Plain function pointers, so the
/// record is `Copy` and the snapshot can lift the table out before it mutates.
#[derive(Clone, Copy)]
pub struct SnapshotCapability {
    /// Stable key this capability's member world is stored under. Changing it
    /// orphans the world in snapshots written before the change.
    pub key: &'static str,
    /// Which member world this capability currently owns, if it has one yet.
    /// A capability whose components nothing has written has no member world
    /// and contributes nothing to the snapshot.
    pub member_world: fn(&World) -> Option<usize>,
    /// Rebuilds the capability's schema so its rows restore with the same
    /// component layout they were captured under.
    pub registry: fn() -> ComponentRegistry,
    /// Repoints the capability at its restored member world and resets whatever
    /// simulation state the restored rows supersede. Called with `None` when
    /// the snapshot carried no world for this key.
    pub restore: fn(&mut World, Option<usize>),
}

/// The snapshot's capability table. Each plugin appends to it at composition;
/// [`snapshot_world`](super::snapshot_world) and
/// [`restore_world`](super::restore_world) walk it in registration order.
#[derive(Default)]
pub struct SnapshotCapabilityHooks {
    pub capabilities: Vec<SnapshotCapability>,
}

/// Appends one capability, creating the table on the first registration so
/// composition order does not matter.
pub fn register_snapshot_capability(world: &mut World, capability: SnapshotCapability) {
    if world.ecs.resource::<SnapshotCapabilityHooks>().is_none() {
        world
            .ecs
            .insert_resource(SnapshotCapabilityHooks::default());
    }
    world
        .res_mut::<SnapshotCapabilityHooks>()
        .capabilities
        .push(capability);
}

/// Every capability registered so far, or an empty list when no plugin that
/// owns a member world was composed.
pub fn snapshot_capabilities(world: &World) -> Vec<SnapshotCapability> {
    world
        .ecs
        .resource::<SnapshotCapabilityHooks>()
        .map(|hooks| hooks.capabilities.clone())
        .unwrap_or_default()
}