aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorurisinger <60300761+urisinger@users.noreply.github.com>2025-09-15 07:51:50 +0300
committerGitHub <noreply@github.com>2025-09-14 23:51:50 -0500
commitbcefd0213db05b4c29c82a1031f4d6e838e1fc1f (patch)
tree563797281982fbcced3491e8b3096448903fddc9
parent3a58a39563a596e3b08ad41e93070c84e205bddb (diff)
downloadazalea-drasl-bcefd0213db05b4c29c82a1031f4d6e838e1fc1f.tar.xz
Add functions for getting block properties as strings (#240)
* add tests and fix * fix integer properties * refactor azalea-block-macros and improve apis --------- Co-authored-by: mat <git@matdoes.dev>
-rw-r--r--azalea-block/azalea-block-macros/src/lib.rs532
-rw-r--r--azalea-block/azalea-block-macros/src/property/generate.rs302
-rw-r--r--azalea-block/azalea-block-macros/src/property/mod.rs2
-rw-r--r--azalea-block/azalea-block-macros/src/property/parse.rs181
-rw-r--r--azalea-block/src/generated.rs3202
-rw-r--r--azalea-block/src/lib.rs82
-rw-r--r--codegen/lib/code/blocks.py4
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(