bevy_sync 0.18.5

Plugin for synchronizing entities and components between server and its clients.
Documentation
use bevy_asset::RenderAssetUsages;
use bevy_image::Image;
use lz4_compression::{compress, decompress};
use serde::{Deserialize, Serialize};
use tracing::error;
use wgpu_types::{Extent3d, TextureDimension, TextureFormat};

use crate::binreflect::BINCODE_OPTIONS;

pub(crate) fn image_to_bin(image: &Image) -> Option<Vec<u8>> {
    let img = ImageData {
        width: image.texture_descriptor.size.width,
        height: image.texture_descriptor.size.height,
        depth_or_array_layers: image.texture_descriptor.size.depth_or_array_layers,
        dimensions: image.texture_descriptor.dimension.into(),
        format: image.texture_descriptor.format,
        data: image.data.clone()?,
    };
    let input = match bincode::serde::encode_to_vec(&img, BINCODE_OPTIONS) {
        Ok(x) => x,
        Err(e) => {
            error!("Error encoding image: {e}");
            return None;
        }
    };
    Some(compress::compress(&input))
}

pub(crate) fn bin_to_image(bin: &[u8]) -> Option<Image> {
    let bin = match decompress::decompress(bin) {
        Ok(x) => x,
        Err(e) => {
            error!("Error decompressing image: {e:?}");
            return None;
        }
    };
    let (img, _): (ImageData, _) = match bincode::serde::decode_from_slice(&bin, BINCODE_OPTIONS) {
        Ok(x) => x,
        Err(e) => {
            error!("Error decoding mesh: {e}");
            return None;
        }
    };

    Some(Image::new(
        Extent3d {
            width: img.width,
            height: img.height,
            depth_or_array_layers: img.depth_or_array_layers,
        },
        img.dimensions.into(),
        img.data,
        img.format,
        RenderAssetUsages::RENDER_WORLD | RenderAssetUsages::MAIN_WORLD,
    ))
}

#[derive(Serialize, Deserialize)]
struct ImageData {
    width: u32,
    height: u32,
    depth_or_array_layers: u32,
    dimensions: Dimensions,
    format: TextureFormat,
    data: Vec<u8>,
}

#[derive(Serialize, Deserialize)]
struct Dimensions(u8);

impl From<TextureDimension> for Dimensions {
    fn from(value: TextureDimension) -> Self {
        Self(match value {
            TextureDimension::D1 => 1,
            TextureDimension::D2 => 2,
            TextureDimension::D3 => 3,
        })
    }
}

#[allow(clippy::match_same_arms)]
impl From<Dimensions> for TextureDimension {
    fn from(value: Dimensions) -> Self {
        match value.0 {
            1 => TextureDimension::D1,
            2 => TextureDimension::D2,
            3 => TextureDimension::D3,
            _ => TextureDimension::D2,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_image() {
        let img = Image::default();
        let bin = image_to_bin(&img).unwrap();
        let img2 = bin_to_image(&bin).unwrap();
        assert_eq!(img.data, img2.data);
    }
}