bevy_sync 0.19.0

Plugin for synchronizing entities and components between server and its clients.
Documentation
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, &registry);
        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(), &registry);
            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, &registry)
            && old == self.1
        {
            debug!("Not writing material {} as it is the same", self.0);
            return;
        }

        let component_data = bin_to_reflect(&self.1, &registry);
        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()
}