use alloc::collections::VecDeque;
use bevy::prelude::*;
use log::trace;
use crate::prelude::*;
#[derive(Debug, Resource)]
pub struct ServerMutateTicks {
ticks: VecDeque<TickMessages>,
last_tick: RepliconTick,
}
impl ServerMutateTicks {
pub fn last_tick(&self) -> RepliconTick {
self.last_tick
}
pub fn mask(&self) -> u64 {
let mut bitmask = 0;
for (i, tick) in self.ticks.iter().enumerate() {
if tick.all_received() {
bitmask |= 1 << i;
}
}
bitmask
}
pub fn contains(&self, tick: RepliconTick) -> bool {
if tick > self.last_tick {
return false;
}
let ago = self.last_tick - tick;
if let Some(tick) = self.ticks.get(ago as usize) {
tick.all_received()
} else {
true
}
}
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 - self.ticks.len() as u32 {
return true;
}
let end_tick = if end_tick < self.last_tick {
end_tick
} else {
self.last_tick
};
let end = (self.last_tick - start_tick) as usize;
let start = (self.last_tick - end_tick) as usize;
self.ticks
.range(start..=end)
.any(|tick| tick.all_received())
}
pub fn confirm(&mut self, tick: RepliconTick, messages_count: usize) -> bool {
let len = self.ticks.len();
debug_assert_eq!(len, u64::BITS as usize);
if tick > self.last_tick {
let delta = (tick - self.last_tick) as usize;
trace!("confirming `{tick:?}` which is {delta} ticks since last");
if delta >= len {
self.ticks.clear();
self.ticks.resize(u64::BITS as usize, Default::default());
} else {
for _ in 0..delta {
self.ticks.pop_back();
self.ticks.push_front(Default::default());
}
}
self.last_tick = tick;
self.ticks[0].confirm(messages_count)
} else {
let delta = (self.last_tick - tick) as usize;
trace!("confirming `{tick:?}` which is {delta} ticks ago");
if delta < len {
self.ticks[delta].confirm(messages_count)
} else {
false
}
}
}
pub(super) fn clear(&mut self) {
for tick in &mut self.ticks {
*tick = Default::default();
}
self.last_tick = Default::default();
}
}
impl Default for ServerMutateTicks {
fn default() -> Self {
Self {
ticks: VecDeque::from([Default::default(); u64::BITS as usize]),
last_tick: Default::default(),
}
}
}
#[derive(Clone, Copy, Debug, Default)]
struct TickMessages {
messages_count: usize,
received: usize,
}
impl TickMessages {
fn confirm(&mut self, messages_count: usize) -> bool {
debug_assert_ne!(messages_count, 0);
debug_assert!(
self.messages_count == 0 || self.messages_count == messages_count,
"messages count shouldn't change, expected {}, but got {messages_count}",
self.messages_count
);
self.messages_count = messages_count;
self.received += 1;
debug_assert!(
self.received <= self.messages_count,
"expected at most {} messages, but confirmed {}",
self.messages_count,
self.received,
);
self.all_received()
}
fn all_received(&self) -> bool {
self.messages_count != 0 && self.messages_count == self.received
}
}
#[derive(Message, Debug, Clone, Copy)]
pub struct MutateTickReceived {
pub tick: RepliconTick,
}
#[cfg(test)]
mod tests {
use test_log::test;
use super::*;
#[test]
fn contains() {
let mut ticks = ServerMutateTicks::default();
ticks.confirm(RepliconTick::new(1), 1);
assert!(!ticks.contains(RepliconTick::new(0)));
assert!(ticks.contains(RepliconTick::new(1)));
assert!(!ticks.contains(RepliconTick::new(2)));
assert!(!ticks.contains(RepliconTick::new(u32::MAX)));
}
#[test]
fn contains_with_wrapping() {
let mut ticks = ServerMutateTicks::default();
ticks.confirm(RepliconTick::new(u64::BITS + 1), 1);
assert!(ticks.contains(RepliconTick::new(0)));
assert!(ticks.contains(RepliconTick::new(1)));
assert!(!ticks.contains(RepliconTick::new(2)));
assert!(!ticks.contains(RepliconTick::new(u64::BITS)));
assert!(ticks.contains(RepliconTick::new(u64::BITS + 1)));
assert!(!ticks.contains(RepliconTick::new(u64::BITS + 2)));
}
#[test]
fn contains_any() {
let mut ticks = ServerMutateTicks::default();
ticks.confirm(RepliconTick::new(1), 1);
assert!(ticks.contains_any(RepliconTick::new(0), RepliconTick::new(1)));
assert!(ticks.contains_any(RepliconTick::new(1), RepliconTick::new(1)));
assert!(ticks.contains_any(RepliconTick::new(1), RepliconTick::new(2)));
assert!(!ticks.contains_any(RepliconTick::new(2), RepliconTick::new(3)));
assert!(!ticks.contains_any(RepliconTick::new(u32::MAX), RepliconTick::new(0)));
assert!(ticks.contains_any(RepliconTick::new(u32::MAX), RepliconTick::new(1)));
assert!(ticks.contains_any(RepliconTick::new(0), RepliconTick::new(2)));
assert!(ticks.contains_any(RepliconTick::new(u32::MAX), RepliconTick::new(3)));
}
#[test]
fn contains_any_with_wrapping() {
let mut ticks = ServerMutateTicks::default();
ticks.confirm(RepliconTick::new(u64::BITS + 1), 1);
assert!(ticks.contains_any(RepliconTick::new(0), RepliconTick::new(1)));
assert!(ticks.contains_any(RepliconTick::new(1), RepliconTick::new(2)));
assert!(!ticks.contains_any(RepliconTick::new(2), RepliconTick::new(3)));
assert!(ticks.contains_any(RepliconTick::new(0), RepliconTick::new(3)));
assert!(ticks.contains_any(
RepliconTick::new(u64::BITS),
RepliconTick::new(u64::BITS + 1)
));
assert!(ticks.contains_any(
RepliconTick::new(u64::BITS + 1),
RepliconTick::new(u64::BITS + 2)
));
assert!(!ticks.contains_any(
RepliconTick::new(u64::BITS + 2),
RepliconTick::new(u64::BITS + 3)
));
assert!(ticks.contains_any(
RepliconTick::new(u64::BITS),
RepliconTick::new(u64::BITS + 3)
));
}
#[test]
fn contains_any_with_older() {
let mut ticks = ServerMutateTicks::default();
ticks.confirm(RepliconTick::new(1), 1);
assert_eq!(ticks.mask(), 0b1);
ticks.confirm(RepliconTick::new(u32::MAX), 1);
assert_eq!(ticks.mask(), 0b101);
assert!(ticks.contains_any(RepliconTick::new(0), RepliconTick::new(1)));
assert!(ticks.contains_any(RepliconTick::new(1), RepliconTick::new(2)));
assert!(!ticks.contains_any(RepliconTick::new(2), RepliconTick::new(3)));
assert!(ticks.contains_any(RepliconTick::new(u32::MAX), RepliconTick::new(0)));
assert!(ticks.contains_any(RepliconTick::new(0), RepliconTick::new(2)));
assert!(ticks.contains_any(RepliconTick::new(u32::MAX), RepliconTick::new(3)));
assert!(ticks.contains_any(RepliconTick::new(u32::MAX - 1), RepliconTick::new(u32::MAX)));
assert!(!ticks.contains_any(
RepliconTick::new(u32::MAX - 2),
RepliconTick::new(u32::MAX - 1)
));
}
#[test]
fn confirm_newer() {
let mut ticks = ServerMutateTicks::default();
ticks.confirm(RepliconTick::new(1), 1);
ticks.confirm(RepliconTick::new(2), 1);
assert_eq!(ticks.mask(), 0b11);
assert!(!ticks.contains(RepliconTick::new(0)));
assert!(ticks.contains(RepliconTick::new(1)));
assert!(ticks.contains(RepliconTick::new(2)));
}
#[test]
fn confirm_older() {
let mut ticks = ServerMutateTicks::default();
ticks.confirm(RepliconTick::new(1), 1);
ticks.confirm(RepliconTick::new(0), 1);
assert_eq!(ticks.mask(), 0b11);
assert!(ticks.contains(RepliconTick::new(0)));
assert!(ticks.contains(RepliconTick::new(1)));
assert!(!ticks.contains(RepliconTick::new(2)));
}
#[test]
fn confirm_same() {
let mut ticks = ServerMutateTicks::default();
assert!(!ticks.confirm(RepliconTick::new(1), 2));
assert_eq!(ticks.mask(), 0b0);
assert!(ticks.confirm(RepliconTick::new(1), 2));
assert_eq!(ticks.mask(), 0b1);
assert!(!ticks.contains(RepliconTick::new(0)));
assert!(ticks.contains(RepliconTick::new(1)));
assert!(!ticks.contains(RepliconTick::new(2)));
}
#[test]
fn confirm_with_wrapping() {
let mut ticks = ServerMutateTicks::default();
ticks.confirm(RepliconTick::new(1), 1);
ticks.confirm(RepliconTick::new(u64::BITS + 1), 1);
assert_eq!(ticks.mask(), 0b1);
assert!(ticks.contains(RepliconTick::new(0)));
assert!(ticks.contains(RepliconTick::new(1)));
assert!(!ticks.contains(RepliconTick::new(2)));
assert!(!ticks.contains(RepliconTick::new(u64::BITS)));
assert!(ticks.contains(RepliconTick::new(u64::BITS + 1)));
assert!(!ticks.contains(RepliconTick::new(u64::BITS + 2)));
}
#[test]
fn confirm_with_overflow() {
let mut ticks = ServerMutateTicks::default();
ticks.confirm(RepliconTick::new(u32::MAX), 1);
ticks.confirm(RepliconTick::new(1), 1);
assert_eq!(ticks.mask(), 0b101);
assert!(!ticks.contains(RepliconTick::new(0)));
assert!(ticks.contains(RepliconTick::new(1)));
assert!(!ticks.contains(RepliconTick::new(3)));
assert!(ticks.contains(RepliconTick::new(u32::MAX)));
}
}