diff options
| -rw-r--r-- | azalea-block/azalea-block-macros/src/lib.rs | 532 | ||||
| -rw-r--r-- | azalea-block/azalea-block-macros/src/property/generate.rs | 302 | ||||
| -rw-r--r-- | azalea-block/azalea-block-macros/src/property/mod.rs | 2 | ||||
| -rw-r--r-- | azalea-block/azalea-block-macros/src/property/parse.rs | 181 | ||||
| -rw-r--r-- | azalea-block/src/generated.rs | 3202 | ||||
| -rw-r--r-- | azalea-block/src/lib.rs | 82 | ||||
| -rw-r--r-- | codegen/lib/code/blocks.py | 4 |
7 files changed, 2270 insertions, 2035 deletions
diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs index 0dfe0a24..3c9f3230 100644 --- a/azalea-block/azalea-block-macros/src/lib.rs +++ b/azalea-block/azalea-block-macros/src/lib.rs @@ -1,5 +1,6 @@ //! An internal crate used by `azalea_block`. +mod property; mod utils; use std::{collections::HashMap, fmt::Write}; @@ -8,54 +9,22 @@ use proc_macro::TokenStream; use proc_macro2::TokenTree; use quote::quote; use syn::{ - Expr, Ident, LitStr, Token, braced, - ext::IdentExt, - parenthesized, + Expr, Ident, Token, braced, parse::{Parse, ParseStream, Result}, parse_macro_input, - punctuated::Punctuated, - token, }; use utils::{combinations_of, to_pascal_case}; -// must be the same as the type in `azalea-block/src/lib.rs` -type BlockStateIntegerRepr = u16; -enum PropertyType { - /// `Axis { X, Y, Z }` - Enum { - enum_name: Ident, - variants: Punctuated<Ident, Token![,]>, +use crate::property::{ + generate::{ + generate_properties_code, get_property_value_type, get_property_variant_types, + make_property_struct_names_to_names, }, - /// `Snowy(bool)` - Boolean { struct_name: Ident }, -} - -/// `"snowy" => Snowy(bool)` -struct PropertyDefinition { - name: LitStr, - property_type: PropertyType, -} - -/// Comma separated PropertyDefinitions (`"snowy" => Snowy(bool),`) -struct PropertyDefinitions { - properties: Vec<PropertyDefinition>, -} + parse::{PropertyDefinition, PropertyWithNameAndDefault, parse_property_definitions}, +}; -/// `"snowy": Snowy(false)` or `"axis": properties::Axis::Y` -#[derive(Debug)] -struct PropertyWithNameAndDefault { - // "snowy" "axis" - name: String, - /// The property name, potentially modified so it works better as a struct - /// field. - name_ident: Ident, - // Snowy / Axis - property_type: Ident, - property_value_type: Ident, - is_enum: bool, - // false / properties::Axis::Y - default: proc_macro2::TokenStream, -} +// must be the same as the type in `azalea-block/src/lib.rs` +type BlockStateIntegerRepr = u16; /// ```ignore /// grass_block => BlockBehavior::default(), { @@ -67,140 +36,10 @@ struct BlockDefinition { behavior: Expr, properties_and_defaults: Vec<PropertyWithNameAndDefault>, } -impl Parse for PropertyWithNameAndDefault { - fn parse(input: ParseStream) -> Result<Self> { - // `"snowy": Snowy(false)` or `"axis": properties::Axis::Y` - let property_name = input.parse::<LitStr>()?.value(); - input.parse::<Token![:]>()?; - - let first_ident = input.call(Ident::parse_any)?; - let mut property_default = quote! { #first_ident }; - - let property_type: Ident; - let property_value_type: Ident; - let mut is_enum = false; - - if input.parse::<Token![::]>().is_ok() { - // enum - is_enum = true; - property_type = first_ident.clone(); - property_value_type = first_ident; - let variant = input.parse::<Ident>()?; - property_default = quote! { properties::#property_default::#variant }; - } else { - // must be a unit struct if it's not an enum - let content; - let _paren_token: token::Paren = parenthesized!(content in input); - // we use this instead of .parse so it works with rust keywords like true and - // false - let unit_struct_inner = content.call(Ident::parse_any)?; - let unit_struct_inner_string = unit_struct_inner.to_string(); - - if matches!(unit_struct_inner_string.as_str(), "true" | "false") { - property_value_type = Ident::new("bool", first_ident.span()); - property_type = first_ident; - property_default = quote! { #unit_struct_inner }; - } else { - return Err(input.error("Expected a boolean or an enum variant")); - } - }; - - let property_name_ident = name_to_ident(&property_name); - - Ok(PropertyWithNameAndDefault { - name: property_name, - name_ident: property_name_ident, - property_type, - property_value_type, - is_enum, - default: property_default, - }) - } -} struct BlockDefinitions { blocks: Vec<BlockDefinition>, } -struct MakeBlockStates { - property_definitions: PropertyDefinitions, - block_definitions: BlockDefinitions, -} - -impl Parse for PropertyType { - fn parse(input: ParseStream) -> Result<Self> { - // like `Axis { X, Y, Z }` or `Waterlogged(bool)` - - let keyword = Ident::parse(input)?; - - fn parse_braced(input: ParseStream) -> Result<Punctuated<Ident, Token![,]>> { - let content; - braced!(content in input); - let variants = content.parse_terminated(Ident::parse, Token![,])?; - Ok(variants) - } - - fn parse_paren(input: ParseStream) -> Result<Ident> { - let content; - parenthesized!(content in input); - let inner = content.parse::<Ident>()?; - Ok(inner) - } - - if let Ok(variants) = parse_braced(input) { - Ok(Self::Enum { - enum_name: keyword, - variants, - }) - } else if let Ok(inner) = parse_paren(input) { - assert_eq!( - inner.to_string(), - "bool", - "Currently only bool unit structs are supported" - ); - Ok(Self::Boolean { - struct_name: keyword, - }) - } else { - Err(input.error("Expected a unit struct or an enum")) - } - } -} - -impl Parse for PropertyDefinition { - fn parse(input: ParseStream) -> Result<Self> { - // "face" => Face { - // Floor, - // Wall, - // Ceiling - // }, - - // if you're wondering, the reason it's in quotes is because `type` is - // a keyword in rust so if we don't put it in quotes it results in a - // syntax error - let name = input.parse()?; - input.parse::<Token![=>]>()?; - let property_type = input.parse()?; - - input.parse::<Token![,]>()?; - Ok(PropertyDefinition { - name, - property_type, - }) - } -} - -impl Parse for PropertyDefinitions { - fn parse(input: ParseStream) -> Result<Self> { - let mut property_definitions = Vec::new(); - while !input.is_empty() { - property_definitions.push(input.parse()?); - } - - Ok(PropertyDefinitions { - properties: property_definitions, - }) - } -} impl Parse for BlockDefinition { fn parse(input: ParseStream) -> Result<Self> { @@ -249,6 +88,10 @@ impl Parse for BlockDefinitions { } } +struct MakeBlockStates { + pub properties: Vec<PropertyDefinition>, + pub blocks: BlockDefinitions, +} impl Parse for MakeBlockStates { fn parse(input: ParseStream) -> Result<Self> { // Properties => { ... } Blocks => { ... } @@ -257,7 +100,7 @@ impl Parse for MakeBlockStates { input.parse::<Token![=>]>()?; let content; braced!(content in input); - let properties = content.parse()?; + let properties = parse_property_definitions(&content)?; input.parse::<Token![,]>()?; @@ -268,23 +111,26 @@ impl Parse for MakeBlockStates { braced!(content in input); let blocks = content.parse()?; - Ok(MakeBlockStates { - property_definitions: properties, - block_definitions: blocks, - }) + Ok(MakeBlockStates { properties, blocks }) } } struct PropertyVariantData { pub block_state_ids: Vec<BlockStateIntegerRepr>, + pub kind: PropertyKind, pub ident: Ident, - pub variant_index: usize, - pub is_enum: bool, + pub index: usize, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum PropertyKind { + Enum, + Bool, } #[derive(Clone, Debug)] -struct PropertyMeta { - pub name: String, +struct PropertyVariantMeta { + pub ident: Ident, pub index: usize, } @@ -292,108 +138,15 @@ struct PropertyMeta { pub fn make_block_states(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as MakeBlockStates); - let mut property_enums = quote! {}; let mut properties_map = HashMap::new(); - let mut property_struct_names_to_names = HashMap::new(); - - let mut state_id: BlockStateIntegerRepr = 0; - - for property in &input.property_definitions.properties { - let property_struct_name: Ident; - // this is usually the same as property_struct_name except for bool - let property_value_name: Ident; - let mut property_variant_types = Vec::new(); - - match &property.property_type { - PropertyType::Enum { - enum_name, - variants, - } => { - let mut property_enum_variants = quote! {}; - let mut property_from_number_variants = quote! {}; - - property_value_name = enum_name.clone(); - property_struct_name = enum_name.clone(); - - property_struct_names_to_names.insert( - property_struct_name.to_string(), - property.name.clone().value(), - ); - - for i in 0..variants.len() { - let variant = &variants[i]; - - let i_lit = syn::Lit::Int(syn::LitInt::new( - &i.to_string(), - proc_macro2::Span::call_site(), - )); - - property_enum_variants.extend(quote! { - #variant = #i_lit, - }); - - // i_lit is used here instead of i because otherwise it says 0size - // in the expansion and that looks uglier - property_from_number_variants.extend(quote! { - #i_lit => #property_struct_name::#variant, - }); - - property_variant_types.push(PropertyMeta { - name: variant.to_string(), - index: i, - }); - } - - property_enums.extend(quote! { - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub enum #property_struct_name { - #property_enum_variants - } - - impl From<BlockStateIntegerRepr> for #property_struct_name { - fn from(value: BlockStateIntegerRepr) -> Self { - match value { - #property_from_number_variants - _ => panic!("Invalid property value: {}", value), - } - } - } - }); - } - PropertyType::Boolean { struct_name } => { - property_value_name = Ident::new("bool", proc_macro2::Span::call_site()); - property_struct_name = struct_name.clone(); - property_variant_types = vec![ - PropertyMeta { - name: "true".into(), - index: 0, - }, - PropertyMeta { - name: "false".into(), - index: 1, - }, - ]; - - property_enums.extend(quote! { - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct #property_struct_name(pub bool); - - impl From<BlockStateIntegerRepr> for #property_struct_name { - /// In Minecraft, `0 = true` and `1 = false`. - fn from(value: BlockStateIntegerRepr) -> Self { - match value { - 0 => Self(true), - 1 => Self(false), - _ => panic!("Invalid property value: {}", value), - } - } - } - }); - } - } - properties_map.insert(property_value_name.to_string(), property_variant_types); + for property in &input.properties { + let property_value_type = get_property_value_type(&property.data); + let property_variant_types = get_property_variant_types(&property.data); + properties_map.insert(property_value_type.to_string(), property_variant_types); } + let property_struct_names_to_names = make_property_struct_names_to_names(&input.properties); + let mut block_state_enum_variants = quote! {}; let mut block_structs = quote! {}; @@ -402,15 +155,11 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let mut from_registry_block_to_blockstate_match = quote! {}; let mut from_registry_block_to_blockstates_match = quote! {}; - // { - // Waterlogged: [ - // [ vec of waterlogged = true state ids ], - // [ vec of waterlogged = false state ids ] - // } - // } - let mut properties_to_state_ids: HashMap<String, Vec<PropertyVariantData>> = HashMap::new(); + // keys are enum names like Waterlogged + let mut properties_to_state_ids = HashMap::<String, Vec<PropertyVariantData>>::new(); - for block in &input.block_definitions.blocks { + let mut state_id: BlockStateIntegerRepr = 0; + for block in &input.blocks.blocks { let block_property_names = &block .properties_and_defaults .iter() @@ -418,9 +167,6 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { .collect::<Vec<_>>(); let mut block_properties_vec = Vec::new(); for property_name in block_property_names { - // if property_name == "stage" { - // panic!("{:?}", block.properties_and_defaults); - // } let property_variants = properties_map .get(property_name) .unwrap_or_else(|| panic!("Property '{property_name}' not found")) @@ -465,7 +211,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { name: property_name, property_type: property.property_type.clone(), property_value_type: property.property_value_type.clone(), - is_enum: property.is_enum, + kind: property.kind, default: property.default.clone(), }); } @@ -482,14 +228,17 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { for PropertyWithNameAndDefault { property_value_type, name_ident, - is_enum, + kind, .. } in &properties_with_name { - block_struct_fields.extend(if *is_enum { - quote! { pub #name_ident: properties::#property_value_type, } - } else { - quote! { pub #name_ident: #property_value_type, } + block_struct_fields.extend(match kind { + PropertyKind::Enum => { + quote! { pub #name_ident: properties::#property_value_type, } + } + PropertyKind::Bool => { + quote! { pub #name_ident: #property_value_type, } + } }); } @@ -525,24 +274,30 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let property_name_ident = &property.name_ident; let property_value_name_ident = &property.property_type; let variant = &combination[i]; - let variant_ident = Ident::new(&variant.name, proc_macro2::Span::call_site()); - - // this terrible code just gets the property default as a string - let property_default_as_string = - match property.default.clone().into_iter().last().unwrap() { - TokenTree::Ident(ident) => ident.to_string(), - _ => { - panic!() - } - }; - if property_default_as_string != variant.name { + let variant_ident = variant.ident.clone(); + + // property.default is a TokenStream, so we have to parse it like this + let property_default_ident = property + .default + .clone() + .into_iter() + .last() + .and_then(|tt| match tt { + TokenTree::Ident(ident) => Some(ident), + _ => None, + }) + .unwrap(); + if property_default_ident.to_string() != variant.ident.to_string() { is_default = false; } - let property_variant = if property.is_enum { - quote! {properties::#property_value_name_ident::#variant_ident} - } else { - quote! {#variant_ident} + let property_variant = match property.kind { + PropertyKind::Enum => { + quote! { properties::#property_value_name_ident::#variant_ident } + } + PropertyKind::Bool => { + quote! { #variant_ident } + } }; from_block_to_state_combination_match_inner.extend(quote! { @@ -562,8 +317,8 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { property_variants.push(PropertyVariantData { block_state_ids: vec![state_id], ident: variant_ident, - variant_index: variant.index, - is_enum: property.is_enum, + index: variant.index, + kind: property.kind, }); } } @@ -678,6 +433,28 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { azalea_registry::Block::#block_name_pascal_case => BlockStates::from(#first_state_id..=#last_state_id), }); + let mut property_map_inner = quote! {}; + let mut get_property_inner = quote! {}; + + for PropertyWithNameAndDefault { + name, + name_ident, + kind, + .. + } in &properties_with_name + { + let variant_name_tokens = match kind { + PropertyKind::Enum => quote! { self.#name_ident.to_static_str() }, + PropertyKind::Bool => quote! { if self.#name_ident { "true" } else { "false" } }, + }; + property_map_inner.extend(quote! { + map.insert(#name, #variant_name_tokens); + }); + get_property_inner.extend(quote! { + #name => Some(#variant_name_tokens), + }); + } + let mut block_default_fields = quote! {}; for PropertyWithNameAndDefault { name_ident, @@ -702,6 +479,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { } else { block_struct.extend(quote! { { #block_struct_fields } }); } + block_struct.extend(quote! { impl BlockTrait for #block_struct_name { fn behavior(&self) -> BlockBehavior { @@ -716,6 +494,18 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { fn as_registry_block(&self) -> azalea_registry::Block { azalea_registry::Block::#block_name_pascal_case } + + fn property_map(&self) -> std::collections::HashMap<&'static str, &'static str> { + let mut map = std::collections::HashMap::new(); + #property_map_inner + map + } + fn get_property(&self, name: &str) -> Option<&'static str> { + match name { + #get_property_inner + _ => None, + } + } } impl From<#block_struct_name> for BlockState { @@ -755,124 +545,14 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { } }; - // now impl Property for every property - // ``` - // match state_id { - // // this is just an example of how it might look, these state ids are definitely not correct - // 0 | 3 | 6 => Some(Self::Axis::X), - // 1 | 4 | 7 => Some(Self::Axis::Y), - // 2 | 5 | 8 => Some(Self::Axis::Z), - // _ => None, - // } - // ``` - let mut property_impls = quote! {}; - for (property_struct_name, property_values) in properties_to_state_ids { - let mut is_enum = false; - - let mut some_block_states_count = 0; - for PropertyVariantData { - block_state_ids, - is_enum: is_enum_, - .. - } in &property_values - { - some_block_states_count += block_state_ids.len(); - is_enum = *is_enum_; - } - - let mut try_from_block_state; - // do a simpler lookup if there's few block states - if some_block_states_count > 2048 { - // create a lookup table - 0 indicates None - let table_size = last_state_id as usize + 1; - let mut table = vec![0; table_size]; - for PropertyVariantData { - block_state_ids, - variant_index, - .. - } in property_values - { - for block_state_id in block_state_ids { - // add 1 since we're offsetting for zero - table[block_state_id as usize] = variant_index + 1; - } - } - - let mut table_inner = quote! {}; - for entry in table { - // this makes it not put the "usize" after the number like 0usize - let literal_int = syn::Lit::Int(syn::LitInt::new( - &entry.to_string(), - proc_macro2::Span::call_site(), - )); - table_inner.extend(quote! { #literal_int, }); - } - - try_from_block_state = quote! { - static TABLE: &[BlockStateIntegerRepr; #table_size] = &[#table_inner]; - let res = TABLE[block_state.id() as usize]; - if res == 0 { return None }; - }; - if is_enum { - try_from_block_state.extend(quote! { Some(Self::from(res - 1)) }); - } else { - try_from_block_state.extend(quote! { Some(res != 2) }); - } - } else { - let mut enum_inner_generated = quote! {}; - for PropertyVariantData { - block_state_ids, - ident, - .. - } in property_values - { - enum_inner_generated.extend(if is_enum { - quote! { - #(#block_state_ids)|* => Some(Self::#ident), - } - } else { - quote! { - #(#block_state_ids)|* => Some(#ident), - } - }); - } - - try_from_block_state = quote! { - match block_state.id() { - #enum_inner_generated - _ => None - } - }; - } - - let property_struct_name = - Ident::new(&property_struct_name, proc_macro2::Span::call_site()); - - let value = if is_enum { - quote! { Self } - } else { - quote! { bool } - }; - - let property_impl = quote! { - impl Property for #property_struct_name { - type Value = #value; - - fn try_from_block_state(block_state: BlockState) -> Option<Self::Value> { - #try_from_block_state - } - } - }; - property_impls.extend(property_impl); - } + let properties_code = + generate_properties_code(&input.properties, &properties_to_state_ids, last_state_id); generated.extend(quote! { pub mod properties { use super::*; - #property_enums - - #property_impls + #properties_code } pub mod blocks { diff --git a/azalea-block/azalea-block-macros/src/property/generate.rs b/azalea-block/azalea-block-macros/src/property/generate.rs new file mode 100644 index 00000000..531fdc76 --- /dev/null +++ b/azalea-block/azalea-block-macros/src/property/generate.rs @@ -0,0 +1,302 @@ +use std::collections::HashMap; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{Ident, Lit, LitInt}; + +use crate::{ + BlockStateIntegerRepr, PropertyKind, PropertyVariantData, PropertyVariantMeta, + property::parse::{PropertyData, PropertyDefinition}, +}; + +pub fn generate_properties_code( + properties: &[PropertyDefinition], + properties_to_state_ids: &HashMap<String, Vec<PropertyVariantData>>, + last_state_id: BlockStateIntegerRepr, +) -> TokenStream { + let mut properties_code = quote! {}; + for property in properties { + generate_property_code( + property, + properties_to_state_ids, + last_state_id, + &mut properties_code, + ); + } + + properties_code +} + +fn generate_property_code( + property: &PropertyDefinition, + properties_to_state_ids: &HashMap<String, Vec<PropertyVariantData>>, + last_state_id: BlockStateIntegerRepr, + properties_code: &mut TokenStream, +) { + let property_struct_name = get_property_type_name(&property.data); + + let mut to_static_str_inner = quote! {}; + + match &property.data { + PropertyData::Enum { variants, .. } => { + let mut property_enum_variants = quote! {}; + let mut property_from_number_variants = quote! {}; + + for i in 0..variants.len() { + let variant = &variants[i]; + let variant_str = variant.name.value(); + let variant_ident = variant.ident.clone(); + let i_lit = Lit::Int(LitInt::new(&i.to_string(), proc_macro2::Span::call_site())); + + property_enum_variants.extend(quote! { + #variant_ident = #i_lit, + }); + property_from_number_variants.extend(quote! { + #i_lit => Self::#variant_ident, + }); + to_static_str_inner.extend(quote! { + Self::#variant_ident => #variant_str, + }); + } + + properties_code.extend(quote! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum #property_struct_name { + #property_enum_variants + } + + impl From<BlockStateIntegerRepr> for #property_struct_name { + fn from(value: BlockStateIntegerRepr) -> Self { + match value { + #property_from_number_variants + _ => panic!("Invalid property value: {value}"), + } + } + } + }); + } + PropertyData::Bool { .. } => { + to_static_str_inner.extend(quote! { + Self(true) => "true", + Self(false) => "false", + }); + + properties_code.extend(quote! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct #property_struct_name(pub bool); + + impl From<BlockStateIntegerRepr> for #property_struct_name { + /// In Minecraft, `0` = `true` and `1` = `false`. + fn from(value: BlockStateIntegerRepr) -> Self { + match value { + 0 => Self(true), + 1 => Self(false), + _ => panic!("Invalid property value: {value}"), + } + } + } + }); + } + } + + let property_values = properties_to_state_ids + .get(&property_struct_name.to_string()) + .expect("Property values not found for property"); + + let try_from_block_state = generate_try_from_block_state(&property_values, last_state_id); + + let value_tokens = match get_property_kind(&property_values) { + PropertyKind::Enum => quote! { Self }, + PropertyKind::Bool => quote! { bool }, + }; + let property_impl = quote! { + impl Property for #property_struct_name { + type Value = #value_tokens; + + fn try_from_block_state(block_state: BlockState) -> Option<Self::Value> { + #try_from_block_state + } + + fn to_static_str(&self) -> &'static str { + match self { + #to_static_str_inner + } + } + } + + impl std::fmt::Display for #property_struct_name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_static_str()) + } + } + }; + + properties_code.extend(property_impl); +} + +// generates a match statement or lookup table that's able to convert the +// `block_state` variable to an Option<property value kind> +pub fn generate_try_from_block_state( + property_values: &[PropertyVariantData], + last_state_id: BlockStateIntegerRepr, +) -> proc_macro2::TokenStream { + let mut some_block_states_count = 0; + for variant in property_values { + some_block_states_count += variant.block_state_ids.len(); + } + + let property_kind = get_property_kind(property_values); + + let mut try_from_block_state; + // do a simpler lookup if there's few block states + if some_block_states_count > 2048 { + // create a lookup table - 0 indicates None + let table_size = last_state_id as usize + 1; + let mut table = vec![0; table_size]; + for PropertyVariantData { + block_state_ids, + index: variant_index, + .. + } in property_values + { + for &block_state_id in block_state_ids { + // add 1 since we're offsetting for zero + table[block_state_id as usize] = variant_index + 1; + } + } + + let mut table_inner = quote! {}; + for entry in table { + // this makes it not put the "usize" after the number like 0usize + let literal_int = syn::Lit::Int(syn::LitInt::new( + &entry.to_string(), + proc_macro2::Span::call_site(), + )); + table_inner.extend(quote! { #literal_int, }); + } + + try_from_block_state = quote! { + static TABLE: &[BlockStateIntegerRepr; #table_size] = &[#table_inner]; + let res = TABLE[block_state.id() as usize]; + if res == 0 { return None }; + }; + try_from_block_state.extend(match property_kind { + PropertyKind::Enum => { + quote! { Some(Self::from(res - 1)) } + } + PropertyKind::Bool => { + quote! { Some(res != 2) } + } + }) + } else { + // ``` + // match state_id { + // // this is just an example of how it might look, these state ids are definitely not correct + // 0 | 3 | 6 => Some(Self::Axis::X), + // 1 | 4 | 7 => Some(Self::Axis::Y), + // 2 | 5 | 8 => Some(Self::Axis::Z), + // _ => None, + // } + // ``` + + let mut enum_inner_generated = quote! {}; + for PropertyVariantData { + block_state_ids, + ident, + .. + } in property_values + { + enum_inner_generated.extend(match property_kind { + PropertyKind::Enum => { + quote! { #(#block_state_ids)|* => Some(Self::#ident), } + } + PropertyKind::Bool => { + quote! { #(#block_state_ids)|* => Some(#ident), } + } + }); + } + + try_from_block_state = quote! { + match block_state.id() { + #enum_inner_generated + _ => None + } + }; + } + + try_from_block_state +} + +pub fn get_property_kind(property_values: &[PropertyVariantData]) -> PropertyKind { + property_values + .first() + .map(|v| v.kind) + .unwrap_or(PropertyKind::Enum) +} + +pub fn get_property_variant_types(data: &PropertyData) -> Vec<PropertyVariantMeta> { + match &data { + PropertyData::Enum { variants, .. } => { + let mut property_variant_types = Vec::new(); + + for index in 0..variants.len() { + let variant_ident = variants[index].ident.clone(); + property_variant_types.push(PropertyVariantMeta { + ident: variant_ident, + index, + }); + } + + property_variant_types + } + PropertyData::Bool { .. } => { + vec![ + PropertyVariantMeta { + ident: Ident::new("true", proc_macro2::Span::call_site()), + index: 0, + }, + PropertyVariantMeta { + ident: Ident::new("false", proc_macro2::Span::call_site()), + index: 1, + }, + ] + } + } +} + +/// Returns either `bool` or the enum name. +pub fn get_property_value_type(data: &PropertyData) -> Ident { + match data { + PropertyData::Enum { enum_name, .. } => enum_name.clone(), + PropertyData::Bool { .. } => Ident::new("bool", proc_macro2::Span::call_site()), + } +} +/// Returns the enum or struct name of the property. +fn get_property_type_name(data: &PropertyData) -> Ident { + match data { + PropertyData::Enum { enum_name, .. } => enum_name.clone(), + PropertyData::Bool { struct_name } => struct_name.clone(), + } +} + +pub fn make_property_struct_names_to_names( + properties: &[PropertyDefinition], +) -> HashMap<String, String> { + let mut property_struct_names_to_names = HashMap::new(); + + for property in properties { + match &property.data { + PropertyData::Enum { enum_name, .. } => { + let property_struct_name = enum_name.clone(); + + property_struct_names_to_names.insert( + property_struct_name.to_string(), + property.name.clone().value(), + ); + } + _ => {} + } + } + + property_struct_names_to_names +} diff --git a/azalea-block/azalea-block-macros/src/property/mod.rs b/azalea-block/azalea-block-macros/src/property/mod.rs new file mode 100644 index 00000000..3f14f20a --- /dev/null +++ b/azalea-block/azalea-block-macros/src/property/mod.rs @@ -0,0 +1,2 @@ +pub mod generate; +pub mod parse; diff --git a/azalea-block/azalea-block-macros/src/property/parse.rs b/azalea-block/azalea-block-macros/src/property/parse.rs new file mode 100644 index 00000000..65dd4c41 --- /dev/null +++ b/azalea-block/azalea-block-macros/src/property/parse.rs @@ -0,0 +1,181 @@ +use proc_macro2::Ident; +use quote::quote; +use syn::{ + LitStr, Token, braced, + ext::IdentExt, + parenthesized, + parse::{self, Parse, ParseStream}, + punctuated::Punctuated, + token, +}; + +use crate::{PropertyKind, name_to_ident}; + +/// `"snowy": Snowy(false)` or `"axis": properties::Axis::Y` +#[derive(Debug)] +pub struct PropertyWithNameAndDefault { + // "snowy" "axis" + pub name: String, + /// The property name, potentially modified so it works better as a struct + /// field. + pub name_ident: Ident, + // Snowy / Axis + pub property_type: Ident, + pub property_value_type: Ident, + /// Whether it's an enum or a boolean. + pub kind: PropertyKind, + // false / properties::Axis::Y + pub default: proc_macro2::TokenStream, +} +impl Parse for PropertyWithNameAndDefault { + fn parse(input: ParseStream) -> parse::Result<Self> { + // `"snowy": Snowy(false)` or `"axis": properties::Axis::Y` + let property_name = input.parse::<LitStr>()?.value(); + input.parse::<Token![:]>()?; + + let first_ident = input.call(Ident::parse_any)?; + let mut property_default = quote! { #first_ident }; + + let property_type: Ident; + let property_value_type: Ident; + let mut kind = PropertyKind::Bool; + + if input.parse::<Token![::]>().is_ok() { + // enum + kind = PropertyKind::Enum; + property_type = first_ident.clone(); + property_value_type = first_ident; + let variant = input.parse::<Ident>()?; + property_default = quote! { properties::#property_default::#variant }; + } else { + // must be a unit struct if it's not an enum + let content; + let _paren_token: token::Paren = parenthesized!(content in input); + // we use this instead of .parse so it works with rust keywords like true and + // false + let unit_struct_inner = content.call(Ident::parse_any)?; + let unit_struct_inner_string = unit_struct_inner.to_string(); + + if matches!(unit_struct_inner_string.as_str(), "true" | "false") { + property_value_type = Ident::new("bool", first_ident.span()); + property_type = first_ident; + property_default = quote! { #unit_struct_inner }; + } else { + return Err(input.error("Expected a boolean or an enum variant")); + } + }; + + let property_name_ident = name_to_ident(&property_name); + + Ok(PropertyWithNameAndDefault { + name: property_name, + name_ident: property_name_ident, + property_type, + property_value_type, + kind, + default: property_default, + }) + } +} + +/// `"snowy" => Snowy(bool)` +pub struct PropertyDefinition { + pub name: LitStr, + pub data: PropertyData, +} +impl Parse for PropertyDefinition { + fn parse(input: ParseStream) -> parse::Result<Self> { + // "face" => Face { + // Floor, + // Wall, + // Ceiling + // }, + + // if you're wondering, the reason it's in quotes is because `type` is + // a keyword in rust so if we don't put it in quotes it results in a + // syntax error + let name = input.parse()?; + input.parse::<Token![=>]>()?; + let property_type = input.parse()?; + + input.parse::<Token![,]>()?; + Ok(PropertyDefinition { + name, + data: property_type, + }) + } +} + +pub enum PropertyData { + /// `Axis { X = "x", Y = "y", Z = "z" }` + Enum { + enum_name: Ident, + variants: Vec<PropertyVariant>, + }, + /// `Snowy(bool)` + Bool { struct_name: Ident }, +} +impl Parse for PropertyData { + // like `Axis { X = "x", Y = "y", Z = "z" }` or `Waterlogged(bool)` + fn parse(input: ParseStream) -> parse::Result<Self> { + let keyword = Ident::parse(input)?; + + fn parse_braced( + input: ParseStream, + ) -> parse::Result<Punctuated<PropertyVariant, Token![,]>> { + let content; + braced!(content in input); + let variants = content.parse_terminated(parse_variant, Token![,])?; + Ok(variants) + } + + /// Parses something like `X = "x"` + fn parse_variant(input: ParseStream) -> parse::Result<PropertyVariant> { + let ident = Ident::parse(input)?; + input.parse::<Token![=]>()?; + let name = input.parse::<syn::LitStr>()?; + Ok(PropertyVariant { ident, name }) + } + + fn parse_paren(input: ParseStream) -> parse::Result<Ident> { + let content; + parenthesized!(content in input); + let inner = content.parse::<Ident>()?; + Ok(inner) + } + + if let Ok(variants) = parse_braced(input) { + Ok(Self::Enum { + enum_name: keyword, + variants: variants.into_iter().collect(), + }) + } else if let Ok(inner) = parse_paren(input) { + assert_eq!( + inner.to_string(), + "bool", + "Currently only bool unit structs are supported" + ); + Ok(Self::Bool { + struct_name: keyword, + }) + } else { + Err(input.error("Expected a unit struct or an enum")) + } + } +} + +pub struct PropertyVariant { + /// The Rust identifier for the property variant, like `X` or `_1`. + pub ident: Ident, + /// The Minecraft name for the property variant, like `"x"` or `"1"`. + pub name: LitStr, +} + +/// Parses comma separated `PropertyDefinition`s like `"snowy" => Snowy(bool),` +pub fn parse_property_definitions(input: ParseStream) -> parse::Result<Vec<PropertyDefinition>> { + let mut property_definitions = Vec::new(); + while !input.is_empty() { + property_definitions.push(input.parse()?); + } + Ok(property_definitions) +} diff --git a/azalea-block/src/generated.rs b/azalea-block/src/generated.rs index f7ec4e6c..cd5fb011 100644 --- a/azalea-block/src/generated.rs +++ b/azalea-block/src/generated.rs @@ -13,292 +13,292 @@ make_block_states! { Properties => { "snowy" => Snowy(bool), "axis" => Axis { - X, - Y, - Z, + X = "x", + Y = "y", + Z = "z", }, "stage" => OakSaplingStage { - _0, - _1, + _0 = "0", + _1 = "1", }, "stage" => SpruceSaplingStage { - _0, - _1, + _0 = "0", + _1 = "1", }, "stage" => BirchSaplingStage { - _0, - _1, + _0 = "0", + _1 = "1", }, "stage" => JungleSaplingStage { - _0, - _1, + _0 = "0", + _1 = "1", }, "stage" => AcaciaSaplingStage { - _0, - _1, + _0 = "0", + _1 = "1", }, "stage" => CherrySaplingStage { - _0, - _1, + _0 = "0", + _1 = "1", }, "stage" => DarkOakSaplingStage { - _0, - _1, + _0 = "0", + _1 = "1", }, "stage" => PaleOakSaplingStage { - _0, - _1, + _0 = "0", + _1 = "1", }, "age" => MangrovePropaguleAge { - _0, - _1, - _2, - _3, - _4, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "hanging" => Hanging(bool), "stage" => MangrovePropaguleStage { - _0, - _1, + _0 = "0", + _1 = "1", }, "waterlogged" => Waterlogged(bool), "level" => WaterLevel { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "level" => LavaLevel { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "dusted" => SuspiciousSandDusted { - _0, - _1, - _2, - _3, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", }, "dusted" => SuspiciousGravelDusted { - _0, - _1, - _2, - _3, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", }, "distance" => OakLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "persistent" => Persistent(bool), "distance" => SpruceLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "distance" => BirchLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "distance" => JungleLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "distance" => AcaciaLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "distance" => CherryLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "distance" => DarkOakLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "distance" => PaleOakLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "distance" => MangroveLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "distance" => AzaleaLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "distance" => FloweringAzaleaLeavesDistance { - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "facing" => FacingCubic { - North, - East, - South, - West, - Up, - Down, + North = "north", + East = "east", + South = "south", + West = "west", + Up = "up", + Down = "down", }, "triggered" => Triggered(bool), "instrument" => Sound { - Harp, - Basedrum, - Snare, - Hat, - Bass, - Flute, - Bell, - Guitar, - Chime, - Xylophone, - IronXylophone, - CowBell, - Didgeridoo, - Bit, - Banjo, - Pling, - Zombie, - Skeleton, - Creeper, - Dragon, - WitherSkeleton, - Piglin, - CustomHead, + Harp = "harp", + Basedrum = "basedrum", + Snare = "snare", + Hat = "hat", + Bass = "bass", + Flute = "flute", + Bell = "bell", + Guitar = "guitar", + Chime = "chime", + Xylophone = "xylophone", + IronXylophone = "iron_xylophone", + CowBell = "cow_bell", + Didgeridoo = "didgeridoo", + Bit = "bit", + Banjo = "banjo", + Pling = "pling", + Zombie = "zombie", + Skeleton = "skeleton", + Creeper = "creeper", + Dragon = "dragon", + WitherSkeleton = "wither_skeleton", + Piglin = "piglin", + CustomHead = "custom_head", }, "note" => NoteBlockNote { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, - _16, - _17, - _18, - _19, - _20, - _21, - _22, - _23, - _24, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", + _16 = "16", + _17 = "17", + _18 = "18", + _19 = "19", + _20 = "20", + _21 = "21", + _22 = "22", + _23 = "23", + _24 = "24", }, "powered" => Powered(bool), "facing" => FacingCardinal { - North, - South, - West, - East, + North = "north", + South = "south", + West = "west", + East = "east", }, "occupied" => Occupied(bool), "part" => Part { - Head, - Foot, + Head = "head", + Foot = "foot", }, "shape" => RailShape { - NorthSouth, - EastWest, - AscendingEast, - AscendingWest, - AscendingNorth, - AscendingSouth, + NorthSouth = "north_south", + EastWest = "east_west", + AscendingEast = "ascending_east", + AscendingWest = "ascending_west", + AscendingNorth = "ascending_north", + AscendingSouth = "ascending_south", }, "extended" => Extended(bool), "half" => Half { - Upper, - Lower, + Upper = "upper", + Lower = "lower", }, "type" => PistonType { - Normal, - Sticky, + Normal = "normal", + Sticky = "sticky", }, "short" => Short(bool), "unstable" => Unstable(bool), @@ -309,22 +309,22 @@ make_block_states! { "slot_4_occupied" => Slot4Occupied(bool), "slot_5_occupied" => Slot5Occupied(bool), "age" => FireAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "east" => East(bool), "north" => North(bool), @@ -332,1687 +332,1687 @@ make_block_states! { "up" => Up(bool), "west" => West(bool), "creaking_heart_state" => CreakingHeartState { - Uprooted, - Dormant, - Awake, + Uprooted = "uprooted", + Dormant = "dormant", + Awake = "awake", }, "natural" => Natural(bool), "half" => TopBottom { - Top, - Bottom, + Top = "top", + Bottom = "bottom", }, "shape" => StairShape { - Straight, - InnerLeft, - InnerRight, - OuterLeft, - OuterRight, + Straight = "straight", + InnerLeft = "inner_left", + InnerRight = "inner_right", + OuterLeft = "outer_left", + OuterRight = "outer_right", }, "type" => ChestType { - Single, - Left, - Right, + Single = "single", + Left = "left", + Right = "right", }, "east" => WireEast { - Up, - Side, - None, + Up = "up", + Side = "side", + None = "none", }, "north" => WireNorth { - Up, - Side, - None, + Up = "up", + Side = "side", + None = "none", }, "power" => RedstoneWirePower { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "south" => WireSouth { - Up, - Side, - None, + Up = "up", + Side = "side", + None = "none", }, "west" => WireWest { - Up, - Side, - None, + Up = "up", + Side = "side", + None = "none", }, "age" => WheatAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "moisture" => FarmlandMoisture { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "lit" => Lit(bool), "rotation" => OakSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => SpruceSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => BirchSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => AcaciaSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => CherrySignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => JungleSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => DarkOakSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => PaleOakSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => MangroveSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => BambooSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "hinge" => Hinge { - Left, - Right, + Left = "left", + Right = "right", }, "open" => Open(bool), "shape" => Shape { - NorthSouth, - EastWest, - AscendingEast, - AscendingWest, - AscendingNorth, - AscendingSouth, - SouthEast, - SouthWest, - NorthWest, - NorthEast, + NorthSouth = "north_south", + EastWest = "east_west", + AscendingEast = "ascending_east", + AscendingWest = "ascending_west", + AscendingNorth = "ascending_north", + AscendingSouth = "ascending_south", + SouthEast = "south_east", + SouthWest = "south_west", + NorthWest = "north_west", + NorthEast = "north_east", }, "attached" => Attached(bool), "rotation" => OakHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => SpruceHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => BirchHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => AcaciaHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => CherryHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => JungleHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => DarkOakHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => PaleOakHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => CrimsonHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => WarpedHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => MangroveHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => BambooHangingSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "face" => Face { - Floor, - Wall, - Ceiling, + Floor = "floor", + Wall = "wall", + Ceiling = "ceiling", }, "layers" => SnowLayers { - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", }, "age" => CactusAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "age" => SugarCaneAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "has_record" => HasRecord(bool), "axis" => AxisXZ { - X, - Z, + X = "x", + Z = "z", }, "bites" => CakeBites { - _0, - _1, - _2, - _3, - _4, - _5, - _6, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", }, "delay" => RepeaterDelay { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "locked" => Locked(bool), "down" => Down(bool), "age" => PumpkinStemAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "age" => MelonStemAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "in_wall" => InWall(bool), "type" => Type { - Top, - Bottom, - Double, + Top = "top", + Bottom = "bottom", + Double = "double", }, "east" => WallEast { - None, - Low, - Tall, + None = "none", + Low = "low", + Tall = "tall", }, "north" => WallNorth { - None, - Low, - Tall, + None = "none", + Low = "low", + Tall = "tall", }, "south" => WallSouth { - None, - Low, - Tall, + None = "none", + Low = "low", + Tall = "tall", }, "west" => WallWest { - None, - Low, - Tall, + None = "none", + Low = "low", + Tall = "tall", }, "age" => NetherWartAge { - _0, - _1, - _2, - _3, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", }, "has_bottle_0" => HasBottle0(bool), "has_bottle_1" => HasBottle1(bool), "has_bottle_2" => HasBottle2(bool), "level" => WaterCauldronLevel { - _1, - _2, - _3, + _1 = "1", + _2 = "2", + _3 = "3", }, "level" => PowderSnowCauldronLevel { - _1, - _2, - _3, + _1 = "1", + _2 = "2", + _3 = "3", }, "eye" => Eye(bool), "age" => CocoaAge { - _0, - _1, - _2, + _0 = "0", + _1 = "1", + _2 = "2", }, "disarmed" => Disarmed(bool), "conditional" => Conditional(bool), "age" => CarrotsAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "age" => PotatoesAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "rotation" => SkeletonSkullRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => WitherSkeletonSkullRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => ZombieHeadRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => PlayerHeadRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => CreeperHeadRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => DragonHeadRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => PiglinHeadRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "power" => LightWeightedPressurePlatePower { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "power" => HeavyWeightedPressurePlatePower { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "mode" => ComparatorType { - Compare, - Subtract, + Compare = "compare", + Subtract = "subtract", }, "inverted" => Inverted(bool), "power" => DaylightDetectorPower { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "enabled" => Enabled(bool), "facing" => Facing { - Down, - North, - South, - West, - East, + Down = "down", + North = "north", + South = "south", + West = "west", + East = "east", }, "level" => LightLevel { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => WhiteBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => OrangeBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => MagentaBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => LightBlueBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => YellowBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => LimeBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => PinkBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => GrayBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => LightGrayBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => CyanBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => PurpleBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => BlueBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => BrownBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => GreenBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => RedBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => BlackBannerRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "age" => ChorusFlowerAge { - _0, - _1, - _2, - _3, - _4, - _5, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", }, "age" => TorchflowerCropAge { - _0, - _1, + _0 = "0", + _1 = "1", }, "age" => PitcherCropAge { - _0, - _1, - _2, - _3, - _4, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "age" => BeetrootsAge { - _0, - _1, - _2, - _3, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", }, "age" => FrostedIceAge { - _0, - _1, - _2, - _3, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", }, "age" => KelpAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, - _16, - _17, - _18, - _19, - _20, - _21, - _22, - _23, - _24, - _25, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", + _16 = "16", + _17 = "17", + _18 = "18", + _19 = "19", + _20 = "20", + _21 = "21", + _22 = "22", + _23 = "23", + _24 = "24", + _25 = "25", }, "eggs" => TurtleEggEggs { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "hatch" => TurtleEggHatch { - _0, - _1, - _2, + _0 = "0", + _1 = "1", + _2 = "2", }, "hatch" => SnifferEggHatch { - _0, - _1, - _2, + _0 = "0", + _1 = "1", + _2 = "2", }, "hydration" => DriedGhastHydration { - _0, - _1, - _2, - _3, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", }, "pickles" => SeaPicklePickles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "age" => BambooAge { - _0, - _1, + _0 = "0", + _1 = "1", }, "leaves" => Leaves { - None, - Small, - Large, + None = "none", + Small = "small", + Large = "large", }, "stage" => BambooStage { - _0, - _1, + _0 = "0", + _1 = "1", }, "drag" => Drag(bool), "bottom" => Bottom(bool), "distance" => ScaffoldingDistance { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", }, "has_book" => HasBook(bool), "attachment" => Attachment { - Floor, - Ceiling, - SingleWall, - DoubleWall, + Floor = "floor", + Ceiling = "ceiling", + SingleWall = "single_wall", + DoubleWall = "double_wall", }, "signal_fire" => SignalFire(bool), "age" => SweetBerryBushAge { - _0, - _1, - _2, - _3, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", }, "age" => WeepingVinesAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, - _16, - _17, - _18, - _19, - _20, - _21, - _22, - _23, - _24, - _25, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", + _16 = "16", + _17 = "17", + _18 = "18", + _19 = "19", + _20 = "20", + _21 = "21", + _22 = "22", + _23 = "23", + _24 = "24", + _25 = "25", }, "age" => TwistingVinesAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, - _16, - _17, - _18, - _19, - _20, - _21, - _22, - _23, - _24, - _25, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", + _16 = "16", + _17 = "17", + _18 = "18", + _19 = "19", + _20 = "20", + _21 = "21", + _22 = "22", + _23 = "23", + _24 = "24", + _25 = "25", }, "rotation" => CrimsonSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "rotation" => WarpedSignRotation { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "mode" => StructureMode { - Save, - Load, - Corner, - Data, + Save = "save", + Load = "load", + Corner = "corner", + Data = "data", }, "orientation" => Orientation { - DownEast, - DownNorth, - DownSouth, - DownWest, - UpEast, - UpNorth, - UpSouth, - UpWest, - WestUp, - EastUp, - NorthUp, - SouthUp, + DownEast = "down_east", + DownNorth = "down_north", + DownSouth = "down_south", + DownWest = "down_west", + UpEast = "up_east", + UpNorth = "up_north", + UpSouth = "up_south", + UpWest = "up_west", + WestUp = "west_up", + EastUp = "east_up", + NorthUp = "north_up", + SouthUp = "south_up", }, "mode" => TestMode { - Start, - Log, - Fail, - Accept, + Start = "start", + Log = "log", + Fail = "fail", + Accept = "accept", }, "level" => ComposterLevel { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", }, "power" => TargetPower { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "honey_level" => BeeNestHoneyLevel { - _0, - _1, - _2, - _3, - _4, - _5, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", }, "honey_level" => BeehiveHoneyLevel { - _0, - _1, - _2, - _3, - _4, - _5, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", }, "charges" => RespawnAnchorCharges { - _0, - _1, - _2, - _3, - _4, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => CandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => WhiteCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => OrangeCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => MagentaCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => LightBlueCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => YellowCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => LimeCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => PinkCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => GrayCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => LightGrayCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => CyanCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => PurpleCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => BlueCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => BrownCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => GreenCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => RedCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "candles" => BlackCandleCandles { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "power" => SculkSensorPower { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "sculk_sensor_phase" => SculkSensorPhase { - Inactive, - Active, - Cooldown, + Inactive = "inactive", + Active = "active", + Cooldown = "cooldown", }, "power" => CalibratedSculkSensorPower { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", }, "bloom" => Bloom(bool), "can_summon" => CanSummon(bool), "shrieking" => Shrieking(bool), "thickness" => Thickness { - TipMerge, - Tip, - Frustum, - Middle, - Base, + TipMerge = "tip_merge", + Tip = "tip", + Frustum = "frustum", + Middle = "middle", + Base = "base", }, "vertical_direction" => VerticalDirection { - Up, - Down, + Up = "up", + Down = "down", }, "age" => CaveVinesAge { - _0, - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, - _16, - _17, - _18, - _19, - _20, - _21, - _22, - _23, - _24, - _25, + _0 = "0", + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", + _5 = "5", + _6 = "6", + _7 = "7", + _8 = "8", + _9 = "9", + _10 = "10", + _11 = "11", + _12 = "12", + _13 = "13", + _14 = "14", + _15 = "15", + _16 = "16", + _17 = "17", + _18 = "18", + _19 = "19", + _20 = "20", + _21 = "21", + _22 = "22", + _23 = "23", + _24 = "24", + _25 = "25", }, "berries" => Berries(bool), "flower_amount" => PinkPetalsFlowerAmount { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "flower_amount" => WildflowersFlowerAmount { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "segment_amount" => LeafLitterSegmentAmount { - _1, - _2, - _3, - _4, + _1 = "1", + _2 = "2", + _3 = "3", + _4 = "4", }, "tilt" => Tilt { - None, - Unstable, - Partial, - Full, + None = "none", + Unstable = "unstable", + Partial = "partial", + Full = "full", }, "cracked" => Cracked(bool), "crafting" => Crafting(bool), "ominous" => Ominous(bool), "trial_spawner_state" => TrialSpawnerState { - Inactive, - WaitingForPlayers, - Active, - WaitingForRewardEjection, - EjectingReward, - Cooldown, + Inactive = "inactive", + WaitingForPlayers = "waiting_for_players", + Active = "active", + WaitingForRewardEjection = "waiting_for_reward_ejection", + EjectingReward = "ejecting_reward", + Cooldown = "cooldown", }, "vault_state" => VaultState { - Inactive, - Active, - Unlocking, - Ejecting, + Inactive = "inactive", + Active = "active", + Unlocking = "unlocking", + Ejecting = "ejecting", }, "tip" => Tip(bool), }, diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs index ead63bef..aa6d5dba 100644 --- a/azalea-block/src/lib.rs +++ b/azalea-block/src/lib.rs @@ -7,7 +7,7 @@ mod generated; mod range; use core::fmt::Debug; -use std::any::Any; +use std::{any::Any, collections::HashMap}; pub use behavior::BlockBehavior; // re-exported for convenience @@ -17,16 +17,34 @@ pub use range::BlockStates; pub trait BlockTrait: Debug + Any { fn behavior(&self) -> BlockBehavior; - /// Get the Minecraft ID for this block. For example `stone` or - /// `grass_block`. + /// Get the Minecraft string ID for this block. + /// + /// For example, `stone` or `grass_block`. fn id(&self) -> &'static str; - /// Convert the block to a block state. This is lossless, as the block - /// contains all the state data. + /// Convert the block to a block state. + /// + /// This is a lossless conversion, as [`BlockState`] also contains state + /// data. fn as_block_state(&self) -> BlockState; - /// Convert the block to an [`azalea_registry::Block`]. This is lossy, as - /// `azalea_registry::Block` doesn't contain any state data. + /// Convert the block to an [`azalea_registry::Block`]. + /// + /// This is a lossy conversion, as [`azalea_registry::Block`] doesn't + /// contain any state data. fn as_registry_block(&self) -> azalea_registry::Block; + + /// Returns a map of property names on this block to their values as + /// strings. + /// + /// Consider using [`Self::get_property`] if you only need a single + /// property. + fn property_map(&self) -> HashMap<&'static str, &'static str>; + /// Get a property's value as a string by its name, or `None` if the block + /// has no property with that name. + /// + /// To get all properties, you may use [`Self::property_map`]. + fn get_property(&self, name: &str) -> Option<&'static str>; } + impl dyn BlockTrait { pub fn downcast_ref<T: BlockTrait>(&self) -> Option<&T> { (self as &dyn Any).downcast_ref::<T>() @@ -37,6 +55,9 @@ pub trait Property { type Value; fn try_from_block_state(state: BlockState) -> Option<Self::Value>; + + /// Convert the value of the property to a string, like "x" or "true". + fn to_static_str(&self) -> &'static str; } #[cfg(test)] @@ -60,4 +81,51 @@ mod tests { .clone(); assert_eq!(block, block_from_state); } + + #[test] + pub fn test_property_map() { + let block = crate::blocks::OakTrapdoor { + facing: crate::properties::FacingCardinal::East, + half: crate::properties::TopBottom::Bottom, + open: true, + powered: false, + waterlogged: false, + }; + + let property_map = block.property_map(); + + assert_eq!(property_map.len(), 5); + assert_eq!(property_map.get("facing"), Some(&"east")); + assert_eq!(property_map.get("half"), Some(&"bottom")); + assert_eq!(property_map.get("open"), Some(&"true")); + assert_eq!(property_map.get("powered"), Some(&"false")); + assert_eq!(property_map.get("waterlogged"), Some(&"false")); + } + + #[test] + pub fn test_integer_properties() { + // Test with oak sapling that has an integer-like stage property + let sapling_stage_0 = crate::blocks::OakSapling { + stage: crate::properties::OakSaplingStage::_0, + }; + + let sapling_stage_1 = crate::blocks::OakSapling { + stage: crate::properties::OakSaplingStage::_1, + }; + + // Test stage 0 + let properties_0 = sapling_stage_0.property_map(); + assert_eq!(properties_0.len(), 1); + assert_eq!(properties_0.get("stage"), Some(&"0")); + assert_eq!(sapling_stage_0.get_property("stage"), Some("0")); + + // Test stage 1 + let properties_1 = sapling_stage_1.property_map(); + assert_eq!(properties_1.len(), 1); + assert_eq!(properties_1.get("stage"), Some(&"1")); + assert_eq!(sapling_stage_1.get_property("stage"), Some("1")); + + // Test non-existent property + assert_eq!(sapling_stage_0.get_property("nonexistent"), None); + } } diff --git a/codegen/lib/code/blocks.py b/codegen/lib/code/blocks.py index 550ccf83..9f783690 100644 --- a/codegen/lib/code/blocks.py +++ b/codegen/lib/code/blocks.py @@ -74,7 +74,9 @@ def generate_blocks( else: property_shape_code = f"{property_struct_name} {{\n" for variant in property_variants: - property_shape_code += f" {to_camel_case(variant)},\n" + property_shape_code += ( + f' {to_camel_case(variant)} = "{variant}",\n' + ) property_shape_code += " }" new_make_block_states_macro_code.append( |
