Skip to content

ENG2-P8-03: World snapshot save/load (serde over the component registry) #801

Description

@aram-devdocs

Parent

  • Program: ENG2 — GoudEngine v2 Rebuild (see the pinned master tracking issue)
  • Phase / Milestone: Phase 8 — Capability Gaps (eng2-p8-capabilities)
  • Batch / Group: Batch 8.1 — Highest-demand capabilities, Group C
  • Runbook spec: docs/src/runbook/phases/phase-8.md (committed with the roadmap)

Summary

Add a full World snapshot save/load API (serde over the component registry) that round-trips an entire world's entities and components to/from bytes, exposed via FFI to all 10 SDKs.

Architecture Context

Layer: Layer 3 (Services, ecs/world/) for the snapshot walk/apply logic; Layer 5 (FFI) for the new export surface.
Modules/types touched:

  • goud_engine/src/ecs/world/ — new snapshot module (e.g. snapshot.rs) that iterates all live entities and all component storages registered via register_serializable::<T>().
  • goud_engine/src/core/serialization/ — reuse existing serde/byte-encoding conventions (delta.rs) where they fit; a full snapshot is not a delta, so this is a new encode/decode path, not a repurposing of DeltaEncode.
  • goud_engine/src/ffi/scene_loading.rs or a new ffi/world_snapshot.rs — new FFI exports (today this file only has goud_scene_load/goud_scene_unload).

Boundary constraints (only those that apply):

  • FFI exports: #[no_mangle] extern "C", #[repr(C)], null checks, // SAFETY: comments.
  • Generated *.g.rs/*.g.cs/*.g.ts are never hand-edited; change Rust + FFI + schema, rerun ./codegen.sh.

Pattern to follow: goud_engine/src/ecs/world/serialize_entity.rs — already implements per-entity serialize_entity/deserialize_entity_components against types registered via World::register_serializable::<T>(). The world snapshot is this same per-entity mechanism looped over every live entity plus the entity/hierarchy graph, not a new serialization scheme.

Scope

  • World-wide snapshot: walk all live entities, call the existing per-entity serialize_entity for each, and wrap the result with enough entity/hierarchy metadata to reconstruct parent/child relationships (Hierarchy component) on load.
  • Snapshot format versioning: include a schema/version tag so future component additions don't silently corrupt old snapshots.
  • Load path: clear or diff against the current World, recreate entities in a stable order, call deserialize_entity_components per entity, then re-run transform propagation once.
  • FFI exports: goud_world_snapshot_save (context -> bytes), goud_world_snapshot_load (context, bytes -> success/error code), matching the ownership/error conventions used by goud_scene_load/goud_scene_unload (ffi/scene_loading.rs:47-179).
  • Tests: spec test + round-trip unit tests (save then load reproduces identical entity/component state) including hierarchy relationships.
  • Docs/rules updates: mdBook page documenting the snapshot format and its versioning guarantee; note the register_serializable::<T>() type-name fragility caveat already documented in serialize_entity.rs:19-25.

Acceptance Criteria

  • A world with entities across multiple component types, including parent/child hierarchies, round-trips to an identical world_state_hash (per ENG2-P5-04's hashing hook) after save then load.
  • Snapshot save/load works at S1 scripted-scene scale (per perf-dod.md) without exceeding a documented time budget.
  • FFI snapshot exports have wrappers in all 10 SDKs and pass python codegen/validate_coverage.py.
  • cargo check && cargo fmt --all -- --check && cargo clippy -- -D warnings clean; cargo test green; ./codegen.sh && git diff --exit-code (drift gate)

Breaking Change & Throne Follow-up

None — additive/internal (new FFI exports only; existing goud_scene_load/goud_scene_unload are untouched).

Blocked By

None.

Files Likely Touched

  • New: goud_engine/src/ecs/world/snapshot.rs, goud_engine/src/ffi/world_snapshot.rs (or exports added to ffi/scene_loading.rs)
  • Generated: SDK bindings under sdks/*/ for the new snapshot FFI exports
  • Modified: goud_engine/src/ecs/world/mod.rs (register snapshot module), codegen/goud_sdk.schema.json

Agent Notes

  • The engine's only existing persistence paths today are one-way: core/serialization/delta.rs implements DeltaEncode/DeltaPayload for compact network delta updates (bitmask + changed-field bytes) — this is a network-delta mechanism, not a full-world snapshot format, and reusing it directly would be a category error since a snapshot has no "baseline" to diff against. Separately, ffi/scene_loading.rs:47 (goud_scene_load) and its neighbor at line 123 (goud_scene_unload) only load/unload a scene from a JSON string — grep of the file confirms there is no goud_scene_save-equivalent export anywhere in it (only these two pub unsafe extern "C" fn entries exist in the whole 179-line file).
  • There IS already real per-entity serialization infrastructure to build on: goud_engine/src/ecs/world/serialize_entity.rs (367 lines) implements World::register_serializable::<T>(), serialize_entity, and deserialize_entity_components, keyed by std::any::type_name::<T>(). This is the actual reusable "component registry" the roadmap refers to — a full-world snapshot is this mechanism looped over every live entity plus the Hierarchy component graph, not new serialization machinery from scratch.
  • The doc comment at serialize_entity.rs:19-25 explicitly warns that type_name::<T>() embeds full module paths, so refactoring component module locations breaks old snapshot compatibility — carry this caveat into the new snapshot format's versioning story rather than silently inheriting the fragility.

Verification

cargo check && cargo fmt --all -- --check && cargo clippy -- -D warnings
cargo test
./codegen.sh && git diff --exit-code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions