naia_shared/world/
entity_index.rs1use std::{collections::VecDeque, time::Duration};
2
3use naia_socket_shared::Instant;
4
5#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
22pub struct EntityIndex(pub u32);
23
24impl From<u32> for EntityIndex {
25 fn from(value: u32) -> Self {
26 Self(value)
27 }
28}
29
30impl From<EntityIndex> for u32 {
31 fn from(value: EntityIndex) -> Self {
32 value.0
33 }
34}
35
36#[derive(Clone)]
40pub struct KeyGenerator32<K: From<u32> + Into<u32> + Copy> {
41 recycling_keys: VecDeque<(u32, Instant)>,
42 recycled_keys: VecDeque<u32>,
43 recycle_timeout: Duration,
44 next_new_key: u32,
45 phantom: std::marker::PhantomData<K>,
46}
47
48impl<K: From<u32> + Into<u32> + Copy> KeyGenerator32<K> {
49 pub fn new(recycle_timeout: Duration) -> Self {
51 Self {
52 recycle_timeout,
53 recycling_keys: VecDeque::new(),
54 recycled_keys: VecDeque::new(),
55 next_new_key: 0,
56 phantom: std::marker::PhantomData,
57 }
58 }
59
60 pub fn generate(&mut self) -> K {
62 let now = Instant::now();
63 loop {
64 let Some((_, instant)) = self.recycling_keys.front() else {
65 break;
66 };
67 if instant.elapsed(&now) < self.recycle_timeout {
68 break;
69 }
70 let (key, _) = self.recycling_keys.pop_front().unwrap();
71 self.recycled_keys.push_back(key);
72 }
73 if let Some(key) = self.recycled_keys.pop_front() {
74 return K::from(key);
75 }
76 let output = self.next_new_key;
77 self.next_new_key = self.next_new_key.wrapping_add(1);
78 K::from(output)
79 }
80
81 pub fn recycle_key(&mut self, key: &K) {
83 let key_u32: u32 = (*key).into();
84 self.recycling_keys.push_back((key_u32, Instant::now()));
85 }
86
87 pub fn capacity_hint(&self) -> u32 {
89 self.next_new_key
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn generates_sequential_keys() {
99 let mut g: KeyGenerator32<EntityIndex> = KeyGenerator32::new(Duration::from_secs(60));
100 assert_eq!(g.generate().0, 0);
101 assert_eq!(g.generate().0, 1);
102 assert_eq!(g.generate().0, 2);
103 }
104
105 #[test]
106 fn capacity_hint_matches_next_new_key() {
107 let mut g: KeyGenerator32<EntityIndex> = KeyGenerator32::new(Duration::from_secs(60));
108 assert_eq!(g.capacity_hint(), 0);
109 let _ = g.generate();
110 let _ = g.generate();
111 assert_eq!(g.capacity_hint(), 2);
112 }
113
114 #[test]
115 fn recycle_keeps_key_quarantined_until_timeout() {
116 let mut g: KeyGenerator32<EntityIndex> = KeyGenerator32::new(Duration::from_secs(60));
117 let k = g.generate();
118 g.recycle_key(&k);
119 let next = g.generate();
120 assert_ne!(next.0, k.0, "recycled key should not return before timeout");
121 }
122
123 #[test]
124 fn recycle_returns_after_timeout() {
125 let mut g: KeyGenerator32<EntityIndex> = KeyGenerator32::new(Duration::from_millis(0));
126 let k = g.generate();
127 g.recycle_key(&k);
128 std::thread::sleep(Duration::from_millis(2));
130 let next = g.generate();
131 assert_eq!(next.0, k.0, "recycled key should be reused after timeout");
132 }
133}