1use std::ops::Deref;
4use std::sync::{Arc, OnceLock, Weak};
5
6use simdnbt::borrow::BaseNbtCompound as BorrowedNbtCompound;
7use simdnbt::owned::NbtCompound;
8use steel_registry::block_entity_type::BlockEntityTypeRef;
9use steel_registry::vanilla_block_entity_types;
10use steel_registry::{REGISTRY, RegistryEntry, RegistryExt};
11use steel_utils::{BlockPos, BlockStateId};
12
13use super::SharedBlockEntity;
14#[cfg_attr(
15 not(test),
16 expect(
17 clippy::wildcard_imports,
18 reason = "the registry intentionally imports every block entity implementation"
19 )
20)]
21use super::entities::*;
22use crate::world::World;
23
24pub type BlockEntityFactory = fn(Weak<World>, BlockPos, BlockStateId) -> SharedBlockEntity;
28
29struct BlockEntityEntry {
31 factory: Option<BlockEntityFactory>,
33}
34
35pub struct BlockEntityRegistry {
41 entries: Vec<BlockEntityEntry>,
42}
43
44impl BlockEntityRegistry {
45 #[must_use]
47 pub fn new() -> Self {
48 let count = REGISTRY.block_entity_types.len();
49 let entries = (0..count)
50 .map(|_| BlockEntityEntry { factory: None })
51 .collect();
52
53 Self { entries }
54 }
55
56 pub fn register(&mut self, block_entity_type: BlockEntityTypeRef, factory: BlockEntityFactory) {
58 let id = block_entity_type.id();
59 self.entries[id].factory = Some(factory);
60 }
61
62 #[must_use]
66 pub fn create(
67 &self,
68 block_entity_type: BlockEntityTypeRef,
69 level: Weak<World>,
70 pos: BlockPos,
71 state: BlockStateId,
72 ) -> Option<SharedBlockEntity> {
73 let id = block_entity_type.id();
74 self.entries.get(id)?.factory.map(|f| f(level, pos, state))
75 }
76
77 #[must_use]
83 pub fn create_or_unimplemented(
84 &self,
85 block_entity_type: BlockEntityTypeRef,
86 level: Weak<World>,
87 pos: BlockPos,
88 state: BlockStateId,
89 ) -> SharedBlockEntity {
90 let id = block_entity_type.id();
91 if let Some(factory) = self.entries.get(id).and_then(|entry| entry.factory) {
92 factory(level, pos, state)
93 } else {
94 Arc::new(UnimplementedBlockEntity::new(
95 block_entity_type,
96 level,
97 pos,
98 state,
99 ))
100 }
101 }
102
103 #[must_use]
105 pub fn create_and_load_or_unimplemented(
106 &self,
107 block_entity_type: BlockEntityTypeRef,
108 level: Weak<World>,
109 pos: BlockPos,
110 state: BlockStateId,
111 nbt: &BorrowedNbtCompound<'_>,
112 ) -> SharedBlockEntity {
113 let entity = self.create_or_unimplemented(block_entity_type, level, pos, state);
114 entity.load_with_components(nbt);
115 entity
116 }
117
118 #[must_use]
120 pub fn create_and_load_owned_or_unimplemented(
121 &self,
122 block_entity_type: BlockEntityTypeRef,
123 level: Weak<World>,
124 pos: BlockPos,
125 state: BlockStateId,
126 nbt: NbtCompound,
127 ) -> SharedBlockEntity {
128 let entity = self.create_or_unimplemented(block_entity_type, level, pos, state);
129 if let Err(error) = entity.load_with_owned_components(&nbt) {
130 log::warn!(
131 "failed to reborrow owned NBT for block entity {}: {error}",
132 block_entity_type.key()
133 );
134 }
135 entity
136 }
137
138 #[must_use]
140 pub fn has_factory(&self, block_entity_type: BlockEntityTypeRef) -> bool {
141 let id = block_entity_type.id();
142 self.entries.get(id).is_some_and(|e| e.factory.is_some())
143 }
144}
145
146impl Default for BlockEntityRegistry {
147 fn default() -> Self {
148 Self::new()
149 }
150}
151
152pub struct BlockEntityRegistryLock(OnceLock<BlockEntityRegistry>);
154
155impl Deref for BlockEntityRegistryLock {
156 type Target = BlockEntityRegistry;
157
158 fn deref(&self) -> &Self::Target {
159 self.0.get().expect("Block entity registry not initialized")
160 }
161}
162
163impl BlockEntityRegistryLock {
164 pub fn set(&self, registry: BlockEntityRegistry) -> Result<(), BlockEntityRegistry> {
166 self.0.set(registry)
167 }
168
169 fn get_or_init(&self, init: impl FnOnce() -> BlockEntityRegistry) -> &BlockEntityRegistry {
170 self.0.get_or_init(init)
171 }
172}
173
174pub static BLOCK_ENTITIES: BlockEntityRegistryLock = BlockEntityRegistryLock(OnceLock::new());
178
179pub fn init_block_entities() {
183 BLOCK_ENTITIES.get_or_init(|| {
184 let mut registry = BlockEntityRegistry::new();
185
186 registry.register(&vanilla_block_entity_types::SIGN, |level, pos, state| {
188 Arc::new(SignBlockEntity::new(level, pos, state))
189 });
190
191 registry.register(
193 &vanilla_block_entity_types::HANGING_SIGN,
194 |level, pos, state| Arc::new(SignBlockEntity::new_hanging(level, pos, state)),
195 );
196
197 registry.register(&vanilla_block_entity_types::BARREL, |level, pos, state| {
199 Arc::new(BarrelBlockEntity::new(level, pos, state))
200 });
201
202 registry.register(&vanilla_block_entity_types::BEACON, |level, pos, state| {
203 Arc::new(BeaconBlockEntity::new(level, pos, state))
204 });
205
206 registry.register(&vanilla_block_entity_types::CONDUIT, |level, pos, state| {
207 Arc::new(ConduitBlockEntity::new(level, pos, state))
208 });
209
210 registry.register(&vanilla_block_entity_types::FURNACE, |level, pos, state| {
211 Arc::new(FurnaceBlockEntity::new(level, pos, state))
212 });
213
214 registry.register(
215 &vanilla_block_entity_types::BLAST_FURNACE,
216 |level, pos, state| Arc::new(BlastFurnaceBlockEntity::new(level, pos, state)),
217 );
218
219 registry.register(&vanilla_block_entity_types::SMOKER, |level, pos, state| {
220 Arc::new(SmokerBlockEntity::new(level, pos, state))
221 });
222
223 registry.register(
224 &vanilla_block_entity_types::CAMPFIRE,
225 |level, pos, state| Arc::new(CampfireBlockEntity::new(level, pos, state)),
226 );
227
228 registry.register(
229 &vanilla_block_entity_types::CHISELED_BOOKSHELF,
230 |level, pos, state| Arc::new(ChiseledBookShelfBlockEntity::new(level, pos, state)),
231 );
232
233 registry.register(&vanilla_block_entity_types::JUKEBOX, |level, pos, state| {
235 Arc::new(JukeboxBlockEntity::new(level, pos, state))
236 });
237
238 registry.register(&vanilla_block_entity_types::BEEHIVE, |level, pos, state| {
240 Arc::new(BeehiveBlockEntity::new(level, pos, state))
241 });
242
243 registry.register(&vanilla_block_entity_types::BELL, |level, pos, state| {
244 Arc::new(BellBlockEntity::new(level, pos, state))
245 });
246
247 registry.register(
249 &vanilla_block_entity_types::COMPARATOR,
250 |level, pos, state| Arc::new(ComparatorBlockEntity::new(level, pos, state)),
251 );
252
253 registry.register(
254 &vanilla_block_entity_types::DAYLIGHT_DETECTOR,
255 |level, pos, state| Arc::new(DaylightDetectorBlockEntity::new(level, pos, state)),
256 );
257
258 registry.register(&vanilla_block_entity_types::PISTON, |level, pos, state| {
259 Arc::new(PistonMovingBlockEntity::new(level, pos, state))
260 });
261
262 registry.register(
263 &vanilla_block_entity_types::BRUSHABLE_BLOCK,
264 |level, pos, state| Arc::new(BrushableBlockEntity::new(level, pos, state)),
265 );
266
267 registry.register(
269 &vanilla_block_entity_types::END_GATEWAY,
270 |level, pos, state| Arc::new(EndGatewayBlockEntity::new(level, pos, state)),
271 );
272
273 registry.register(
275 &vanilla_block_entity_types::END_PORTAL,
276 |level, pos, state| Arc::new(EndPortalBlockEntity::new(level, pos, state)),
277 );
278
279 registry.register(
281 &vanilla_block_entity_types::ENDER_CHEST,
282 |level, pos, state| Arc::new(EnderChestBlockEntity::new(level, pos, state)),
283 );
284
285 registry.register(
287 &vanilla_block_entity_types::ENCHANTING_TABLE,
288 |level, pos, state| Arc::new(EnchantingTableBlockEntity::new(level, pos, state)),
289 );
290
291 registry.register(
293 &vanilla_block_entity_types::POTENT_SULFUR,
294 |level, pos, state| Arc::new(PotentSulfurBlockEntity::new(level, pos, state)),
295 );
296
297 registry.register(
298 &vanilla_block_entity_types::SHULKER_BOX,
299 |level, pos, state| Arc::new(ShulkerBoxBlockEntity::new(level, pos, state)),
300 );
301
302 registry.register(
303 &vanilla_block_entity_types::MOB_SPAWNER,
304 |level, pos, state| Arc::new(SpawnerBlockEntity::new(level, pos, state)),
305 );
306
307 registry.register(&vanilla_block_entity_types::SKULL, |level, pos, state| {
309 Arc::new(SkullBlockEntity::new(level, pos, state))
310 });
311
312 registry
313 });
314}