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}