use crate::frame_info::PlayerInput;
use crate::{Config, Frame, InputStatus, NULL_FRAME};
use std::cmp;
const INPUT_QUEUE_LENGTH: usize = 128;
#[derive(Debug, Clone)]
pub(crate) struct InputQueue<T>
where
T: Config,
{
head: usize,
tail: usize,
length: usize,
first_frame: bool,
last_added_frame: Frame,
first_incorrect_frame: Frame,
last_requested_frame: Frame,
frame_delay: usize,
inputs: Vec<PlayerInput<T::Input>>,
prediction: PlayerInput<T::Input>,
}
impl<T: Config> InputQueue<T> {
pub(crate) fn new() -> Self {
Self {
head: 0,
tail: 0,
length: 0,
frame_delay: 0,
first_frame: true,
last_added_frame: NULL_FRAME,
first_incorrect_frame: NULL_FRAME,
last_requested_frame: NULL_FRAME,
prediction: PlayerInput::blank_input(NULL_FRAME),
inputs: vec![PlayerInput::blank_input(NULL_FRAME); INPUT_QUEUE_LENGTH],
}
}
pub(crate) fn first_incorrect_frame(&self) -> Frame {
self.first_incorrect_frame
}
pub(crate) fn set_frame_delay(&mut self, delay: usize) {
self.frame_delay = delay;
}
pub(crate) fn reset_prediction(&mut self) {
self.prediction.frame = NULL_FRAME;
self.first_incorrect_frame = NULL_FRAME;
self.last_requested_frame = NULL_FRAME;
}
pub(crate) fn confirmed_input(&self, requested_frame: Frame) -> PlayerInput<T::Input> {
let offset = requested_frame as usize % INPUT_QUEUE_LENGTH;
if self.inputs[offset].frame == requested_frame {
return self.inputs[offset];
}
panic!("SyncLayer::confirmed_input(): There is no confirmed input for the requested frame");
}
pub(crate) fn discard_confirmed_frames(&mut self, mut frame: Frame) {
if self.last_requested_frame != NULL_FRAME {
frame = cmp::min(frame, self.last_requested_frame);
}
if frame >= self.last_added_frame {
self.tail = self.head;
self.length = 1;
} else if frame <= self.inputs[self.tail].frame {
} else {
let offset = (frame - (self.inputs[self.tail].frame)) as usize;
self.tail = (self.tail + offset) % INPUT_QUEUE_LENGTH;
self.length -= offset;
}
}
pub(crate) fn input(&mut self, requested_frame: Frame) -> (T::Input, InputStatus) {
assert!(self.first_incorrect_frame == NULL_FRAME);
self.last_requested_frame = requested_frame;
assert!(requested_frame >= self.inputs[self.tail].frame);
if self.prediction.frame < 0 {
let mut offset: usize = (requested_frame - self.inputs[self.tail].frame) as usize;
if offset < self.length {
offset = (offset + self.tail) % INPUT_QUEUE_LENGTH;
assert!(self.inputs[offset].frame == requested_frame);
return (self.inputs[offset].input, InputStatus::Confirmed);
}
if requested_frame == 0 || self.last_added_frame == NULL_FRAME {
self.prediction = PlayerInput::blank_input(self.prediction.frame);
} else {
let previous_position = match self.head {
0 => INPUT_QUEUE_LENGTH - 1,
_ => self.head - 1,
};
self.prediction = self.inputs[previous_position];
}
self.prediction.frame += 1;
}
assert!(self.prediction.frame != NULL_FRAME);
let prediction_to_return = self.prediction; (prediction_to_return.input, InputStatus::Predicted)
}
pub(crate) fn add_input(&mut self, input: PlayerInput<T::Input>) -> Frame {
assert!(
self.last_added_frame == NULL_FRAME
|| input.frame + self.frame_delay as i32 == self.last_added_frame + 1
);
let new_frame = self.advance_queue_head(input.frame);
if new_frame != NULL_FRAME {
self.add_input_by_frame(input, new_frame);
}
new_frame
}
fn add_input_by_frame(&mut self, input: PlayerInput<T::Input>, frame_number: Frame) {
let previous_position = match self.head {
0 => INPUT_QUEUE_LENGTH - 1,
_ => self.head - 1,
};
assert!(self.last_added_frame == NULL_FRAME || frame_number == self.last_added_frame + 1);
assert!(frame_number == 0 || self.inputs[previous_position].frame == frame_number - 1);
self.inputs[self.head] = input;
self.inputs[self.head].frame = frame_number;
self.head = (self.head + 1) % INPUT_QUEUE_LENGTH;
self.length += 1;
assert!(self.length <= INPUT_QUEUE_LENGTH);
self.first_frame = false;
self.last_added_frame = frame_number;
if self.prediction.frame != NULL_FRAME {
assert!(frame_number == self.prediction.frame);
if self.first_incorrect_frame == NULL_FRAME && !self.prediction.equal(&input, true) {
self.first_incorrect_frame = frame_number;
}
if self.prediction.frame == self.last_requested_frame
&& self.first_incorrect_frame == NULL_FRAME
{
self.prediction.frame = NULL_FRAME;
} else {
self.prediction.frame += 1;
}
}
}
fn advance_queue_head(&mut self, mut input_frame: Frame) -> Frame {
let previous_position = match self.head {
0 => INPUT_QUEUE_LENGTH - 1,
_ => self.head - 1,
};
let mut expected_frame = if self.first_frame {
0
} else {
self.inputs[previous_position].frame + 1
};
input_frame += self.frame_delay as i32;
if expected_frame > input_frame {
return NULL_FRAME;
}
while expected_frame < input_frame {
let input_to_replicate = self.inputs[previous_position];
self.add_input_by_frame(input_to_replicate, expected_frame);
expected_frame += 1;
}
let previous_position = match self.head {
0 => INPUT_QUEUE_LENGTH - 1,
_ => self.head - 1,
};
assert!(input_frame == 0 || input_frame == self.inputs[previous_position].frame + 1);
input_frame
}
}
#[cfg(test)]
mod input_queue_tests {
use std::net::SocketAddr;
use bytemuck::{Pod, Zeroable};
use super::*;
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Pod, Zeroable)]
struct TestInput {
inp: u8,
}
struct TestConfig;
impl Config for TestConfig {
type Input = TestInput;
type State = Vec<u8>;
type Address = SocketAddr;
}
#[test]
#[should_panic]
fn test_add_input_wrong_frame() {
let mut queue = InputQueue::<TestConfig>::new();
let input = PlayerInput::new(0, TestInput { inp: 0 });
queue.add_input(input); let input_wrong_frame = PlayerInput::new(3, TestInput { inp: 0 });
queue.add_input(input_wrong_frame); }
#[test]
#[should_panic]
fn test_add_input_twice() {
let mut queue = InputQueue::<TestConfig>::new();
let input = PlayerInput::new(0, TestInput { inp: 0 });
queue.add_input(input); queue.add_input(input); }
#[test]
fn test_add_input_sequentially() {
let mut queue = InputQueue::<TestConfig>::new();
for i in 0..10 {
let input = PlayerInput::new(i, TestInput { inp: 0 });
queue.add_input(input);
assert_eq!(queue.last_added_frame, i);
assert_eq!(queue.length, (i + 1) as usize);
}
}
#[test]
fn test_input_sequentially() {
let mut queue = InputQueue::<TestConfig>::new();
for i in 0..10 {
let input = PlayerInput::new(i, TestInput { inp: i as u8 });
queue.add_input(input);
assert_eq!(queue.last_added_frame, i);
assert_eq!(queue.length, (i + 1) as usize);
let (input_in_queue, _status) = queue.input(i);
assert_eq!(input_in_queue.inp, i as u8);
}
}
#[test]
fn test_delayed_inputs() {
let mut queue = InputQueue::<TestConfig>::new();
let delay: i32 = 2;
queue.set_frame_delay(delay as usize);
for i in 0..10 {
let input = PlayerInput::new(i, TestInput { inp: i as u8 });
queue.add_input(input);
assert_eq!(queue.last_added_frame, i + delay);
assert_eq!(queue.length, (i + delay + 1) as usize);
let (input_in_queue, _status) = queue.input(i);
let correct_input = std::cmp::max(0, i - delay) as u8;
assert_eq!(input_in_queue.inp, correct_input);
}
}
}