khora_lanes/scene_lane/strategies/
recipe_lane.rs1use crate::scene_lane::strategies::{
18 DeserializationError, SerializationError, SerializationStrategy,
19};
20use bincode::{config, Decode};
21use khora_core::lane::Lane;
22use khora_core::{ecs::entity::EntityId, graph::topological_sort};
23use khora_data::{
24 ecs::{Component, Parent, SerializableParent, SerializableTransform, Transform, World},
25 scene::{SceneCommand, SceneRecipe},
26};
27use std::{any, collections::HashMap};
28
29type ComponentDeserializer =
34 Box<dyn Fn(&mut World, EntityId, &[u8]) -> Result<(), DeserializationError> + Send + Sync>;
35
36#[derive(Default)]
41struct DeserializerRegistry {
42 map: HashMap<String, ComponentDeserializer>,
43}
44
45impl DeserializerRegistry {
46 fn new() -> Self {
48 let mut registry = Self::default();
49
50 registry.register::<Transform, SerializableTransform>(|st| Transform {
53 translation: st.translation,
54 rotation: st.rotation,
55 scale: st.scale,
56 });
57 registry.register::<Parent, SerializableParent>(|sp| Parent(sp.0));
58
59 registry
60 }
61
62 fn register<C, S>(&mut self, from_serializable: fn(S) -> C)
71 where
72 C: Component,
73 S: Decode<()> + 'static,
74 {
75 let type_name = any::type_name::<C>().to_string();
76 self.map.insert(
77 type_name,
78 Box::new(move |world, entity_id, data| {
79 let (serializable_component, _): (S, _) =
80 bincode::decode_from_slice_with_context(data, config::standard(), ())
81 .map_err(|e| DeserializationError::InvalidFormat(e.to_string()))?;
82 let live_component = from_serializable(serializable_component);
83 world.add_component(entity_id, live_component).ok();
84 Ok(())
85 }),
86 );
87 }
88
89 fn get(&self, type_name: &str) -> Option<&ComponentDeserializer> {
91 self.map.get(type_name)
92 }
93}
94
95pub struct RecipeSerializationLane {
102 deserializers: DeserializerRegistry,
103}
104
105impl Default for RecipeSerializationLane {
106 fn default() -> Self {
107 Self {
108 deserializers: DeserializerRegistry::new(),
109 }
110 }
111}
112
113impl RecipeSerializationLane {
114 pub fn new() -> Self {
116 Self::default()
117 }
118}
119
120impl khora_core::lane::Lane for RecipeSerializationLane {
121 fn strategy_name(&self) -> &'static str {
122 "KH_RECIPE_V1"
123 }
124
125 fn lane_kind(&self) -> khora_core::lane::LaneKind {
126 khora_core::lane::LaneKind::Scene
127 }
128
129 fn as_any(&self) -> &dyn std::any::Any {
130 self
131 }
132
133 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
134 self
135 }
136}
137
138impl SerializationStrategy for RecipeSerializationLane {
139 fn get_strategy_id(&self) -> &'static str {
140 self.strategy_name()
141 }
142
143 fn serialize(&self, world: &World) -> Result<Vec<u8>, SerializationError> {
145 let mut commands = Vec::new();
146
147 let nodes: Vec<EntityId> = world.iter_entities().collect();
149 let mut edges: Vec<(EntityId, EntityId)> = Vec::new();
150 for &entity_id in &nodes {
151 if let Some(parent) = world.get::<Parent>(entity_id) {
152 edges.push((parent.0, entity_id));
154 }
155 }
156
157 let sorted_entities = topological_sort(nodes, edges).map_err(|_| {
159 SerializationError::ProcessingFailed("Cycle detected in scene hierarchy.".to_string())
160 })?;
161
162 for entity_id in sorted_entities {
164 commands.push(SceneCommand::Spawn { id: entity_id });
166
167 if let Some(transform) = world.get::<Transform>(entity_id) {
169 let serializable = SerializableTransform {
171 translation: transform.translation,
172 rotation: transform.rotation,
173 scale: transform.scale,
174 };
175 commands.push(SceneCommand::AddComponent {
176 entity_id,
177 component_type: any::type_name::<Transform>().to_string(),
178 component_data: bincode::encode_to_vec(serializable, config::standard())
180 .unwrap(),
181 });
182 }
183 if let Some(parent) = world.get::<Parent>(entity_id) {
184 let serializable = SerializableParent(parent.0);
185 commands.push(SceneCommand::AddComponent {
186 entity_id,
187 component_type: any::type_name::<Parent>().to_string(),
188 component_data: bincode::encode_to_vec(serializable, config::standard())
189 .unwrap(),
190 });
191 }
192 }
193
194 let scene_recipe = SceneRecipe { commands };
195 bincode::encode_to_vec(&scene_recipe, config::standard())
196 .map_err(|e| SerializationError::ProcessingFailed(e.to_string()))
197 }
198
199 fn deserialize(&self, data: &[u8], world: &mut World) -> Result<(), DeserializationError> {
201 let (recipe, _): (SceneRecipe, _) = bincode::decode_from_slice(data, config::standard())
202 .map_err(|e| DeserializationError::InvalidFormat(e.to_string()))?;
203
204 let mut id_map = HashMap::<EntityId, EntityId>::new();
205
206 for command in recipe.commands {
208 match command {
209 SceneCommand::Spawn { id } => {
210 let new_id = world.spawn(());
211 id_map.insert(id, new_id);
212 }
213 SceneCommand::AddComponent {
214 entity_id,
215 component_type,
216 component_data,
217 } => {
218 if let Some(new_id) = id_map.get(&entity_id) {
220 if let Some(deserializer) = self.deserializers.get(&component_type) {
222 deserializer(world, *new_id, &component_data)?;
223 }
224 }
225 }
226 SceneCommand::SetParent { .. } => {
227 }
229 }
230 }
231
232 Ok(())
233 }
234}