sdec-bevy 0.8.0

Bevy adapter for sdec schema + delta encoding
Documentation
use std::collections::{HashMap, HashSet};

use bevy_ecs::prelude::{Entity, World};
use codec::{DeltaUpdateEntity, EntityId, EntitySnapshot};
use schema::ComponentId;

use crate::mapping::EntityMap;
use crate::schema::BevySchema;

#[derive(Debug, Default)]
pub struct BevyChangeSet {
    pub creates: Vec<EntitySnapshot>,
    pub destroys: Vec<EntityId>,
    pub updates: Vec<DeltaUpdateEntity>,
}

#[derive(Debug, Default)]
pub(crate) struct ExtractScratch {
    create_entities: HashSet<Entity>,
    update_entities: HashMap<Entity, Vec<ComponentId>>,
    destroys: HashSet<EntityId>,
}

pub fn extract_changes(
    schema: &BevySchema,
    world: &mut World,
    entities: &mut EntityMap,
) -> BevyChangeSet {
    let mut scratch = ExtractScratch::default();
    let mut changes = BevyChangeSet::default();
    extract_changes_with_scratch(schema, world, entities, &mut scratch, &mut changes);
    changes
}

pub(crate) fn extract_changes_with_scratch(
    schema: &BevySchema,
    world: &mut World,
    entities: &mut EntityMap,
    scratch: &mut ExtractScratch,
    out: &mut BevyChangeSet,
) {
    scratch.create_entities.clear();
    scratch.update_entities.clear();
    scratch.destroys.clear();
    out.creates.clear();
    out.updates.clear();
    out.destroys.clear();

    for adapter in schema.adapters() {
        for entity in adapter.added_entities(world) {
            scratch.create_entities.insert(entity);
        }
        for entity in adapter.changed_entities(world) {
            scratch
                .update_entities
                .entry(entity)
                .or_default()
                .push(adapter.component_id());
        }
        for entity in adapter.removed_entities(world) {
            if let Some(id) = entities.entity_id_known(entity) {
                scratch.destroys.insert(id);
            }
        }
    }

    out.creates.reserve(scratch.create_entities.len());
    for entity in scratch.create_entities.iter().copied() {
        let id = entities.entity_id(entity);
        let components = schema.snapshot_entity(world, entity);
        if components.is_empty() {
            continue;
        }
        out.creates.push(EntitySnapshot { id, components });
    }

    out.updates.reserve(scratch.update_entities.len());
    for (entity, components) in scratch.update_entities.iter() {
        if scratch.create_entities.contains(entity) {
            continue;
        }
        let id = entities.entity_id(*entity);
        let mut delta_components = Vec::new();
        for component_id in components {
            if let Some(adapter) = schema.adapter_by_component(*component_id) {
                if let Some(component_update) = adapter.update_component(world, *entity) {
                    delta_components.push(component_update);
                }
            }
        }
        if !delta_components.is_empty() {
            out.updates.push(DeltaUpdateEntity {
                id,
                components: delta_components,
            });
        }
    }

    out.destroys.reserve(scratch.destroys.len());
    out.destroys.extend(scratch.destroys.iter().copied());
    out.destroys.sort_by_key(|id| id.raw());
    out.creates.sort_by_key(|entity| entity.id.raw());
    out.updates.sort_by_key(|entity| entity.id.raw());
}