naia_shared/connection/
entity_priority.rs1use std::{collections::HashMap, hash::Hash};
2
3#[derive(Clone, Debug, Default)]
7pub(crate) struct EntityPriorityData {
8 pub(crate) accumulated: f32,
10 pub(crate) gain_override: Option<f32>,
12 pub(crate) last_sent_tick: Option<u32>,
15}
16
17pub struct EntityPriorityRef<'a, E: Copy + Eq + Hash> {
21 pub(crate) state: Option<&'a EntityPriorityData>,
22 pub(crate) entity: E,
23}
24
25impl<'a, E: Copy + Eq + Hash> EntityPriorityRef<'a, E> {
26 pub fn empty(entity: E) -> Self {
30 Self {
31 state: None,
32 entity,
33 }
34 }
35
36 pub fn entity(&self) -> E {
38 self.entity
39 }
40
41 pub fn accumulated(&self) -> f32 {
44 self.state.map(|s| s.accumulated).unwrap_or(0.0)
45 }
46
47 pub fn gain(&self) -> Option<f32> {
50 self.state.and_then(|s| s.gain_override)
51 }
52
53 pub fn is_overridden(&self) -> bool {
55 self.gain().is_some()
56 }
57}
58
59pub struct EntityPriorityMut<'a, E: Copy + Eq + Hash> {
67 pub(crate) entries: &'a mut HashMap<E, EntityPriorityData>,
68 pub(crate) entity: E,
69}
70
71impl<'a, E: Copy + Eq + Hash> EntityPriorityMut<'a, E> {
72 pub fn entity(&self) -> E {
76 self.entity
77 }
78
79 pub fn accumulated(&self) -> f32 {
81 self.entries
82 .get(&self.entity)
83 .map(|s| s.accumulated)
84 .unwrap_or(0.0)
85 }
86
87 pub fn gain(&self) -> Option<f32> {
89 self.entries
90 .get(&self.entity)
91 .and_then(|s| s.gain_override)
92 }
93
94 pub fn is_overridden(&self) -> bool {
96 self.gain().is_some()
97 }
98
99 pub fn set_gain(&mut self, gain: f32) -> &mut Self {
104 self.entries
105 .entry(self.entity)
106 .or_default()
107 .gain_override = Some(gain);
108 self
109 }
110
111 pub fn boost_once(&mut self, amount: f32) -> &mut Self {
115 self.entries
116 .entry(self.entity)
117 .or_default()
118 .accumulated += amount;
119 self
120 }
121
122 pub fn reset(&mut self) -> &mut Self {
125 if let Some(data) = self.entries.get_mut(&self.entity) {
126 data.gain_override = None;
127 }
128 self
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 fn fresh() -> HashMap<u32, EntityPriorityData> {
137 HashMap::new()
138 }
139
140 #[test]
141 fn set_gain_lazy_creates_entry() {
142 let mut entries = fresh();
143 let mut m = EntityPriorityMut {
144 entries: &mut entries,
145 entity: 7u32,
146 };
147 assert_eq!(m.gain(), None);
148 m.set_gain(5.0);
149 assert_eq!(m.gain(), Some(5.0));
150 assert!(m.is_overridden());
151 }
152
153 #[test]
154 fn set_gain_then_reset_returns_to_default() {
155 let mut entries = fresh();
156 let mut m = EntityPriorityMut {
157 entries: &mut entries,
158 entity: 7u32,
159 };
160 m.set_gain(5.0);
161 m.reset();
162 assert_eq!(m.gain(), None);
163 assert!(!m.is_overridden());
164 assert!(entries.contains_key(&7u32));
166 }
167
168 #[test]
169 fn boost_once_is_additive_and_preserves_gain() {
170 let mut entries = fresh();
171 let mut m = EntityPriorityMut {
172 entries: &mut entries,
173 entity: 7u32,
174 };
175 m.set_gain(3.0);
176 m.boost_once(10.0);
177 m.boost_once(5.0);
178 assert_eq!(m.accumulated(), 15.0);
179 assert_eq!(m.gain(), Some(3.0));
180 }
181
182 #[test]
183 fn boost_once_lazy_creates_entry() {
184 let mut entries = fresh();
185 let mut m = EntityPriorityMut {
186 entries: &mut entries,
187 entity: 7u32,
188 };
189 m.boost_once(4.0);
190 assert_eq!(m.accumulated(), 4.0);
191 assert_eq!(m.gain(), None);
193 }
194
195 #[test]
196 fn reset_on_absent_entry_is_noop() {
197 let mut entries = fresh();
198 let mut m = EntityPriorityMut {
199 entries: &mut entries,
200 entity: 7u32,
201 };
202 m.reset();
203 assert!(!entries.contains_key(&7u32));
204 }
205
206 #[test]
209 fn b_bdd_4_set_gain_then_reset_yields_default() {
210 let mut entries = fresh();
211 let mut m = EntityPriorityMut {
212 entries: &mut entries,
213 entity: 7u32,
214 };
215 m.set_gain(5.0);
216 assert!(m.is_overridden());
217 m.reset();
218 assert_eq!(m.gain(), None);
219 assert!(!m.is_overridden());
220 assert!(entries.contains_key(&7u32));
221 }
222
223 #[test]
227 fn b_bdd_5_boost_once_does_not_mutate_gain() {
228 let mut entries = fresh();
229 let mut m = EntityPriorityMut {
230 entries: &mut entries,
231 entity: 7u32,
232 };
233 m.set_gain(3.0);
234 m.boost_once(100.0);
235 assert_eq!(m.accumulated(), 100.0);
236 assert_eq!(m.gain(), Some(3.0));
237 }
238
239 #[test]
243 fn b_bdd_6_set_gain_persists_across_mutations() {
244 let mut entries = fresh();
245 {
246 let mut m = EntityPriorityMut {
247 entries: &mut entries,
248 entity: 7u32,
249 };
250 m.set_gain(5.0);
251 m.boost_once(10.0);
252 }
253 let m = EntityPriorityMut {
255 entries: &mut entries,
256 entity: 7u32,
257 };
258 assert_eq!(m.gain(), Some(5.0));
259 assert_eq!(m.accumulated(), 10.0);
260 }
261
262 #[test]
265 fn ref_reads_match_mut_reads() {
266 let mut entries = fresh();
267 {
268 let mut m = EntityPriorityMut {
269 entries: &mut entries,
270 entity: 7u32,
271 };
272 m.set_gain(2.0);
273 m.boost_once(7.0);
274 }
275 let r = EntityPriorityRef {
276 state: entries.get(&7u32),
277 entity: 7u32,
278 };
279 assert_eq!(r.gain(), Some(2.0));
280 assert_eq!(r.accumulated(), 7.0);
281 assert!(r.is_overridden());
282 }
283
284 #[test]
286 fn empty_ref_reads_defaults() {
287 let r = EntityPriorityRef::<u32>::empty(42);
288 assert_eq!(r.entity(), 42);
289 assert_eq!(r.gain(), None);
290 assert_eq!(r.accumulated(), 0.0);
291 assert!(!r.is_overridden());
292 }
293}