bevy_sync 0.19.1

Plugin for synchronizing entities and components between server and its clients.
Documentation
use std::marker::PhantomData;

use bevy_app::{App, Plugin, FixedUpdate};
use bevy_asset::{Assets, Handle};
use bevy_audio::{AudioPlayer, AudioSource};
use bevy_ecs::{
    query::Added,
    system::{Query, ResMut},
};
use bevy_image::Image;
use bevy_mesh::{Mesh, Mesh3d};
use bevy_pbr::{MeshMaterial3d, StandardMaterial};
use tracing::debug;
use uuid::Uuid;

use crate::SyncMark;

pub(crate) struct HandleUUIDFixPlugin;

impl Plugin for HandleUUIDFixPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(FixedUpdate, replace_mesh_ids);
        app.add_systems(FixedUpdate, replace_material_ids);
        app.add_systems(FixedUpdate, replace_audio_ids);
    }
}

fn replace_mesh_ids(
    mut meshes: ResMut<Assets<Mesh>>,
    mut q: Query<&mut Mesh3d, Added<SyncMark>>,
) {
    for mut m in &mut q {
        if !m.is_strong() {
            continue;
        }
        let Some(asset) = meshes.remove(m.id()) else {
            continue;
        };
        let id = Uuid::new_v4();
        let _ = meshes.insert(id, asset);
        m.0 = Handle::Uuid(id, PhantomData);
        debug!("Reassigned mesh to uuid {:?}", id);
    }
}

fn replace_material_ids(
    mut images: ResMut<Assets<Image>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    mut q: Query<&mut MeshMaterial3d<StandardMaterial>, Added<SyncMark>>,
) {
    for mut m in &mut q {
        if !m.is_strong() {
            continue;
        }
        let Some(mut asset) = materials.remove(m.id()) else {
            continue;
        };
        let id = Uuid::new_v4();
        replace_material_image_ids(&mut images, &mut asset);
        let _ = materials.insert(id, asset);
        m.0 = Handle::Uuid(id, PhantomData);
        debug!("Reassigned material to uuid {:?}", id);
    }
}

fn replace_material_image_ids(images: &mut Assets<Image>, material: &mut StandardMaterial) {
    macro_rules! swap_image {
        ($image:expr) => {
            if let Some(h) = $image.clone() {
                $image = replace_image_id(images, h);
            }
        };
    }
    swap_image!(material.base_color_texture);
    swap_image!(material.emissive_texture);
    swap_image!(material.normal_map_texture);
    swap_image!(material.occlusion_texture);
    swap_image!(material.metallic_roughness_texture);
}

fn replace_image_id(images: &mut Assets<Image>, image: Handle<Image>) -> Option<Handle<Image>> {
    if image.is_uuid() {
        return Some(image);
    }
    let id = Uuid::new_v4();
    let image = images.remove(image.id())?;
    let _ = images.insert(id, image);
    debug!("Reassigned image to uuid {:?}", id);
    Some(Handle::Uuid(id, PhantomData))
}

fn replace_audio_ids(
    mut audios: ResMut<Assets<AudioSource>>,
    mut q: Query<&mut AudioPlayer, Added<SyncMark>>,
) {
    for mut p in &mut q {
        if !p.0.is_strong() {
            continue;
        }
        let Some(asset) = audios.remove(p.0.id()) else {
            continue;
        };
        let id = Uuid::new_v4();
        let _ = audios.insert(id, asset);
        p.0 = Handle::Uuid(id, PhantomData);
        debug!("Reassigned audio to uuid {:?}", id);
    }
}