oxygengine_core/
prefab.rs

1use crate::{
2    app::{AppBuilder, AppLifeCycle},
3    assets::{asset::AssetId, database::AssetsDatabase, protocols::prefab::PrefabAsset},
4    ecs::{
5        components::{Name, NonPersistent, NonPersistentPrefabProxy, Tag},
6        hierarchy::{Parent, ParentPrefabProxy},
7        life_cycle::EntityChanges,
8        pipeline::{PipelineBuilder, PipelineBuilderError},
9        Universe,
10    },
11    state::StateToken,
12};
13use hecs::*;
14use serde::{de::DeserializeOwned, Deserialize, Serialize};
15use std::collections::HashMap;
16
17pub use serde_json::Value as PrefabValue;
18
19type ComponentFactory = Box<
20    dyn Fn(
21            &mut EntityBuilder,
22            &PrefabValue,
23            &HashMap<String, Entity>,
24            StateToken,
25        ) -> Result<(), PrefabError>
26        + Send
27        + Sync,
28>;
29
30#[derive(Debug)]
31pub enum PrefabError {
32    CouldNotSerialize(String),
33    CouldNotDeserialize(String),
34    Custom(String),
35}
36
37pub trait Prefab: Serialize + DeserializeOwned + Sized {
38    fn from_prefab(data: &PrefabValue) -> Result<Self, PrefabError> {
39        match serde_json::from_value(data.clone()) {
40            Ok(result) => {
41                let mut result: Self = result;
42                result.post_from_prefab();
43                Ok(result)
44            }
45            Err(error) => Err(PrefabError::CouldNotDeserialize(error.to_string())),
46        }
47    }
48
49    fn from_prefab_with_extras(
50        data: &PrefabValue,
51        _named_entities: &HashMap<String, Entity>,
52        _state_token: StateToken,
53    ) -> Result<Self, PrefabError> {
54        Self::from_prefab(data)
55    }
56
57    fn to_prefab(&self) -> Result<PrefabValue, PrefabError> {
58        match serde_json::to_value(self) {
59            Ok(result) => Ok(result),
60            Err(error) => Err(PrefabError::CouldNotDeserialize(error.to_string())),
61        }
62    }
63
64    fn from_prefab_str(data: &str) -> Result<Self, PrefabError> {
65        match serde_json::from_str(data) {
66            Ok(result) => {
67                let mut result: Self = result;
68                result.post_from_prefab();
69                Ok(result)
70            }
71            Err(error) => Err(PrefabError::CouldNotDeserialize(error.to_string())),
72        }
73    }
74
75    fn to_prefab_string(&self) -> Result<String, PrefabError> {
76        match serde_json::to_string_pretty(self) {
77            Ok(result) => Ok(result),
78            Err(error) => Err(PrefabError::CouldNotSerialize(error.to_string())),
79        }
80    }
81
82    fn post_from_prefab(&mut self) {}
83}
84
85pub trait PrefabProxy<P>: Component + Sized
86where
87    P: Prefab,
88{
89    fn from_proxy_with_extras(
90        proxy: P,
91        named_entities: &HashMap<String, Entity>,
92        state_token: StateToken,
93    ) -> Result<Self, PrefabError>;
94}
95
96impl Prefab for PrefabValue {}
97pub trait PrefabComponent: Prefab + Component {}
98
99#[derive(Debug, Default, Clone, Serialize, Deserialize)]
100pub struct PrefabScene {
101    #[serde(default)]
102    pub template_name: Option<String>,
103    #[serde(default)]
104    pub dependencies: Vec<String>,
105    #[serde(default)]
106    pub entities: Vec<PrefabSceneEntity>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub enum PrefabSceneEntity {
111    Data(PrefabSceneEntityData),
112    Template(String),
113}
114
115impl Default for PrefabSceneEntity {
116    fn default() -> Self {
117        Self::Data(Default::default())
118    }
119}
120
121impl Prefab for PrefabScene {}
122impl Prefab for PrefabSceneEntity {}
123
124#[derive(Debug, Default, Clone, Serialize, Deserialize)]
125pub struct PrefabSceneEntityData {
126    #[serde(default)]
127    pub uid: Option<String>,
128    #[serde(default)]
129    pub components: HashMap<String, PrefabValue>,
130}
131
132impl Prefab for PrefabSceneEntityData {}
133
134#[derive(Default)]
135pub struct PrefabManager {
136    component_factory: HashMap<String, ComponentFactory>,
137    templates: HashMap<String, PrefabScene>,
138}
139
140impl PrefabManager {
141    pub fn register_component_factory<T>(&mut self, name: &str)
142    where
143        T: PrefabComponent,
144    {
145        self.component_factory.insert(
146            name.to_owned(),
147            Box::new(|builder, prefab, named_entities, state_token| {
148                builder.add(T::from_prefab_with_extras(
149                    prefab,
150                    named_entities,
151                    state_token,
152                )?);
153                Ok(())
154            }),
155        );
156    }
157
158    pub fn register_component_factory_proxy<T, P>(&mut self, name: &str)
159    where
160        P: Prefab,
161        T: PrefabProxy<P>,
162    {
163        self.component_factory.insert(
164            name.to_owned(),
165            Box::new(|builder, prefab, named_entities, state_token| {
166                let p = P::from_prefab(prefab)?;
167                builder.add(T::from_proxy_with_extras(p, named_entities, state_token)?);
168                Ok(())
169            }),
170        );
171    }
172
173    pub fn unregister_component_factory(&mut self, name: &str) {
174        self.component_factory.remove(name);
175    }
176
177    pub fn register_scene_template(&mut self, prefab: PrefabScene) -> Result<(), PrefabError> {
178        if let Some(name) = &prefab.template_name {
179            if self.templates.contains_key(name) {
180                Err(PrefabError::Custom(format!(
181                    "There is already registered template: {}",
182                    name
183                )))
184            } else {
185                self.templates.insert(name.to_owned(), prefab);
186                Ok(())
187            }
188        } else {
189            Err(PrefabError::Custom(
190                "Template prefabs must have set template name".to_owned(),
191            ))
192        }
193    }
194
195    pub fn unregister_scene_template(&mut self, name: &str) {
196        self.templates.remove(name);
197    }
198
199    pub fn find_template(&self, name: &str) -> Option<&PrefabScene> {
200        self.templates.get(name)
201    }
202
203    pub fn instantiate(
204        &mut self,
205        name: &str,
206        universe: &mut Universe,
207    ) -> Result<Vec<Entity>, PrefabError> {
208        let state_token = universe
209            .expect_resource::<AppLifeCycle>()
210            .current_state_token();
211        let mut world = universe.world_mut();
212        let mut changes = universe.expect_resource_mut::<EntityChanges>();
213        self.instantiate_direct(name, &mut world, &mut changes, state_token)
214    }
215
216    pub fn instantiate_direct(
217        &mut self,
218        name: &str,
219        world: &mut World,
220        changes: &mut EntityChanges,
221        state_token: StateToken,
222    ) -> Result<Vec<Entity>, PrefabError> {
223        Ok(self
224            .build_template(name, world, changes, state_token, &Default::default())?
225            .0)
226    }
227
228    pub fn load_scene_from_prefab(
229        &mut self,
230        prefab: &PrefabScene,
231        universe: &mut Universe,
232    ) -> Result<Vec<Entity>, PrefabError> {
233        let state_token = universe
234            .expect_resource::<AppLifeCycle>()
235            .current_state_token();
236        self.load_scene_from_prefab_direct(
237            prefab,
238            &mut universe.world_mut(),
239            &mut universe.expect_resource_mut::<EntityChanges>(),
240            state_token,
241        )
242    }
243
244    pub fn load_scene_from_prefab_direct(
245        &mut self,
246        prefab: &PrefabScene,
247        world: &mut World,
248        changes: &mut EntityChanges,
249        state_token: StateToken,
250    ) -> Result<Vec<Entity>, PrefabError> {
251        Ok(self
252            .load_scene_from_prefab_inner(prefab, world, changes, state_token, &Default::default())?
253            .0)
254    }
255
256    fn load_scene_from_prefab_inner(
257        &mut self,
258        prefab: &PrefabScene,
259        world: &mut World,
260        changes: &mut EntityChanges,
261        state_token: StateToken,
262        named_entities: &HashMap<String, Entity>,
263    ) -> Result<(Vec<Entity>, HashMap<String, Entity>), PrefabError> {
264        let mut named_entities = named_entities.clone();
265        let mut result_entities = vec![];
266        for entity_meta in &prefab.entities {
267            match entity_meta {
268                PrefabSceneEntity::Data(data) => {
269                    let entity = self.build_entity(
270                        &data.components,
271                        world,
272                        changes,
273                        state_token,
274                        &named_entities,
275                    )?;
276                    if let Some(uid) = &data.uid {
277                        named_entities.insert(uid.to_owned(), entity);
278                    }
279                    result_entities.push(entity);
280                }
281                PrefabSceneEntity::Template(name) => {
282                    let (entities, uids) =
283                        self.build_template(name, world, changes, state_token, &named_entities)?;
284                    for (uid, entity) in uids {
285                        named_entities.insert(uid.to_owned(), entity);
286                    }
287                    result_entities.extend(entities);
288                }
289            }
290        }
291        Ok((result_entities, named_entities))
292    }
293
294    fn build_entity(
295        &mut self,
296        components: &HashMap<String, PrefabValue>,
297        world: &mut World,
298        changes: &mut EntityChanges,
299        state_token: StateToken,
300        named_entities: &HashMap<String, Entity>,
301    ) -> Result<Entity, PrefabError> {
302        let mut entity_builder = EntityBuilder::new();
303        for (key, component_meta) in components {
304            if let Some(factory) = self.component_factory.get_mut(key) {
305                factory(
306                    &mut entity_builder,
307                    component_meta,
308                    named_entities,
309                    state_token,
310                )?;
311            } else {
312                return Err(PrefabError::CouldNotDeserialize(format!(
313                    "Could not find component factory: {}",
314                    key
315                )));
316            }
317        }
318        let entity = world.reserve_entity();
319        let components = entity_builder.build();
320        world.spawn_at(entity, components);
321        changes.spawned.insert(entity);
322        let components = changes.added_components.entry(entity).or_default();
323        components.extend(entity_builder.component_types());
324        Ok(entity)
325    }
326
327    fn build_template(
328        &mut self,
329        name: &str,
330        world: &mut World,
331        changes: &mut EntityChanges,
332        state_token: StateToken,
333        named_entities: &HashMap<String, Entity>,
334    ) -> Result<(Vec<Entity>, HashMap<String, Entity>), PrefabError> {
335        if let Some(prefab) = self.templates.get(name).cloned() {
336            self.load_scene_from_prefab_inner(&prefab, world, changes, state_token, named_entities)
337        } else {
338            Err(PrefabError::Custom(format!(
339                "There is no template registered: {}",
340                name
341            )))
342        }
343    }
344}
345
346#[derive(Default)]
347pub struct PrefabSystemCache {
348    templates_table: HashMap<AssetId, String>,
349}
350pub type PrefabSystemResources<'a> = (
351    &'a AssetsDatabase,
352    &'a mut PrefabManager,
353    &'a mut PrefabSystemCache,
354);
355
356pub fn prefab_system(universe: &mut Universe) {
357    let (assets, mut prefabs, mut cache) = universe.query_resources::<PrefabSystemResources>();
358
359    for id in assets.lately_loaded_protocol("prefab") {
360        let id = *id;
361        let asset = assets
362            .asset_by_id(id)
363            .expect("trying to use not loaded prefab asset");
364        let asset = asset
365            .get::<PrefabAsset>()
366            .expect("trying to use non-prefab asset");
367        if let Some(name) = &asset.get().template_name {
368            if prefabs.register_scene_template(asset.get().clone()).is_ok() {
369                cache.templates_table.insert(id, name.to_owned());
370            }
371        }
372    }
373    for id in assets.lately_unloaded_protocol("prefab") {
374        if let Some(name) = cache.templates_table.remove(id) {
375            prefabs.unregister_scene_template(&name);
376        }
377    }
378}
379
380pub fn bundle_installer<PB, PMS>(
381    builder: &mut AppBuilder<PB>,
382    mut prefab_manager_setup: PMS,
383) -> Result<(), PipelineBuilderError>
384where
385    PB: PipelineBuilder,
386    PMS: FnMut(&mut PrefabManager),
387{
388    let mut manager = PrefabManager::default();
389    manager.register_component_factory_proxy::<Parent, ParentPrefabProxy>("Parent");
390    manager.register_component_factory::<Tag>("Tag");
391    manager.register_component_factory::<Name>("Name");
392    manager.register_component_factory_proxy::<NonPersistent, NonPersistentPrefabProxy>(
393        "NonPersistent",
394    );
395    prefab_manager_setup(&mut manager);
396    builder.install_resource(manager);
397    builder.install_resource(PrefabSystemCache::default());
398    builder.install_system::<PrefabSystemResources>("prefab", prefab_system, &[])?;
399    Ok(())
400}