1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use std::collections::VecDeque;

use naia_shared::{sequence_greater_than, Tick};

pub struct CommandHistory<T: Clone> {
    buffer: VecDeque<(Tick, T)>,
}

impl<T: Clone> CommandHistory<T> {
    pub fn new() -> Self {
        CommandHistory {
            buffer: VecDeque::new(),
        }
    }

    pub fn replays(&mut self, start_tick: &Tick) -> Vec<(Tick, T)> {
        // Remove history of commands until current received tick
        self.remove_to_and_including(*start_tick);

        // Get copies of all remaining stored Commands
        let mut output = Vec::new();

        for (tick, command) in self.buffer.iter() {
            output.push((*tick, command.clone()));
        }

        output
    }

    // this only goes forward
    pub fn insert(&mut self, command_tick: Tick, new_command: T) {
        if let Some((last_most_recent_command_tick, _)) = self.buffer.front() {
            if !sequence_greater_than(command_tick, *last_most_recent_command_tick) {
                panic!("You must always insert a more recent command into the CommandHistory than the one you last inserted.");
            }
        }

        // go ahead and push
        self.buffer.push_front((command_tick, new_command));
    }

    fn remove_to_and_including(&mut self, index: Tick) {
        loop {
            let back_index = match self.buffer.back() {
                Some((index, _)) => *index,
                None => {
                    return;
                }
            };
            if sequence_greater_than(back_index, index) {
                return;
            }
            self.buffer.pop_back();
        }
    }

    pub fn can_insert(&self, tick: &Tick) -> bool {
        if let Some((last_most_recent_command_tick, _)) = self.buffer.front() {
            if !sequence_greater_than(*tick, *last_most_recent_command_tick) {
                return false;
            }
        }
        return true;
    }
}