Skip to main content

concinnity_engine/
blob.rs

1//! Blob file reading and lazy payload residency live in `concinnity_host::store`;
2//! re-export them under the historical crate::blob::* paths. `pub` so the editor
3//! crate's in-memory build path can construct `BlobData`.
4pub use concinnity_host::store::blob::*;
5
6use crate::ecs::ComponentAsset;
7use crate::ecs::World;
8use crate::ecs::asset_id::AssetId;
9use crate::result::CnResult;
10
11/// A world that reads its compiled payloads from `blob`. The world names the
12/// payload store only through its access seam, so this is where the blob file
13/// format meets it.
14pub fn world_from(blob: BlobData) -> World {
15    World::from_payloads(Box::new(blob))
16}
17
18// Load the primary blob, resolve every stored def to a `ComponentAsset`, and
19// return the resource stream, the world manifest, and `BlobData` alongside
20// them. The blob carries a component stream (systems are internal and
21// constructed at runtime), a resource stream (compiled resources addressed by
22// a per-kind handle, which the caller loads into per-kind tables), and the
23// manifest summarizing both (the caller pre-sizes ECS columns from its
24// per-type counts).
25//
26// Each component that has a compiled payload carries its `PayloadLocator`
27// injected into it (see `ComponentAsset::inject_locator`). Only blob 0's payload
28// section is read into memory by `load_raw`; overflow blobs are read from disk
29// lazily on first access.
30// A loaded component paired with its def's name id, so the caller can index
31// the entity it mints for it (the world's name -> entity map).
32type NamedComponent = (Option<AssetId>, ComponentAsset);
33
34// The decoded primary blob: resolved components, the resource stream, the
35// baked per-scene groups, the physics reservation, the manifest, and the lazy
36// payload reader.
37pub(crate) struct LoadedBlob {
38    pub(crate) components: Vec<NamedComponent>,
39    pub(crate) resources: Vec<ResourceRecord>,
40    pub(crate) scene_groups: Vec<concinnity_core::ecs::SceneGroup>,
41    pub(crate) mesh_bounds: Vec<concinnity_core::ecs::MeshBoundsRecord>,
42    pub(crate) physics_budget: Option<concinnity_core::ecs::PhysicsBudgetRecord>,
43    pub(crate) manifest: WorldManifest,
44    pub(crate) blob: BlobData,
45}
46
47pub(crate) fn load() -> Result<LoadedBlob, CnResult> {
48    resolve(concinnity_host::store::blob::load_raw()?)
49}
50
51// `load` against a primary blob file named directly, rather than the
52// state root's `data/` layout. Overflow blobs are its siblings by index.
53pub(crate) fn load_at(primary: &std::path::Path) -> Result<LoadedBlob, CnResult> {
54    resolve(concinnity_host::store::blob::load_raw_at(primary)?)
55}
56
57fn resolve((meta, blob_data): (BlobMeta, BlobData)) -> Result<LoadedBlob, CnResult> {
58    let components = meta
59        .defs
60        .iter()
61        .map(|def| {
62            // Every record is baked: the bytes are the serialized runtime
63            // component (cook already ran the asset -> component translation).
64            let mut component = ComponentAsset::from_baked(def)?;
65            if let Some(locator) = &def.payload {
66                component.inject_locator(locator.clone());
67            }
68            Ok((def.name, component))
69        })
70        .collect::<Result<Vec<_>, CnResult>>()?;
71
72    Ok(LoadedBlob {
73        components,
74        resources: meta.resources,
75        scene_groups: meta.scene_groups,
76        mesh_bounds: meta.mesh_bounds,
77        physics_budget: meta.physics_budget,
78        manifest: meta.manifest,
79        blob: blob_data,
80    })
81}