Skip to main content

steel_registry/registry/
mod.rs

1//! Registry ownership, lookup, tagging, and reference codecs.
2
3pub 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
115/// Returns `false` if the registry was already published.
116pub 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
138/// Trait for types stored in a registry, allowing self-lookup of their numeric ID.
139pub trait RegistryEntry: PartialEq + 'static {
140    fn key(&self) -> &Identifier;
141    fn try_id(&self) -> Option<usize>;
142
143    /// # Panics
144    /// Panics if the entry is not registered.
145    fn id(&self) -> usize {
146        self.try_id().expect("entry not found in registry")
147    }
148}
149
150/// Generic trait for registries with a typed entry.
151///
152/// `Entry` is the concrete type (e.g. `Block`); all lookups return `&'static Entry`
153/// to enforce cheap pointer copies and prevent expensive clones.
154pub 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
165/// Trait for registries that support tagging entries.
166pub 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(&registry.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}