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