use std::collections::{HashMap, HashSet, VecDeque};
use glam::{Quat, Vec3};
use super::codec::{ByteReader, ByteWriter, CodecError};
use super::stats::Stats;
pub const FLAG_GROUNDED: u16 = 1 << 0;
pub const FLAG_SPRINTING: u16 = 1 << 1;
pub const FLAG_AIRBORNE: u16 = 1 << 2;
pub const FLAG_DEAD: u16 = 1 << 3;
pub const FLAG_HIDDEN: u16 = 1 << 4;
const POS_EPS: f32 = 1e-3;
const ROT_EPS: f32 = 1e-4;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct EntityState {
pub id: u64,
pub flags: u16,
pub pos: Vec3,
pub vel: Vec3,
pub rot: Quat,
}
impl EntityState {
pub fn new(id: u64) -> Self {
Self { id, flags: 0, pos: Vec3::ZERO, vel: Vec3::ZERO, rot: Quat::IDENTITY }
}
pub fn at(id: u64, pos: Vec3) -> Self {
Self { pos, ..Self::new(id) }
}
pub fn approx_eq(&self, other: &EntityState) -> bool {
self.flags == other.flags
&& self.pos.distance_squared(other.pos) < POS_EPS * POS_EPS
&& self.vel.distance_squared(other.vel) < POS_EPS * POS_EPS
&& (1.0 - self.rot.dot(other.rot).abs()) < ROT_EPS
}
pub fn lerp(&self, other: &EntityState, t: f32) -> EntityState {
EntityState {
id: self.id,
flags: if t < 0.5 { self.flags } else { other.flags },
pos: self.pos.lerp(other.pos, t),
vel: self.vel.lerp(other.vel, t),
rot: self.rot.slerp(other.rot, t),
}
}
pub(crate) fn encode(&self, w: &mut ByteWriter) {
w.u64(self.id).u16(self.flags).vec3(self.pos).vec3(self.vel).quat(self.rot);
}
pub(crate) fn decode(r: &mut ByteReader) -> Result<Self, CodecError> {
Ok(Self {
id: r.u64()?,
flags: r.u16()?,
pos: r.vec3()?,
vel: r.vec3()?,
rot: r.quat()?,
})
}
}
#[derive(Clone, Debug, Default)]
pub struct Snapshot {
pub tick: u64,
pub baseline_tick: u64,
pub keyframe: bool,
pub states: Vec<EntityState>,
pub removed: Vec<u64>,
pub stats: Vec<(u64, Stats)>,
}
impl Snapshot {
pub fn keyframe(tick: u64, states: Vec<EntityState>) -> Self {
Self { tick, baseline_tick: 0, keyframe: true, states, removed: Vec::new(), stats: Vec::new() }
}
pub fn with_stats(mut self, stats: Vec<(u64, Stats)>) -> Self {
self.stats = stats;
self
}
pub fn get(&self, id: u64) -> Option<&EntityState> {
self.states.iter().find(|e| e.id == id)
}
pub fn stats_of(&self, id: u64) -> Option<&Stats> {
self.stats.iter().find(|(eid, _)| *eid == id).map(|(_, s)| s)
}
pub fn delta_from(baseline: &Snapshot, current: &Snapshot) -> Snapshot {
let base: HashMap<u64, &EntityState> =
baseline.states.iter().map(|e| (e.id, e)).collect();
let base_stats: HashMap<u64, &Stats> =
baseline.stats.iter().map(|(id, s)| (*id, s)).collect();
let mut states = Vec::new();
for e in ¤t.states {
match base.get(&e.id) {
Some(b) if b.approx_eq(e) => {}
_ => states.push(*e),
}
}
let cur_ids: HashSet<u64> = current.states.iter().map(|e| e.id).collect();
let removed: Vec<u64> = baseline
.states
.iter()
.map(|e| e.id)
.filter(|id| !cur_ids.contains(id))
.collect();
let mut stats = Vec::new();
for (id, s) in ¤t.stats {
match base_stats.get(id) {
Some(b) if *b == s => {}
_ => stats.push((*id, s.clone())),
}
}
Snapshot {
tick: current.tick,
baseline_tick: baseline.tick,
keyframe: false,
states,
removed,
stats,
}
}
pub fn apply_delta(baseline: &Snapshot, delta: &Snapshot) -> Snapshot {
let mut by_id: HashMap<u64, EntityState> =
baseline.states.iter().map(|e| (e.id, *e)).collect();
for e in &delta.states {
by_id.insert(e.id, *e);
}
for id in &delta.removed {
by_id.remove(id);
}
let mut states: Vec<EntityState> = by_id.into_values().collect();
states.sort_by_key(|e| e.id);
let mut stats_map: HashMap<u64, Stats> =
baseline.stats.iter().cloned().collect();
for (id, s) in &delta.stats {
stats_map.insert(*id, s.clone());
}
for id in &delta.removed {
stats_map.remove(id);
}
let mut stats: Vec<(u64, Stats)> = stats_map.into_iter().collect();
stats.sort_by_key(|(id, _)| *id);
Snapshot { tick: delta.tick, baseline_tick: 0, keyframe: true, states, removed: Vec::new(), stats }
}
pub(crate) fn encode(&self, w: &mut ByteWriter) {
w.u64(self.tick).u64(self.baseline_tick).bool(self.keyframe);
w.u32(self.states.len() as u32);
for e in &self.states {
e.encode(w);
}
w.u32(self.removed.len() as u32);
for id in &self.removed {
w.u64(*id);
}
w.u32(self.stats.len() as u32);
for (id, s) in &self.stats {
w.u64(*id);
s.encode(w);
}
}
pub(crate) fn decode(r: &mut ByteReader) -> Result<Self, CodecError> {
let tick = r.u64()?;
let baseline_tick = r.u64()?;
let keyframe = r.bool()?;
let n = r.u32()? as usize;
let mut states = Vec::with_capacity(n.min(4096));
for _ in 0..n {
states.push(EntityState::decode(r)?);
}
let rn = r.u32()? as usize;
let mut removed = Vec::with_capacity(rn.min(4096));
for _ in 0..rn {
removed.push(r.u64()?);
}
let sn = r.u32()? as usize;
let mut stats = Vec::with_capacity(sn.min(4096));
for _ in 0..sn {
let id = r.u64()?;
stats.push((id, Stats::decode(r)?));
}
Ok(Self { tick, baseline_tick, keyframe, states, removed, stats })
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut w = ByteWriter::new();
self.encode(&mut w);
w.finish()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CodecError> {
let mut r = ByteReader::new(bytes);
let s = Self::decode(&mut r)?;
r.expect_end()?;
Ok(s)
}
}
struct TimedSnapshot {
recv_time: f64,
snap: Snapshot,
}
pub struct InterpolationBuffer {
delay: f64,
capacity: usize,
frames: VecDeque<TimedSnapshot>,
}
impl InterpolationBuffer {
pub fn new(delay: f64) -> Self {
Self { delay, capacity: 32, frames: VecDeque::new() }
}
pub fn delay(&self) -> f64 {
self.delay
}
pub fn set_delay(&mut self, delay: f64) {
self.delay = delay;
}
pub fn clear(&mut self) {
self.frames.clear();
}
pub fn latest_tick(&self) -> Option<u64> {
self.frames.back().map(|f| f.snap.tick)
}
pub fn push(&mut self, snap: Snapshot, now: f64) {
if let Some(back) = self.frames.back() {
if snap.tick <= back.snap.tick {
return; }
}
self.frames.push_back(TimedSnapshot { recv_time: now, snap });
while self.frames.len() > self.capacity {
self.frames.pop_front();
}
}
pub fn sample(&self, now: f64) -> Vec<EntityState> {
let target = now - self.delay;
if self.frames.is_empty() {
return Vec::new();
}
if self.frames.len() == 1 {
return self.frames[0].snap.states.clone();
}
for w in 0..self.frames.len() - 1 {
let a = &self.frames[w];
let b = &self.frames[w + 1];
if target >= a.recv_time && target <= b.recv_time {
let span = (b.recv_time - a.recv_time).max(1e-6);
let t = ((target - a.recv_time) / span).clamp(0.0, 1.0) as f32;
return blend(&a.snap, &b.snap, t);
}
}
if target < self.frames[0].recv_time {
self.frames[0].snap.states.clone()
} else {
self.frames.back().unwrap().snap.states.clone()
}
}
}
fn blend(a: &Snapshot, b: &Snapshot, t: f32) -> Vec<EntityState> {
let a_map: HashMap<u64, &EntityState> = a.states.iter().map(|e| (e.id, e)).collect();
b.states
.iter()
.map(|be| match a_map.get(&be.id) {
Some(ae) => ae.lerp(be, t),
None => *be,
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn ent(id: u64, x: f32) -> EntityState {
EntityState::at(id, Vec3::new(x, 0.0, 0.0))
}
#[test]
fn snapshot_roundtrip() {
let snap = Snapshot::keyframe(7, vec![ent(1, 1.0), ent(2, 2.0)])
.with_stats(vec![(1, Stats::new().with(0, 50))]);
let bytes = snap.to_bytes();
let back = Snapshot::from_bytes(&bytes).unwrap();
assert_eq!(back.tick, 7);
assert_eq!(back.states.len(), 2);
assert_eq!(back.stats_of(1).unwrap().get(0), 50);
}
#[test]
fn delta_then_apply_reconstructs() {
let base = Snapshot::keyframe(1, vec![ent(1, 0.0), ent(2, 0.0), ent(3, 0.0)]);
let current = Snapshot::keyframe(2, vec![ent(1, 5.0), ent(2, 0.0), ent(4, 9.0)]);
let delta = Snapshot::delta_from(&base, ¤t);
assert!(!delta.keyframe);
assert_eq!(delta.states.len(), 2);
assert_eq!(delta.removed, vec![3]);
let rebuilt = Snapshot::apply_delta(&base, &delta);
let ids: Vec<u64> = rebuilt.states.iter().map(|e| e.id).collect();
assert_eq!(ids, vec![1, 2, 4]);
assert_eq!(rebuilt.get(1).unwrap().pos.x, 5.0);
}
#[test]
fn interpolation_midpoint() {
let mut buf = InterpolationBuffer::new(1.0);
buf.push(Snapshot::keyframe(1, vec![ent(1, 0.0)]), 0.0);
buf.push(Snapshot::keyframe(2, vec![ent(1, 10.0)]), 1.0);
let s = buf.sample(1.5);
assert!((s[0].pos.x - 5.0).abs() < 1e-4, "got {}", s[0].pos.x);
}
#[test]
fn stale_snapshot_dropped() {
let mut buf = InterpolationBuffer::new(0.1);
buf.push(Snapshot::keyframe(5, vec![ent(1, 0.0)]), 0.0);
buf.push(Snapshot::keyframe(3, vec![ent(1, 9.0)]), 0.1); assert_eq!(buf.latest_tick(), Some(5));
}
}