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_core::lane::Lane;
22use khora_data::{
23 ecs::{GlobalTransform, Parent, SerializableParent, SerializableTransform, Transform, World},
24 scene::{ComponentDefinition, EntityDefinition, SceneDefinition},
25};
26use std::collections::HashMap;
27
28/// A serialization strategy that uses a stable, intermediate representation (`SceneDefinition`).
29///
30/// This lane is designed for long-term stability and human-readability. It translates
31/// the live world state into a decoupled format, ensuring that changes to the internal
32/// component structures do not break scene files.
33#[derive(Default)]
34pub struct DefinitionSerializationLane;
35
36impl DefinitionSerializationLane {
37 /// Creates a new instance of the DefinitionSerializationLane.
38 pub fn new() -> Self {
39 Self
40 }
41}
42
43impl khora_core::lane::Lane for DefinitionSerializationLane {
44 fn strategy_name(&self) -> &'static str {
45 "KH_DEFINITION_RON_V1"
46 }
47
48 fn lane_kind(&self) -> khora_core::lane::LaneKind {
49 khora_core::lane::LaneKind::Scene
50 }
51
52 fn as_any(&self) -> &dyn std::any::Any {
53 self
54 }
55
56 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
57 self
58 }
59}
60
61impl SerializationStrategy for DefinitionSerializationLane {
62 /// Returns a unique identifier for this serialization strategy.
63 fn get_strategy_id(&self) -> &'static str {
64 self.strategy_name()
65 }
66
67 /// Serializes the given world into a byte vector using the stable intermediate representation.
68 fn serialize(&self, world: &World) -> Result<Vec<u8>, SerializationError> {
69 let mut entity_defs = Vec::new();
70
71 // Iterate over all entities that have at least one component.
72 for entity_id in world.iter_entities() {
73 let mut component_defs = Vec::new();
74
75 // Translate `Transform` component if it exists.
76 if let Some(transform) = world.get::<Transform>(entity_id) {
77 let serializable_transform = SerializableTransform {
78 translation: transform.translation,
79 rotation: transform.rotation,
80 scale: transform.scale,
81 };
82 component_defs.push(ComponentDefinition::Transform(serializable_transform));
83 }
84
85 // Translate `Parent` component if it exists.
86 if let Some(parent) = world.get::<Parent>(entity_id) {
87 component_defs.push(ComponentDefinition::Parent(SerializableParent(parent.0)));
88 }
89
90 // Only include entities that have components we care about.
91 if !component_defs.is_empty() {
92 entity_defs.push(EntityDefinition {
93 id: entity_id,
94 components: component_defs,
95 });
96 }
97 }
98
99 let scene_definition = SceneDefinition {
100 entities: entity_defs,
101 };
102
103 // Use RON for human-readable output.
104 let pretty_config = ron::ser::PrettyConfig::default().indentor(" ".to_string());
105 ron::ser::to_string_pretty(&scene_definition, pretty_config)
106 .map(|s| s.into_bytes())
107 .map_err(|e| SerializationError::ProcessingFailed(e.to_string()))
108 }
109
110 /// Deserializes the given byte slice into the provided world using the stable intermediate representation.
111 fn deserialize(&self, data: &[u8], world: &mut World) -> Result<(), DeserializationError> {
112 let scene_def: SceneDefinition = ron::de::from_bytes(data)
113 .map_err(|e| DeserializationError::InvalidFormat(e.to_string()))?;
114
115 let mut id_map = HashMap::<EntityId, EntityId>::new();
116
117 // First Pass: Spawn all entities and create an ID map.
118 // This ensures all entities exist before we try to establish parent-child relationships.
119 for entity_def in &scene_def.entities {
120 // We use `spawn(())` to create an entity with no components initially.
121 let new_id = world.spawn(());
122 id_map.insert(entity_def.id, new_id);
123 }
124
125 // Second Pass: Add components to the newly created entities.
126 for entity_def in &scene_def.entities {
127 let new_id = id_map[&entity_def.id]; // We can unwrap, we know the ID exists.
128
129 for component_def in &entity_def.components {
130 match component_def {
131 ComponentDefinition::Transform(st) => {
132 let transform = Transform {
133 translation: st.translation,
134 rotation: st.rotation,
135 scale: st.scale,
136 };
137 world.add_component(new_id, transform).ok();
138 world
139 .add_component(new_id, GlobalTransform::identity())
140 .ok();
141 }
142 ComponentDefinition::Parent(sp) => {
143 // Use the map to find the *new* ID of the parent entity.
144 if let Some(new_parent_id) = id_map.get(&sp.0) {
145 world.add_component(new_id, Parent(*new_parent_id)).ok();
146 } else {
147 // This would be an error: the file references a parent that wasn't defined.
148 // We'll ignore it for now.
149 }
150 }
151 }
152 }
153 }
154
155 Ok(())
156 }
157}