Skip to main content

aetheris_protocol/
types.rs

1//! Protocol-level primitive types.
2use serde::{Deserialize, Serialize};
3
4/// A globally unique entity identifier used in all network communication.
5/// Assigned by the server. Immutable for the lifetime of the entity.
6///
7/// This is NOT the ECS's internal entity ID. The `WorldState` adapter
8/// translates between `NetworkId` and the ECS's local handle.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
10pub struct NetworkId(pub u64);
11
12/// The ECS's internal entity handle. Opaque to the network layer.
13/// In Phase 1 (Bevy), this wraps `bevy_ecs::entity::Entity`.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct LocalId(pub u64);
16
17/// A unique identifier for a connected client session.
18/// Assigned by the transport layer on connection, released on disconnect.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
20pub struct ClientId(pub u64);
21
22/// A component type identifier. Used by the Encoder to determine
23/// how to serialize/deserialize a specific component's fields.
24///
25/// In Phase 1, this is a simple enum discriminant.
26/// In Phase 3, this may become a compile-time type hash.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
28pub struct ComponentKind(pub u16);
29
30/// Standard transform component used for replication (`ComponentKind` 1).
31#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
32#[repr(C)]
33pub struct Transform {
34    /// Position X
35    pub x: f32,
36    /// Position Y
37    pub y: f32,
38    /// Position Z
39    pub z: f32,
40    /// Rotation in radians
41    pub rotation: f32,
42    /// Entity type (Phase 1 / Playground only)
43    pub entity_type: u16,
44}
45
46use std::sync::atomic::{AtomicU64, Ordering};
47use thiserror::Error;
48
49#[derive(Debug, Error, PartialEq, Eq)]
50pub enum AllocatorError {
51    #[error("NetworkId overflow (reached u64::MAX)")]
52    Overflow,
53    #[error("NetworkId allocator exhausted (reached limit)")]
54    Exhausted,
55}
56
57/// Authoritative allocator for [`NetworkId`]s.
58///
59/// Used by the server to ensure IDs are unique and monotonically increasing.
60/// Thread-safe and lock-free.
61#[derive(Debug)]
62pub struct NetworkIdAllocator {
63    start_id: u64,
64    next: AtomicU64,
65}
66
67impl Default for NetworkIdAllocator {
68    fn default() -> Self {
69        Self::new(1)
70    }
71}
72
73impl NetworkIdAllocator {
74    /// Creates a new allocator starting from a specific ID. 0 is reserved.
75    #[must_use]
76    pub fn new(start_id: u64) -> Self {
77        Self {
78            start_id,
79            next: AtomicU64::new(start_id),
80        }
81    }
82
83    /// Allocates a new unique [`NetworkId`].
84    ///
85    /// # Errors
86    /// Returns [`AllocatorError::Overflow`] if the next ID would exceed `u64::MAX`.
87    pub fn allocate(&self) -> Result<NetworkId, AllocatorError> {
88        let val = self
89            .next
90            .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |curr| {
91                if curr == u64::MAX {
92                    None
93                } else {
94                    Some(curr + 1)
95                }
96            })
97            .map_err(|_| AllocatorError::Overflow)?;
98
99        if val == 0 {
100            return Err(AllocatorError::Exhausted);
101        }
102
103        Ok(NetworkId(val))
104    }
105
106    /// Resets the allocator to its initial `start_id`.
107    /// Use only in tests or clear-world scenarios.
108    pub fn reset(&self) {
109        self.next.store(self.start_id, Ordering::Relaxed);
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_primitive_derives() {
119        let nid1 = NetworkId(42);
120        let nid2 = nid1;
121        assert_eq!(nid1, nid2);
122
123        let lid1 = LocalId(42);
124        let lid2 = LocalId(42);
125        assert_eq!(lid1, lid2);
126
127        let cid = ClientId(99);
128        assert_eq!(format!("{cid:?}"), "ClientId(99)");
129
130        let kind = ComponentKind(1);
131        assert_eq!(kind.0, 1);
132    }
133}