1pub mod err;
27pub mod prelude;
28
29use crate::err::ClientError;
30use app_version::VersionProvider;
31use flood_rs::{BufferDeserializer, Deserialize, Serialize};
32use log::trace;
33use metricator::MinMaxAvg;
34use monotonic_time_rs::{Millis, MillisDuration};
35use network_metrics::{CombinedMetrics, NetworkMetrics};
36use nimble_client_logic::err::ClientLogicError;
37use nimble_client_logic::LocalIndex;
38use nimble_client_logic::{ClientLogic, ClientLogicPhase, LocalPlayer};
39use nimble_layer::NimbleLayer;
40use nimble_protocol::prelude::HostToClientCommands;
41use nimble_rectify::{Rectify, RectifyCallbacks};
42use nimble_step::Step;
43use nimble_step_map::StepMap;
44use std::cmp::min;
45use std::fmt::{Debug, Display};
46use tick_id::TickId;
47use time_tick::TimeTick;
48
49pub type MillisDurationRange = RangeToFactor<MillisDuration, MillisDuration>;
50
51pub struct RangeToFactor<V, F> {
52 range_min: V,
53 min_factor: F,
54 range_max: V,
55 max_factor: F,
56 factor: F,
57}
58
59impl<V: PartialOrd, F> RangeToFactor<V, F> {
60 pub const fn new(range_min: V, range_max: V, min_factor: F, factor: F, max_factor: F) -> Self {
61 Self {
62 range_min,
63 min_factor,
64 range_max,
65 max_factor,
66 factor,
67 }
68 }
69
70 #[inline]
71 pub fn get_factor(&self, input: &V) -> &F {
72 if input < &self.range_min {
73 &self.min_factor
74 } else if input > &self.range_max {
75 &self.max_factor
76 } else {
77 &self.factor
78 }
79 }
80}
81
82pub trait GameCallbacks<StepT: Display>:
83 RectifyCallbacks<StepMap<Step<StepT>>> + VersionProvider + BufferDeserializer
84{
85}
86
87impl<T, StepT> GameCallbacks<StepT> for T
88where
89 T: RectifyCallbacks<StepMap<Step<StepT>>> + VersionProvider + BufferDeserializer,
90 StepT: Display,
91{
92}
93
94#[derive(Debug, PartialEq, Eq)]
95pub enum ClientPhase {
96 Normal,
97 CanSendPredicted,
98}
99
100pub struct Client<
105 GameT: GameCallbacks<StepT> + Debug,
106 StepT: Clone + Deserialize + Serialize + Debug + Display,
107> {
108 nimble_layer: NimbleLayer,
109 logic: ClientLogic<GameT, StepT>,
110 metrics: NetworkMetrics,
111 rectify: Rectify<GameT, StepMap<Step<StepT>>>,
112 authoritative_range_to_tick_duration_ms: RangeToFactor<u8, f32>,
113 authoritative_time_tick: TimeTick,
114 prediction_range_to_tick_duration_ms: RangeToFactor<i32, f32>,
115 pub prediction_time_tick: TimeTick,
116 max_prediction_count: usize,
117 last_need_prediction_count: u16,
118 phase: ClientPhase,
119 tick_duration_ms: MillisDuration,
120}
121
122impl<
123 StepT: Clone + Deserialize + Serialize + Debug + Display + Eq,
124 GameT: GameCallbacks<StepT> + Debug,
125 > Client<GameT, StepT>
126{
127 #[must_use]
133 pub fn new(now: Millis) -> Self {
134 let deterministic_app_version = GameT::version();
135 Self {
136 nimble_layer: NimbleLayer::new(),
137 logic: ClientLogic::<GameT, StepT>::new(deterministic_app_version),
138 metrics: NetworkMetrics::new(now),
139
140 authoritative_range_to_tick_duration_ms: RangeToFactor::new(2, 5, 0.9, 1.0, 2.0), authoritative_time_tick: TimeTick::new(now, MillisDuration::from_millis(16), 4),
142
143 prediction_range_to_tick_duration_ms: RangeToFactor::new(-1, 3, 0.85, 1.0, 2.0), prediction_time_tick: TimeTick::new(now, MillisDuration::from_millis(16), 4),
145
146 rectify: Rectify::default(),
147 last_need_prediction_count: 0,
148 phase: ClientPhase::Normal,
149 max_prediction_count: 10, tick_duration_ms: MillisDuration::from_millis(16),
151 }
152 }
153
154 #[must_use]
155 pub const fn with_tick_duration(mut self, tick_duration: MillisDuration) -> Self {
156 self.tick_duration_ms = tick_duration;
157 self
158 }
159
160 const MAX_DATAGRAM_SIZE: usize = 1024;
161
162 pub fn send(&mut self, now: Millis) -> Result<Vec<Vec<u8>>, ClientError> {
179 let messages = self.logic.send(now);
180 let datagrams =
181 datagram_chunker::serialize_to_datagrams(messages, Self::MAX_DATAGRAM_SIZE)?;
182 self.metrics.sent_datagrams(&datagrams);
183
184 let datagrams_with_header = self.nimble_layer.send(&datagrams)?;
185
186 Ok(datagrams_with_header)
187 }
188
189 pub fn receive(&mut self, now: Millis, datagram: &[u8]) -> Result<(), ClientError> {
207 self.metrics.received_datagram(datagram);
208 let datagram_without_header = self.nimble_layer.receive(datagram)?;
209 let commands = datagram_chunker::deserialize_datagram::<HostToClientCommands<Step<StepT>>>(
210 datagram_without_header,
211 )?;
212 for command in commands {
213 self.logic.receive(now, &command)?;
214 }
215
216 Ok(())
217 }
218
219 pub const fn debug_rectify(&self) -> &Rectify<GameT, StepMap<Step<StepT>>> {
220 &self.rectify
221 }
222
223 pub fn update(&mut self, now: Millis) -> Result<(), ClientError> {
240 trace!("client: update {now}");
241 self.metrics.update_metrics(now);
242
243 let factor = self.authoritative_range_to_tick_duration_ms.get_factor(
244 &u8::try_from(self.logic.debug_authoritative_steps().len())
245 .map_err(|_| ClientLogicError::TooManyAuthoritativeSteps)?,
246 );
247 self.authoritative_time_tick
248 .set_tick_duration(*factor * self.tick_duration_ms);
249 self.authoritative_time_tick.calculate_ticks(now);
250
251 let (first_tick_id_in_vector, auth_steps) = self.logic.pop_all_authoritative_steps();
252 let mut current_tick_id = first_tick_id_in_vector;
253 for auth_step in auth_steps {
254 if current_tick_id == self.rectify.waiting_for_authoritative_tick_id() {
255 self.rectify
256 .push_authoritative_with_check(current_tick_id, auth_step)?;
257 }
258 current_tick_id = TickId(current_tick_id.0 + 1);
259 }
260
261 if self.logic.phase() == &ClientLogicPhase::SendPredictedSteps
262 && self.phase != ClientPhase::CanSendPredicted
263 {
264 self.prediction_time_tick.reset(now);
265 self.phase = ClientPhase::CanSendPredicted;
266 }
267
268 match self.phase {
269 ClientPhase::Normal => {}
270 ClientPhase::CanSendPredicted => {
271 self.adjust_prediction_ticker();
272 self.last_need_prediction_count = self.prediction_time_tick.calculate_ticks(now);
273 if self.logic.predicted_step_count_in_queue() >= self.max_prediction_count {
274 trace!(
275 "prediction queue is maxed out: {}",
276 self.max_prediction_count
277 );
278 self.last_need_prediction_count = 0;
279 self.prediction_time_tick.reset(now);
280 }
281
282 trace!("prediction count: {}", self.last_need_prediction_count);
283 if let Some(game) = self.logic.game_mut() {
284 self.rectify.update(game);
285 }
286 }
287 }
288
289 Ok(())
290 }
291
292 fn delta_prediction_count(&self) -> i32 {
299 if self.logic.can_push_predicted_step() {
300 let optimal_prediction_tick_count = self.optimal_prediction_tick_count();
301 let prediction_count_in_queue = self.logic.predicted_step_count_in_queue();
302 trace!("optimal according to latency {optimal_prediction_tick_count}, outgoing queue {prediction_count_in_queue}");
303
304 prediction_count_in_queue as i32 - optimal_prediction_tick_count as i32
305 } else {
306 0
307 }
308 }
309
310 fn adjust_prediction_ticker(&mut self) {
312 let delta_prediction = self.delta_prediction_count();
313 let factor = self
314 .prediction_range_to_tick_duration_ms
315 .get_factor(&delta_prediction);
316 trace!(
317 "delta-prediction: {delta_prediction} resulted in factor: {factor} for latency {}",
318 self.latency().unwrap_or(MinMaxAvg::new(0, 0.0, 0))
319 );
320
321 self.prediction_time_tick
322 .set_tick_duration(*factor * self.tick_duration_ms);
323 }
324
325 fn optimal_prediction_tick_count(&self) -> usize {
335 const MAXIMUM_PREDICTION_COUNT: usize = 10; const MINIMUM_DELTA_TICK: u32 = 2;
337
338 self.latency().map_or(2, |latency_ms| {
339 let latency_in_ticks =
340 (latency_ms.avg as u16 / self.tick_duration_ms.as_millis() as u16) + 1;
341 let tick_delta = self.server_buffer_delta_ticks().unwrap_or(0);
342 let buffer_add = if (tick_delta as u32) < MINIMUM_DELTA_TICK {
343 ((MINIMUM_DELTA_TICK as i32) - i32::from(tick_delta)) as u32
344 } else {
345 0
346 };
347
348 let count = (u32::from(latency_in_ticks) + buffer_add) as usize;
349
350 min(count, MAXIMUM_PREDICTION_COUNT)
351 })
352 }
353
354 pub const fn game(&self) -> Option<&GameT> {
363 self.logic.game()
364 }
365
366 pub fn required_prediction_count(&self) -> usize {
372 if self.logic.can_push_predicted_step() {
373 self.last_need_prediction_count as usize
374 } else {
375 0
376 }
377 }
378
379 pub const fn can_join_player(&self) -> bool {
385 self.game().is_some()
386 }
387
388 pub fn local_players(&self) -> Vec<LocalPlayer> {
394 self.logic.local_players()
395 }
396
397 pub fn push_predicted_step(
414 &mut self,
415 tick_id: TickId,
416 step: &StepMap<StepT>,
417 ) -> Result<(), ClientError> {
418 let count = step.len();
419 if count > self.required_prediction_count() {
420 return Err(ClientError::PredictionQueueOverflow);
421 }
422 self.prediction_time_tick.performed_ticks(count as u16);
423
424 self.logic.push_predicted_step(tick_id, step.clone())?;
425
426 let mut seq_map = StepMap::<Step<StepT>>::new();
427
428 for (participant_id, step) in step {
429 seq_map.insert(*participant_id, Step::Custom(step.clone()))?;
430 }
431 self.rectify.push_predicted(tick_id, seq_map)?;
432
433 Ok(())
434 }
435
436 pub fn latency(&self) -> Option<MinMaxAvg<u16>> {
442 self.logic.latency()
443 }
444
445 pub fn metrics(&self) -> CombinedMetrics {
451 self.metrics.metrics()
452 }
453
454 pub fn server_buffer_delta_ticks(&self) -> Option<i16> {
462 self.logic.server_buffer_delta_ticks()
463 }
464
465 pub fn request_join_player(
480 &mut self,
481 local_players: Vec<LocalIndex>,
482 ) -> Result<(), ClientError> {
483 self.logic.set_joining_player(local_players);
484 Ok(())
485 }
486}