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}