Skip to main content

gizmo_core/entity/
mod.rs

1/// ECS Entity tanımlayıcısı — Packed u64 temsili.
2///
3/// Alt 32 bit = entity ID (slot index), üst 32 bit = generation (yeniden kullanım sayacı).
4/// Generation, aynı slot'a (ID'ye) yeni bir entity atandığında artırılır ve eski referansların
5/// (dangling entity) güvenli şekilde tespit edilmesini sağlar.
6///
7/// # Layout
8/// ```text
9/// ┌──────────────────────────────────────────────────────────────────┐
10/// │  63 ───────────── 32 │ 31 ───────────── 0 │
11/// │     generation (u32) │       id (u32)     │
12/// └──────────────────────────────────────────────────────────────────┘
13/// ```
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
15pub struct Entity(u64);
16
17impl Entity {
18    /// Geçersiz / null entity sentinel değeri.
19    /// `Option<Entity>` yerine kullanılabilir (ergonomi ve cache dostu).
20    pub const INVALID: Self = Self(u64::MAX);
21
22    /// Builds an `Entity` handle from an explicit `id` + `generation`.
23    ///
24    /// FOOTGUN: if you only have a raw `id` (e.g. from the UI, a script, or a saved
25    /// reference) do NOT fabricate `Entity::new(id, 0)` to call a generation-checked API
26    /// (`World::is_alive`/`entity_component_types`/`get_entity`, queries): once the id slot
27    /// has been recycled (despawn→spawn bumps the generation) that gen-0 handle is stale
28    /// and silently misses / mis-targets. Use [`World::entity(id)`](crate::World::entity),
29    /// which reconstructs the CURRENT generation. `new` is for deserialization and tests
30    /// where the generation is known, or for purely id-keyed internal addressing.
31    #[inline]
32    pub fn new(id: u32, generation: u32) -> Self {
33        Self(((generation as u64) << 32) | id as u64)
34    }
35
36    /// Entity'nin slot indeksini (ID) döndürür.
37    #[inline]
38    pub fn id(self) -> u32 {
39        self.0 as u32
40    }
41
42    /// Entity'nin generation (nesil) sayacını döndürür.
43    #[inline]
44    pub fn generation(self) -> u32 {
45        (self.0 >> 32) as u32
46    }
47
48    /// Bu entity'nin geçerli (INVALID olmayan) olup olmadığını kontrol eder.
49    #[inline]
50    pub fn is_valid(self) -> bool {
51        self != Self::INVALID
52    }
53
54    /// Entity'yi ham u64 bit temsiline dönüştürür.
55    /// Serializasyon, network sync ve hash key olarak kullanılabilir.
56    #[inline]
57    pub fn to_bits(self) -> u64 {
58        self.0
59    }
60
61    /// Ham u64 bit temsilinden Entity oluşturur.
62    #[inline]
63    pub fn from_bits(bits: u64) -> Self {
64        Self(bits)
65    }
66}
67
68impl std::fmt::Display for Entity {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        if *self == Self::INVALID {
71            write!(f, "Entity(INVALID)")
72        } else {
73            write!(f, "Entity({}:{})", self.id(), self.generation())
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_new_and_accessors() {
84        let e = Entity::new(42, 7);
85        assert_eq!(e.id(), 42);
86        assert_eq!(e.generation(), 7);
87    }
88
89    #[test]
90    fn test_zero_generation() {
91        let e = Entity::new(0, 0);
92        assert_eq!(e.id(), 0);
93        assert_eq!(e.generation(), 0);
94        assert!(e.is_valid());
95    }
96
97    #[test]
98    fn test_max_values() {
99        let e = Entity::new(u32::MAX - 1, u32::MAX - 1);
100        assert_eq!(e.id(), u32::MAX - 1);
101        assert_eq!(e.generation(), u32::MAX - 1);
102        assert!(e.is_valid());
103    }
104
105    #[test]
106    fn test_invalid_sentinel() {
107        assert!(!Entity::INVALID.is_valid());
108        assert_eq!(Entity::INVALID.id(), u32::MAX);
109        assert_eq!(Entity::INVALID.generation(), u32::MAX);
110    }
111
112    #[test]
113    fn test_to_bits_from_bits_roundtrip() {
114        let e = Entity::new(123, 456);
115        let bits = e.to_bits();
116        let e2 = Entity::from_bits(bits);
117        assert_eq!(e, e2);
118        assert_eq!(e2.id(), 123);
119        assert_eq!(e2.generation(), 456);
120    }
121
122    #[test]
123    fn test_display() {
124        let e = Entity::new(5, 2);
125        assert_eq!(format!("{}", e), "Entity(5:2)");
126    }
127
128    #[test]
129    fn test_display_invalid() {
130        assert_eq!(format!("{}", Entity::INVALID), "Entity(INVALID)");
131    }
132
133    #[test]
134    fn test_equality_and_hash() {
135        use std::collections::HashSet;
136        let e1 = Entity::new(1, 0);
137        let e2 = Entity::new(1, 0);
138        let e3 = Entity::new(1, 1); // Aynı ID, farklı generation
139
140        assert_eq!(e1, e2);
141        assert_ne!(e1, e3);
142
143        let mut set = HashSet::new();
144        set.insert(e1);
145        assert!(set.contains(&e2));
146        assert!(!set.contains(&e3));
147    }
148
149    #[test]
150    fn test_copy_semantics() {
151        let e1 = Entity::new(10, 3);
152        let e2 = e1; // Copy
153        assert_eq!(e1, e2);
154        assert_eq!(e1.id(), e2.id());
155    }
156
157    #[test]
158    fn test_serde_roundtrip() {
159        let e = Entity::new(999, 42);
160        let serialized = ron::to_string(&e).expect("serialize failed");
161        let deserialized: Entity = ron::from_str(&serialized).expect("deserialize failed");
162        assert_eq!(e, deserialized);
163    }
164}
165pub mod allocator;