Skip to main content

steel_registry/structure/processor/
data.rs

1//! Typed structure processor-list codec data.
2
3use serde::{Deserialize, Deserializer, de::Error as _};
4use steel_utils::{Identifier, value_providers::IntProvider};
5
6use crate::shared_structs::{
7    BlockStateData, deserialize_optional_tag_identifier, deserialize_tag_identifier,
8};
9
10/// Codec payload for a structure processor list.
11#[derive(Debug, Clone, Deserialize)]
12#[serde(deny_unknown_fields)]
13pub struct StructureProcessorListData {
14    /// Ordered processors.
15    pub processors: Vec<StructureProcessorKind>,
16}
17
18/// A typed vanilla structure processor.
19#[derive(Debug, Clone, Deserialize)]
20#[serde(tag = "processor_type")]
21pub enum StructureProcessorKind {
22    /// Randomly drops input blocks.
23    #[serde(rename = "minecraft:block_rot")]
24    BlockRot {
25        /// Optional tag restricting which blocks may be dropped.
26        #[serde(default, deserialize_with = "deserialize_optional_tag_identifier")]
27        rottable_blocks: Option<Identifier>,
28        /// Keep probability.
29        integrity: f32,
30    },
31    /// Prevents replacement of protected world blocks.
32    #[serde(rename = "minecraft:protected_blocks")]
33    ProtectedBlocks {
34        /// Vanilla field name is `value`; it stores the cannot-replace tag.
35        #[serde(rename = "value", deserialize_with = "deserialize_tag_identifier")]
36        cannot_replace: Identifier,
37    },
38    /// Applies the first matching rule.
39    #[serde(rename = "minecraft:rule")]
40    Rule { rules: Vec<ProcessorRuleData> },
41    /// Ages stone/obsidian structure blocks, used by ruined portals.
42    #[serde(rename = "minecraft:block_age")]
43    BlockAge { mossiness: f32 },
44    /// Keeps non-full structure blocks submerged in existing lava.
45    #[serde(rename = "minecraft:lava_submerged_block")]
46    LavaSubmergedBlock,
47    /// Replaces stone ruin blocks with blackstone variants.
48    #[serde(rename = "minecraft:blackstone_replace")]
49    BlackstoneReplace,
50    /// Delegates to another processor but caps successful modifications.
51    #[serde(rename = "minecraft:capped")]
52    Capped {
53        delegate: Box<StructureProcessorKind>,
54        limit: IntProvider,
55    },
56}
57
58/// One rule inside vanilla's `RuleProcessor`.
59#[derive(Debug, Clone, Deserialize)]
60#[serde(deny_unknown_fields)]
61pub struct ProcessorRuleData {
62    pub input_predicate: StructureRuleTestData,
63    pub location_predicate: StructureRuleTestData,
64    #[serde(default)]
65    pub position_predicate: PosRuleTestData,
66    pub output_state: BlockStateData,
67    #[serde(default)]
68    pub block_entity_modifier: RuleBlockEntityModifierData,
69}
70
71/// Block-state rule tests used by `RuleProcessor`.
72#[derive(Debug, Clone, Deserialize)]
73#[serde(tag = "predicate_type")]
74pub enum StructureRuleTestData {
75    #[serde(rename = "minecraft:always_true")]
76    AlwaysTrue,
77    #[serde(rename = "minecraft:block_match")]
78    BlockMatch { block: Identifier },
79    #[serde(rename = "minecraft:random_block_match")]
80    RandomBlockMatch { block: Identifier, probability: f32 },
81    #[serde(rename = "minecraft:tag_match")]
82    TagMatch { tag: Identifier },
83    #[serde(rename = "minecraft:blockstate_match")]
84    BlockStateMatch { block_state: BlockStateData },
85    #[serde(rename = "minecraft:random_blockstate_match")]
86    RandomBlockStateMatch {
87        block_state: BlockStateData,
88        probability: f32,
89    },
90    #[serde(rename = "minecraft:all_of")]
91    AllOf { rules: Vec<StructureRuleTestData> },
92    #[serde(rename = "minecraft:any_of")]
93    AnyOf { rules: Vec<StructureRuleTestData> },
94    #[serde(rename = "minecraft:not")]
95    Not { rule: Box<StructureRuleTestData> },
96    #[serde(rename = "minecraft:height_match")]
97    HeightMatch {
98        min_inclusive: i32,
99        max_inclusive: i32,
100    },
101}
102
103/// Position rule tests used by `RuleProcessor`.
104#[derive(Debug, Clone, Deserialize, Default)]
105#[serde(tag = "predicate_type")]
106pub enum PosRuleTestData {
107    #[default]
108    #[serde(rename = "minecraft:always_true")]
109    AlwaysTrue,
110    #[serde(rename = "minecraft:axis_aligned_linear_pos")]
111    AxisAlignedLinearPos {
112        #[serde(
113            default = "default_structure_processor_axis",
114            deserialize_with = "deserialize_processor_axis"
115        )]
116        axis: StructureProcessorAxis,
117        #[serde(default)]
118        min_chance: f32,
119        #[serde(default)]
120        max_chance: f32,
121        #[serde(default)]
122        min_dist: i32,
123        #[serde(default)]
124        max_dist: i32,
125    },
126}
127
128/// Axis enum for position predicates.
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130pub enum StructureProcessorAxis {
131    X,
132    Y,
133    Z,
134}
135
136const fn default_structure_processor_axis() -> StructureProcessorAxis {
137    StructureProcessorAxis::Y
138}
139
140fn deserialize_processor_axis<'de, D: Deserializer<'de>>(
141    deserializer: D,
142) -> Result<StructureProcessorAxis, D::Error> {
143    let value = String::deserialize(deserializer)?;
144    match value.as_str() {
145        "x" => Ok(StructureProcessorAxis::X),
146        "y" => Ok(StructureProcessorAxis::Y),
147        "z" => Ok(StructureProcessorAxis::Z),
148        _ => Err(D::Error::custom("invalid structure processor axis")),
149    }
150}
151
152/// Rule block-entity NBT modifiers.
153#[derive(Debug, Clone, Deserialize, Default)]
154#[serde(tag = "type")]
155pub enum RuleBlockEntityModifierData {
156    /// Vanilla passthrough when the field is absent.
157    #[default]
158    Passthrough,
159    /// Appends loot table metadata to the output block entity.
160    #[serde(rename = "minecraft:append_loot")]
161    AppendLoot { loot_table: Identifier },
162}