Skip to main content

shuyi_ui/
tick.rs

1//! A stage's rules in fixed steps (ADR-0003 ยง2): [`TickDriver`] steps a
2//! [`TickSim`] as many times as a frame has steps due
3//! ([`FrameInput::ticks`]), gives each step what the player did since the
4//! last one, and hands each step's events on once. [`ActionIds`] turns a
5//! game's action into the id a step's input holds, and back.
6//!
7//! A tick stage reads input from its steps' [`TickInput`], never from
8//! [`FrameInput::actions`]: frames and steps don't line up (a 120 Hz
9//! display runs a step every other frame; a late frame runs up to 4), so a
10//! press read straight from a frame would be lost on a frame with no step
11//! and seen again on a frame with several, and a key's repeats would count
12//! as presses.
13//!
14//! ```
15//! use shuyi_core::tick::TickRecord;
16//! use shuyi_core::toy::{Dot, DotEvent};
17//! use shuyi_ui::input::sample::Action;
18//! use shuyi_ui::screen::FrameInput;
19//! use shuyi_ui::tick::TickDriver;
20//! # let made = shuyi_core::Made::new("dot", shuyi_core::ContentHash(0));
21//!
22//! let mut dot = Dot::default();
23//! let mut driver = TickDriver::new().recording(TickRecord::new(made, 1));
24//! // A tap on a frame with no step due: kept for the next step.
25//! let tap = FrameInput::new(vec![Action::CursorRight], 0.004, vec![]);
26//! assert!(driver.update(&mut dot, &tap).is_empty());
27//! // Two steps due: the tap moves the dot in the first, and it stops in
28//! // the second.
29//! let late = FrameInput::new(vec![], 0.033, vec![]).with_ticks(2);
30//! let events = driver.update(&mut dot, &late);
31//! assert_eq!(events, [(2, DotEvent::Stopped { x: 1, y: 0 })]);
32//! assert_eq!(driver.record().map(TickRecord::len), Some(2));
33//! ```
34
35use crate::prelude::*;
36
37use shuyi_assets::keymap::ActionSet;
38use shuyi_core::tick::{ActionId, SeatInput, TickInput, TickRecord, TickSim};
39
40use crate::input::frame::FrameInput;
41
42/// A game's action as a step's input holds it ([`ActionId`]): its place
43/// in [`ActionSet::ALL`]. Every [`ActionSet`] has it.
44pub trait ActionIds: ActionSet {
45    /// Its id: its place in [`ActionSet::ALL`], from 0 (`u16::MAX` for an
46    /// action that isn't there, or past it).
47    fn id(self) -> ActionId;
48
49    /// The action whose id is `id`, if there is one.
50    fn from_id(id: ActionId) -> Option<Self>;
51}
52
53impl<A: ActionSet> ActionIds for A {
54    fn id(self) -> ActionId {
55        let place = Self::ALL.iter().position(|&action| action == self);
56        ActionId(
57            place
58                .and_then(|p| u16::try_from(p).ok())
59                .unwrap_or(u16::MAX),
60        )
61    }
62
63    fn from_id(id: ActionId) -> Option<Self> {
64        Self::ALL.get(usize::from(id.0)).copied()
65    }
66}
67
68/// An action going down or coming up, waiting for a step.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70enum Edge {
71    /// It went down.
72    Press,
73    /// It came up.
74    Release,
75}
76
77/// What one seat did since the steps last took it: each press and release
78/// read and not yet given to a step, in order.
79#[derive(Debug, Clone)]
80struct Latch<A> {
81    /// The edges not given to a step yet, in the order they came.
82    queue: Vec<(A, Edge)>,
83    /// The actions whose last edge read is a press.
84    down: Vec<A>,
85    /// The actions whose last edge given to a step is a press.
86    given: Vec<A>,
87    /// The actions held at the end of the last frame read.
88    held: Vec<A>,
89}
90
91impl<A: ActionSet> Latch<A> {
92    fn new() -> Self {
93        Self {
94            queue: Vec::new(),
95            down: Vec::new(),
96            given: Vec::new(),
97            held: Vec::new(),
98        }
99    }
100
101    /// Reads one frame's `presses`, `released` actions and the actions
102    /// `held` at its end. An action's presses and releases alternate for
103    /// the steps: a press of an action already down (another of its keys)
104    /// and a release of one whose press no step saw (held since before the
105    /// stage) are dropped, and an action down but no longer held is let
106    /// go.
107    fn read(&mut self, presses: &[A], released: &[A], held: &[A]) {
108        // The actions this frame moved, in the order they came, then those
109        // down that it didn't name.
110        let mut actions: Vec<A> = Vec::new();
111        for &action in presses.iter().chain(released).chain(&self.down) {
112            if !actions.contains(&action) {
113                actions.push(action);
114            }
115        }
116        let mut lists: Vec<(A, Vec<Edge>)> = Vec::new();
117        for action in actions {
118            let edges = self.edges(action, presses, released, held);
119            if !edges.is_empty() {
120                lists.push((action, edges));
121            }
122        }
123        // Round by round, each action's first edge before any one's second,
124        // so presses keep their order across actions.
125        let rounds = lists.iter().map(|(_, edges)| edges.len()).max();
126        for round in 0..rounds.unwrap_or(0) {
127            for (action, edges) in &lists {
128                if let Some(&edge) = edges.get(round) {
129                    self.queue.push((*action, edge));
130                }
131            }
132        }
133        self.held = held.to_vec();
134    }
135
136    /// `action`'s edges in a frame of `presses` and `released` that ends
137    /// with `held`, in order; it notes whether the action is down after
138    /// them.
139    fn edges(&mut self, action: A, presses: &[A], released: &[A], held: &[A]) -> Vec<Edge> {
140        let count = |list: &[A]| list.iter().filter(|&&a| a == action).count();
141        let mut ups = count(released);
142        let (was_held, is_held) = (self.held.contains(&action), held.contains(&action));
143        // The presses that put it down: one for each release, one more if
144        // it ends held and one less if it started held. Any other is
145        // another key of an action that was down already.
146        let mut downs = (ups + usize::from(is_held))
147            .saturating_sub(usize::from(was_held))
148            .min(count(presses));
149        let mut seen = self.down.contains(&action);
150        let mut down = was_held || seen;
151        let mut edges = Vec::new();
152        loop {
153            if down {
154                if ups == 0 {
155                    break;
156                }
157                ups -= 1;
158                // A release of a press no step saw is dropped.
159                if core::mem::take(&mut seen) {
160                    edges.push(Edge::Release);
161                }
162            } else {
163                if downs == 0 {
164                    break;
165                }
166                downs -= 1;
167                edges.push(Edge::Press);
168                seen = true;
169            }
170            down = !down;
171        }
172        if seen && !is_held {
173            edges.push(Edge::Release);
174            seen = false;
175        }
176        self.down.retain(|&a| a != action);
177        if seen {
178            self.down.push(action);
179        }
180        edges
181    }
182
183    /// Gives one step its presses and releases, in order: for each action,
184    /// a release of a press an earlier step had, then a press. Whatever an
185    /// action has beyond waits for the next step, so nothing is lost: a
186    /// press always lands in a step before its release's, and an action
187    /// pressed again before a step shows its release and the press again
188    /// in the next.
189    fn step(&mut self) -> (Vec<A>, Vec<A>) {
190        let mut pressed: Vec<A> = Vec::new();
191        let mut released: Vec<A> = Vec::new();
192        let mut kept = Vec::new();
193        for (action, edge) in self.queue.drain(..) {
194            let pressed_now = pressed.contains(&action);
195            match edge {
196                Edge::Release if !pressed_now && !released.contains(&action) => {
197                    released.push(action);
198                    self.given.retain(|&a| a != action);
199                }
200                Edge::Press if !pressed_now => {
201                    pressed.push(action);
202                    self.given.push(action);
203                }
204                Edge::Release | Edge::Press => kept.push((action, edge)),
205            }
206        }
207        self.queue = kept;
208        (pressed, released)
209    }
210
211    /// Forgets every press and release no step has had yet. A press a step
212    /// had stays down, so its release still comes.
213    fn clear(&mut self) {
214        self.queue.clear();
215        self.down.clone_from(&self.given);
216    }
217}
218
219/// Runs a stage's [`TickSim`] in fixed steps: as many a frame as
220/// [`FrameInput::ticks`] says are due (counted by the platform's `App`, 60
221/// a second, at most 4 a frame). It keeps no clock of its own.
222///
223/// # Input reaches the steps
224///
225/// It keeps what each frame read until a step takes it, so the frame rate
226/// never changes which presses a simulation sees:
227///
228/// - **A press is pressed in one step**: the first run in or after the
229///   frame that read it, the first of a frame's steps when it has several.
230///   A frame that runs no step (less than a step's time passed, an action
231///   handed in by [`Game::act`](crate::Game::act), a slowed or stopped
232///   game) keeps it for the next.
233/// - **A release is released in one step**, after its press's: a tap
234///   between two steps is pressed in one and released in the next, so it
235///   is never lost.
236/// - **Held is the frame's**: every step a frame runs sees the actions
237///   held at its end. A tap is pressed without being held.
238/// - **Key repeats never reach a step** ([`FrameInput::presses`]).
239/// - **An action pressed twice before a step** isn't merged: the second
240///   press waits. The next step has the first press, the one after the
241///   release and the press again, and so on, so every press and release
242///   reaches a step, in order. An action is pressed when it goes down: a
243///   second key on an action already held presses nothing.
244/// - **Covered or resumed, it forgets.** A stage calls
245///   [`clear`](Self::clear) from [`Screen::covered`], and a frame after a
246///   suspend ([`FrameInput::resumed`]) clears it, so a press made before a
247///   pause screen or a suspend never fires after it. A press a step had
248///   stays down, and its release still comes.
249///
250/// Each step's input is one seat, seat 0, from the game's input. With a
251/// [`recording`](Self::recording) it is kept in a [`TickRecord`]: what each
252/// step saw, so the record replays the same whatever frame rate made it.
253///
254/// # Events are handed on once
255///
256/// [`update`](Self::update) hands on each step's events with the step's
257/// number (counted from 1), in order, a frame catching up included. It
258/// keeps the highest step it has handed on: after a rewind
259/// ([`rewind_to`](Self::rewind_to)), the steps that run again are stepped
260/// for their state, but their events aren't handed on a second time. A
261/// simulation asks for sound only through its events, so no sound plays
262/// twice.
263///
264/// [`Screen::covered`]: crate::Screen::covered
265#[derive(Debug, Clone)]
266pub struct TickDriver<A: ActionSet> {
267    latch: Latch<A>,
268    /// The steps run: the number of the last.
269    steps: u64,
270    /// The highest step whose events were handed on.
271    handed: u64,
272    /// What each step saw, while recording.
273    record: Option<TickRecord>,
274}
275
276impl<A: ActionSet> TickDriver<A> {
277    /// A driver that has run no step, and records nothing.
278    pub fn new() -> Self {
279        Self {
280            latch: Latch::new(),
281            steps: 0,
282            handed: 0,
283            record: None,
284        }
285    }
286
287    /// The same driver recording each step's input from now on into
288    /// `record` ([`record`](Self::record)).
289    #[must_use]
290    pub fn recording(mut self, record: TickRecord) -> Self {
291        self.record = Some(record);
292        self
293    }
294
295    /// Reads `input`'s presses and releases and runs `input.ticks()` steps
296    /// of `sim`, each with its seat's [`TickInput`] ([type docs](Self)).
297    /// Gives back the events of the steps not handed on before, each with
298    /// its step's number, in order.
299    pub fn update<S: TickSim>(
300        &mut self,
301        sim: &mut S,
302        input: &FrameInput<A>,
303    ) -> Vec<(u64, S::Event)> {
304        if input.resumed() {
305            self.latch.clear();
306        }
307        self.latch
308            .read(input.presses(), input.released(), input.held());
309        let mut handed = Vec::new();
310        for _ in 0..input.ticks() {
311            let (pressed, released) = self.latch.step();
312            let ids = |actions: &[A]| actions.iter().map(|&a| a.id()).collect();
313            let seat = SeatInput::new(ids(&pressed), ids(&self.latch.held), ids(&released));
314            let tick = TickInput::one(seat);
315            let events = sim.step(&tick);
316            if let Some(record) = &mut self.record {
317                record.push(tick);
318            }
319            self.steps += 1;
320            if self.steps > self.handed {
321                self.handed = self.steps;
322                let step = self.steps;
323                handed.extend(events.into_iter().map(|event| (step, event)));
324            }
325        }
326        handed
327    }
328
329    /// Forgets every press and release no step has had yet: from
330    /// [`Screen::covered`](crate::Screen::covered), so a press made just
331    /// before a pause screen never fires after it.
332    pub fn clear(&mut self) {
333        self.latch.clear();
334    }
335
336    /// Sets the steps run back to `step`, when the game has loaded its state
337    /// at that step (a rollback): the steps after it run again for their
338    /// state, and their events, handed on before, aren't handed on again.
339    /// The record forgets the steps after it. A `step` at or past the steps
340    /// run changes nothing.
341    pub fn rewind_to(&mut self, step: u64) {
342        if step >= self.steps {
343            return;
344        }
345        self.steps = step;
346        if let Some(record) = &mut self.record {
347            record.truncate(step);
348        }
349    }
350
351    /// The steps run: the number of the last one.
352    pub fn steps(&self) -> u64 {
353        self.steps
354    }
355
356    /// The highest step whose events were handed on.
357    pub fn handed(&self) -> u64 {
358        self.handed
359    }
360
361    /// The record of each step's input, while recording.
362    pub fn record(&self) -> Option<&TickRecord> {
363        self.record.as_ref()
364    }
365
366    /// Stops recording, handing back the record.
367    pub fn take_record(&mut self) -> Option<TickRecord> {
368        self.record.take()
369    }
370}
371
372impl<A: ActionSet> Default for TickDriver<A> {
373    fn default() -> Self {
374        Self::new()
375    }
376}
377
378#[cfg(test)]
379mod tests;