steel_core/player/chat/
message_validator.rs1use std::collections::VecDeque;
4
5const MAX_TRACKED_MESSAGES: usize = 20;
7
8#[derive(Debug, Clone)]
10struct TrackedEntry {
11 signature: Option<Box<[u8]>>,
12 pending: bool, }
14
15impl TrackedEntry {
16 fn acknowledge(self) -> Self {
17 Self {
18 signature: self.signature,
19 pending: false,
20 }
21 }
22}
23
24#[derive(Debug)]
26pub struct LastSeenMessagesValidator {
27 tracked_messages: VecDeque<Option<TrackedEntry>>,
28 last_pending_signature: Option<Box<[u8]>>,
29}
30
31impl Default for LastSeenMessagesValidator {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37impl LastSeenMessagesValidator {
38 #[must_use]
40 pub fn new() -> Self {
41 let mut tracked_messages = VecDeque::with_capacity(MAX_TRACKED_MESSAGES);
42 for _ in 0..MAX_TRACKED_MESSAGES {
43 tracked_messages.push_back(None);
44 }
45 Self {
46 tracked_messages,
47 last_pending_signature: None,
48 }
49 }
50
51 pub fn add_pending(&mut self, signature: Option<Box<[u8]>>) {
54 if signature.as_ref() != self.last_pending_signature.as_ref() {
57 let entry = TrackedEntry {
58 signature: signature.clone(),
59 pending: true,
60 };
61 self.tracked_messages.push_back(Some(entry));
62 self.last_pending_signature = signature;
63 }
64 }
65
66 #[must_use]
68 pub fn tracked_count(&self) -> usize {
69 self.tracked_messages.len()
70 }
71
72 pub fn apply_offset(&mut self, offset: i32) -> Result<(), String> {
74 let removable = self
75 .tracked_messages
76 .len()
77 .saturating_sub(MAX_TRACKED_MESSAGES);
78 if offset < 0 || offset as usize > removable {
79 return Err(format!(
80 "Advanced last seen window by {offset} messages, but expected at most {removable}"
81 ));
82 }
83 for _ in 0..offset {
84 self.tracked_messages.pop_front();
85 }
86 Ok(())
87 }
88
89 pub fn apply_update(
94 &mut self,
95 acknowledged: [u8; 3], offset: i32,
97 checksum: u8,
98 ) -> Result<Vec<Box<[u8]>>, String> {
99 log::debug!(
100 "apply_update: offset={}, checksum={}, tracked_messages.len()={}, acknowledged={:?}",
101 offset,
102 checksum,
103 self.tracked_messages.len(),
104 acknowledged
105 );
106
107 self.apply_offset(offset)?;
109
110 let mut acknowledged_signatures = Vec::new();
111
112 for i in 0..MAX_TRACKED_MESSAGES.min(self.tracked_messages.len()) {
114 let bit_index = i;
115 let byte_index = bit_index / 8;
116 let bit_offset = bit_index % 8;
117 let is_acknowledged = (acknowledged[byte_index] & (1 << bit_offset)) != 0;
118
119 if let Some(entry_opt) = self.tracked_messages.get_mut(i) {
120 if is_acknowledged {
121 if let Some(entry) = entry_opt {
123 log::debug!(
124 "Index {}: Client acknowledged message (pending={})",
125 i,
126 entry.pending
127 );
128 let acknowledged_entry = entry.clone().acknowledge();
129 if let Some(sig) = &entry.signature {
130 acknowledged_signatures.push(sig.clone());
131 }
132 *entry_opt = Some(acknowledged_entry);
133 } else {
134 log::error!(
135 "Index {i}: Client acknowledged unknown/ignored message! tracked_messages[{i}] = None"
136 );
137 return Err(format!(
138 "Last seen update acknowledged unknown or previously ignored message at index {i}"
139 ));
140 }
141 } else {
142 if let Some(entry) = entry_opt
144 && !entry.pending
145 {
146 log::error!("Index {i}: Client ignored previously acknowledged message!");
147 return Err(format!(
148 "Last seen update ignored previously acknowledged message at index {i}"
149 ));
150 }
151 log::debug!("Index {i}: Client did not acknowledge (setting to None)");
152 *entry_opt = None;
154 }
155 }
156 }
157
158 log::debug!(
159 "apply_update: Successfully acknowledged {} signatures",
160 acknowledged_signatures.len()
161 );
162
163 if checksum != 0 {
165 }
168
169 Ok(acknowledged_signatures)
170 }
171}