1use std::collections::{BTreeSet, VecDeque};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{NetcodeError, NetcodeResult};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(default, deny_unknown_fields)]
10#[non_exhaustive]
11pub struct InputSenderConfig {
12 pub history_capacity: usize,
14 pub redundancy: usize,
16}
17
18impl Default for InputSenderConfig {
19 fn default() -> Self {
20 Self {
21 history_capacity: 64,
22 redundancy: 3,
23 }
24 }
25}
26
27impl InputSenderConfig {
28 pub fn validate(&self) -> NetcodeResult<()> {
30 if self.history_capacity == 0 {
31 return Err(NetcodeError::InvalidConfig(
32 "input history capacity must be positive",
33 ));
34 }
35 if self.redundancy == 0 || self.redundancy > self.history_capacity {
36 return Err(NetcodeError::InvalidConfig(
37 "input redundancy must be within history capacity",
38 ));
39 }
40 Ok(())
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(default, deny_unknown_fields)]
47#[non_exhaustive]
48pub struct InputReceiverConfig {
49 pub max_inputs_per_packet: usize,
51 pub reorder_window: u64,
53 pub max_past_ticks: u64,
55 pub max_future_ticks: u64,
57}
58
59impl Default for InputReceiverConfig {
60 fn default() -> Self {
61 Self {
62 max_inputs_per_packet: 16,
63 reorder_window: 256,
64 max_past_ticks: 12,
65 max_future_ticks: 120,
66 }
67 }
68}
69
70impl InputReceiverConfig {
71 pub fn validate(&self) -> NetcodeResult<()> {
73 if self.max_inputs_per_packet == 0 {
74 return Err(NetcodeError::InvalidConfig(
75 "maximum inputs per packet must be positive",
76 ));
77 }
78 if self.reorder_window == 0 {
79 return Err(NetcodeError::InvalidConfig(
80 "input reorder window must be positive",
81 ));
82 }
83 if self.max_future_ticks == 0 {
84 return Err(NetcodeError::InvalidConfig(
85 "maximum future ticks must be positive",
86 ));
87 }
88 Ok(())
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
94pub struct InputFrame<T> {
95 pub sequence: u64,
97 pub target_tick: u64,
99 pub input: T,
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105pub struct InputPacket<T> {
106 pub client_tick: u64,
108 pub acknowledged_server_tick: u64,
110 pub inputs: Vec<InputFrame<T>>,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
116pub struct InputAck {
117 pub server_tick: u64,
119 pub acknowledged_sequence: u64,
121}
122
123#[derive(Debug, Clone)]
125pub struct InputSender<T> {
126 config: InputSenderConfig,
127 next_sequence: u64,
128 acknowledged_sequence: u64,
129 acknowledged_server_tick: u64,
130 history: VecDeque<InputFrame<T>>,
131}
132
133impl<T> InputSender<T> {
134 pub fn new(config: InputSenderConfig) -> NetcodeResult<Self> {
136 Self::with_next_sequence(config, 1)
137 }
138
139 pub fn with_next_sequence(
141 config: InputSenderConfig,
142 next_sequence: u64,
143 ) -> NetcodeResult<Self> {
144 config.validate()?;
145 if next_sequence == 0 {
146 return Err(NetcodeError::InvalidConfig(
147 "next input sequence must be positive",
148 ));
149 }
150 Ok(Self {
151 config,
152 next_sequence,
153 acknowledged_sequence: next_sequence - 1,
154 acknowledged_server_tick: 0,
155 history: VecDeque::with_capacity(config.history_capacity),
156 })
157 }
158
159 pub fn record(&mut self, target_tick: u64, input: T) -> NetcodeResult<u64> {
161 if target_tick == 0 {
162 return Err(NetcodeError::InvalidInput(
163 "input target Tick must be positive",
164 ));
165 }
166 if self.history.len() >= self.config.history_capacity {
167 return Err(NetcodeError::InputHistoryFull);
168 }
169 let sequence = self.next_sequence;
170 self.next_sequence = self
171 .next_sequence
172 .checked_add(1)
173 .ok_or(NetcodeError::SequenceExhausted)?;
174 self.history.push_back(InputFrame {
175 sequence,
176 target_tick,
177 input,
178 });
179 Ok(sequence)
180 }
181
182 pub fn acknowledge(&mut self, acknowledgement: InputAck) -> NetcodeResult<usize> {
184 let last_issued = self.next_sequence - 1;
185 if acknowledgement.acknowledged_sequence > last_issued {
186 return Err(NetcodeError::InvalidAcknowledgement);
187 }
188 self.acknowledged_server_tick = self
189 .acknowledged_server_tick
190 .max(acknowledgement.server_tick);
191 if acknowledgement.acknowledged_sequence <= self.acknowledged_sequence {
192 return Ok(0);
193 }
194 self.acknowledged_sequence = acknowledgement.acknowledged_sequence;
195 let before = self.history.len();
196 while self
197 .history
198 .front()
199 .is_some_and(|frame| frame.sequence <= self.acknowledged_sequence)
200 {
201 self.history.pop_front();
202 }
203 Ok(before - self.history.len())
204 }
205
206 pub fn acknowledged_sequence(&self) -> u64 {
208 self.acknowledged_sequence
209 }
210
211 pub fn acknowledged_server_tick(&self) -> u64 {
213 self.acknowledged_server_tick
214 }
215
216 pub fn pending_len(&self) -> usize {
218 self.history.len()
219 }
220
221 pub fn is_empty(&self) -> bool {
223 self.history.is_empty()
224 }
225}
226
227impl<T: Clone> InputSender<T> {
228 pub fn packet(&self, client_tick: u64) -> InputPacket<T> {
230 let skip = self.history.len().saturating_sub(self.config.redundancy);
231 InputPacket {
232 client_tick,
233 acknowledged_server_tick: self.acknowledged_server_tick,
234 inputs: self.history.iter().skip(skip).cloned().collect(),
235 }
236 }
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq)]
241pub enum SequenceDisposition {
242 Accepted,
244 Duplicate,
246}
247
248#[derive(Debug, Clone)]
250pub struct SequenceWindow {
251 reorder_window: u64,
252 acknowledged: u64,
253 pending: BTreeSet<u64>,
254}
255
256impl SequenceWindow {
257 pub fn new(reorder_window: u64) -> NetcodeResult<Self> {
259 Self::with_acknowledged(reorder_window, 0)
260 }
261
262 pub fn with_acknowledged(reorder_window: u64, acknowledged: u64) -> NetcodeResult<Self> {
264 if reorder_window == 0 {
265 return Err(NetcodeError::InvalidConfig(
266 "input reorder window must be positive",
267 ));
268 }
269 Ok(Self {
270 reorder_window,
271 acknowledged,
272 pending: BTreeSet::new(),
273 })
274 }
275
276 pub fn observe(&mut self, sequence: u64) -> NetcodeResult<SequenceDisposition> {
278 if sequence == 0 {
279 return Err(NetcodeError::InvalidInput(
280 "input sequence must be positive",
281 ));
282 }
283 if sequence <= self.acknowledged || self.pending.contains(&sequence) {
284 return Ok(SequenceDisposition::Duplicate);
285 }
286 if sequence > self.acknowledged.saturating_add(self.reorder_window) {
287 return Err(NetcodeError::InvalidInput(
288 "input sequence exceeds the reorder window",
289 ));
290 }
291 self.pending.insert(sequence);
292 while let Some(next) = self.acknowledged.checked_add(1) {
293 if !self.pending.remove(&next) {
294 break;
295 }
296 self.acknowledged = next;
297 }
298 Ok(SequenceDisposition::Accepted)
299 }
300
301 pub fn acknowledged(&self) -> u64 {
303 self.acknowledged
304 }
305
306 pub fn pending_len(&self) -> usize {
308 self.pending.len()
309 }
310}
311
312#[derive(Debug, Clone, PartialEq, Eq)]
314pub struct InputReceiveReport<T> {
315 pub accepted: Vec<InputFrame<T>>,
317 pub duplicates: usize,
319 pub client_tick: u64,
321 pub acknowledged_server_tick: u64,
323 pub acknowledgement: InputAck,
325}
326
327#[derive(Debug, Clone)]
329pub struct InputReceiver {
330 config: InputReceiverConfig,
331 sequences: SequenceWindow,
332}
333
334impl InputReceiver {
335 pub fn new(config: InputReceiverConfig) -> NetcodeResult<Self> {
337 Self::with_acknowledged(config, 0)
338 }
339
340 pub fn with_acknowledged(
342 config: InputReceiverConfig,
343 acknowledged: u64,
344 ) -> NetcodeResult<Self> {
345 config.validate()?;
346 Ok(Self {
347 sequences: SequenceWindow::with_acknowledged(config.reorder_window, acknowledged)?,
348 config,
349 })
350 }
351
352 pub fn receive<T>(
358 &mut self,
359 current_server_tick: u64,
360 mut packet: InputPacket<T>,
361 ) -> NetcodeResult<InputReceiveReport<T>> {
362 if packet.inputs.len() > self.config.max_inputs_per_packet {
363 return Err(NetcodeError::InvalidInput(
364 "packet contains too many input frames",
365 ));
366 }
367 if packet.acknowledged_server_tick > current_server_tick {
368 return Err(NetcodeError::InvalidInput(
369 "client acknowledged a future server Tick",
370 ));
371 }
372 packet.inputs.sort_by_key(|frame| frame.sequence);
373 let mut next_sequences = self.sequences.clone();
374 let mut accepted = Vec::with_capacity(packet.inputs.len());
375 let mut duplicates = 0;
376 let earliest = current_server_tick.saturating_sub(self.config.max_past_ticks);
377 let latest = current_server_tick.saturating_add(self.config.max_future_ticks);
378
379 for frame in packet.inputs {
380 match next_sequences.observe(frame.sequence)? {
381 SequenceDisposition::Duplicate => duplicates += 1,
382 SequenceDisposition::Accepted => {
383 if frame.target_tick == 0
384 || frame.target_tick < earliest
385 || frame.target_tick > latest
386 {
387 return Err(NetcodeError::InvalidInput(
388 "input target Tick is outside the receive window",
389 ));
390 }
391 accepted.push(frame);
392 }
393 }
394 }
395
396 self.sequences = next_sequences;
397 Ok(InputReceiveReport {
398 accepted,
399 duplicates,
400 client_tick: packet.client_tick,
401 acknowledged_server_tick: packet.acknowledged_server_tick,
402 acknowledgement: InputAck {
403 server_tick: current_server_tick,
404 acknowledged_sequence: self.sequences.acknowledged(),
405 },
406 })
407 }
408
409 pub fn acknowledged_sequence(&self) -> u64 {
411 self.sequences.acknowledged()
412 }
413
414 pub fn pending_sequences(&self) -> usize {
416 self.sequences.pending_len()
417 }
418}