use crate::{
app::{AppBuilder, AppLifeCycle},
assets::{asset::AssetId, database::AssetsDatabase, protocols::prefab::PrefabAsset},
ecs::{
components::{Name, NonPersistent, NonPersistentPrefabProxy, Tag},
hierarchy::{Parent, ParentPrefabProxy},
life_cycle::EntityChanges,
pipeline::{PipelineBuilder, PipelineBuilderError},
Universe,
},
state::StateToken,
};
use hecs::*;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::collections::HashMap;
pub use serde_json::Value as PrefabValue;
type ComponentFactory = Box<
dyn Fn(
&mut EntityBuilder,
&PrefabValue,
&HashMap<String, Entity>,
StateToken,
) -> Result<(), PrefabError>
+ Send
+ Sync,
>;
#[derive(Debug)]
pub enum PrefabError {
CouldNotSerialize(String),
CouldNotDeserialize(String),
Custom(String),
}
pub trait Prefab: Serialize + DeserializeOwned + Sized {
fn from_prefab(data: &PrefabValue) -> Result<Self, PrefabError> {
match serde_json::from_value(data.clone()) {
Ok(result) => {
let mut result: Self = result;
result.post_from_prefab();
Ok(result)
}
Err(error) => Err(PrefabError::CouldNotDeserialize(error.to_string())),
}
}
fn from_prefab_with_extras(
data: &PrefabValue,
_named_entities: &HashMap<String, Entity>,
_state_token: StateToken,
) -> Result<Self, PrefabError> {
Self::from_prefab(data)
}
fn to_prefab(&self) -> Result<PrefabValue, PrefabError> {
match serde_json::to_value(self) {
Ok(result) => Ok(result),
Err(error) => Err(PrefabError::CouldNotDeserialize(error.to_string())),
}
}
fn from_prefab_str(data: &str) -> Result<Self, PrefabError> {
match serde_json::from_str(data) {
Ok(result) => {
let mut result: Self = result;
result.post_from_prefab();
Ok(result)
}
Err(error) => Err(PrefabError::CouldNotDeserialize(error.to_string())),
}
}
fn to_prefab_string(&self) -> Result<String, PrefabError> {
match serde_json::to_string_pretty(self) {
Ok(result) => Ok(result),
Err(error) => Err(PrefabError::CouldNotSerialize(error.to_string())),
}
}
fn post_from_prefab(&mut self) {}
}
pub trait PrefabProxy<P>: Component + Sized
where
P: Prefab,
{
fn from_proxy_with_extras(
proxy: P,
named_entities: &HashMap<String, Entity>,
state_token: StateToken,
) -> Result<Self, PrefabError>;
}
impl Prefab for PrefabValue {}
pub trait PrefabComponent: Prefab + Component {}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct PrefabScene {
#[serde(default)]
pub template_name: Option<String>,
#[serde(default)]
pub dependencies: Vec<String>,
#[serde(default)]
pub entities: Vec<PrefabSceneEntity>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PrefabSceneEntity {
Data(PrefabSceneEntityData),
Template(String),
}
impl Default for PrefabSceneEntity {
fn default() -> Self {
Self::Data(Default::default())
}
}
impl Prefab for PrefabScene {}
impl Prefab for PrefabSceneEntity {}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct PrefabSceneEntityData {
#[serde(default)]
pub uid: Option<String>,
#[serde(default)]
pub components: HashMap<String, PrefabValue>,
}
impl Prefab for PrefabSceneEntityData {}
#[derive(Default)]
pub struct PrefabManager {
component_factory: HashMap<String, ComponentFactory>,
templates: HashMap<String, PrefabScene>,
}
impl PrefabManager {
pub fn register_component_factory<T>(&mut self, name: &str)
where
T: PrefabComponent,
{
self.component_factory.insert(
name.to_owned(),
Box::new(|builder, prefab, named_entities, state_token| {
builder.add(T::from_prefab_with_extras(
prefab,
named_entities,
state_token,
)?);
Ok(())
}),
);
}
pub fn register_component_factory_proxy<T, P>(&mut self, name: &str)
where
P: Prefab,
T: PrefabProxy<P>,
{
self.component_factory.insert(
name.to_owned(),
Box::new(|builder, prefab, named_entities, state_token| {
let p = P::from_prefab(prefab)?;
builder.add(T::from_proxy_with_extras(p, named_entities, state_token)?);
Ok(())
}),
);
}
pub fn unregister_component_factory(&mut self, name: &str) {
self.component_factory.remove(name);
}
pub fn register_scene_template(&mut self, prefab: PrefabScene) -> Result<(), PrefabError> {
if let Some(name) = &prefab.template_name {
if self.templates.contains_key(name) {
Err(PrefabError::Custom(format!(
"There is already registered template: {}",
name
)))
} else {
self.templates.insert(name.to_owned(), prefab);
Ok(())
}
} else {
Err(PrefabError::Custom(
"Template prefabs must have set template name".to_owned(),
))
}
}
pub fn unregister_scene_template(&mut self, name: &str) {
self.templates.remove(name);
}
pub fn find_template(&self, name: &str) -> Option<&PrefabScene> {
self.templates.get(name)
}
pub fn instantiate(
&mut self,
name: &str,
universe: &mut Universe,
) -> Result<Vec<Entity>, PrefabError> {
let state_token = universe
.expect_resource::<AppLifeCycle>()
.current_state_token();
let mut world = universe.world_mut();
let mut changes = universe.expect_resource_mut::<EntityChanges>();
self.instantiate_direct(name, &mut world, &mut changes, state_token)
}
pub fn instantiate_direct(
&mut self,
name: &str,
world: &mut World,
changes: &mut EntityChanges,
state_token: StateToken,
) -> Result<Vec<Entity>, PrefabError> {
Ok(self
.build_template(name, world, changes, state_token, &Default::default())?
.0)
}
pub fn load_scene_from_prefab(
&mut self,
prefab: &PrefabScene,
universe: &mut Universe,
) -> Result<Vec<Entity>, PrefabError> {
let state_token = universe
.expect_resource::<AppLifeCycle>()
.current_state_token();
self.load_scene_from_prefab_direct(
prefab,
&mut universe.world_mut(),
&mut universe.expect_resource_mut::<EntityChanges>(),
state_token,
)
}
pub fn load_scene_from_prefab_direct(
&mut self,
prefab: &PrefabScene,
world: &mut World,
changes: &mut EntityChanges,
state_token: StateToken,
) -> Result<Vec<Entity>, PrefabError> {
Ok(self
.load_scene_from_prefab_inner(prefab, world, changes, state_token, &Default::default())?
.0)
}
fn load_scene_from_prefab_inner(
&mut self,
prefab: &PrefabScene,
world: &mut World,
changes: &mut EntityChanges,
state_token: StateToken,
named_entities: &HashMap<String, Entity>,
) -> Result<(Vec<Entity>, HashMap<String, Entity>), PrefabError> {
let mut named_entities = named_entities.clone();
let mut result_entities = vec![];
for entity_meta in &prefab.entities {
match entity_meta {
PrefabSceneEntity::Data(data) => {
let entity = self.build_entity(
&data.components,
world,
changes,
state_token,
&named_entities,
)?;
if let Some(uid) = &data.uid {
named_entities.insert(uid.to_owned(), entity);
}
result_entities.push(entity);
}
PrefabSceneEntity::Template(name) => {
let (entities, uids) =
self.build_template(name, world, changes, state_token, &named_entities)?;
for (uid, entity) in uids {
named_entities.insert(uid.to_owned(), entity);
}
result_entities.extend(entities);
}
}
}
Ok((result_entities, named_entities))
}
fn build_entity(
&mut self,
components: &HashMap<String, PrefabValue>,
world: &mut World,
changes: &mut EntityChanges,
state_token: StateToken,
named_entities: &HashMap<String, Entity>,
) -> Result<Entity, PrefabError> {
let mut entity_builder = EntityBuilder::new();
for (key, component_meta) in components {
if let Some(factory) = self.component_factory.get_mut(key) {
factory(
&mut entity_builder,
component_meta,
named_entities,
state_token,
)?;
} else {
return Err(PrefabError::CouldNotDeserialize(format!(
"Could not find component factory: {}",
key
)));
}
}
let entity = world.reserve_entity();
let components = entity_builder.build();
world.spawn_at(entity, components);
changes.spawned.insert(entity);
let components = changes.added_components.entry(entity).or_default();
components.extend(entity_builder.component_types());
Ok(entity)
}
fn build_template(
&mut self,
name: &str,
world: &mut World,
changes: &mut EntityChanges,
state_token: StateToken,
named_entities: &HashMap<String, Entity>,
) -> Result<(Vec<Entity>, HashMap<String, Entity>), PrefabError> {
if let Some(prefab) = self.templates.get(name).cloned() {
self.load_scene_from_prefab_inner(&prefab, world, changes, state_token, named_entities)
} else {
Err(PrefabError::Custom(format!(
"There is no template registered: {}",
name
)))
}
}
}
#[derive(Default)]
pub struct PrefabSystemCache {
templates_table: HashMap<AssetId, String>,
}
pub type PrefabSystemResources<'a> = (
&'a AssetsDatabase,
&'a mut PrefabManager,
&'a mut PrefabSystemCache,
);
pub fn prefab_system(universe: &mut Universe) {
let (assets, mut prefabs, mut cache) = universe.query_resources::<PrefabSystemResources>();
for id in assets.lately_loaded_protocol("prefab") {
let id = *id;
let asset = assets
.asset_by_id(id)
.expect("trying to use not loaded prefab asset");
let asset = asset
.get::<PrefabAsset>()
.expect("trying to use non-prefab asset");
if let Some(name) = &asset.get().template_name {
if prefabs.register_scene_template(asset.get().clone()).is_ok() {
cache.templates_table.insert(id, name.to_owned());
}
}
}
for id in assets.lately_unloaded_protocol("prefab") {
if let Some(name) = cache.templates_table.remove(id) {
prefabs.unregister_scene_template(&name);
}
}
}
pub fn bundle_installer<PB, PMS>(
builder: &mut AppBuilder<PB>,
mut prefab_manager_setup: PMS,
) -> Result<(), PipelineBuilderError>
where
PB: PipelineBuilder,
PMS: FnMut(&mut PrefabManager),
{
let mut manager = PrefabManager::default();
manager.register_component_factory_proxy::<Parent, ParentPrefabProxy>("Parent");
manager.register_component_factory::<Tag>("Tag");
manager.register_component_factory::<Name>("Name");
manager.register_component_factory_proxy::<NonPersistent, NonPersistentPrefabProxy>(
"NonPersistent",
);
prefab_manager_setup(&mut manager);
builder.install_resource(manager);
builder.install_resource(PrefabSystemCache::default());
builder.install_system::<PrefabSystemResources>("prefab", prefab_system, &[])?;
Ok(())
}