reaction 0.2.0

Universal low-latency input handling for game engines
Documentation
use crate::event::events::TimestampedEvent;
use std::collections::VecDeque;

impl Default for InputRecorder {
    fn default() -> Self {
        Self::new()
    }
}

pub struct InputRecorder {
    recording: Vec<TimestampedEvent>,
    is_recording: bool,
    start_time: f64,
}

impl InputRecorder {
    pub fn new() -> Self {
        Self {
            recording: Vec::new(),
            is_recording: false,
            start_time: 0.0,
        }
    }

    pub fn start(&mut self, current_time: f64) {
        self.recording.clear();
        self.is_recording = true;
        self.start_time = current_time;
    }

    pub fn stop(&mut self) {
        self.is_recording = false;
    }

    pub fn record(&mut self, mut event: TimestampedEvent) {
        if self.is_recording {
            // Relativize timestamp if needed, or store absolute?
            // Relative is better for playback.
            event.timestamp -= self.start_time;
            self.recording.push(event);
        }
    }

    pub fn export_recording(&self) -> Vec<TimestampedEvent> {
        self.recording.clone()
    }
}

pub struct InputPlayer {
    recording: VecDeque<TimestampedEvent>,
    playing: bool,
    playback_start_time: f64,
}

impl InputPlayer {
    pub fn new(recording: Vec<TimestampedEvent>) -> Self {
        Self {
            recording: recording.into(),
            playing: false,
            playback_start_time: 0.0,
        }
    }

    pub fn play(&mut self, current_time: f64) {
        self.playing = true;
        self.playback_start_time = current_time;
    }

    pub fn stop(&mut self) {
        self.playing = false;
    }

    pub fn get_events_for_frame(&mut self, current_time: f64) -> Vec<TimestampedEvent> {
        if !self.playing {
            return Vec::new();
        }

        let relative_time = current_time - self.playback_start_time;
        let mut events = Vec::new();

        while let Some(front) = self.recording.front() {
            if front.timestamp <= relative_time {
                if let Some(mut evt) = self.recording.pop_front() {
                    evt.timestamp = current_time; // Retarget timestamp to now
                    events.push(evt);
                }
            } else {
                break;
            }
        }

        if self.recording.is_empty() {
            self.playing = false;
        }

        events
    }
}