use std::collections::VecDeque;
pub const MAX_PENDING: usize = 240;
#[derive(Clone, Debug)]
pub struct Prediction<I> {
pending: VecDeque<(u64, I)>,
}
impl<I> Default for Prediction<I> {
fn default() -> Self {
Self {
pending: VecDeque::new(),
}
}
}
impl<I> Prediction<I> {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, tick: u64, input: I) {
if self.pending.len() == MAX_PENDING {
self.pending.pop_front();
}
self.pending.push_back((tick, input));
}
pub fn reconcile<S>(
&mut self,
acked_tick: u64,
mut state: S,
mut step: impl FnMut(&mut S, &I),
) -> S {
while self
.pending
.front()
.is_some_and(|(tick, _)| *tick <= acked_tick)
{
self.pending.pop_front();
}
for (_, input) in &self.pending {
step(&mut state, input);
}
state
}
pub fn pending(&self) -> impl Iterator<Item = &(u64, I)> {
self.pending.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reconcile_replays_unconfirmed_inputs_on_the_host_state() {
fn step(x: &mut f32, input: &f32) {
*x += input;
}
let mut prediction = Prediction::new();
let mut client = 0.0;
for tick in 1..=5 {
prediction.push(tick, 1.0);
step(&mut client, &1.0);
}
assert_eq!(prediction.reconcile(3, 3.0, step), client);
assert_eq!(prediction.pending().count(), 2);
assert_eq!(prediction.reconcile(4, 2.0, step), 3.0);
assert_eq!(
prediction.pending().map(|(t, _)| *t).collect::<Vec<_>>(),
[5]
);
assert_eq!(prediction.reconcile(2, 2.0, step), 3.0);
for tick in 6..6 + MAX_PENDING as u64 + 10 {
prediction.push(tick, 0.0);
}
assert_eq!(prediction.pending().count(), MAX_PENDING);
}
}