Skip to main content

lightyear_inputs_native/
input_message.rs

1use crate::action_state::{ActionState, InputMarker};
2use alloc::{vec, vec::Vec};
3use bevy_ecs::entity::{EntityMapper, MapEntities};
4use bevy_reflect::{FromReflect, Reflect, Reflectable};
5use core::cmp::max;
6use core::fmt::Debug;
7use core::time::Duration;
8use lightyear_core::prelude::Tick;
9use lightyear_inputs::input_buffer::{Compressed, InputBuffer};
10use lightyear_inputs::input_message::{ActionStateSequence, InputSnapshot};
11use serde::de::DeserializeOwned;
12use serde::{Deserialize, Serialize};
13
14pub type NativeBuffer<A> = InputBuffer<ActionState<A>, A>;
15
16#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Reflect)]
17pub struct NativeStateSequence<A> {
18    states: Vec<Compressed<A>>,
19}
20
21impl<A: Debug + Default + PartialEq + Clone + Send + Sync + 'static> InputSnapshot
22    for ActionState<A>
23{
24    fn decay_tick(&mut self, tick_duration: Duration) {}
25}
26
27impl<A> IntoIterator for NativeStateSequence<A> {
28    type Item = Compressed<ActionState<A>>;
29    type IntoIter = core::iter::Map<
30        vec::IntoIter<Compressed<A>>,
31        fn(Compressed<A>) -> Compressed<ActionState<A>>,
32    >;
33
34    fn into_iter(self) -> Self::IntoIter {
35        self.states.into_iter().map(|input| match input {
36            Compressed::Absent => Compressed::Absent,
37            Compressed::SameAsPrecedent => Compressed::SameAsPrecedent,
38            Compressed::Input(i) => Compressed::Input(ActionState(i)),
39        })
40    }
41}
42
43impl<
44    A: Serialize
45        + DeserializeOwned
46        + Clone
47        + PartialEq
48        + Send
49        + Sync
50        + Debug
51        + Default
52        + Reflectable
53        + FromReflect
54        + 'static,
55> ActionStateSequence for NativeStateSequence<A>
56{
57    type Action = A;
58    type Snapshot = ActionState<A>;
59    type State = ActionState<A>;
60    type Marker = InputMarker<A>;
61
62    fn len(&self) -> usize {
63        self.states.len()
64    }
65
66    fn get_snapshots_from_message(
67        self,
68        tick_duration: Duration,
69    ) -> impl Iterator<Item = Compressed<Self::Snapshot>> {
70        self.states.into_iter().map(|input| match input {
71            Compressed::Absent => Compressed::Absent,
72            Compressed::SameAsPrecedent => Compressed::SameAsPrecedent,
73            Compressed::Input(i) => Compressed::Input(ActionState(i)),
74        })
75    }
76
77    fn build_from_input_buffer<'w, 's>(
78        input_buffer: &InputBuffer<Self::Snapshot, Self::Action>,
79        num_ticks: u32,
80        end_tick: Tick,
81    ) -> Option<Self> {
82        let buffer_start_tick = input_buffer.start_tick?;
83        // find the first tick for which we have an `ActionState` buffered
84        let start_tick = max(end_tick - num_ticks + 1, buffer_start_tick);
85
86        // find the initial state, (which we convert out of SameAsPrecedent)
87        let start_state = input_buffer
88            .get(start_tick)
89            .map_or(Compressed::Absent, |input| input.into());
90        let mut states = vec![start_state];
91
92        // append the other states until the end tick
93        let buffer_start = (start_tick + 1 - buffer_start_tick) as usize;
94        let buffer_end = (end_tick + 1 - buffer_start_tick) as usize;
95        for idx in buffer_start..buffer_end {
96            let state =
97                input_buffer
98                    .buffer
99                    .get(idx)
100                    .map_or(Compressed::Absent, |input| match input {
101                        Compressed::Absent => Compressed::Absent,
102                        Compressed::SameAsPrecedent => Compressed::SameAsPrecedent,
103                        Compressed::Input(v) => v.into(),
104                    });
105            states.push(state);
106        }
107        Some(Self { states })
108    }
109
110    fn to_snapshot(state: &ActionState<A>) -> Self::Snapshot {
111        (*state).clone()
112    }
113
114    fn from_snapshot(state: &mut ActionState<A>, snapshot: &Self::Snapshot) {
115        *state = snapshot.clone();
116    }
117}
118
119impl<A: MapEntities> MapEntities for NativeStateSequence<A> {
120    fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
121        self.states.iter_mut().for_each(|state| {
122            if let Compressed::Input(action_state) = state {
123                action_state.map_entities(entity_mapper);
124            }
125        });
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132    use alloc::collections::VecDeque;
133    use std::time::Duration;
134    use test_log::test;
135
136    #[test]
137    fn test_build_sequence_from_buffer() {
138        let mut input_buffer = InputBuffer::default();
139        input_buffer.set_empty(Tick(2));
140        input_buffer.set(Tick(3), ActionState(1));
141        input_buffer.set(Tick(7), ActionState(2));
142
143        let sequence =
144            NativeStateSequence::<usize>::build_from_input_buffer(&input_buffer, 9, Tick(10))
145                .unwrap();
146        assert_eq!(
147            sequence,
148            NativeStateSequence::<usize> {
149                states: vec![
150                    // tick 2
151                    Compressed::Absent,
152                    // tick 3
153                    Compressed::Input(1),
154                    Compressed::SameAsPrecedent,
155                    Compressed::SameAsPrecedent,
156                    Compressed::SameAsPrecedent,
157                    Compressed::Input(2),
158                    // TODO: why is it marked as absent instead of SameAsPrecedent??
159                    //  by default, when inputs are absent should we mark them as SameAsPrecedent?
160                    Compressed::Absent,
161                    Compressed::Absent,
162                    Compressed::Absent,
163                ],
164            }
165        );
166    }
167
168    #[test]
169    fn test_update_buffer_from_sequence() {
170        let mut input_buffer = InputBuffer::default();
171        let sequence = NativeStateSequence::<i32> {
172            states: vec![
173                // tick 13
174                Compressed::Absent,
175                // tick 14
176                Compressed::Input(0),
177                Compressed::SameAsPrecedent,
178                // tick 16
179                Compressed::Input(1),
180                Compressed::SameAsPrecedent,
181                // tick 18
182                Compressed::Absent,
183                Compressed::SameAsPrecedent,
184                // Tick 20
185                Compressed::SameAsPrecedent,
186            ],
187        };
188        sequence.update_buffer(&mut input_buffer, Tick(20), Duration::default());
189        assert_eq!(input_buffer.get(Tick(20)), None,);
190        assert_eq!(input_buffer.get(Tick(19)), None,);
191        assert_eq!(input_buffer.get(Tick(18)), None,);
192        assert_eq!(input_buffer.get(Tick(17)), Some(&ActionState::<i32>(1)));
193        assert_eq!(input_buffer.get(Tick(16)), Some(&ActionState::<i32>(1)));
194        assert_eq!(input_buffer.get(Tick(15)), Some(&ActionState::<i32>(0)));
195        assert_eq!(input_buffer.get(Tick(14)), Some(&ActionState::<i32>(0)));
196        assert_eq!(input_buffer.get(Tick(13)), None,);
197    }
198
199    /// Test that the sequence updates the buffer correctly when the sequence's start tick is lower than the input
200    /// buffer's start tick
201    #[test]
202    fn test_update_buffer_from_sequence_lower_start_tick() {
203        let mut input_buffer = InputBuffer {
204            start_tick: Some(Tick(10)),
205            buffer: VecDeque::from([
206                Compressed::Input(ActionState(0)),
207                Compressed::SameAsPrecedent,
208                Compressed::SameAsPrecedent,
209            ]),
210            last_remote_tick: Some(Tick(12)),
211            marker: core::marker::PhantomData,
212        };
213        let sequence = NativeStateSequence::<usize> {
214            states: vec![
215                // tick 7
216                Compressed::Absent,
217                Compressed::SameAsPrecedent,
218                // tick 9
219                Compressed::Input(0),
220                Compressed::SameAsPrecedent,
221                Compressed::SameAsPrecedent,
222                Compressed::SameAsPrecedent,
223                Compressed::SameAsPrecedent,
224                Compressed::Input(1),
225            ],
226        };
227        let mismatch = sequence.update_buffer(&mut input_buffer, Tick(14), Duration::default());
228        assert_eq!(mismatch, Some(Tick(14)));
229        assert_eq!(input_buffer.get(Tick(14)), Some(&ActionState(1)));
230        assert_eq!(input_buffer.get(Tick(13)), Some(&ActionState(0)));
231        assert_eq!(input_buffer.get(Tick(12)), Some(&ActionState(0)));
232        assert_eq!(input_buffer.get(Tick(11)), Some(&ActionState(0)));
233        assert_eq!(input_buffer.get(Tick(10)), Some(&ActionState(0)));
234        assert_eq!(input_buffer.get(Tick(9)), None);
235        assert_eq!(input_buffer.get(Tick(8)), None);
236        assert_eq!(input_buffer.get(Tick(7)), None);
237    }
238
239    #[test]
240    fn test_update_buffer_from_sequence_absent() {
241        let mut input_buffer = InputBuffer {
242            start_tick: Some(Tick(10)),
243            buffer: VecDeque::from([
244                Compressed::Input(ActionState(0)),
245                Compressed::Absent,
246                Compressed::SameAsPrecedent,
247            ]),
248            last_remote_tick: Some(Tick(12)),
249            marker: core::marker::PhantomData,
250        };
251        let sequence = NativeStateSequence::<usize> {
252            states: vec![
253                // Tick 11
254                Compressed::Absent,
255                // Tick 12
256                Compressed::SameAsPrecedent,
257            ],
258        };
259        sequence.update_buffer(&mut input_buffer, Tick(12), Duration::default());
260        assert_eq!(input_buffer.get(Tick(12)), None);
261        assert_eq!(input_buffer.get(Tick(11)), None);
262        assert_eq!(input_buffer.get(Tick(10)), Some(&ActionState(0)));
263    }
264
265    #[test]
266    fn test_update_buffer_from_sequence_present() {
267        let mut input_buffer = InputBuffer {
268            start_tick: Some(Tick(10)),
269            buffer: VecDeque::from([Compressed::Absent, Compressed::SameAsPrecedent]),
270            last_remote_tick: Some(Tick(11)),
271            marker: core::marker::PhantomData,
272        };
273        let sequence = NativeStateSequence::<usize> {
274            states: vec![
275                // Tick 9
276                Compressed::Input(0),
277                // Tick 10
278                Compressed::Absent,
279                // Tick 11
280                Compressed::SameAsPrecedent,
281            ],
282        };
283        let mismatch = sequence.update_buffer(&mut input_buffer, Tick(11), Duration::default());
284        assert_eq!(mismatch, None);
285        assert_eq!(input_buffer.get(Tick(11)), None);
286        assert_eq!(input_buffer.get(Tick(10)), None);
287        assert_eq!(input_buffer.get(Tick(9)), None);
288    }
289
290    /// Check that everything after the mismatch is cleared
291    #[test]
292    fn test_update_buffer_from_sequence_clip_after() {
293        let mut input_buffer = InputBuffer {
294            start_tick: Some(Tick(10)),
295            buffer: VecDeque::from([Compressed::Absent, Compressed::Input(ActionState(3))]),
296            last_remote_tick: Some(Tick(9)),
297            marker: core::marker::PhantomData,
298        };
299        let sequence = NativeStateSequence::<usize> {
300            states: vec![
301                // Tick 10
302                Compressed::Input(0),
303            ],
304        };
305        let mismatch = sequence.update_buffer(&mut input_buffer, Tick(10), Duration::default());
306        assert_eq!(mismatch, Some(Tick(10)));
307        // check that everything after tick 10 is empty
308        assert_eq!(input_buffer.get(Tick(11)), None);
309        assert_eq!(input_buffer.len(), 1);
310    }
311}