Skip to main content

veloren_common/
uid.rs

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