steel_core/world/player_index/
map.rs1use std::sync::Arc;
4
5use arc_swap::ArcSwap;
6use scc::HashMap;
7use steel_utils::locks::SyncMutex;
8use uuid::Uuid;
9
10use crate::{entity::Entity, player::Player};
11
12struct PlayerSlot {
13 player: ArcSwap<RegisteredPlayer>,
14}
15
16struct RegisteredPlayer {
17 player: Arc<Player>,
18 _damage_history_owner: Arc<()>,
19}
20
21impl RegisteredPlayer {
22 fn new(player: Arc<Player>) -> Self {
23 Self {
24 _damage_history_owner: player.base().damage_history().retain_owner(),
25 player,
26 }
27 }
28}
29
30impl PlayerSlot {
31 fn new(player: Arc<Player>) -> Self {
32 Self {
33 player: ArcSwap::from_pointee(RegisteredPlayer::new(player)),
34 }
35 }
36
37 fn load(&self) -> Arc<Player> {
38 Arc::clone(&self.player.load().player)
39 }
40
41 fn replace(&self, expected: &Arc<Player>, replacement: Arc<Player>) -> bool {
42 let current = self.player.load_full();
43 if !Arc::ptr_eq(¤t.player, expected) {
44 return false;
45 }
46 let replacement = Arc::new(RegisteredPlayer::new(replacement));
47 let previous = self.player.compare_and_swap(¤t, replacement);
48 Arc::ptr_eq(&previous, ¤t)
49 }
50}
51
52pub struct PlayerMap {
57 by_uuid: HashMap<Uuid, Arc<PlayerSlot>>,
59 by_entity_id: HashMap<i32, Arc<PlayerSlot>>,
61 order: SyncMutex<Vec<Uuid>>,
63 mutations: SyncMutex<()>,
65}
66
67impl Default for PlayerMap {
68 fn default() -> Self {
69 Self::new()
70 }
71}
72
73impl PlayerMap {
74 #[must_use]
76 pub fn new() -> Self {
77 Self {
78 by_uuid: HashMap::new(),
79 by_entity_id: HashMap::new(),
80 order: SyncMutex::new(Vec::new()),
81 mutations: SyncMutex::new(()),
82 }
83 }
84
85 pub fn insert(&self, player: Arc<Player>) -> bool {
95 let uuid = player.gameprofile.id();
96 let entity_id = player.id();
97 let _mutation = self.mutations.lock();
98 let slot = Arc::new(PlayerSlot::new(player));
99
100 if self.by_uuid.insert_sync(uuid, Arc::clone(&slot)).is_err() {
101 return false;
102 }
103
104 if self.by_entity_id.insert_sync(entity_id, slot).is_err() {
105 let _ = self.by_uuid.remove_sync(&uuid);
106 panic!("player entity id {entity_id} is already registered");
107 }
108 self.order.lock().push(uuid);
109 true
110 }
111
112 #[expect(
116 clippy::unused_async,
117 clippy::unused_async_trait_impl,
118 reason = "keeps the existing asynchronous PlayerMap API while mutation is synchronized internally"
119 )]
120 pub async fn remove(&self, uuid: &Uuid) -> Option<Arc<Player>> {
121 self.remove_sync(uuid)
122 }
123
124 #[expect(
130 clippy::unused_async,
131 clippy::unused_async_trait_impl,
132 reason = "keeps the existing asynchronous PlayerMap API while mutation is synchronized internally"
133 )]
134 pub async fn remove_player(&self, player: &Arc<Player>) -> Option<Arc<Player>> {
135 self.remove_player_sync(player)
136 }
137
138 pub fn remove_sync(&self, uuid: &Uuid) -> Option<Arc<Player>> {
143 let _mutation = self.mutations.lock();
144 let (_, slot) = self.by_uuid.remove_sync(uuid)?;
145 let player = slot.load();
146 let _ = self
147 .by_entity_id
148 .remove_if_sync(&player.id(), |current| Arc::ptr_eq(current, &slot));
149 self.order.lock().retain(|player_uuid| player_uuid != uuid);
150 Some(player)
151 }
152
153 pub fn remove_player_sync(&self, player: &Arc<Player>) -> Option<Arc<Player>> {
155 let uuid = player.gameprofile.id();
156 let _mutation = self.mutations.lock();
157 let slot = self
158 .by_uuid
159 .read_sync(&uuid, |_, current| Arc::clone(current))?;
160 let current = slot.load();
161 if !Arc::ptr_eq(¤t, player) {
162 return None;
163 }
164
165 let (_, removed_slot) = self
166 .by_uuid
167 .remove_if_sync(&uuid, |current| Arc::ptr_eq(current, &slot))?;
168 let _ = self
169 .by_entity_id
170 .remove_if_sync(¤t.id(), |indexed| Arc::ptr_eq(indexed, &removed_slot));
171 self.order
172 .lock()
173 .retain(|indexed_uuid| *indexed_uuid != uuid);
174 Some(current)
175 }
176
177 pub fn replace_player(&self, expected: &Arc<Player>, replacement: Arc<Player>) -> bool {
186 let uuid = expected.gameprofile.id();
187 let entity_id = expected.id();
188 if replacement.gameprofile.id() != uuid || replacement.id() != entity_id {
189 return false;
190 }
191
192 let _mutation = self.mutations.lock();
193 let Some(slot) = self
194 .by_uuid
195 .read_sync(&uuid, |_, current| Arc::clone(current))
196 else {
197 return false;
198 };
199 let Some(entity_slot) = self
200 .by_entity_id
201 .read_sync(&entity_id, |_, current| Arc::clone(current))
202 else {
203 return false;
204 };
205 if !Arc::ptr_eq(&slot, &entity_slot) {
206 return false;
207 }
208
209 slot.replace(expected, replacement)
210 }
211
212 #[must_use]
214 pub fn get_by_uuid(&self, uuid: &Uuid) -> Option<Arc<Player>> {
215 self.by_uuid.read_sync(uuid, |_, slot| slot.load())
216 }
217
218 #[must_use]
220 pub fn get_by_entity_id(&self, entity_id: i32) -> Option<Arc<Player>> {
221 self.by_entity_id
222 .read_sync(&entity_id, |_, slot| slot.load())
223 }
224
225 pub fn iter_players<F>(&self, mut f: F)
229 where
230 F: FnMut(&Uuid, &Arc<Player>) -> bool,
231 {
232 let order = self.order.lock().iter().copied().collect::<Vec<_>>();
233 for uuid in order {
234 let Some(player) = self.get_by_uuid(&uuid) else {
235 continue;
236 };
237 if !f(&uuid, &player) {
238 return;
239 }
240 }
241 }
242
243 #[must_use]
245 pub fn len(&self) -> usize {
246 self.by_uuid.len()
247 }
248
249 #[must_use]
251 pub fn is_empty(&self) -> bool {
252 self.by_uuid.is_empty()
253 }
254}
255
256#[cfg(test)]
257mod tests;