1pub mod holder;
4pub mod holder_set;
5pub mod reference;
6mod tags;
7
8pub use holder::{RegistryHolder, RegistryHolderEntry};
9pub use holder_set::{RegistryHolderSet, RegistryHolderSetEntry};
10pub use reference::{RegistryReference, RegistryReferenceEntry};
11pub use tags::RegistryTags;
12
13use crate::game_events::GameEventRegistry;
14use crate::stat::custom::CustomStatRegistry;
15use crate::stat::{StatTypeRegistry, vanilla_stat_types};
16use crate::ticket_type::TicketTypeRegistry;
17use crate::world_clock::WorldClockRegistry;
18use crate::{
19 attribute::AttributeRegistry,
20 banner_pattern::BannerPatternRegistry,
21 biome::BiomeRegistry,
22 block_entity_type::BlockEntityTypeRegistry,
23 blocks::BlockRegistry,
24 carver::ConfiguredCarverRegistry,
25 cat_sound_variant::CatSoundVariantRegistry,
26 cat_variant::CatVariantRegistry,
27 chat_type::ChatTypeRegistry,
28 chicken_sound_variant::ChickenSoundVariantRegistry,
29 chicken_variant::ChickenVariantRegistry,
30 consume_effect::{self, ConsumeEffectTypeRegistry},
31 cow_sound_variant::CowSoundVariantRegistry,
32 cow_variant::CowVariantRegistry,
33 damage_type::DamageTypeRegistry,
34 data_component_predicate::{self, DataComponentPredicateTypeRegistry},
35 data_components::{DataComponentRegistry, vanilla_components},
36 dialog::DialogRegistry,
37 dimension_type::DimensionTypeRegistry,
38 enchantment::EnchantmentRegistry,
39 entity_data::{EntityDataSerializerRegistry, register_vanilla_entity_data_serializers},
40 entity_type::EntityTypeRegistry,
41 feature::{
42 ConfiguredFeatureKind, ConfiguredFeatureRef, ConfiguredFeatureRegistry, PlacedFeatureData,
43 PlacedFeatureRef, PlacedFeatureRegistry,
44 },
45 fluid::FluidRegistry,
46 frog_variant::FrogVariantRegistry,
47 game_rules::GameRuleRegistry,
48 instrument::InstrumentRegistry,
49 items::ItemRegistry,
50 jukebox_song::JukeboxSongRegistry,
51 loot_table::LootTableRegistry,
52 map_decoration_type::MapDecorationTypeRegistry,
53 menu_type::MenuTypeRegistry,
54 mob_effect::MobEffectRegistry,
55 painting_variant::PaintingVariantRegistry,
56 particle_type::ParticleTypeRegistry,
57 pig_sound_variant::PigSoundVariantRegistry,
58 pig_variant::PigVariantRegistry,
59 poi::PoiTypeRegistry,
60 position_source::PositionSourceTypeRegistry,
61 potion::PotionRegistry,
62 recipe::{
63 RecipeBookCategoryRegistry, RecipeRegistry, RecipeTypeRegistry,
64 vanilla_recipe_book_categories, vanilla_recipe_types,
65 },
66 sound_event::SoundEventRegistry,
67 sound_events, steel_ticket_types,
68 structure::StructureRegistry,
69 structure_processor::StructureProcessorListRegistry,
70 template_pool,
71 timeline::TimelineRegistry,
72 trim_material::TrimMaterialRegistry,
73 trim_pattern::TrimPatternRegistry,
74 vanilla_attributes, vanilla_banner_pattern_tags, vanilla_banner_patterns, vanilla_biome_tags,
75 vanilla_biomes, vanilla_block_entity_types, vanilla_block_tags, vanilla_blocks,
76 vanilla_cat_sound_variants, vanilla_cat_variants, vanilla_chat_types,
77 vanilla_chicken_sound_variants, vanilla_chicken_variants, vanilla_configured_carvers,
78 vanilla_configured_features, vanilla_cow_sound_variants, vanilla_cow_variants,
79 vanilla_custom_stats, vanilla_damage_type_tags, vanilla_damage_types, vanilla_dialog_tags,
80 vanilla_dialogs, vanilla_dimension_types, vanilla_enchantment_tags, vanilla_enchantments,
81 vanilla_entities, vanilla_entity_type_tags, vanilla_fluid_tags, vanilla_fluids,
82 vanilla_frog_variants, vanilla_game_events, vanilla_game_rules, vanilla_instrument_tags,
83 vanilla_instruments, vanilla_item_tags, vanilla_items, vanilla_jukebox_songs,
84 vanilla_loot_tables, vanilla_map_decoration_types, vanilla_menu_types, vanilla_mob_effects,
85 vanilla_painting_variant_tags, vanilla_painting_variants, vanilla_particle_types,
86 vanilla_pig_sound_variants, vanilla_pig_variants, vanilla_placed_features,
87 vanilla_poi_type_tags, vanilla_poi_types, vanilla_position_source_types, vanilla_potion_tags,
88 vanilla_potions, vanilla_recipes, vanilla_structure_processors, vanilla_structure_tags,
89 vanilla_structures, vanilla_template_pools, vanilla_ticket_types, vanilla_timeline_tags,
90 vanilla_timelines, vanilla_trim_materials, vanilla_trim_patterns, vanilla_villager_professions,
91 vanilla_villager_types, vanilla_wolf_sound_variants, vanilla_wolf_variants,
92 vanilla_world_clocks, vanilla_zombie_nautilus_variants,
93 villager_profession::VillagerProfessionRegistry,
94 villager_type::VillagerTypeRegistry,
95 wolf_sound_variant::WolfSoundVariantRegistry,
96 wolf_variant::WolfVariantRegistry,
97 zombie_nautilus_variant::ZombieNautilusVariantRegistry,
98};
99use std::{
100 fmt::Debug,
101 ops::Deref,
102 sync::{Once, OnceLock},
103};
104use steel_utils::Identifier;
105
106pub struct RegistryLock(OnceLock<Registry>);
107
108impl RegistryLock {
109 #[expect(clippy::result_large_err)]
110 pub fn init(&self, value: Registry) -> Result<(), Registry> {
111 self.0.set(value)
112 }
113}
114
115pub fn init_vanilla_registry() -> bool {
117 static INIT: Once = Once::new();
118
119 let mut published = false;
120 INIT.call_once(|| {
121 let mut registry = Registry::new_vanilla();
122 registry.freeze();
123 published = REGISTRY.init(registry).is_ok();
124 });
125 published
126}
127
128impl Deref for RegistryLock {
129 type Target = Registry;
130
131 fn deref(&self) -> &Self::Target {
132 self.0.get().expect("Registry not init")
133 }
134}
135
136pub static REGISTRY: RegistryLock = RegistryLock(OnceLock::new());
137
138pub trait RegistryEntry: PartialEq + 'static {
140 fn key(&self) -> &Identifier;
141 fn try_id(&self) -> Option<usize>;
142
143 fn id(&self) -> usize {
146 self.try_id().expect("entry not found in registry")
147 }
148}
149
150pub trait RegistryExt {
155 type Entry: RegistryEntry;
156
157 fn freeze(&mut self);
158 fn by_id(&self, id: usize) -> Option<&'static Self::Entry>;
159 fn by_key(&self, key: &Identifier) -> Option<&'static Self::Entry>;
160 fn id_from_key(&self, key: &Identifier) -> Option<usize>;
161 fn len(&self) -> usize;
162 fn is_empty(&self) -> bool;
163}
164
165pub trait TaggedRegistryExt: RegistryExt {
167 fn register_tag(&mut self, tag: Identifier, keys: &[&'static str]);
168 fn modify_tag(&mut self, tag: &Identifier, f: impl FnOnce(Vec<Identifier>) -> Vec<Identifier>);
169 fn is_in_tag(&self, entry: &'static Self::Entry, tag: &Identifier) -> bool;
170 fn get_tag(&self, tag: &Identifier) -> Option<Vec<&'static Self::Entry>>;
171 fn iter_tag(&self, tag: &Identifier) -> impl Iterator<Item = &'static Self::Entry> + '_;
172 fn tag_keys(&self) -> impl Iterator<Item = &Identifier> + '_;
173}
174
175pub const BLOCKS_REGISTRY: Identifier = Identifier::vanilla_static("block");
176pub const ITEMS_REGISTRY: Identifier = Identifier::vanilla_static("item");
177pub const BIOMES_REGISTRY: Identifier = Identifier::vanilla_static("worldgen/biome");
178pub const CHAT_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("chat_type");
179pub const TRIM_PATTERN_REGISTRY: Identifier = Identifier::vanilla_static("trim_pattern");
180pub const TRIM_MATERIAL_REGISTRY: Identifier = Identifier::vanilla_static("trim_material");
181pub const WOLF_VARIANT_REGISTRY: Identifier = Identifier::vanilla_static("wolf_variant");
182pub const WOLF_SOUND_VARIANT_REGISTRY: Identifier =
183 Identifier::vanilla_static("wolf_sound_variant");
184pub const PIG_VARIANT_REGISTRY: Identifier = Identifier::vanilla_static("pig_variant");
185pub const PIG_SOUND_VARIANT_REGISTRY: Identifier = Identifier::vanilla_static("pig_sound_variant");
186pub const CHICKEN_SOUND_VARIANT_REGISTRY: Identifier =
187 Identifier::vanilla_static("chicken_sound_variant");
188pub const CAT_SOUND_VARIANT_REGISTRY: Identifier = Identifier::vanilla_static("cat_sound_variant");
189pub const COW_SOUND_VARIANT_REGISTRY: Identifier = Identifier::vanilla_static("cow_sound_variant");
190pub const FROG_VARIANT_REGISTRY: Identifier = Identifier::vanilla_static("frog_variant");
191pub const CAT_VARIANT_REGISTRY: Identifier = Identifier::vanilla_static("cat_variant");
192pub const COW_VARIANT_REGISTRY: Identifier = Identifier::vanilla_static("cow_variant");
193pub const CHICKEN_VARIANT_REGISTRY: Identifier = Identifier::vanilla_static("chicken_variant");
194pub const PAINTING_VARIANT_REGISTRY: Identifier = Identifier::vanilla_static("painting_variant");
195pub const PARTICLE_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("particle_type");
196pub const POSITION_SOURCE_TYPE_REGISTRY: Identifier =
197 Identifier::vanilla_static("position_source_type");
198pub const VILLAGER_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("villager_type");
199pub const VILLAGER_PROFESSION_REGISTRY: Identifier =
200 Identifier::vanilla_static("villager_profession");
201pub const DIMENSION_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("dimension_type");
202pub const DAMAGE_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("damage_type");
203pub const BANNER_PATTERN_REGISTRY: Identifier = Identifier::vanilla_static("banner_pattern");
204pub const ENCHANTMENT_REGISTRY: Identifier = Identifier::vanilla_static("enchantment");
205pub const JUKEBOX_SONG_REGISTRY: Identifier = Identifier::vanilla_static("jukebox_song");
206pub const INSTRUMENT_REGISTRY: Identifier = Identifier::vanilla_static("instrument");
207pub const DIALOG_REGISTRY: Identifier = Identifier::vanilla_static("dialog");
208pub const MENU_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("menu");
209pub const MOB_EFFECT_REGISTRY: Identifier = Identifier::vanilla_static("mob_effect");
210pub const MAP_DECORATION_TYPE_REGISTRY: Identifier =
211 Identifier::vanilla_static("map_decoration_type");
212pub const ZOMBIE_NAUTILUS_VARIANT_REGISTRY: Identifier =
213 Identifier::vanilla_static("zombie_nautilus_variant");
214pub const TIMELINE_REGISTRY: Identifier = Identifier::vanilla_static("timeline");
215pub const LOOT_TABLE_REGISTRY: Identifier = Identifier::vanilla_static("loot_table");
216pub const BLOCK_ENTITY_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("block_entity_type");
217pub const FLUID_REGISTRY: Identifier = Identifier::vanilla_static("fluid");
218pub const ENTITY_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("entity_type");
219pub const SOUND_EVENT_REGISTRY: Identifier = Identifier::vanilla_static("sound_event");
220pub const POI_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("point_of_interest_type");
221pub const WORLD_CLOCK_REGISTRY: Identifier = Identifier::vanilla_static("world_clock");
222pub const CONFIGURED_CARVER_REGISTRY: Identifier =
223 Identifier::vanilla_static("worldgen/configured_carver");
224pub const CONFIGURED_FEATURE_REGISTRY: Identifier =
225 Identifier::vanilla_static("worldgen/configured_feature");
226pub const PLACED_FEATURE_REGISTRY: Identifier =
227 Identifier::vanilla_static("worldgen/placed_feature");
228pub const STRUCTURE_REGISTRY: Identifier = Identifier::vanilla_static("worldgen/structure");
229pub const STRUCTURE_PROCESSOR_LIST_REGISTRY: Identifier =
230 Identifier::vanilla_static("worldgen/processor_list");
231pub const CUSTOM_STAT_REGISTRY: Identifier = Identifier::vanilla_static("custom_stat");
232pub const STAT_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("stat_type");
233pub const TICKET_TYPE_REGISTRY: Identifier = Identifier::vanilla_static("ticket_type");
234
235pub struct Registry {
236 pub attributes: AttributeRegistry,
237 pub blocks: BlockRegistry,
238 pub items: ItemRegistry,
239 pub data_components: DataComponentRegistry,
240 pub data_component_predicate_types: DataComponentPredicateTypeRegistry,
241 pub consume_effect_types: ConsumeEffectTypeRegistry,
242 pub entity_data_serializers: EntityDataSerializerRegistry,
243 pub biomes: BiomeRegistry,
244 pub chat_types: ChatTypeRegistry,
245 pub trim_patterns: TrimPatternRegistry,
246 pub trim_materials: TrimMaterialRegistry,
247 pub wolf_variants: WolfVariantRegistry,
248 pub wolf_sound_variants: WolfSoundVariantRegistry,
249 pub pig_sound_variants: PigSoundVariantRegistry,
250 pub chicken_sound_variants: ChickenSoundVariantRegistry,
251 pub cat_sound_variants: CatSoundVariantRegistry,
252 pub cow_sound_variants: CowSoundVariantRegistry,
253 pub pig_variants: PigVariantRegistry,
254 pub frog_variants: FrogVariantRegistry,
255 pub cat_variants: CatVariantRegistry,
256 pub cow_variants: CowVariantRegistry,
257 pub chicken_variants: ChickenVariantRegistry,
258 pub painting_variants: PaintingVariantRegistry,
259 pub particle_types: ParticleTypeRegistry,
260 pub position_source_types: PositionSourceTypeRegistry,
261 pub villager_types: VillagerTypeRegistry,
262 pub villager_professions: VillagerProfessionRegistry,
263 pub dimension_types: DimensionTypeRegistry,
264 pub damage_types: DamageTypeRegistry,
265 pub banner_patterns: BannerPatternRegistry,
266 pub jukebox_songs: JukeboxSongRegistry,
267 pub instruments: InstrumentRegistry,
268 pub dialogs: DialogRegistry,
269 pub menu_types: MenuTypeRegistry,
270 pub mob_effects: MobEffectRegistry,
271 pub map_decoration_types: MapDecorationTypeRegistry,
272 pub potions: PotionRegistry,
273 pub zombie_nautilus_variants: ZombieNautilusVariantRegistry,
274 pub timelines: TimelineRegistry,
275 pub recipe_types: RecipeTypeRegistry,
276 pub recipe_book_categories: RecipeBookCategoryRegistry,
277 pub recipes: RecipeRegistry,
278 pub entity_types: EntityTypeRegistry,
279 pub loot_tables: LootTableRegistry,
280 pub block_entity_types: BlockEntityTypeRegistry,
281 pub game_rules: GameRuleRegistry,
282 pub game_events: GameEventRegistry,
283 pub sound_events: SoundEventRegistry,
284 pub fluids: FluidRegistry,
285 pub poi_types: PoiTypeRegistry,
286 pub enchantments: EnchantmentRegistry,
287 pub world_clocks: WorldClockRegistry,
288 pub configured_carvers: ConfiguredCarverRegistry,
289 pub configured_features: ConfiguredFeatureRegistry,
290 pub placed_features: PlacedFeatureRegistry,
291 pub structures: StructureRegistry,
292 pub structure_processors: StructureProcessorListRegistry,
293 pub custom_stats: CustomStatRegistry,
294 pub stat_types: StatTypeRegistry,
295 pub ticket_types: TicketTypeRegistry,
296}
297
298impl Debug for Registry {
299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300 f.write_str("Registry {")
301 .and_then(|()| f.write_fmt(format_args!("Blocks Loaded: {}", self.blocks.len())))
302 .and_then(|()| f.write_str("}"))
303 }
304}
305
306impl Registry {
307 #[must_use]
308 pub fn new_vanilla() -> Self {
309 let mut registry = Self::new_empty();
310
311 vanilla_attributes::register_attributes(&mut registry.attributes);
312
313 vanilla_blocks::register_blocks(&mut registry.blocks);
314 vanilla_block_tags::BlockTag::register_block_tags(&mut registry.blocks);
315
316 vanilla_components::register_vanilla_data_components(&mut registry.data_components);
317
318 data_component_predicate::vanilla_data_component_predicate_types::register_data_component_predicate_types(
319 &mut registry.data_component_predicate_types,
320 );
321
322 consume_effect::vanilla_consume_effect_types::register_consume_effect_types(
323 &mut registry.consume_effect_types,
324 );
325
326 register_vanilla_entity_data_serializers(&mut registry.entity_data_serializers);
327
328 vanilla_items::register_items(&mut registry.items);
329 vanilla_item_tags::ItemTag::register_item_tags(&mut registry.items);
330
331 vanilla_biomes::register_biomes(&mut registry.biomes);
332 vanilla_biome_tags::BiomeTag::register_biome_tags(&mut registry.biomes);
333 vanilla_chat_types::register_chat_types(&mut registry.chat_types);
334 vanilla_trim_patterns::register_trim_patterns(&mut registry.trim_patterns);
335 vanilla_trim_materials::register_trim_materials(&mut registry.trim_materials);
336 vanilla_wolf_variants::register_wolf_variants(&mut registry.wolf_variants);
337 vanilla_wolf_sound_variants::register_wolf_sound_variants(
338 &mut registry.wolf_sound_variants,
339 );
340 vanilla_pig_variants::register_pig_variants(&mut registry.pig_variants);
341 vanilla_pig_sound_variants::register_pig_sound_variants(&mut registry.pig_sound_variants);
342 vanilla_chicken_sound_variants::register_chicken_sound_variants(
343 &mut registry.chicken_sound_variants,
344 );
345 vanilla_cat_sound_variants::register_cat_sound_variants(&mut registry.cat_sound_variants);
346 vanilla_cow_sound_variants::register_cow_sound_variants(&mut registry.cow_sound_variants);
347 vanilla_frog_variants::register_frog_variants(&mut registry.frog_variants);
348 vanilla_cat_variants::register_cat_variants(&mut registry.cat_variants);
349 vanilla_cow_variants::register_cow_variants(&mut registry.cow_variants);
350 vanilla_chicken_variants::register_chicken_variants(&mut registry.chicken_variants);
351 vanilla_painting_variants::register_painting_variants(&mut registry.painting_variants);
352 vanilla_particle_types::register_particle_types(&mut registry.particle_types);
353 vanilla_position_source_types::register_position_source_types(
354 &mut registry.position_source_types,
355 );
356 vanilla_villager_types::register_villager_types(&mut registry.villager_types);
357 vanilla_villager_professions::register_villager_professions(
358 &mut registry.villager_professions,
359 );
360 vanilla_painting_variant_tags::PaintingVariantTag::register_painting_variant_tags(
361 &mut registry.painting_variants,
362 );
363 vanilla_dimension_types::register_dimension_types(&mut registry.dimension_types);
364 vanilla_damage_types::register_damage_types(&mut registry.damage_types);
365 vanilla_damage_type_tags::DamageTypeTag::register_damage_type_tags(
366 &mut registry.damage_types,
367 );
368 vanilla_banner_patterns::register_banner_patterns(&mut registry.banner_patterns);
369 vanilla_banner_pattern_tags::BannerPatternTag::register_banner_pattern_tags(
370 &mut registry.banner_patterns,
371 );
372 vanilla_jukebox_songs::register_jukebox_songs(&mut registry.jukebox_songs);
373 vanilla_instruments::register_instruments(&mut registry.instruments);
374 vanilla_instrument_tags::InstrumentTag::register_instrument_tags(&mut registry.instruments);
375 vanilla_dialogs::register_dialogs(&mut registry.dialogs);
376 vanilla_dialog_tags::DialogTag::register_dialog_tags(&mut registry.dialogs);
377 vanilla_menu_types::register_menu_types(&mut registry.menu_types);
378 vanilla_mob_effects::register_mob_effects(&mut registry.mob_effects);
379 vanilla_map_decoration_types::register_map_decoration_types(
380 &mut registry.map_decoration_types,
381 );
382 vanilla_potions::register_potions(&mut registry.potions);
383 vanilla_potion_tags::PotionTag::register_potion_tags(&mut registry.potions);
384 vanilla_zombie_nautilus_variants::register_zombie_nautilus_variants(
385 &mut registry.zombie_nautilus_variants,
386 );
387 vanilla_timelines::register_timelines(&mut registry.timelines);
388 vanilla_timeline_tags::TimelineTag::register_timeline_tags(&mut registry.timelines);
389 vanilla_recipe_types::register(&mut registry.recipe_types);
390 vanilla_recipe_book_categories::register(&mut registry.recipe_book_categories);
391 vanilla_recipes::register_recipes(&mut registry.recipes);
392 vanilla_entities::register_entity_types(&mut registry.entity_types);
393 vanilla_entity_type_tags::EntityTypeTag::register_entity_type_tags(
394 &mut registry.entity_types,
395 );
396 vanilla_loot_tables::register_loot_tables(&mut registry.loot_tables);
397 vanilla_block_entity_types::register_block_entity_types(&mut registry.block_entity_types);
398 vanilla_game_rules::register_game_rules(&mut registry.game_rules);
399 vanilla_game_events::register_game_events(&mut registry.game_events);
400 sound_events::register_sound_events(&mut registry.sound_events);
401
402 vanilla_fluids::register_fluids(&mut registry.fluids);
403 vanilla_fluid_tags::FluidTag::register_fluid_tags(&mut registry.fluids);
404
405 vanilla_poi_types::register_poi_types(&mut registry.poi_types);
406 vanilla_poi_type_tags::PoiTag::register_poi_tags(&mut registry.poi_types);
407 registry.poi_types.build_state_index(®istry.blocks);
408
409 vanilla_enchantments::register_enchantments(&mut registry.enchantments);
410 vanilla_enchantment_tags::EnchantmentTag::register_enchantment_tags(
411 &mut registry.enchantments,
412 );
413
414 vanilla_world_clocks::register_world_clocks(&mut registry.world_clocks);
415 vanilla_structures::register_structures(&mut registry.structures);
416 vanilla_structure_tags::StructureTag::register_structure_tags(&mut registry.structures);
417 vanilla_structure_processors::register_structure_processor_lists(
418 &mut registry.structure_processors,
419 );
420
421 vanilla_configured_carvers::register_configured_carvers(&mut registry.configured_carvers);
422 vanilla_configured_features::register_configured_features(
423 &mut registry.configured_features,
424 );
425 vanilla_placed_features::register_placed_features(&mut registry.placed_features);
426
427 vanilla_custom_stats::register_custom_stats(&mut registry.custom_stats);
428 vanilla_stat_types::register_vanilla_stat_types(&mut registry.stat_types);
429
430 vanilla_ticket_types::register_vanilla_ticket_types(&mut registry.ticket_types);
431 steel_ticket_types::register_steel_ticket_types(&mut registry.ticket_types);
432
433 registry
434 }
435
436 pub fn freeze(&mut self) {
437 self.validate_references();
438
439 self.attributes.freeze();
440 self.blocks.freeze();
441 self.data_components.freeze();
442 self.data_component_predicate_types.freeze();
443 self.consume_effect_types.freeze();
444 self.entity_data_serializers.freeze();
445 self.items.freeze();
446 self.biomes.freeze();
447 self.chat_types.freeze();
448 self.trim_patterns.freeze();
449 self.trim_materials.freeze();
450 self.wolf_variants.freeze();
451 self.wolf_sound_variants.freeze();
452 self.pig_variants.freeze();
453 self.pig_sound_variants.freeze();
454 self.chicken_sound_variants.freeze();
455 self.cat_sound_variants.freeze();
456 self.cow_sound_variants.freeze();
457 self.frog_variants.freeze();
458 self.cat_variants.freeze();
459 self.cow_variants.freeze();
460 self.chicken_variants.freeze();
461 self.painting_variants.freeze();
462 self.particle_types.freeze();
463 self.position_source_types.freeze();
464 self.villager_types.freeze();
465 self.villager_professions.freeze();
466 self.dimension_types.freeze();
467 self.damage_types.freeze();
468 self.banner_patterns.freeze();
469 self.jukebox_songs.freeze();
470 self.instruments.freeze();
471 self.dialogs.freeze();
472 self.menu_types.freeze();
473 self.mob_effects.freeze();
474 self.map_decoration_types.freeze();
475 self.potions.freeze();
476 self.zombie_nautilus_variants.freeze();
477 self.timelines.freeze();
478 self.recipe_types.freeze();
479 self.recipe_book_categories.freeze();
480 self.recipes.freeze(&self.recipe_types);
481 self.entity_types.freeze();
482 self.loot_tables.freeze();
483 self.block_entity_types.freeze();
484 self.game_rules.freeze();
485 self.game_events.freeze();
486 self.sound_events.freeze();
487 self.fluids.freeze();
488 self.poi_types.freeze();
489 self.enchantments.freeze();
490 self.world_clocks.freeze();
491 self.configured_carvers.freeze();
492 self.configured_features.freeze();
493 self.placed_features.freeze();
494 self.structures.freeze();
495 self.structure_processors.freeze();
496 self.custom_stats.freeze();
497 self.stat_types.freeze();
498 self.ticket_types.freeze();
499 }
500
501 fn validate_references(&self) {
502 let mut time_markers = rustc_hash::FxHashSet::default();
503 for (_, timeline) in self.timelines.iter() {
504 assert!(
505 self.world_clocks.by_key(&timeline.clock.key).is_some(),
506 "timeline {} references unknown world clock {}",
507 timeline.key,
508 timeline.clock.key
509 );
510 if let Some(period_ticks) = timeline.period_ticks {
511 assert!(
512 period_ticks > 0,
513 "timeline {} has invalid period_ticks {}",
514 timeline.key,
515 period_ticks
516 );
517 }
518 for marker in timeline.time_markers {
519 assert!(
520 marker.ticks >= 0,
521 "time marker {} has invalid tick {}",
522 marker.key,
523 marker.ticks
524 );
525 if let Some(period_ticks) = timeline.period_ticks {
526 assert!(
527 marker.ticks < period_ticks,
528 "time marker {} tick {} is outside timeline {} period {}",
529 marker.key,
530 marker.ticks,
531 timeline.key,
532 period_ticks
533 );
534 }
535 assert!(
536 time_markers.insert((timeline.clock.key.clone(), marker.key.clone())),
537 "time marker {} is defined multiple times for world clock {}",
538 marker.key,
539 timeline.clock.key
540 );
541 }
542 }
543
544 for (_, dimension_type) in self.dimension_types.iter() {
545 if let Some(clock) = dimension_type.default_clock {
546 assert!(
547 self.world_clocks.by_key(&clock.key).is_some(),
548 "dimension type {} references unknown default world clock {}",
549 dimension_type.key,
550 clock.key
551 );
552 }
553 }
554
555 for (_, biome) in self.biomes.iter() {
556 for carver_key in &biome.carvers {
557 assert!(
558 self.configured_carvers.by_key(carver_key).is_some(),
559 "biome {} references unknown configured carver {}",
560 biome.key,
561 carver_key
562 );
563 }
564
565 for feature_stage in &biome.features {
566 for placed_feature_key in feature_stage {
567 assert!(
568 self.placed_features.by_key(placed_feature_key).is_some(),
569 "biome {} references unknown placed feature {}",
570 biome.key,
571 placed_feature_key
572 );
573 }
574 }
575 }
576
577 for (_, placed_feature) in self.placed_features.iter() {
578 self.validate_placed_feature_data(&placed_feature.data);
579 }
580
581 for (_, configured_feature) in self.configured_features.iter() {
582 self.validate_configured_feature_kind(&configured_feature.kind);
583 }
584
585 if !self.placed_features.is_empty() {
586 for pool in vanilla_template_pools::vanilla_template_pools() {
587 for (element, _) in &pool.elements {
588 self.validate_template_pool_feature_refs(element);
589 }
590 }
591 }
592 }
593
594 fn validate_placed_feature_ref(&self, feature: &PlacedFeatureRef) {
595 match feature {
596 PlacedFeatureRef::Reference(feature) => {
597 let key = &feature.key;
598 assert!(
599 self.placed_features.by_key(key).is_some(),
600 "unknown placed feature reference {key}"
601 );
602 }
603 PlacedFeatureRef::Inline(data) => self.validate_placed_feature_data(data),
604 }
605 }
606
607 fn validate_placed_feature_data(&self, feature: &PlacedFeatureData) {
608 self.validate_configured_feature_ref(&feature.feature);
609 }
610
611 fn validate_configured_feature_ref(&self, feature: &ConfiguredFeatureRef) {
612 match feature {
613 ConfiguredFeatureRef::Reference(feature) => {
614 let key = &feature.key;
615 assert!(
616 self.configured_features.by_key(key).is_some(),
617 "unknown configured feature reference {key}"
618 );
619 }
620 ConfiguredFeatureRef::Inline(kind) => self.validate_configured_feature_kind(kind),
621 }
622 }
623
624 fn validate_configured_feature_kind(&self, kind: &ConfiguredFeatureKind) {
625 match kind {
626 ConfiguredFeatureKind::RandomBooleanSelector(config) => {
627 self.validate_placed_feature_ref(&config.feature_true);
628 self.validate_placed_feature_ref(&config.feature_false);
629 }
630 ConfiguredFeatureKind::RandomSelector(config) => {
631 for feature in &config.features {
632 self.validate_placed_feature_ref(&feature.feature);
633 }
634 self.validate_placed_feature_ref(&config.default);
635 }
636 ConfiguredFeatureKind::WeightedRandomSelector(config) => {
637 for feature in &config.features {
638 self.validate_placed_feature_ref(&feature.data);
639 }
640 }
641 ConfiguredFeatureKind::RootSystem(config) => {
642 self.validate_placed_feature_ref(&config.feature);
643 }
644 ConfiguredFeatureKind::Fossil(config) => {
645 assert!(
646 self.structure_processors
647 .by_key(&config.fossil_processors)
648 .is_some(),
649 "fossil configured feature references unknown processor list {}",
650 config.fossil_processors
651 );
652 assert!(
653 self.structure_processors
654 .by_key(&config.overlay_processors)
655 .is_some(),
656 "fossil configured feature references unknown processor list {}",
657 config.overlay_processors
658 );
659 }
660 ConfiguredFeatureKind::SimpleRandomSelector(config) => {
661 for feature in &config.features {
662 self.validate_placed_feature_ref(feature);
663 }
664 }
665 ConfiguredFeatureKind::Sequence(config) => {
666 for feature in &config.features {
667 self.validate_placed_feature_ref(feature);
668 }
669 }
670 ConfiguredFeatureKind::VegetationPatch(config)
671 | ConfiguredFeatureKind::WaterloggedVegetationPatch(config) => {
672 self.validate_placed_feature_ref(&config.vegetation_feature);
673 }
674 _ => {}
675 }
676 }
677
678 fn validate_template_pool_feature_refs(&self, element: &template_pool::PoolElement) {
679 match element {
680 template_pool::PoolElement::Feature { feature, .. } => {
681 assert!(
682 self.placed_features.by_key(feature).is_some(),
683 "template pool references unknown placed feature {feature}"
684 );
685 }
686 template_pool::PoolElement::List { elements, .. } => {
687 for element in elements {
688 self.validate_template_pool_feature_refs(element);
689 }
690 }
691 template_pool::PoolElement::Single { .. }
692 | template_pool::PoolElement::LegacySingle { .. }
693 | template_pool::PoolElement::Empty => {}
694 }
695 }
696
697 #[must_use]
698 pub fn new_empty() -> Self {
699 Self {
700 attributes: AttributeRegistry::new(),
701 blocks: BlockRegistry::new(),
702 data_components: DataComponentRegistry::new(),
703 data_component_predicate_types: DataComponentPredicateTypeRegistry::new(),
704 consume_effect_types: ConsumeEffectTypeRegistry::new(),
705 entity_data_serializers: EntityDataSerializerRegistry::new(),
706 items: ItemRegistry::new(),
707 biomes: BiomeRegistry::new(),
708 chat_types: ChatTypeRegistry::new(),
709 trim_patterns: TrimPatternRegistry::new(),
710 trim_materials: TrimMaterialRegistry::new(),
711 wolf_variants: WolfVariantRegistry::new(),
712 wolf_sound_variants: WolfSoundVariantRegistry::new(),
713 pig_variants: PigVariantRegistry::new(),
714 pig_sound_variants: PigSoundVariantRegistry::new(),
715 chicken_sound_variants: ChickenSoundVariantRegistry::new(),
716 cat_sound_variants: CatSoundVariantRegistry::new(),
717 cow_sound_variants: CowSoundVariantRegistry::new(),
718 frog_variants: FrogVariantRegistry::new(),
719 cat_variants: CatVariantRegistry::new(),
720 cow_variants: CowVariantRegistry::new(),
721 chicken_variants: ChickenVariantRegistry::new(),
722 painting_variants: PaintingVariantRegistry::new(),
723 particle_types: ParticleTypeRegistry::new(),
724 position_source_types: PositionSourceTypeRegistry::new(),
725 villager_types: VillagerTypeRegistry::new(),
726 villager_professions: VillagerProfessionRegistry::new(),
727 dimension_types: DimensionTypeRegistry::new(),
728 damage_types: DamageTypeRegistry::new(),
729 banner_patterns: BannerPatternRegistry::new(),
730 jukebox_songs: JukeboxSongRegistry::new(),
731 instruments: InstrumentRegistry::new(),
732 dialogs: DialogRegistry::new(),
733 menu_types: MenuTypeRegistry::new(),
734 mob_effects: MobEffectRegistry::new(),
735 map_decoration_types: MapDecorationTypeRegistry::new(),
736 potions: PotionRegistry::new(),
737 zombie_nautilus_variants: ZombieNautilusVariantRegistry::new(),
738 timelines: TimelineRegistry::new(),
739 recipe_types: RecipeTypeRegistry::new(),
740 recipe_book_categories: RecipeBookCategoryRegistry::new(),
741 recipes: RecipeRegistry::new(),
742 entity_types: EntityTypeRegistry::new(),
743 loot_tables: LootTableRegistry::new(),
744 block_entity_types: BlockEntityTypeRegistry::new(),
745 game_rules: GameRuleRegistry::new(),
746 game_events: GameEventRegistry::new(),
747 sound_events: SoundEventRegistry::new(),
748 fluids: FluidRegistry::new(),
749 world_clocks: WorldClockRegistry::new(),
750 poi_types: PoiTypeRegistry::new(),
751 enchantments: EnchantmentRegistry::new(),
752 configured_carvers: ConfiguredCarverRegistry::new(),
753 configured_features: ConfiguredFeatureRegistry::new(),
754 placed_features: PlacedFeatureRegistry::new(),
755 structures: StructureRegistry::new(),
756 structure_processors: StructureProcessorListRegistry::new(),
757 custom_stats: CustomStatRegistry::new(),
758 stat_types: StatTypeRegistry::new(),
759 ticket_types: TicketTypeRegistry::new(),
760 }
761 }
762}
763
764#[cfg(test)]
765mod tests {
766 use std::sync::OnceLock;
767
768 use rustc_hash::FxHashMap;
769 use steel_utils::Identifier;
770
771 use crate::biome::{Biome, BiomeEffects, GrassColorModifier, TemperatureModifier};
772
773 use super::{Registry, RegistryExt};
774
775 fn biome_with_refs(carvers: Vec<Identifier>, features: Vec<Vec<Identifier>>) -> &'static Biome {
776 Box::leak(Box::new(Biome {
777 key: Identifier::new_static("test", "missing_carver_biome"),
778 has_precipitation: false,
779 temperature: 0.5,
780 downfall: 0.0,
781 temperature_modifier: TemperatureModifier::None,
782 effects: BiomeEffects {
783 fog_color: 0,
784 sky_color: 0,
785 water_color: 0,
786 water_fog_color: 0,
787 foliage_color: None,
788 grass_color: None,
789 dry_foliage_color: None,
790 grass_color_modifier: GrassColorModifier::None,
791 music: None,
792 ambient_sound: None,
793 additions_sound: None,
794 mood_sound: None,
795 particle: None,
796 },
797 creature_spawn_probability: 0.0,
798 spawners: FxHashMap::default(),
799 spawn_costs: FxHashMap::default(),
800 carvers,
801 features,
802 id: OnceLock::new(),
803 }))
804 }
805
806 #[test]
807 #[should_panic(expected = "references unknown configured carver")]
808 fn freeze_rejects_missing_biome_carver_reference() {
809 let mut registry = Registry::new_empty();
810 registry.biomes.register(biome_with_refs(
811 vec![Identifier::vanilla_static("missing_carver")],
812 Vec::new(),
813 ));
814
815 registry.freeze();
816 }
817
818 #[test]
819 #[should_panic(expected = "references unknown placed feature")]
820 fn freeze_rejects_missing_biome_placed_feature_reference() {
821 let mut registry = Registry::new_empty();
822 registry.biomes.register(biome_with_refs(
823 Vec::new(),
824 vec![vec![Identifier::vanilla_static("missing_feature")]],
825 ));
826
827 registry.freeze();
828 }
829
830 #[test]
831 fn vanilla_feature_registries_initialize_and_validate() {
832 let mut registry = Registry::new_vanilla();
833 registry.freeze();
834
835 assert!(
836 registry
837 .configured_features
838 .by_key(&Identifier::vanilla_static("ore_diamond_small"))
839 .is_some()
840 );
841 assert!(
842 registry
843 .placed_features
844 .by_key(&Identifier::vanilla_static("ore_diamond"))
845 .is_some()
846 );
847 }
848
849 #[test]
850 fn vanilla_static_entity_data_registries_initialize_in_vanilla_order() {
851 let registry = Registry::new_vanilla();
852 let entity_effect = Identifier::vanilla_static("entity_effect");
853 let dust = Identifier::vanilla_static("dust");
854 let plains = Identifier::vanilla_static("plains");
855 let none = Identifier::vanilla_static("none");
856 let tabby = Identifier::vanilla_static("tabby");
857 let angry = Identifier::vanilla_static("angry");
858 let big = Identifier::vanilla_static("big");
859 let earth = Identifier::vanilla_static("earth");
860
861 assert_eq!(
862 registry.particle_types.by_id(21).map(|entry| &entry.key),
863 Some(&dust)
864 );
865 assert_eq!(
866 registry.particle_types.by_id(28).map(|entry| &entry.key),
867 Some(&entity_effect)
868 );
869 assert_eq!(
870 registry.villager_types.by_id(2).map(|entry| &entry.key),
871 Some(&plains)
872 );
873 assert_eq!(
874 registry
875 .villager_professions
876 .by_id(0)
877 .map(|entry| &entry.key),
878 Some(&none)
879 );
880 assert_eq!(
881 registry.cat_variants.by_id(9).map(|entry| &entry.key),
882 Some(&tabby)
883 );
884 assert_eq!(
885 registry
886 .wolf_sound_variants
887 .by_id(0)
888 .map(|entry| &entry.key),
889 Some(&angry)
890 );
891 assert_eq!(
892 registry.pig_sound_variants.by_id(0).map(|entry| &entry.key),
893 Some(&big)
894 );
895 assert_eq!(
896 registry.painting_variants.by_id(16).map(|entry| &entry.key),
897 Some(&earth)
898 );
899 }
900
901 #[test]
902 fn vanilla_game_events_initialize_in_vanilla_order() {
903 let registry = Registry::new_vanilla();
904 let block_activate = Identifier::vanilla_static("block_activate");
905 let unequip = Identifier::vanilla_static("unequip");
906 let resonate_1 = Identifier::vanilla_static("resonate_1");
907 let resonate_10 = Identifier::vanilla_static("resonate_10");
908
909 assert_eq!(
910 registry.game_events.by_id(0).map(|event| &event.key),
911 Some(&block_activate)
912 );
913 assert_eq!(
914 registry.game_events.by_id(45).map(|event| &event.key),
915 Some(&unequip)
916 );
917 assert_eq!(
918 registry.game_events.by_id(46).map(|event| &event.key),
919 Some(&resonate_1)
920 );
921 assert_eq!(
922 registry.game_events.by_id(55).map(|event| &event.key),
923 Some(&resonate_10)
924 );
925 }
926}