sdec-codec 0.8.0

Snapshot and delta encoding/decoding for the sdec codec
Documentation
use codec::{
    apply_delta_snapshot_from_packet, encode_delta_snapshot, encode_delta_snapshot_for_client,
    CodecLimits, ComponentSnapshot, EntitySnapshot, FieldValue, Snapshot,
};
use schema::{ComponentDef, ComponentId, FieldCodec, FieldDef, FieldId, Schema};
use wire::{decode_packet, Limits as WireLimits, SectionTag};

fn schema_with_uint_fields(field_count: usize) -> Schema {
    let mut component = ComponentDef::new(ComponentId::new(1).unwrap());
    for idx in 0..field_count {
        component = component.field(FieldDef::new(
            FieldId::new((idx + 1) as u16).unwrap(),
            FieldCodec::uint(10),
        ));
    }
    Schema::new(vec![component]).unwrap()
}

fn entity_with_uint_fields(id: u32, field_count: usize, value: u64) -> EntitySnapshot {
    EntitySnapshot {
        id: codec::EntityId::new(id),
        components: vec![ComponentSnapshot {
            id: ComponentId::new(1).unwrap(),
            fields: (0..field_count).map(|_| FieldValue::UInt(value)).collect(),
        }],
    }
}

#[test]
fn delta_selects_sparse_encoding_for_sparse_change() {
    let schema = schema_with_uint_fields(32);
    let limits = CodecLimits {
        max_fields_per_component: 32,
        ..CodecLimits::for_testing()
    };
    let baseline = Snapshot {
        tick: codec::SnapshotTick::new(1),
        entities: vec![entity_with_uint_fields(1, 32, 0)],
    };
    let mut current_entity = entity_with_uint_fields(1, 32, 0);
    current_entity.components[0].fields[3] = FieldValue::UInt(1);
    let current = Snapshot {
        tick: codec::SnapshotTick::new(2),
        entities: vec![current_entity.clone()],
    };

    let mut buf = [0u8; 512];
    let bytes = encode_delta_snapshot(
        &schema,
        current.tick,
        baseline.tick,
        &baseline,
        &current,
        &limits,
        &mut buf,
    )
    .unwrap();

    let packet = decode_packet(&buf[..bytes], &WireLimits::for_testing()).unwrap();
    assert!(packet
        .sections
        .iter()
        .any(|section| section.tag == SectionTag::EntityUpdateSparsePacked));

    let applied = apply_delta_snapshot_from_packet(&schema, &baseline, &packet, &limits).unwrap();
    assert_eq!(applied.entities, current.entities);
}

#[test]
fn delta_selects_masked_encoding_for_dense_change() {
    let schema = schema_with_uint_fields(8);
    let baseline = Snapshot {
        tick: codec::SnapshotTick::new(1),
        entities: vec![entity_with_uint_fields(1, 8, 0)],
    };
    let current = Snapshot {
        tick: codec::SnapshotTick::new(2),
        entities: vec![entity_with_uint_fields(1, 8, 5)],
    };

    let mut buf = [0u8; 512];
    let bytes = encode_delta_snapshot(
        &schema,
        current.tick,
        baseline.tick,
        &baseline,
        &current,
        &CodecLimits::for_testing(),
        &mut buf,
    )
    .unwrap();

    let packet = decode_packet(&buf[..bytes], &WireLimits::for_testing()).unwrap();
    assert!(packet
        .sections
        .iter()
        .any(|section| section.tag == SectionTag::EntityUpdate));
}

#[test]
fn delta_for_client_uses_sparse_encoding() {
    let schema = schema_with_uint_fields(16);
    let baseline = Snapshot {
        tick: codec::SnapshotTick::new(1),
        entities: vec![entity_with_uint_fields(1, 16, 0)],
    };
    let mut current_entity = entity_with_uint_fields(1, 16, 0);
    current_entity.components[0].fields[0] = FieldValue::UInt(1);
    let current = Snapshot {
        tick: codec::SnapshotTick::new(2),
        entities: vec![current_entity.clone()],
    };

    let mut buf = [0u8; 512];
    let bytes = encode_delta_snapshot_for_client(
        &schema,
        current.tick,
        baseline.tick,
        &baseline,
        &current,
        &CodecLimits::for_testing(),
        &mut buf,
    )
    .unwrap();

    let packet = decode_packet(&buf[..bytes], &WireLimits::for_testing()).unwrap();
    assert!(packet
        .sections
        .iter()
        .any(|section| section.tag == SectionTag::EntityUpdateSparsePacked));

    let applied =
        apply_delta_snapshot_from_packet(&schema, &baseline, &packet, &CodecLimits::for_testing())
            .unwrap();
    assert_eq!(applied.entities, current.entities);
}