1use std::io::Cursor;
4use std::ops::Deref;
5use std::sync::{Arc, OnceLock, Weak};
6
7use simdnbt::borrow::NbtCompound as BorrowedRootNbtCompound;
8use simdnbt::borrow::{
9 BaseNbtCompound as BorrowedNbtCompound, read_compound as read_borrowed_compound,
10};
11use simdnbt::owned::NbtCompound;
12use steel_registry::block_entity_type::BlockEntityTypeRef;
13use steel_registry::vanilla_block_entity_types;
14use steel_registry::{REGISTRY, RegistryEntry, RegistryExt};
15use steel_utils::{BlockPos, BlockStateId};
16
17use super::SharedBlockEntity;
18#[cfg_attr(
19 not(test),
20 expect(
21 clippy::wildcard_imports,
22 reason = "the registry intentionally imports every block entity implementation"
23 )
24)]
25use super::entities::*;
26use crate::world::World;
27
28pub type BlockEntityFactory = fn(Weak<World>, BlockPos, BlockStateId) -> SharedBlockEntity;
32
33struct BlockEntityEntry {
35 factory: Option<BlockEntityFactory>,
37}
38
39pub struct BlockEntityRegistry {
45 entries: Vec<BlockEntityEntry>,
46}
47
48impl BlockEntityRegistry {
49 #[must_use]
51 pub fn new() -> Self {
52 let count = REGISTRY.block_entity_types.len();
53 let entries = (0..count)
54 .map(|_| BlockEntityEntry { factory: None })
55 .collect();
56
57 Self { entries }
58 }
59
60 pub fn register(&mut self, block_entity_type: BlockEntityTypeRef, factory: BlockEntityFactory) {
62 let id = block_entity_type.id();
63 self.entries[id].factory = Some(factory);
64 }
65
66 #[must_use]
70 pub fn create(
71 &self,
72 block_entity_type: BlockEntityTypeRef,
73 level: Weak<World>,
74 pos: BlockPos,
75 state: BlockStateId,
76 ) -> Option<SharedBlockEntity> {
77 let id = block_entity_type.id();
78 self.entries.get(id)?.factory.map(|f| f(level, pos, state))
79 }
80
81 #[must_use]
87 pub fn create_or_unimplemented(
88 &self,
89 block_entity_type: BlockEntityTypeRef,
90 level: Weak<World>,
91 pos: BlockPos,
92 state: BlockStateId,
93 ) -> SharedBlockEntity {
94 let id = block_entity_type.id();
95 if let Some(factory) = self.entries.get(id).and_then(|entry| entry.factory) {
96 factory(level, pos, state)
97 } else {
98 Arc::new(UnimplementedBlockEntity::new(
99 block_entity_type,
100 level,
101 pos,
102 state,
103 ))
104 }
105 }
106
107 #[must_use]
109 pub fn create_and_load_or_unimplemented(
110 &self,
111 block_entity_type: BlockEntityTypeRef,
112 level: Weak<World>,
113 pos: BlockPos,
114 state: BlockStateId,
115 nbt: &BorrowedNbtCompound<'_>,
116 ) -> SharedBlockEntity {
117 let id = block_entity_type.id();
118 if let Some(factory) = self.entries.get(id).and_then(|entry| entry.factory) {
119 let entity = factory(level, pos, state);
120 entity.load_additional(nbt);
121 entity
122 } else {
123 let nbt_view: BorrowedRootNbtCompound<'_, '_> = nbt.into();
124 Arc::new(UnimplementedBlockEntity::with_data(
125 block_entity_type,
126 level,
127 pos,
128 state,
129 nbt_view.to_owned(),
130 ))
131 }
132 }
133
134 #[must_use]
136 pub fn create_and_load_owned_or_unimplemented(
137 &self,
138 block_entity_type: BlockEntityTypeRef,
139 level: Weak<World>,
140 pos: BlockPos,
141 state: BlockStateId,
142 nbt: NbtCompound,
143 ) -> SharedBlockEntity {
144 let id = block_entity_type.id();
145 if let Some(factory) = self.entries.get(id).and_then(|entry| entry.factory) {
146 let entity = factory(level, pos, state);
147 let mut nbt_bytes = Vec::new();
148 nbt.write(&mut nbt_bytes);
149 if let Ok(borrowed) = read_borrowed_compound(&mut Cursor::new(&nbt_bytes)) {
150 entity.load_additional(&borrowed);
151 } else {
152 log::warn!(
153 "failed to reborrow owned NBT for block entity {}",
154 block_entity_type.key()
155 );
156 }
157 entity
158 } else {
159 Arc::new(UnimplementedBlockEntity::with_data(
160 block_entity_type,
161 level,
162 pos,
163 state,
164 nbt,
165 ))
166 }
167 }
168
169 #[must_use]
171 pub fn has_factory(&self, block_entity_type: BlockEntityTypeRef) -> bool {
172 let id = block_entity_type.id();
173 self.entries.get(id).is_some_and(|e| e.factory.is_some())
174 }
175}
176
177impl Default for BlockEntityRegistry {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183pub struct BlockEntityRegistryLock(OnceLock<BlockEntityRegistry>);
185
186impl Deref for BlockEntityRegistryLock {
187 type Target = BlockEntityRegistry;
188
189 fn deref(&self) -> &Self::Target {
190 self.0.get().expect("Block entity registry not initialized")
191 }
192}
193
194impl BlockEntityRegistryLock {
195 pub fn set(&self, registry: BlockEntityRegistry) -> Result<(), BlockEntityRegistry> {
197 self.0.set(registry)
198 }
199
200 fn get_or_init(&self, init: impl FnOnce() -> BlockEntityRegistry) -> &BlockEntityRegistry {
201 self.0.get_or_init(init)
202 }
203}
204
205pub static BLOCK_ENTITIES: BlockEntityRegistryLock = BlockEntityRegistryLock(OnceLock::new());
209
210pub fn init_block_entities() {
214 BLOCK_ENTITIES.get_or_init(|| {
215 let mut registry = BlockEntityRegistry::new();
216
217 registry.register(&vanilla_block_entity_types::SIGN, |level, pos, state| {
219 Arc::new(SignBlockEntity::new(level, pos, state))
220 });
221
222 registry.register(
224 &vanilla_block_entity_types::HANGING_SIGN,
225 |level, pos, state| Arc::new(SignBlockEntity::new_hanging(level, pos, state)),
226 );
227
228 registry.register(&vanilla_block_entity_types::BARREL, |level, pos, state| {
230 Arc::new(BarrelBlockEntity::new(level, pos, state))
231 });
232
233 registry.register(&vanilla_block_entity_types::BEACON, |level, pos, state| {
234 Arc::new(BeaconBlockEntity::new(level, pos, state))
235 });
236
237 registry.register(&vanilla_block_entity_types::FURNACE, |level, pos, state| {
238 Arc::new(FurnaceBlockEntity::new(level, pos, state))
239 });
240
241 registry.register(
242 &vanilla_block_entity_types::BLAST_FURNACE,
243 |level, pos, state| Arc::new(BlastFurnaceBlockEntity::new(level, pos, state)),
244 );
245
246 registry.register(&vanilla_block_entity_types::SMOKER, |level, pos, state| {
247 Arc::new(SmokerBlockEntity::new(level, pos, state))
248 });
249
250 registry.register(
251 &vanilla_block_entity_types::CAMPFIRE,
252 |level, pos, state| Arc::new(CampfireBlockEntity::new(level, pos, state)),
253 );
254
255 registry.register(
256 &vanilla_block_entity_types::CHISELED_BOOKSHELF,
257 |level, pos, state| Arc::new(ChiseledBookShelfBlockEntity::new(level, pos, state)),
258 );
259
260 registry.register(&vanilla_block_entity_types::JUKEBOX, |level, pos, state| {
262 Arc::new(JukeboxBlockEntity::new(level, pos, state))
263 });
264
265 registry.register(&vanilla_block_entity_types::BEEHIVE, |level, pos, state| {
267 Arc::new(BeehiveBlockEntity::new(level, pos, state))
268 });
269
270 registry.register(&vanilla_block_entity_types::BELL, |level, pos, state| {
271 Arc::new(BellBlockEntity::new(level, pos, state))
272 });
273
274 registry.register(
276 &vanilla_block_entity_types::COMPARATOR,
277 |level, pos, state| Arc::new(ComparatorBlockEntity::new(level, pos, state)),
278 );
279
280 registry.register(
281 &vanilla_block_entity_types::DAYLIGHT_DETECTOR,
282 |level, pos, state| Arc::new(DaylightDetectorBlockEntity::new(level, pos, state)),
283 );
284
285 registry.register(&vanilla_block_entity_types::PISTON, |level, pos, state| {
286 Arc::new(PistonMovingBlockEntity::new(level, pos, state))
287 });
288
289 registry.register(
290 &vanilla_block_entity_types::BRUSHABLE_BLOCK,
291 |level, pos, state| Arc::new(BrushableBlockEntity::new(level, pos, state)),
292 );
293
294 registry.register(
296 &vanilla_block_entity_types::END_GATEWAY,
297 |level, pos, state| Arc::new(EndGatewayBlockEntity::new(level, pos, state)),
298 );
299
300 registry.register(
302 &vanilla_block_entity_types::END_PORTAL,
303 |level, pos, state| Arc::new(EndPortalBlockEntity::new(level, pos, state)),
304 );
305
306 registry.register(
308 &vanilla_block_entity_types::ENDER_CHEST,
309 |level, pos, state| Arc::new(EnderChestBlockEntity::new(level, pos, state)),
310 );
311
312 registry.register(
314 &vanilla_block_entity_types::POTENT_SULFUR,
315 |level, pos, state| Arc::new(PotentSulfurBlockEntity::new(level, pos, state)),
316 );
317
318 registry.register(
319 &vanilla_block_entity_types::SHULKER_BOX,
320 |level, pos, state| Arc::new(ShulkerBoxBlockEntity::new(level, pos, state)),
321 );
322
323 registry
324 });
325}