use core::fmt::{self, Debug, Formatter};
use bevy::prelude::*;
use crate::prelude::*;
#[derive(Component, Reflect)]
pub struct ConfirmHistory {
mask: u64,
last_tick: RepliconTick,
}
impl Debug for ConfirmHistory {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "ConfirmHistory [{:?} {:b}]", self.last_tick, self.mask)
}
}
impl ConfirmHistory {
pub fn new(last_tick: RepliconTick) -> Self {
Self { mask: 1, last_tick }
}
pub fn last_tick(&self) -> RepliconTick {
self.last_tick
}
pub fn mask(&self) -> u64 {
self.mask
}
pub fn contains(&self, tick: RepliconTick) -> bool {
if tick > self.last_tick {
return false;
}
let ago = self.last_tick - tick;
ago >= u64::BITS || ((self.mask >> ago) & 1) == 1
}
pub fn contains_any(&self, start_tick: RepliconTick, end_tick: RepliconTick) -> bool {
debug_assert!(start_tick <= end_tick);
if start_tick > self.last_tick {
return false;
}
if start_tick <= self.last_tick - u64::BITS {
return true;
}
let end_tick = if end_tick < self.last_tick {
end_tick
} else {
self.last_tick
};
let len = end_tick - start_tick + 1; let range = (1 << len) - 1; let offset = self.last_tick - end_tick;
let mask = range << offset;
self.mask & mask != 0
}
pub fn confirm(&mut self, tick: RepliconTick) {
if tick > self.last_tick {
self.set_last_tick(tick);
} else {
let ago = self.last_tick - tick;
if ago < u64::BITS {
self.set(ago);
}
}
}
pub(super) fn set(&mut self, ago: u32) {
debug_assert!(ago < u64::BITS);
self.mask |= 1 << ago;
}
pub(super) fn set_last_tick(&mut self, tick: RepliconTick) {
debug_assert!(tick >= self.last_tick);
let diff = tick - self.last_tick;
self.mask = self.mask.wrapping_shl(diff);
self.last_tick = tick;
self.mask |= 1;
}
}
#[derive(Message, Debug, Clone, Copy)]
pub struct EntityReplicated {
pub entity: Entity,
pub tick: RepliconTick,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contains() {
let history = ConfirmHistory::new(RepliconTick::new(1));
assert!(!history.contains(RepliconTick::new(0)));
assert!(history.contains(RepliconTick::new(1)));
assert!(!history.contains(RepliconTick::new(2)));
assert!(!history.contains(RepliconTick::new(u32::MAX)));
}
#[test]
fn contains_with_wrapping() {
let history = ConfirmHistory::new(RepliconTick::new(u64::BITS + 1));
assert!(history.contains(RepliconTick::new(0)));
assert!(history.contains(RepliconTick::new(1)));
assert!(!history.contains(RepliconTick::new(2)));
assert!(!history.contains(RepliconTick::new(u64::BITS)));
assert!(history.contains(RepliconTick::new(u64::BITS + 1)));
assert!(!history.contains(RepliconTick::new(u64::BITS + 2)));
}
#[test]
fn contains_any() {
let history = ConfirmHistory::new(RepliconTick::new(1));
assert!(history.contains_any(RepliconTick::new(0), RepliconTick::new(1)));
assert!(history.contains_any(RepliconTick::new(1), RepliconTick::new(1)));
assert!(history.contains_any(RepliconTick::new(1), RepliconTick::new(2)));
assert!(!history.contains_any(RepliconTick::new(2), RepliconTick::new(3)));
assert!(!history.contains_any(RepliconTick::new(u32::MAX), RepliconTick::new(0)));
assert!(history.contains_any(RepliconTick::new(u32::MAX), RepliconTick::new(1)));
assert!(history.contains_any(RepliconTick::new(0), RepliconTick::new(2)));
assert!(history.contains_any(RepliconTick::new(u32::MAX), RepliconTick::new(3)));
}
#[test]
fn contains_any_with_wrapping() {
let history = ConfirmHistory::new(RepliconTick::new(u64::BITS + 1));
assert!(history.contains_any(RepliconTick::new(0), RepliconTick::new(1)));
assert!(history.contains_any(RepliconTick::new(1), RepliconTick::new(2)));
assert!(!history.contains_any(RepliconTick::new(2), RepliconTick::new(3)));
assert!(history.contains_any(RepliconTick::new(0), RepliconTick::new(3)));
assert!(history.contains_any(
RepliconTick::new(u64::BITS),
RepliconTick::new(u64::BITS + 1)
));
assert!(history.contains_any(
RepliconTick::new(u64::BITS + 1),
RepliconTick::new(u64::BITS + 2)
));
assert!(!history.contains_any(
RepliconTick::new(u64::BITS + 2),
RepliconTick::new(u64::BITS + 3)
));
assert!(history.contains_any(
RepliconTick::new(u64::BITS),
RepliconTick::new(u64::BITS + 3)
));
}
#[test]
fn contains_any_with_older() {
let mut history = ConfirmHistory::new(RepliconTick::new(1));
assert_eq!(history.mask(), 0b1);
history.confirm(RepliconTick::new(u32::MAX));
assert_eq!(history.mask(), 0b101);
assert!(history.contains_any(RepliconTick::new(0), RepliconTick::new(1)));
assert!(history.contains_any(RepliconTick::new(1), RepliconTick::new(2)));
assert!(!history.contains_any(RepliconTick::new(2), RepliconTick::new(3)));
assert!(history.contains_any(RepliconTick::new(u32::MAX), RepliconTick::new(0)));
assert!(history.contains_any(RepliconTick::new(0), RepliconTick::new(2)));
assert!(history.contains_any(RepliconTick::new(u32::MAX), RepliconTick::new(3)));
assert!(history.contains_any(RepliconTick::new(u32::MAX - 1), RepliconTick::new(u32::MAX)));
assert!(!history.contains_any(
RepliconTick::new(u32::MAX - 2),
RepliconTick::new(u32::MAX - 1)
));
}
#[test]
fn confirm_newer() {
let mut history = ConfirmHistory::new(RepliconTick::new(1));
history.confirm(RepliconTick::new(2));
assert_eq!(history.mask(), 0b11);
assert!(!history.contains(RepliconTick::new(0)));
assert!(history.contains(RepliconTick::new(1)));
assert!(history.contains(RepliconTick::new(2)));
}
#[test]
fn confirm_older() {
let mut history = ConfirmHistory::new(RepliconTick::new(1));
history.confirm(RepliconTick::new(0));
assert_eq!(history.mask(), 0b11);
assert!(history.contains(RepliconTick::new(0)));
assert!(history.contains(RepliconTick::new(1)));
assert!(!history.contains(RepliconTick::new(2)));
}
#[test]
fn confirm_same() {
let mut history = ConfirmHistory::new(RepliconTick::new(1));
history.confirm(RepliconTick::new(1));
assert_eq!(history.mask(), 0b1);
assert!(!history.contains(RepliconTick::new(0)));
assert!(history.contains(RepliconTick::new(1)));
assert!(!history.contains(RepliconTick::new(2)));
}
#[test]
fn confirm_with_wrapping() {
let mut history = ConfirmHistory::new(RepliconTick::new(1));
history.confirm(RepliconTick::new(u64::BITS + 1));
assert_eq!(history.mask(), 0b1);
assert!(history.contains(RepliconTick::new(0)));
assert!(history.contains(RepliconTick::new(1)));
assert!(!history.contains(RepliconTick::new(2)));
assert!(!history.contains(RepliconTick::new(u64::BITS)));
assert!(history.contains(RepliconTick::new(u64::BITS + 1)));
assert!(!history.contains(RepliconTick::new(u64::BITS + 2)));
}
#[test]
fn confirm_with_overflow() {
let mut history = ConfirmHistory::new(RepliconTick::new(u32::MAX));
history.confirm(RepliconTick::new(1));
assert_eq!(history.mask(), 0b101);
assert!(!history.contains(RepliconTick::new(0)));
assert!(history.contains(RepliconTick::new(1)));
assert!(!history.contains(RepliconTick::new(3)));
assert!(history.contains(RepliconTick::new(u32::MAX)));
}
}