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