Skip to main content

naia_shared/world/local/
local_entity.rs

1use naia_serde::{BitReader, BitWrite, ConstBitLength, Serde, SerdeErr, UnsignedVariableInteger};
2
3use crate::{EntityDoesNotExistError, GlobalEntity, LocalEntityAndGlobalEntityConverter};
4
5/// A connection-local entity ID that records whether the entity is host-owned or remote-owned.
6#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
7pub enum OwnedLocalEntity {
8    /// Entity whose authoritative state originates on this side of the connection.
9    Host {
10        /// Wire-level entity ID within the host pool.
11        id: u16,
12        /// `true` if this entity belongs to the static pool.
13        is_static: bool,
14    },
15    /// Entity whose authoritative state originates on the far side of the connection.
16    Remote {
17        /// Wire-level entity ID within the remote pool.
18        id: u16,
19        /// `true` if this entity belongs to the static pool.
20        is_static: bool,
21    },
22}
23
24impl OwnedLocalEntity {
25    /// Creates a dynamic `Host` variant from a [`HostEntity`].
26    pub fn new_host(id: HostEntity) -> Self {
27        Self::Host { id: id.value(), is_static: false }
28    }
29
30    /// Creates a dynamic `Host` variant from a raw `u16` ID.
31    pub fn new_host_dynamic(id: u16) -> Self {
32        Self::Host { id, is_static: false }
33    }
34
35    /// Creates a static `Host` variant from a raw `u16` ID.
36    pub fn new_host_static(id: u16) -> Self {
37        Self::Host { id, is_static: true }
38    }
39
40    /// Creates a `Remote` variant from a [`RemoteEntity`], preserving its `is_static` flag.
41    pub fn new_remote(id: RemoteEntity) -> Self {
42        Self::Remote { id: id.value(), is_static: id.is_static() }
43    }
44
45    /// Creates a dynamic `Remote` variant from a raw `u16` ID.
46    pub fn new_remote_dynamic(id: u16) -> Self {
47        Self::Remote { id, is_static: false }
48    }
49
50    /// Creates a static `Remote` variant from a raw `u16` ID.
51    pub fn new_remote_static(id: u16) -> Self {
52        Self::Remote { id, is_static: true }
53    }
54
55    /// Returns `true` if this is a `Host` variant.
56    pub fn is_host(&self) -> bool {
57        match self {
58            Self::Host { .. } => true,
59            Self::Remote { .. } => false,
60        }
61    }
62
63    /// Returns `true` if this is a `Remote` variant.
64    pub fn is_remote(&self) -> bool {
65        !self.is_host()
66    }
67
68    /// Returns `true` if this entity belongs to the static pool.
69    pub fn is_static(&self) -> bool {
70        match self {
71            Self::Host { is_static, .. } => *is_static,
72            Self::Remote { is_static, .. } => *is_static,
73        }
74    }
75
76    /// Returns the raw `u16` wire ID for this entity, regardless of variant.
77    pub fn id(&self) -> u16 {
78        match self {
79            Self::Host { id, .. } | Self::Remote { id, .. } => *id,
80        }
81    }
82
83    /// Serializes this entity into the bit stream, writing host/remote flag, static flag, and ID.
84    pub fn ser(&self, writer: &mut dyn BitWrite) {
85        match self {
86            Self::Host { id, is_static } => {
87                true.ser(writer);
88                is_static.ser(writer);
89                UnsignedVariableInteger::<7>::new(*id).ser(writer);
90            }
91            Self::Remote { id, is_static } => {
92                false.ser(writer);
93                is_static.ser(writer);
94                UnsignedVariableInteger::<7>::new(*id).ser(writer);
95            }
96        }
97    }
98
99    /// Deserializes an `OwnedLocalEntity` from the bit stream.
100    pub fn de(reader: &mut BitReader) -> Result<Self, SerdeErr> {
101        let is_host = bool::de(reader)?;
102        let is_static = bool::de(reader)?;
103        let id = UnsignedVariableInteger::<7>::de(reader)?.get() as u16;
104        if is_host {
105            Ok(Self::Host { id, is_static })
106        } else {
107            Ok(Self::Remote { id, is_static })
108        }
109    }
110
111    /// Returns the encoded bit length of this entity.
112    pub fn bit_length(&self) -> u32 {
113        match self {
114            Self::Host { id, .. } | Self::Remote { id, .. } => {
115                bool::const_bit_length()   // is_host
116                + bool::const_bit_length() // is_static
117                + UnsignedVariableInteger::<7>::new(*id).bit_length()
118            }
119        }
120    }
121
122    pub(crate) fn convert_to_global(
123        &self,
124        converter: &dyn LocalEntityAndGlobalEntityConverter,
125    ) -> Result<GlobalEntity, EntityDoesNotExistError> {
126        match self {
127            OwnedLocalEntity::Host { id, is_static: true } => {
128                converter.static_host_entity_to_global_entity(&HostEntity::new(*id))
129            }
130            OwnedLocalEntity::Host { id, is_static: false } => {
131                converter.host_entity_to_global_entity(&HostEntity::new(*id))
132            }
133            OwnedLocalEntity::Remote { id, is_static } => {
134                let remote = if *is_static {
135                    RemoteEntity::new_static(*id)
136                } else {
137                    RemoteEntity::new(*id)
138                };
139                converter.remote_entity_to_global_entity(&remote)
140            }
141        }
142    }
143
144    pub(crate) fn take_remote(&self) -> RemoteEntity {
145        let OwnedLocalEntity::Remote { id, is_static } = self else {
146            panic!("Expected RemoteEntity")
147        };
148        if *is_static { RemoteEntity::new_static(*id) } else { RemoteEntity::new(*id) }
149    }
150
151    pub(crate) fn to_reversed(self) -> OwnedLocalEntity {
152        match self {
153            OwnedLocalEntity::Host { id, is_static } => OwnedLocalEntity::Remote { id, is_static },
154            OwnedLocalEntity::Remote { id, is_static } => OwnedLocalEntity::Host { id, is_static },
155        }
156    }
157
158    /// Extracts the inner [`HostEntity`], panicking if this is a `Remote` variant.
159    pub fn host(&self) -> HostEntity {
160        match self {
161            OwnedLocalEntity::Host { id, is_static } => {
162                if *is_static { HostEntity::new_static(*id) } else { HostEntity::new(*id) }
163            }
164            OwnedLocalEntity::Remote { .. } => panic!("Expected OwnedLocalEntity::Host, found OwnedLocalEntity::Remote"),
165        }
166    }
167
168    /// Extracts the inner [`RemoteEntity`], panicking if this is a `Host` variant.
169    pub fn remote(&self) -> RemoteEntity {
170        match self {
171            OwnedLocalEntity::Remote { id, is_static } => {
172                if *is_static { RemoteEntity::new_static(*id) } else { RemoteEntity::new(*id) }
173            }
174            OwnedLocalEntity::Host { .. } => panic!("Expected OwnedLocalEntity::Remote, found OwnedLocalEntity::Host"),
175        }
176    }
177}
178
179/// A host-assigned entity ID. Carries `is_static` so that static and dynamic
180/// entities from pools that both start at 0 remain distinct as hash map keys.
181#[derive(Copy, Eq, Hash, Clone, PartialEq, Debug)]
182pub struct HostEntity {
183    id: u16,
184    is_static: bool,
185}
186
187impl HostEntity {
188    /// Creates a dynamic host entity with the given `id`.
189    pub fn new(id: u16) -> Self {
190        Self { id, is_static: false }
191    }
192
193    /// Creates a static host entity with the given `id`.
194    pub fn new_static(id: u16) -> Self {
195        Self { id, is_static: true }
196    }
197
198    /// Returns the raw `u16` wire ID.
199    pub fn value(&self) -> u16 {
200        self.id
201    }
202
203    /// Returns `true` if this entity is from the static pool.
204    pub fn is_static(&self) -> bool {
205        self.is_static
206    }
207
208    /// Converts this host entity into the equivalent [`RemoteEntity`] with the same ID and static flag.
209    pub fn to_remote(self) -> RemoteEntity {
210        if self.is_static { RemoteEntity::new_static(self.id) } else { RemoteEntity::new(self.id) }
211    }
212
213    /// Serializes the entity ID into the bit stream (ID only; authority messages use dynamic entities).
214    pub fn ser(&self, writer: &mut dyn BitWrite) {
215        UnsignedVariableInteger::<7>::new(self.value()).ser(writer);
216    }
217
218    /// Deserializes a dynamic host entity from the bit stream.
219    pub fn de(reader: &mut BitReader) -> Result<Self, SerdeErr> {
220        let value = UnsignedVariableInteger::<7>::de(reader)?.get();
221        Ok(Self { id: value as u16, is_static: false }) // authority messages only use dynamic entities
222    }
223
224    /// Returns the encoded bit length of this entity's ID.
225    pub fn bit_length(&self) -> u32 {
226        UnsignedVariableInteger::<7>::new(self.value()).bit_length()
227    }
228
229    /// Wraps this entity as an `OwnedLocalEntity::Host`, preserving the `is_static` flag.
230    pub fn copy_to_owned(&self) -> OwnedLocalEntity {
231        OwnedLocalEntity::Host { id: self.value(), is_static: self.is_static }
232    }
233
234    /// Wraps this entity as a dynamic `OwnedLocalEntity::Host` (forcing `is_static = false`).
235    pub fn copy_to_owned_dynamic(&self) -> OwnedLocalEntity {
236        OwnedLocalEntity::Host { id: self.value(), is_static: false }
237    }
238
239    /// Wraps this entity as a static `OwnedLocalEntity::Host` (forcing `is_static = true`).
240    pub fn copy_to_owned_static(&self) -> OwnedLocalEntity {
241        OwnedLocalEntity::Host { id: self.value(), is_static: true }
242    }
243}
244
245/// A connection-local entity ID assigned by the remote peer, used on the receiving side of replication.
246#[derive(Copy, Eq, Hash, Clone, PartialEq, Debug)]
247pub struct RemoteEntity {
248    id: u16,
249    is_static: bool,
250}
251
252impl RemoteEntity {
253    /// Creates a dynamic remote entity with the given `id`.
254    pub fn new(id: u16) -> Self {
255        Self { id, is_static: false }
256    }
257
258    /// Creates a static remote entity with the given `id`.
259    pub fn new_static(id: u16) -> Self {
260        Self { id, is_static: true }
261    }
262
263    /// Returns the raw `u16` wire ID.
264    pub fn value(&self) -> u16 {
265        self.id
266    }
267
268    /// Returns `true` if this entity is from the static pool.
269    pub fn is_static(&self) -> bool {
270        self.is_static
271    }
272
273    /// Converts this remote entity into the equivalent [`HostEntity`] with the same ID and static flag.
274    pub fn to_host(self) -> HostEntity {
275        if self.is_static { HostEntity::new_static(self.id) } else { HostEntity::new(self.id) }
276    }
277
278    /// Serializes only the entity ID into the bit stream (authority messages always use dynamic entities).
279    pub fn ser(&self, writer: &mut dyn BitWrite) {
280        UnsignedVariableInteger::<7>::new(self.value()).ser(writer);
281    }
282
283    /// Deserializes a dynamic remote entity from the bit stream.
284    pub fn de(reader: &mut BitReader) -> Result<Self, SerdeErr> {
285        let value = UnsignedVariableInteger::<7>::de(reader)?.get();
286        Ok(Self { id: value as u16, is_static: false })
287    }
288
289    /// Wraps this entity as an `OwnedLocalEntity::Remote`, preserving the `is_static` flag.
290    pub fn copy_to_owned(&self) -> OwnedLocalEntity {
291        OwnedLocalEntity::Remote { id: self.id, is_static: self.is_static }
292    }
293}