use alloc::collections::VecDeque;
use alloc::{
format,
string::{String, ToString},
vec::Vec,
};
use bevy_ecs::component::Component;
use bevy_reflect::Reflect;
use core::fmt::{Debug, Formatter};
use lightyear_core::tick::Tick;
use serde::{Deserialize, Serialize};
use tracing::trace;
#[derive(Component, Debug, Reflect)]
pub struct InputBuffer<T> {
pub start_tick: Option<Tick>,
pub buffer: VecDeque<InputData<T>>,
}
impl<T: Debug> core::fmt::Display for InputBuffer<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let ty = core::any::type_name::<T>();
let Some(tick) = self.start_tick else {
return write!(f, "EmptyInputBuffer");
};
let buffer_str = self
.buffer
.iter()
.enumerate()
.map(|(i, item)| {
let str = match item {
InputData::Absent => "Absent".to_string(),
InputData::SameAsPrecedent => "SameAsPrecedent".to_string(),
InputData::Input(data) => format!("{data:?}"),
};
format!("{:?}: {}\n", tick + i as i16, str)
})
.collect::<Vec<String>>()
.join("");
write!(f, "InputBuffer<{ty:?}>:\n {buffer_str}")
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Reflect)]
pub enum InputData<T> {
Absent,
SameAsPrecedent,
Input(T),
}
impl<T> From<Option<T>> for InputData<T> {
fn from(value: Option<T>) -> Self {
match value {
Some(value) => InputData::Input(value),
_ => InputData::Absent,
}
}
}
impl<T> Default for InputBuffer<T> {
fn default() -> Self {
Self {
buffer: VecDeque::new(),
start_tick: None,
}
}
}
impl<T: Clone + PartialEq> InputBuffer<T> {
pub(crate) fn len(&self) -> usize {
self.buffer.len()
}
pub fn extend_to_range(&mut self, start_tick: Tick, end_tick: Tick) {
if self.start_tick.is_none() {
self.start_tick = Some(start_tick);
}
let mut current_start = self.start_tick.unwrap();
if start_tick < current_start {
let prepend_count = (current_start - start_tick) as usize;
for _ in 0..prepend_count {
self.buffer.push_front(InputData::Absent);
}
self.start_tick = Some(start_tick);
current_start = start_tick;
}
let current_end = current_start + (self.buffer.len() as i16 - 1);
if end_tick > current_end {
let append_count = (end_tick - current_end) as usize;
for _ in 0..append_count {
self.buffer.push_back(InputData::Absent);
}
}
}
pub fn set(&mut self, tick: Tick, value: T) {
if let Some(precedent) = self.get(tick - 1) {
if precedent == &value {
self.set_raw(tick, InputData::SameAsPrecedent);
return;
}
}
self.set_raw(tick, InputData::Input(value));
}
pub fn set_empty(&mut self, tick: Tick) {
self.set_raw(tick, InputData::Absent);
}
pub fn set_raw(&mut self, tick: Tick, value: InputData<T>) {
let Some(start_tick) = self.start_tick else {
self.start_tick = Some(tick);
self.buffer.push_back(value);
return;
};
if tick < start_tick {
return;
}
let end_tick = start_tick + (self.buffer.len() as i16 - 1);
if tick > end_tick {
for _ in 0..(tick - end_tick - 1) {
trace!("fill ticks");
self.buffer.push_back(InputData::SameAsPrecedent);
}
self.buffer.push_back(InputData::Absent);
}
let entry = self.buffer.get_mut((tick - start_tick) as usize).unwrap();
*entry = value;
}
pub fn pop(&mut self, tick: Tick) -> Option<T> {
let start_tick = self.start_tick?;
if tick < start_tick {
return None;
}
if tick > start_tick + (self.buffer.len() as i16 - 1) {
self.buffer = VecDeque::new();
self.start_tick = Some(tick + 1);
return None;
}
let mut popped = InputData::Absent;
for _ in 0..(tick + 1 - start_tick) {
let data = self.buffer.pop_front().unwrap();
match data {
InputData::Absent | InputData::Input(_) => {
popped = data;
}
_ => {}
}
}
self.start_tick = Some(tick + 1);
if let Some(InputData::SameAsPrecedent) = self.buffer.front() {
*self.buffer.front_mut().unwrap() = popped.clone();
}
match popped {
InputData::Input(value) => Some(value),
_ => None,
}
}
pub(crate) fn get_raw(&self, tick: Tick) -> &InputData<T> {
let Some(start_tick) = self.start_tick else {
return &InputData::Absent;
};
if self.buffer.is_empty() {
return &InputData::Absent;
}
if tick < start_tick || tick > start_tick + (self.buffer.len() as i16 - 1) {
return &InputData::Absent;
}
self.buffer.get((tick - start_tick) as usize).unwrap()
}
pub fn get(&self, tick: Tick) -> Option<&T> {
let start_tick = self.start_tick?;
if self.buffer.is_empty() {
return None;
}
if tick < start_tick || tick > start_tick + (self.buffer.len() as i16 - 1) {
return None;
}
let data = self.buffer.get((tick - start_tick) as usize).unwrap();
match data {
InputData::Absent => None,
InputData::SameAsPrecedent => {
self.get(tick - 1)
}
InputData::Input(data) => Some(data),
}
}
pub fn get_last(&self) -> Option<&T> {
let start_tick = self.start_tick?;
if self.buffer.is_empty() {
return None;
}
self.get(start_tick + (self.buffer.len() as i16 - 1))
}
pub fn get_last_with_tick(&self) -> Option<(Tick, &T)> {
let start_tick = self.start_tick?;
if self.buffer.is_empty() {
return None;
}
let end_tick = start_tick + (self.buffer.len() as i16 - 1);
self.get(end_tick)
.map(|action_state| (end_tick, action_state))
}
pub fn end_tick(&self) -> Option<Tick> {
self.start_tick
.map(|start_tick| start_tick + (self.buffer.len() as i16 - 1))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_set_pop() {
let mut input_buffer = InputBuffer::default();
input_buffer.set(Tick(4), 0);
input_buffer.set(Tick(6), 1);
input_buffer.set(Tick(7), 1);
input_buffer.set(Tick(8), 1);
assert_eq!(input_buffer.get(Tick(4)), Some(&0));
assert_eq!(input_buffer.get(Tick(5)), Some(&0));
assert_eq!(input_buffer.get_raw(Tick(5)), &InputData::SameAsPrecedent);
assert_eq!(input_buffer.get(Tick(6)), Some(&1));
assert_eq!(input_buffer.get_raw(Tick(7)), &InputData::SameAsPrecedent);
assert_eq!(input_buffer.get_raw(Tick(8)), &InputData::SameAsPrecedent);
assert_eq!(input_buffer.get(Tick(9)), None);
assert_eq!(input_buffer.pop(Tick(5)), Some(0));
assert_eq!(input_buffer.start_tick, Some(Tick(6)));
assert_eq!(input_buffer.pop(Tick(7)), Some(1));
assert_eq!(input_buffer.start_tick, Some(Tick(8)));
assert_eq!(input_buffer.get(Tick(8)), Some(&1));
assert_eq!(input_buffer.get_raw(Tick(8)), &InputData::Input(1));
assert_eq!(input_buffer.buffer.len(), 1);
}
#[test]
fn test_extend_to_range_empty() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
input_buffer.extend_to_range(Tick(5), Tick(7));
assert_eq!(input_buffer.start_tick, Some(Tick(5)));
assert_eq!(input_buffer.buffer.len(), 3);
assert_eq!(input_buffer.get_raw(Tick(5)), &InputData::Absent);
assert_eq!(input_buffer.get_raw(Tick(6)), &InputData::Absent);
assert_eq!(input_buffer.get_raw(Tick(7)), &InputData::Absent);
}
#[test]
fn test_extend_to_range_right() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
input_buffer.set(Tick(10), 42);
input_buffer.extend_to_range(Tick(10), Tick(13));
assert_eq!(input_buffer.start_tick, Some(Tick(10)));
assert_eq!(input_buffer.buffer.len(), 4);
assert_eq!(input_buffer.get_raw(Tick(10)), &InputData::Input(42));
assert_eq!(input_buffer.get_raw(Tick(11)), &InputData::Absent);
assert_eq!(input_buffer.get_raw(Tick(12)), &InputData::Absent);
assert_eq!(input_buffer.get_raw(Tick(13)), &InputData::Absent);
}
#[test]
fn test_extend_to_range_left() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
input_buffer.set(Tick(10), 42);
input_buffer.extend_to_range(Tick(8), Tick(10));
assert_eq!(input_buffer.start_tick, Some(Tick(8)));
assert_eq!(input_buffer.buffer.len(), 3);
assert_eq!(input_buffer.get_raw(Tick(8)), &InputData::Absent);
assert_eq!(input_buffer.get_raw(Tick(9)), &InputData::Absent);
assert_eq!(input_buffer.get_raw(Tick(10)), &InputData::Input(42));
}
#[test]
fn test_extend_to_range_both_sides() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
input_buffer.set(Tick(5), 1);
input_buffer.set(Tick(6), 2);
input_buffer.extend_to_range(Tick(3), Tick(8));
assert_eq!(input_buffer.start_tick, Some(Tick(3)));
assert_eq!(input_buffer.buffer.len(), 6);
assert_eq!(input_buffer.get_raw(Tick(3)), &InputData::Absent);
assert_eq!(input_buffer.get_raw(Tick(4)), &InputData::Absent);
assert_eq!(input_buffer.get_raw(Tick(5)), &InputData::Input(1));
assert_eq!(input_buffer.get_raw(Tick(6)), &InputData::Input(2));
assert_eq!(input_buffer.get_raw(Tick(7)), &InputData::Absent);
assert_eq!(input_buffer.get_raw(Tick(8)), &InputData::Absent);
}
#[test]
fn test_set_empty_and_get_raw() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
input_buffer.set_empty(Tick(3));
assert_eq!(input_buffer.get_raw(Tick(3)), &InputData::Absent);
assert_eq!(input_buffer.get(Tick(3)), None);
}
#[test]
fn test_set_raw_and_get() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
input_buffer.set_raw(Tick(2), InputData::Input(7));
assert_eq!(input_buffer.get(Tick(2)), Some(&7));
input_buffer.set_raw(Tick(3), InputData::SameAsPrecedent);
assert_eq!(input_buffer.get(Tick(3)), Some(&7));
}
#[test]
fn test_get_last_and_get_last_with_tick() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
assert_eq!(input_buffer.get_last(), None);
assert_eq!(input_buffer.get_last_with_tick(), None);
input_buffer.set(Tick(1), 10);
input_buffer.set(Tick(2), 20);
assert_eq!(input_buffer.get_last(), Some(&20));
assert_eq!(input_buffer.get_last_with_tick(), Some((Tick(2), &20)));
}
#[test]
fn test_end_tick() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
assert_eq!(input_buffer.end_tick(), None);
input_buffer.set(Tick(5), 1);
assert_eq!(input_buffer.end_tick(), Some(Tick(5)));
input_buffer.set(Tick(7), 2);
assert_eq!(input_buffer.end_tick(), Some(Tick(7)));
}
#[test]
fn test_pop_with_absent() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
input_buffer.set(Tick(1), 1);
input_buffer.set(Tick(2), 2);
input_buffer.set_empty(Tick(3));
input_buffer.set(Tick(4), 2);
assert_eq!(input_buffer.pop(Tick(2)), Some(2));
assert_eq!(input_buffer.pop(Tick(3)), None);
assert_eq!(input_buffer.pop(Tick(4)), Some(2));
}
#[test]
fn test_pop_out_of_range() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
input_buffer.set(Tick(10), 5);
assert_eq!(input_buffer.pop(Tick(5)), None);
assert_eq!(input_buffer.pop(Tick(20)), None);
assert_eq!(input_buffer.buffer.len(), 0);
assert_eq!(input_buffer.start_tick, Some(Tick(21)));
}
#[test]
fn test_pop_same_absent_in_gap() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
input_buffer.set(Tick(9), 5);
input_buffer.set(Tick(10), 5);
input_buffer.set_empty(Tick(11));
input_buffer.set_empty(Tick(12));
input_buffer.set_empty(Tick(13));
assert_eq!(input_buffer.pop(Tick(12)), None);
assert_eq!(input_buffer.get(Tick(13)), None);
assert_eq!(input_buffer.buffer.len(), 1);
}
#[test]
fn test_len() {
let mut input_buffer: InputBuffer<i32> = InputBuffer::default();
assert_eq!(input_buffer.len(), 0);
input_buffer.set(Tick(1), 1);
assert_eq!(input_buffer.len(), 1);
input_buffer.set(Tick(2), 2);
assert_eq!(input_buffer.len(), 2);
}
}