steel_registry/
block_entity_type.rs1use rustc_hash::FxHashMap;
2use steel_utils::Identifier;
3
4use crate::blocks::BlockRef;
5
6#[derive(Debug)]
10pub struct BlockEntityType {
11 pub key: Identifier,
12 pub valid_blocks: &'static [BlockRef],
14}
15
16impl BlockEntityType {
17 #[must_use]
18 pub fn is_valid(&self, block: BlockRef) -> bool {
19 self.valid_blocks
20 .iter()
21 .any(|valid_block| std::ptr::eq(*valid_block, block))
22 }
23}
24
25pub type BlockEntityTypeRef = &'static BlockEntityType;
26
27pub struct BlockEntityTypeRegistry {
28 block_entity_types_by_id: Vec<BlockEntityTypeRef>,
29 block_entity_types_by_key: FxHashMap<Identifier, usize>,
30 blocks_with_block_entities: Vec<bool>,
31 allows_registering: bool,
32}
33
34impl BlockEntityTypeRegistry {
35 #[must_use]
36 pub fn new() -> Self {
37 Self {
38 block_entity_types_by_id: Vec::new(),
39 block_entity_types_by_key: FxHashMap::default(),
40 blocks_with_block_entities: Vec::new(),
41 allows_registering: true,
42 }
43 }
44
45 pub fn register(&mut self, block_entity_type: BlockEntityTypeRef) -> usize {
46 assert!(
47 self.allows_registering,
48 "Cannot register block entity types after registry has been frozen"
49 );
50
51 let id = self.block_entity_types_by_id.len();
52 self.block_entity_types_by_id.push(block_entity_type);
53 self.block_entity_types_by_key
54 .insert(block_entity_type.key.clone(), id);
55
56 for block in block_entity_type.valid_blocks {
57 let Some(block_id) = block.id.get().copied() else {
58 panic!(
59 "block {} must be registered before block entity type {}",
60 block.key, block_entity_type.key
61 );
62 };
63 if self.blocks_with_block_entities.len() <= block_id {
64 self.blocks_with_block_entities.resize(block_id + 1, false);
65 }
66 self.blocks_with_block_entities[block_id] = true;
67 }
68
69 id
70 }
71
72 pub fn iter(&self) -> impl Iterator<Item = (usize, BlockEntityTypeRef)> + '_ {
73 self.block_entity_types_by_id.iter().copied().enumerate()
74 }
75
76 #[must_use]
81 pub fn has_block_entity(&self, block: BlockRef) -> bool {
82 let Some(block_id) = block.id.get().copied() else {
83 return false;
84 };
85 self.blocks_with_block_entities
86 .get(block_id)
87 .copied()
88 .unwrap_or(false)
89 }
90}
91
92impl Default for BlockEntityTypeRegistry {
93 fn default() -> Self {
94 Self::new()
95 }
96}
97
98crate::impl_registry!(
99 BlockEntityTypeRegistry,
100 BlockEntityType,
101 block_entity_types_by_id,
102 block_entity_types_by_key,
103 block_entity_types
104);
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::{init_vanilla_registry, vanilla_block_entity_types, vanilla_blocks};
110
111 #[test]
112 fn overlapping_type_memberships_preserve_structural_presence() {
113 init_vanilla_registry();
114 let valid_blocks = Box::leak(vec![&vanilla_blocks::BARREL].into_boxed_slice());
115 let alternate = Box::leak(Box::new(BlockEntityType {
116 key: Identifier::new_static("test", "alternate_barrel"),
117 valid_blocks,
118 }));
119 let mut registry = BlockEntityTypeRegistry::new();
120
121 registry.register(&vanilla_block_entity_types::BARREL);
122 registry.register(alternate);
123
124 assert!(registry.has_block_entity(&vanilla_blocks::BARREL));
125 assert_eq!(registry.iter().count(), 2);
126 }
127}