veloren_common/
uid.rs

1use crate::{
2    character::CharacterId,
3    rtsim::{Actor, RtSimEntity},
4};
5use core::hash::Hash;
6use hashbrown::HashMap;
7use serde::{Deserialize, Serialize};
8use specs::{Component, Entity, FlaggedStorage, VecStorage};
9use std::{fmt, num::NonZeroU64};
10use tracing::error;
11
12#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
13pub struct Uid(pub NonZeroU64);
14
15impl From<Uid> for NonZeroU64 {
16    fn from(uid: Uid) -> NonZeroU64 { uid.0 }
17}
18
19impl From<NonZeroU64> for Uid {
20    fn from(uid: NonZeroU64) -> Self { Self(uid) }
21}
22
23impl fmt::Display for Uid {
24    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) }
25}
26
27impl Component for Uid {
28    type Storage = FlaggedStorage<Self, VecStorage<Self>>;
29}
30
31#[derive(Debug)]
32struct UidAllocator {
33    /// Next Uid.
34    next_uid: u64,
35}
36
37impl UidAllocator {
38    fn new() -> Self { Self { next_uid: 1 } }
39
40    fn allocate(&mut self) -> Uid {
41        let id = self.next_uid;
42        self.next_uid += 1;
43        Uid(NonZeroU64::new(id).expect("Uid cannot be zero"))
44    }
45}
46
47/// Mappings from various Id types to `Entity`s.
48#[derive(Default, Debug)]
49pub struct IdMaps {
50    /// "Universal" IDs (used to communicate entity identity over the
51    /// network).
52    uid_mapping: HashMap<Uid, Entity>,
53
54    // -- Fields below are only used on the server --
55    uid_allocator: UidAllocator,
56
57    /// Character IDs.
58    character_to_ecs: HashMap<CharacterId, Entity>,
59    /// Rtsim Entities.
60    rtsim_to_ecs: HashMap<RtSimEntity, Entity>,
61}
62
63impl IdMaps {
64    pub fn new() -> Self { Default::default() }
65
66    /// Given a `Uid` retrieve the corresponding `Entity`.
67    pub fn uid_entity(&self, id: Uid) -> Option<Entity> { self.uid_mapping.get(&id).copied() }
68
69    /// Given a `CharacterId` retrieve the corresponding `Entity`.
70    pub fn character_entity(&self, id: CharacterId) -> Option<Entity> {
71        self.character_to_ecs.get(&id).copied()
72    }
73
74    /// Given a `RtSimEntity` retrieve the corresponding `Entity`.
75    pub fn rtsim_entity(&self, id: RtSimEntity) -> Option<Entity> {
76        self.rtsim_to_ecs.get(&id).copied()
77    }
78
79    pub fn actor_entity(&self, actor: Actor) -> Option<Entity> {
80        match actor {
81            Actor::Character(character_id) => self.character_entity(character_id),
82            Actor::Npc(npc_id) => self.rtsim_entity(npc_id),
83        }
84    }
85
86    /// Removes mappings for the provided Id(s).
87    ///
88    /// Returns the `Entity` that the provided `Uid` was mapped to.
89    ///
90    /// Used on both the client and the server when deleting entities,
91    /// although the client only ever provides a Some value for the
92    /// `Uid` parameter since the other mappings are not used on the
93    /// client.
94    #[track_caller]
95    pub fn remove_entity(
96        &mut self,
97        expected_entity: Option<Entity>,
98        uid: Option<Uid>,
99        cid: Option<CharacterId>,
100        rid: Option<RtSimEntity>,
101    ) -> Option<Entity> {
102        use std::fmt::Debug;
103        #[cold]
104        #[inline(never)]
105        fn unexpected_entity<ID>() {
106            let kind = core::any::type_name::<ID>();
107            error!("Provided {kind} was mapped to an unexpected entity!");
108        }
109        #[cold]
110        #[inline(never)]
111        #[track_caller]
112        fn not_present<ID: Debug>(id: ID) {
113            let kind = core::any::type_name::<ID>();
114            error!(
115                "Provided {kind} {id:?} was not mapped to any entity! Caller: {}",
116                std::panic::Location::caller()
117            );
118        }
119
120        #[track_caller]
121        fn remove<ID: Hash + Eq + Debug>(
122            mapping: &mut HashMap<ID, Entity>,
123            id: Option<ID>,
124            expected: Option<Entity>,
125        ) -> Option<Entity> {
126            if let Some(id) = id {
127                if let Some(e) = mapping.remove(&id) {
128                    if expected.is_some_and(|expected| e != expected) {
129                        unexpected_entity::<ID>();
130                    }
131                    Some(e)
132                } else {
133                    not_present::<ID>(id);
134                    None
135                }
136            } else {
137                None
138            }
139        }
140
141        let maybe_entity = remove(&mut self.uid_mapping, uid, expected_entity);
142        let expected_entity = expected_entity.or(maybe_entity);
143        remove(&mut self.character_to_ecs, cid, expected_entity);
144        remove(&mut self.rtsim_to_ecs, rid, expected_entity);
145        maybe_entity
146    }
147
148    /// Only used on the client (server solely uses `Self::allocate` to
149    /// allocate and add Uid mappings and `Self::remap` to move the `Uid` to
150    /// a different entity).
151    pub fn add_entity(&mut self, uid: Uid, entity: Entity) {
152        Self::insert(&mut self.uid_mapping, uid, entity);
153    }
154
155    /// Only used on the server.
156    pub fn add_character(&mut self, cid: CharacterId, entity: Entity) {
157        Self::insert(&mut self.character_to_ecs, cid, entity);
158    }
159
160    /// Only used on the server.
161    pub fn add_rtsim(&mut self, rid: RtSimEntity, entity: Entity) {
162        Self::insert(&mut self.rtsim_to_ecs, rid, entity);
163    }
164
165    /// Allocates a new `Uid` and links it to the provided entity.
166    ///
167    /// Only used on the server.
168    pub fn allocate(&mut self, entity: Entity) -> Uid {
169        let uid = self.uid_allocator.allocate();
170        self.uid_mapping.insert(uid, entity);
171        uid
172    }
173
174    /// Links an existing `Uid` to a new entity.
175    ///
176    /// Only used on the server.
177    ///
178    /// Used for `handle_exit_ingame` which moves the same `Uid` to a new
179    /// entity.
180    pub fn remap_entity(&mut self, uid: Uid, new_entity: Entity) {
181        if self.uid_mapping.insert(uid, new_entity).is_none() {
182            error!("Uid {uid:?} remaped but there was no existing entry for it!");
183        }
184    }
185
186    #[cold]
187    #[inline(never)]
188    fn already_present<ID>() {
189        let kind = core::any::type_name::<ID>();
190        error!("Provided {kind} was already mapped to an entity!!!");
191    }
192
193    fn insert<ID: Hash + Eq>(mapping: &mut HashMap<ID, Entity>, new_id: ID, entity: Entity) {
194        if let Some(_previous_entity) = mapping.insert(new_id, entity) {
195            Self::already_present::<ID>();
196        }
197    }
198}
199
200impl Default for UidAllocator {
201    fn default() -> Self { Self::new() }
202}