Flint Engine / Guide / API Reference

flint_core/
id.rs

1//! Stable entity identifiers
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::sync::atomic::{AtomicU64, Ordering};
6
7/// Global counter for generating unique IDs
8static NEXT_ID: AtomicU64 = AtomicU64::new(1);
9
10/// A stable entity identifier that persists across save/load cycles.
11///
12/// Unlike internal ECS entity IDs which may be recycled or change,
13/// `EntityId` provides a stable reference for scene files and queries.
14#[derive(Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
15#[serde(transparent)]
16pub struct EntityId(pub u64);
17
18impl EntityId {
19    /// Create a new unique EntityId
20    pub fn new() -> Self {
21        Self(NEXT_ID.fetch_add(1, Ordering::Relaxed))
22    }
23
24    /// Create an EntityId from a raw value (for deserialization/testing)
25    pub fn from_raw(id: u64) -> Self {
26        Self(id)
27    }
28
29    /// Get the raw u64 value
30    pub fn raw(&self) -> u64 {
31        self.0
32    }
33
34    /// Reset the ID counter (for testing only)
35    #[cfg(test)]
36    pub fn reset_counter() {
37        NEXT_ID.store(1, Ordering::Relaxed);
38    }
39
40    /// Set the counter to at least the given value (for loading scenes)
41    pub fn ensure_counter_above(value: u64) {
42        let mut current = NEXT_ID.load(Ordering::Relaxed);
43        while current <= value {
44            match NEXT_ID.compare_exchange_weak(
45                current,
46                value + 1,
47                Ordering::Relaxed,
48                Ordering::Relaxed,
49            ) {
50                Ok(_) => break,
51                Err(c) => current = c,
52            }
53        }
54    }
55}
56
57impl Default for EntityId {
58    fn default() -> Self {
59        Self::new()
60    }
61}
62
63impl fmt::Debug for EntityId {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        write!(f, "EntityId({})", self.0)
66    }
67}
68
69impl fmt::Display for EntityId {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        write!(f, "{}", self.0)
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn test_id_generation() {
81        EntityId::reset_counter();
82        let id1 = EntityId::new();
83        let id2 = EntityId::new();
84        assert_ne!(id1, id2);
85        assert!(id2.0 > id1.0);
86    }
87
88    #[test]
89    fn test_from_raw() {
90        let id = EntityId::from_raw(42);
91        assert_eq!(id.raw(), 42);
92    }
93
94    #[test]
95    fn test_ensure_counter_above() {
96        EntityId::reset_counter();
97        EntityId::ensure_counter_above(100);
98        let id = EntityId::new();
99        assert!(id.0 > 100);
100    }
101}