khora_lanes/scene_lane/strategies/definition_lane.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A serialization strategy that uses a stable, intermediate representation (`SceneDefinition`).
16
17use crate::scene_lane::strategies::{
18 DeserializationError, SerializationError, SerializationStrategy,
19};
20use khora_core::ecs::entity::EntityId;
21use khora_data::{
22 ecs::{GlobalTransform, Parent, SerializableParent, SerializableTransform, Transform, World},
23 scene::{ComponentDefinition, EntityDefinition, SceneDefinition},
24};
25use std::collections::HashMap;
26
27/// A serialization strategy that uses a stable, intermediate representation (`SceneDefinition`).
28///
29/// This lane is designed for long-term stability and human-readability. It translates
30/// the live world state into a decoupled format, ensuring that changes to the internal
31/// component structures do not break scene files.
32#[derive(Default)]
33pub struct DefinitionSerializationLane;
34
35impl DefinitionSerializationLane {
36 /// Creates a new instance of the DefinitionSerializationLane.
37 pub fn new() -> Self {
38 Self
39 }
40}
41
42impl SerializationStrategy for DefinitionSerializationLane {
43 /// Returns a unique identifier for this serialization strategy.
44 fn get_strategy_id(&self) -> &'static str {
45 // We will have two variants: one for RON (debug) and one for Bincode (stable).
46 // For now, we implement the human-readable one.
47 "KH_DEFINITION_RON_V1"
48 }
49
50 /// Serializes the given world into a byte vector using the stable intermediate representation.
51 fn serialize(&self, world: &World) -> Result<Vec<u8>, SerializationError> {
52 let mut entity_defs = Vec::new();
53
54 // Iterate over all entities that have at least one component.
55 for entity_id in world.iter_entities() {
56 let mut component_defs = Vec::new();
57
58 // Translate `Transform` component if it exists.
59 if let Some(transform) = world.get::<Transform>(entity_id) {
60 let serializable_transform = SerializableTransform {
61 translation: transform.translation,
62 rotation: transform.rotation,
63 scale: transform.scale,
64 };
65 component_defs.push(ComponentDefinition::Transform(serializable_transform));
66 }
67
68 // Translate `Parent` component if it exists.
69 if let Some(parent) = world.get::<Parent>(entity_id) {
70 component_defs.push(ComponentDefinition::Parent(SerializableParent(parent.0)));
71 }
72
73 // Only include entities that have components we care about.
74 if !component_defs.is_empty() {
75 entity_defs.push(EntityDefinition {
76 id: entity_id,
77 components: component_defs,
78 });
79 }
80 }
81
82 let scene_definition = SceneDefinition {
83 entities: entity_defs,
84 };
85
86 // Use RON for human-readable output.
87 let pretty_config = ron::ser::PrettyConfig::default().indentor(" ".to_string());
88 ron::ser::to_string_pretty(&scene_definition, pretty_config)
89 .map(|s| s.into_bytes())
90 .map_err(|e| SerializationError::ProcessingFailed(e.to_string()))
91 }
92
93 /// Deserializes the given byte slice into the provided world using the stable intermediate representation.
94 fn deserialize(&self, data: &[u8], world: &mut World) -> Result<(), DeserializationError> {
95 let scene_def: SceneDefinition = ron::de::from_bytes(data)
96 .map_err(|e| DeserializationError::InvalidFormat(e.to_string()))?;
97
98 let mut id_map = HashMap::<EntityId, EntityId>::new();
99
100 // First Pass: Spawn all entities and create an ID map.
101 // This ensures all entities exist before we try to establish parent-child relationships.
102 for entity_def in &scene_def.entities {
103 // We use `spawn(())` to create an entity with no components initially.
104 let new_id = world.spawn(());
105 id_map.insert(entity_def.id, new_id);
106 }
107
108 // Second Pass: Add components to the newly created entities.
109 for entity_def in &scene_def.entities {
110 let new_id = id_map[&entity_def.id]; // We can unwrap, we know the ID exists.
111
112 for component_def in &entity_def.components {
113 match component_def {
114 ComponentDefinition::Transform(st) => {
115 let transform = Transform {
116 translation: st.translation,
117 rotation: st.rotation,
118 scale: st.scale,
119 };
120 world.add_component(new_id, transform).ok();
121 world
122 .add_component(new_id, GlobalTransform::identity())
123 .ok();
124 }
125 ComponentDefinition::Parent(sp) => {
126 // Use the map to find the *new* ID of the parent entity.
127 if let Some(new_parent_id) = id_map.get(&sp.0) {
128 world.add_component(new_id, Parent(*new_parent_id)).ok();
129 } else {
130 // This would be an error: the file references a parent that wasn't defined.
131 // We'll ignore it for now.
132 }
133 }
134 }
135 }
136 }
137
138 Ok(())
139 }
140}