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}
86
87/// Position rule tests used by `RuleProcessor`.
88#[derive(Debug, Clone, Deserialize, Default)]
89#[serde(tag = "predicate_type")]
90pub enum PosRuleTestData {
91    #[default]
92    #[serde(rename = "minecraft:always_true")]
93    AlwaysTrue,
94    #[serde(rename = "minecraft:axis_aligned_linear_pos")]
95    AxisAlignedLinearPos {
96        #[serde(
97            default = "default_structure_processor_axis",
98            deserialize_with = "deserialize_processor_axis"
99        )]
100        axis: StructureProcessorAxis,
101        #[serde(default)]
102        min_chance: f32,
103        #[serde(default)]
104        max_chance: f32,
105        #[serde(default)]
106        min_dist: i32,
107        #[serde(default)]
108        max_dist: i32,
109    },
110}
111
112/// Axis enum for position predicates.
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum StructureProcessorAxis {
115    X,
116    Y,
117    Z,
118}
119
120const fn default_structure_processor_axis() -> StructureProcessorAxis {
121    StructureProcessorAxis::Y
122}
123
124fn deserialize_processor_axis<'de, D: Deserializer<'de>>(
125    deserializer: D,
126) -> Result<StructureProcessorAxis, D::Error> {
127    let value = String::deserialize(deserializer)?;
128    match value.as_str() {
129        "x" => Ok(StructureProcessorAxis::X),
130        "y" => Ok(StructureProcessorAxis::Y),
131        "z" => Ok(StructureProcessorAxis::Z),
132        _ => Err(D::Error::custom("invalid structure processor axis")),
133    }
134}
135
136/// Rule block-entity NBT modifiers.
137#[derive(Debug, Clone, Deserialize, Default)]
138#[serde(tag = "type")]
139pub enum RuleBlockEntityModifierData {
140    /// Vanilla passthrough when the field is absent.
141    #[default]
142    Passthrough,
143    /// Appends loot table metadata to the output block entity.
144    #[serde(rename = "minecraft:append_loot")]
145    AppendLoot { loot_table: Identifier },
146}