lightyear_link/
conditioner.rs1use bevy_reflect::Reflect;
3use core::time::Duration;
4use lightyear_core::time::Instant;
5use lightyear_utils::ready_buffer::ReadyBuffer;
6use rand::Rng;
7
8#[derive(Clone, Debug, Default, Reflect)]
10pub struct LinkConditionerConfig {
11 pub incoming_latency: Duration,
13 pub incoming_jitter: Duration,
17 pub incoming_loss: f32,
20}
21
22#[derive(Debug, Clone)]
23pub struct LinkConditioner<P: Eq> {
24 config: LinkConditionerConfig,
25 pub time_queue: ReadyBuffer<Instant, P>,
26}
27
28impl<P: Eq> LinkConditioner<P> {
29 pub fn new(config: LinkConditionerConfig) -> Self {
30 LinkConditioner {
31 config,
32 time_queue: ReadyBuffer::new(),
33 }
34 }
35
36 pub(crate) fn condition_packet(&mut self, packet: P, instant: Instant) {
40 let mut rng = rand::rng();
41 if rng.random_range(0.0..1.0) <= self.config.incoming_loss {
42 return;
43 }
44 let mut latency: i32 = self.config.incoming_latency.as_millis() as i32;
45 let mut packet_timestamp = instant;
46 if self.config.incoming_jitter > Duration::default() {
47 let jitter: i32 = self.config.incoming_jitter.as_millis() as i32;
48 latency += rng.random_range(-jitter..jitter);
49 }
50 if latency > 0 {
51 packet_timestamp += Duration::from_millis(latency as u64);
52 }
53 self.time_queue.push(packet_timestamp, packet);
54 }
55
56 pub(crate) fn pop_packet(&mut self, instant: Instant) -> Option<P> {
58 self.time_queue.pop_item(&instant).map(|(_, packet)| packet)
59 }
60}
61
62impl LinkConditionerConfig {
63 pub fn new(incoming_latency: Duration, incoming_jitter: Duration, incoming_loss: f32) -> Self {
65 LinkConditionerConfig {
66 incoming_latency,
67 incoming_jitter,
68 incoming_loss,
69 }
70 }
71
72 pub fn half(self) -> Self {
74 LinkConditionerConfig {
75 incoming_latency: self.incoming_latency / 2,
76 incoming_jitter: self.incoming_jitter / 2,
77 incoming_loss: self.incoming_loss / 2.0,
78 }
79 }
80
81 pub fn good_condition() -> Self {
84 LinkConditionerConfig {
85 incoming_latency: Duration::from_millis(40),
86 incoming_jitter: Duration::from_millis(6),
87 incoming_loss: 0.002,
88 }
89 }
90
91 pub fn average_condition() -> Self {
94 LinkConditionerConfig {
95 incoming_latency: Duration::from_millis(100),
96 incoming_jitter: Duration::from_millis(15),
97 incoming_loss: 0.02,
98 }
99 }
100
101 pub fn poor_condition() -> Self {
104 LinkConditionerConfig {
105 incoming_latency: Duration::from_millis(200),
106 incoming_jitter: Duration::from_millis(30),
107 incoming_loss: 0.10,
108 }
109 }
110}