1use smallvec::SmallVec;
4use std::fmt;
5use std::sync::atomic::{AtomicU64, Ordering};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct FieldId(pub u32);
13
14impl fmt::Display for FieldId {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 write!(f, "{}", self.0)
17 }
18}
19
20impl From<u32> for FieldId {
21 fn from(v: u32) -> Self {
22 Self(v)
23 }
24}
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
28pub struct SpaceId(pub u32);
29
30impl fmt::Display for SpaceId {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 write!(f, "{}", self.0)
33 }
34}
35
36impl From<u32> for SpaceId {
37 fn from(v: u32) -> Self {
38 Self(v)
39 }
40}
41
42static SPACE_INSTANCE_COUNTER: AtomicU64 = AtomicU64::new(1);
44
45#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
56pub struct SpaceInstanceId(u64);
57
58impl SpaceInstanceId {
59 #[must_use]
64 pub fn next() -> Self {
65 Self(SPACE_INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed))
66 }
67}
68
69impl fmt::Display for SpaceInstanceId {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 write!(f, "{}", self.0)
72 }
73}
74
75const ENTITY_SLOT_BITS: u32 = 20;
76const ENTITY_SLOT_MASK: u32 = (1 << ENTITY_SLOT_BITS) - 1;
77const ENTITY_GEN_MAX: u32 = (1 << (32 - ENTITY_SLOT_BITS)) - 1;
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
85pub struct EntityId(u32);
86
87impl EntityId {
88 #[must_use]
94 pub fn new(slot: u32, generation: u32) -> Self {
95 assert!(
96 slot <= ENTITY_SLOT_MASK,
97 "slot {slot} exceeds maximum {ENTITY_SLOT_MASK}"
98 );
99 assert!(
100 generation <= ENTITY_GEN_MAX,
101 "generation {generation} exceeds maximum {ENTITY_GEN_MAX}"
102 );
103 Self((generation << ENTITY_SLOT_BITS) | slot)
104 }
105
106 #[inline]
108 #[must_use]
109 pub fn slot(self) -> u32 {
110 self.0 & ENTITY_SLOT_MASK
111 }
112
113 #[inline]
115 #[must_use]
116 pub fn generation(self) -> u32 {
117 self.0 >> ENTITY_SLOT_BITS
118 }
119
120 #[inline]
122 #[must_use]
123 pub fn as_u32(self) -> u32 {
124 self.0
125 }
126
127 #[inline]
129 #[must_use]
130 pub fn from_u32(raw: u32) -> Self {
131 Self(raw)
132 }
133}
134
135impl fmt::Display for EntityId {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 write!(f, "entity(slot={}, gen={})", self.slot(), self.generation())
138 }
139}
140
141#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
143pub struct PropertyIndex(pub u32);
144
145impl fmt::Display for PropertyIndex {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 write!(f, "{}", self.0)
148 }
149}
150
151impl From<u32> for PropertyIndex {
152 fn from(value: u32) -> Self {
153 Self(value)
154 }
155}
156
157#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
161pub struct TickId(pub u64);
162
163impl fmt::Display for TickId {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 write!(f, "{}", self.0)
166 }
167}
168
169impl From<u64> for TickId {
170 fn from(v: u64) -> Self {
171 Self(v)
172 }
173}
174
175#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
180pub struct WorldGenerationId(pub u64);
181
182impl fmt::Display for WorldGenerationId {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 write!(f, "{}", self.0)
185 }
186}
187
188impl From<u64> for WorldGenerationId {
189 fn from(v: u64) -> Self {
190 Self(v)
191 }
192}
193
194#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
199pub struct ParameterVersion(pub u64);
200
201impl fmt::Display for ParameterVersion {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 write!(f, "{}", self.0)
204 }
205}
206
207impl From<u64> for ParameterVersion {
208 fn from(v: u64) -> Self {
209 Self(v)
210 }
211}
212
213#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
218pub struct ParameterKey(pub u32);
219
220impl fmt::Display for ParameterKey {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 write!(f, "{}", self.0)
223 }
224}
225
226impl From<u32> for ParameterKey {
227 fn from(v: u32) -> Self {
228 Self(v)
229 }
230}
231
232pub type Coord = SmallVec<[i32; 4]>;
238
239#[cfg(test)]
240mod entity_id_tests {
241 use super::*;
242 use proptest::prelude::*;
243
244 #[test]
245 fn new_packs_slot_and_generation() {
246 let id = EntityId::new(42, 7);
247 assert_eq!(id.slot(), 42);
248 assert_eq!(id.generation(), 7);
249 }
250
251 #[test]
252 fn max_slot_value() {
253 let id = EntityId::new(1_048_575, 0);
254 assert_eq!(id.slot(), 1_048_575);
255 assert_eq!(id.generation(), 0);
256 }
257
258 #[test]
259 fn max_generation_value() {
260 let id = EntityId::new(0, 4095);
261 assert_eq!(id.slot(), 0);
262 assert_eq!(id.generation(), 4095);
263 }
264
265 #[test]
266 fn raw_round_trip_preserves_bits() {
267 let id = EntityId::new(1, 1);
268 let raw = id.as_u32();
269 assert_eq!(EntityId::from_u32(raw), id);
270 assert_eq!(raw, (1 << 20) | 1);
271 }
272
273 #[test]
274 #[should_panic(expected = "slot")]
275 fn slot_overflow_panics() {
276 let _ = EntityId::new(1_048_576, 0);
277 }
278
279 #[test]
280 #[should_panic(expected = "generation")]
281 fn generation_overflow_panics() {
282 let _ = EntityId::new(0, 4096);
283 }
284
285 #[test]
286 fn equality_requires_both_slot_and_generation() {
287 assert_ne!(EntityId::new(1, 0), EntityId::new(1, 1));
288 }
289
290 #[test]
291 fn property_index_displays_inner_value() {
292 assert_eq!(PropertyIndex::from(17).to_string(), "17");
293 }
294
295 proptest! {
296 #[test]
297 fn entity_id_round_trip_preserves_slot_generation(
298 slot in 0_u32..=1_048_575,
299 generation in 0_u32..=4_095,
300 ) {
301 let id = EntityId::new(slot, generation);
302
303 prop_assert_eq!(id.slot(), slot);
304 prop_assert_eq!(id.generation(), generation);
305 prop_assert_eq!(EntityId::from_u32(id.as_u32()), id);
306 }
307 }
308}