use bevy::{ecs::entity::hash_map::EntityHashMap, prelude::*, scene::DynamicEntity};
use log::debug;
use crate::{prelude::*, shared::replication::rules::ReplicationRules};
pub fn replicate_into(scene: &mut DynamicScene, world: &World) {
let replicated_id = world.component_id::<Replicated>();
#[cfg(feature = "client")]
let remote_id = world.component_id::<Remote>();
#[cfg(not(feature = "client"))]
let remote_id = None;
let mut entities: EntityHashMap<_> = scene
.entities
.drain(..)
.map(|e| (e.entity, e.components))
.collect();
let registry = world.resource::<AppTypeRegistry>();
let rules = world.resource::<ReplicationRules>();
let registry = registry.read();
for archetype in world.archetypes().iter() {
let has_replicated = replicated_id.is_some_and(|id| archetype.contains(id));
let has_remote = remote_id.is_some_and(|id| archetype.contains(id));
if !has_replicated && !has_remote {
continue;
}
for entity in archetype.entities() {
entities.entry(entity.id()).or_default();
}
for rule in rules.iter().filter(|rule| rule.matches(archetype)) {
for component in &rule.components {
let replicated_component =
unsafe { world.components().get_info_unchecked(component.id) };
let type_name = replicated_component.name();
let type_id = replicated_component
.type_id()
.unwrap_or_else(|| panic!("`{type_name}` should be a Rust type"));
let Some(registration) = registry.get(type_id) else {
debug!("ignoring `{type_name}` because it's not registered");
continue;
};
let Some(reflect_component) = registration.data::<ReflectComponent>() else {
debug!("ignoring `{type_name}` because it's missing `#[reflect(Component)]`");
continue;
};
let from_reflect = registration
.data::<ReflectFromReflect>()
.unwrap_or_else(|| panic!("`{type_name}` should reflect `FromReflect`"));
for entity in archetype.entities() {
let component = reflect_component
.reflect(world.entity(entity.id()))
.unwrap_or_else(|| panic!("entity should have `{type_name}`"));
let component = from_reflect
.from_reflect(component.as_partial_reflect())
.unwrap_or_else(|| panic!("`{type_name}` should be dynamically cloneable"));
let components = entities
.get_mut(&entity.id())
.expect("all entities should be populated ahead of time");
debug!("adding `{type_name}` to `{}`", entity.id());
components.push(component.into_partial_reflect());
}
}
}
}
scene.entities.extend(
entities
.drain()
.map(|(entity, components)| DynamicEntity { entity, components }),
);
}