Skip to main content

steel_registry/
block_transformer.rs

1//! `minecraft:block_transformer` registry entries.
2//!
3//! Vanilla moved this from an inline per-item component to a proper dynamic
4//! registry (`Registries.BLOCK_TRANSFORMER`) referenced by items via
5//! `Holder<BlockTransformer>`. Entries are built from datapack JSON at build
6//! time; the item component itself only stores a registry reference.
7
8use std::io::{Cursor, Error, Result, Write};
9use std::str::FromStr;
10
11use rustc_hash::FxHashMap;
12use simdnbt::ToNbtTag;
13use simdnbt::owned::{NbtList, NbtTag};
14use steel_utils::codec::VarInt;
15use steel_utils::hash::{ComponentHasher, HashComponent};
16use steel_utils::serial::{ReadFrom, WriteTo};
17use steel_utils::{Direction, Identifier};
18
19use crate::feature::BlockStateProviderKind;
20use crate::sound_event::SoundEventHolder;
21use crate::{REGISTRY, RegistryEntry, RegistryExt};
22
23/// Item block transforms, e.g. shovel flattening dirt into a path.
24///
25/// `PartialEq`/`Eq` come from [`crate::impl_registry_entry!`] (identity by key).
26#[derive(Debug, Clone)]
27pub struct BlockTransformer {
28    pub key: Identifier,
29    pub transforms: Vec<BlockTransformData>,
30    pub nbt: fn() -> NbtList,
31}
32
33#[derive(Debug, Clone)]
34pub struct BlockTransformData {
35    pub block_state_provider: BlockStateProviderKind,
36    pub sound: SoundEventHolder,
37    pub particle: TransformParticle,
38    pub disallowed_faces: Vec<Direction>,
39    pub loot: Option<Identifier>,
40    pub drop_strategy: DropStrategy,
41    pub update_from_neighbors: bool,
42    pub transform_type: TransformType,
43    pub consume_on_use: bool,
44    pub item_damage_per_use: i32,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
48pub enum TransformParticle {
49    #[default]
50    None,
51    Scrape,
52    WaxOn,
53    WaxOff,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
57pub enum DropStrategy {
58    ClickedFace,
59    #[default]
60    FromMiddle,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
64pub enum TransformType {
65    #[default]
66    SingleBlock,
67    CopperChest,
68}
69
70/// Registry-sync NBT for the `minecraft:block_transformer` dynamic registry.
71/// Only encoding is needed: entries are build-time data, never decoded from
72/// player-supplied NBT or network input.
73impl ToNbtTag for &BlockTransformer {
74    fn to_nbt_tag(self) -> NbtTag {
75        NbtTag::List((self.nbt)())
76    }
77}
78
79pub type BlockTransformerRef = &'static BlockTransformer;
80
81/// Item component wrapper: a `Holder<BlockTransformer>` reference, wire- and
82/// NBT-encoded like other registry holders (e.g. [`crate::damage_type::DamageTypeComponent`]).
83#[derive(Debug, Clone, PartialEq, Eq)]
84pub struct BlockTransformerComponent {
85    pub block_transformer: BlockTransformerRef,
86}
87
88impl BlockTransformerComponent {
89    #[must_use]
90    pub const fn new(block_transformer: BlockTransformerRef) -> Self {
91        Self { block_transformer }
92    }
93}
94
95impl WriteTo for BlockTransformerComponent {
96    fn write(&self, writer: &mut impl Write) -> Result<()> {
97        let id = self.block_transformer.try_id().ok_or_else(|| {
98            Error::other(format!(
99                "Unknown block transformer: {}",
100                self.block_transformer.key
101            ))
102        })?;
103        let id = i32::try_from(id).map_err(|_| {
104            Error::other(format!("Block transformer id out of protocol range: {id}"))
105        })?;
106        VarInt(id).write(writer)
107    }
108}
109
110impl ReadFrom for BlockTransformerComponent {
111    fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
112        let id = VarInt::read(data)?.0;
113        let id = usize::try_from(id)
114            .map_err(|_| Error::other(format!("Negative block transformer id: {id}")))?;
115        let block_transformer = REGISTRY
116            .block_transformers
117            .by_id(id)
118            .ok_or_else(|| Error::other(format!("Unknown block transformer id: {id}")))?;
119        Ok(Self { block_transformer })
120    }
121}
122
123impl simdnbt::ToNbtTag for BlockTransformerComponent {
124    fn to_nbt_tag(self) -> NbtTag {
125        self.block_transformer.key.to_string().to_nbt_tag()
126    }
127}
128
129impl simdnbt::FromNbtTag for BlockTransformerComponent {
130    fn from_nbt_tag(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
131        let key = Identifier::from_str(&tag.string()?.to_str()).ok()?;
132        REGISTRY
133            .block_transformers
134            .by_key(&key)
135            .map(|block_transformer| Self { block_transformer })
136    }
137}
138
139impl HashComponent for BlockTransformerComponent {
140    fn hash_component(&self, hasher: &mut ComponentHasher) {
141        hasher.put_string(&self.block_transformer.key.to_string());
142    }
143}
144
145pub struct BlockTransformerRegistry {
146    entries_by_id: Vec<BlockTransformerRef>,
147    entries_by_key: FxHashMap<Identifier, usize>,
148    allows_registering: bool,
149}
150
151impl BlockTransformerRegistry {
152    #[must_use]
153    pub fn new() -> Self {
154        Self {
155            entries_by_id: Vec::new(),
156            entries_by_key: FxHashMap::default(),
157            allows_registering: true,
158        }
159    }
160}
161
162crate::impl_standard_methods!(
163    BlockTransformerRegistry,
164    BlockTransformerRef,
165    entries_by_id,
166    entries_by_key,
167    allows_registering
168);
169
170crate::impl_registry!(
171    BlockTransformerRegistry,
172    BlockTransformer,
173    entries_by_id,
174    entries_by_key,
175    block_transformers
176);