use bevy_asset::Assets;
use bevy_ecs::{
entity::Entity,
hierarchy::ChildOf,
name::Name,
reflect::{AppTypeRegistry, ReflectComponent},
system::Command,
world::World,
};
use bevy_entity_uuid::{EntityLookup, EntityUuid, WorldEntityUuid};
use bevy_mesh::skinning::SkinnedMesh;
use bevy_pbr::StandardMaterial;
use bevy_reflect::DynamicTypePath;
use bevy_reflect::{PartialReflect, Reflect};
use tracing::debug;
use uuid::Uuid;
use crate::{
SyncEntity,
binreflect::{bin_to_reflect, reflect_to_bin},
lib_priv::{SkinnedMeshSyncMapper, SyncTrackerRes},
};
pub(crate) struct CreateEntity(Uuid);
impl Command for CreateEntity {
type Out = ();
fn apply(self, world: &mut World) {
world.spawn_empty_uuid(self.0);
}
}
impl From<Uuid> for CreateEntity {
fn from(uuid: Uuid) -> Self {
Self(uuid)
}
}
pub(crate) struct DeleteEntity(Uuid);
impl Command for DeleteEntity {
type Out = ();
fn apply(self, world: &mut World) {
let map = world.resource::<EntityLookup>();
if let Some(id) = map.uuid_to_entity(&self.0) {
world.despawn(id);
}
}
}
impl From<Uuid> for DeleteEntity {
fn from(uuid: Uuid) -> Self {
Self(uuid)
}
}
pub(crate) struct ReparentEntity(Uuid, Uuid);
impl Command for ReparentEntity {
type Out = ();
fn apply(self, world: &mut World) {
let e_id = find_entity_or_create(world, self.0);
let p_id = find_entity_or_create(world, self.1);
let Ok(mut entity) = world.get_entity_mut(e_id) else {
return;
};
let opt_parent = entity.get::<ChildOf>();
if opt_parent.is_none() || opt_parent.unwrap().parent() != p_id {
entity.insert(ChildOf(p_id));
world.entity_mut(p_id).add_child(e_id);
}
}
}
impl From<(Uuid, Uuid)> for ReparentEntity {
fn from(uuids: (Uuid, Uuid)) -> Self {
Self(uuids.0, uuids.1)
}
}
pub(crate) struct ComponentUpdated {
uuid: Uuid,
name: String,
data: Vec<u8>,
}
impl ComponentUpdated {
pub(crate) fn new(uuid: Uuid, name: String, data: Vec<u8>) -> Self {
Self { uuid, name, data }
}
fn is_value_different(
previous_value: Option<&dyn Reflect>,
component_data: &dyn PartialReflect,
) -> bool {
if previous_value.is_none() {
return true;
}
!previous_value
.unwrap()
.reflect_partial_eq(component_data.as_partial_reflect())
.unwrap_or(true)
}
}
impl Command for ComponentUpdated {
type Out = ();
fn apply(self, world: &mut World) {
let e_id = find_entity_or_create(world, self.uuid);
let registry = world.resource::<AppTypeRegistry>().clone();
let registry = registry.read();
let component_data = bin_to_reflect(&self.data, ®istry);
let is_skinned_mesh_mapper =
"bevy_sync::lib_priv::SkinnedMeshSyncMapper".eq(self.name.clone().as_str());
let name = if is_skinned_mesh_mapper {
SkinnedMesh::default().reflect_type_path().to_string()
} else {
self.name.clone()
};
let component_data = if is_skinned_mesh_mapper {
let component = component_data
.try_downcast_ref::<SkinnedMeshSyncMapper>()
.unwrap();
debug!("Transforming SkinnedMeshSyncMapper into SkinnedMesh");
SyncTrackerRes::to_skinned_mesh(world, component.to_owned()).to_dynamic()
} else {
component_data
};
let Some(registration) = registry.get_with_type_path(name.as_str()) else {
debug!("Could not obtain registration for {:?}", name);
return;
};
let Some(reflect_component) = registration.data::<ReflectComponent>() else {
debug!("Could not obtain reflect_component for {:?}", name);
return;
};
let Some(sync_entity) = world.entity(e_id).get::<EntityUuid>() else {
debug!(
"Could not find entity {:?} to apply comopnent change of type {:?}",
e_id, name
);
return;
};
let uuid = sync_entity.uuid;
let previous_value = reflect_component.reflect(world.entity(e_id));
if Self::is_value_different(previous_value, &*component_data) {
let entity = &mut world.entity_mut(e_id);
reflect_component.insert(entity, component_data.as_partial_reflect(), ®istry);
debug!(
"Applied component from network: {}v{} - {} - {} \n {:?}",
e_id.index(),
e_id.generation(),
uuid,
name,
component_data,
);
} else {
debug!(
"Skipped component from network: {}v{} - {} - {} \n {:?}",
e_id.index(),
e_id.generation(),
uuid,
name,
component_data,
);
}
}
}
pub(crate) struct ComponentNameUpdated {
uuid: Uuid,
data: String,
}
impl ComponentNameUpdated {
pub(crate) fn new(uuid: Uuid, data: String) -> Self {
Self { uuid, data }
}
}
impl Command for ComponentNameUpdated {
type Out = ();
fn apply(self, world: &mut World) {
let e_id = find_entity_or_create(world, self.uuid);
let mut e = world.entity_mut(e_id);
let Some(name) = e.get::<Name>() else {
e.insert(Name::new(self.data));
return;
};
if name.as_str() != self.data {
e.insert(Name::new(self.data));
}
}
}
pub(crate) struct UpdateStandardMaterial(Uuid, Vec<u8>);
impl Command for UpdateStandardMaterial {
type Out = ();
fn apply(self, world: &mut World) {
let registry = world.resource::<AppTypeRegistry>().clone();
let registry = registry.read();
let materials = world.resource_mut::<Assets<StandardMaterial>>();
if let Some(old) = materials.get(self.0)
&& let Ok(old) = reflect_to_bin(old, ®istry)
&& old == self.1
{
debug!("Not writing material {} as it is the same", self.0);
return;
}
let component_data = bin_to_reflect(&self.1, ®istry);
let mat = *component_data.try_downcast::<StandardMaterial>().unwrap();
world
.resource_mut::<SyncTrackerRes>()
.pushed_handles_from_network
.insert(self.0);
let mut materials = world.resource_mut::<Assets<StandardMaterial>>();
let _ = materials.insert(self.0, mat);
}
}
impl From<(Uuid, Vec<u8>)> for UpdateStandardMaterial {
fn from(value: (Uuid, Vec<u8>)) -> Self {
Self(value.0, value.1)
}
}
fn find_entity_or_create(world: &mut World, uuid: Uuid) -> Entity {
world.spawn_uuid(uuid, SyncEntity).id()
}