fantasy_craft/scene/
scene_loader.rs1use macroquad::prelude::*;
2use hecs::Entity;
3use serde_json::Value;
4use std::collections::HashMap;
5use crate::core::context::Context;
6use crate::prelude::Parent;
7use crate::scene::scene_format::{SceneFile, SceneEntry};
8use std::error::Error;
10use async_recursion::async_recursion;
11
12pub trait ComponentLoader: Send + Sync + 'static {
13 fn load(&self, ctx: &mut Context, entity: Entity, data: &Value);
14}
15
16pub struct SceneLoader {
17 component_loaders: HashMap<String, Box<dyn ComponentLoader>>,
18}
19
20impl SceneLoader {
21 pub fn new() -> Self {
22 Self {
23 component_loaders: HashMap::new(),
24 }
25 }
26
27 pub fn register<S: Into<String>>(&mut self, name: S, loader: Box<dyn ComponentLoader>) -> &mut Self {
28 self.component_loaders.insert(name.into(), loader);
29 self
30 }
31
32 pub async fn load_scene_from_file(&self, path: &str, ctx: &mut Context) -> Result<(), Box<dyn Error>> {
34 let mut entity_map: HashMap<String, Entity> = HashMap::new();
35 let mut parent_queue: Vec<(Entity, String)> = Vec::new();
36
37 info!("SceneLoader: Starting load from root: {}", path);
38
39 self.load_scene_internal(path, ctx, &mut entity_map, &mut parent_queue).await?;
40
41 self.process_parent_queue(ctx, &entity_map, parent_queue);
42
43 info!("SceneLoader: Loading complete.");
44 Ok(())
45 }
46
47 #[async_recursion]
50 async fn load_scene_internal(
51 &self,
52 path: &str,
53 ctx: &mut Context,
54 entity_map: &mut HashMap<String, Entity>,
55 parent_queue: &mut Vec<(Entity, String)>,
56 ) -> Result<(), Box<dyn Error>> { let json_content = load_string(path)
60 .await
61 .map_err(|e| {
62 error!("SceneLoader: Failed to load file '{}': {}", path, e);
63 Box::new(e) as Box<dyn Error>
65 })?;
66
67 let scene_data: SceneFile = serde_json::from_str(&json_content)
69 .map_err(|e| {
70 error!("SceneLoader: Failed to parse JSON in '{}': {}", path, e);
71 Box::new(e) as Box<dyn Error>
73 })?;
74
75 let current_dir = if let Some(last_slash_idx) = path.rfind('/') {
77 &path[0..=last_slash_idx]
78 } else {
79 ""
80 };
81
82 for entry in scene_data.entities {
83 match entry {
84 SceneEntry::Entity(entity_data) => {
85 let entity = ctx.world.spawn(());
86
87 if entity_map.insert(entity_data.id.clone(), entity).is_some() {
88 warn!("Warning: Duplicate entity ID found: '{}'. Overwriting.", entity_data.id);
89 }
90
91 for (component_name, component_data) in entity_data.components {
92 if component_name == "Parent" {
93 if let Some(target_id) = component_data.as_str() {
94 parent_queue.push((entity, target_id.to_string()));
95 } else {
96 warn!("Warning: 'Parent' target for entity {} is invalid.", entity_data.id);
97 }
98 continue;
99 }
100
101 if let Some(loader) = self.component_loaders.get(&component_name) {
102 loader.load(ctx, entity, &component_data);
103 } else {
104 warn!("Warning: No component loader registered for '{}'", component_name);
105 }
106 }
107 }
108
109 SceneEntry::Import(import_data) => {
110 let import_path_str = format!("{}{}", current_dir, import_data.import);
111
112 info!("Importing sub-scene from: {}", import_path_str);
113
114 self.load_scene_internal(
118 &import_path_str,
119 ctx,
120 entity_map,
121 parent_queue,
122 ).await?;
123 }
124 }
125 }
126
127 Ok(())
128 }
129
130 fn process_parent_queue(
131 &self,
132 ctx: &mut Context,
133 entity_map: &HashMap<String, Entity>,
134 parent_queue: Vec<(Entity, String)>,
135 ) {
136 for (entity, parent_id) in parent_queue {
137 if let Some(parent_entity) = entity_map.get(&parent_id) {
138 if ctx.world.contains(entity) {
139 ctx.world
140 .insert_one(entity, Parent(*parent_entity))
141 .expect("Failed to add Parent component");
142 }
143 } else {
144 warn!("Warning: Parent entity not found with ID '{}' for child entity {:?}", parent_id, entity);
145 }
146 }
147 }
148}