diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2022-10-02 12:29:47 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-02 12:29:47 -0500 |
| commit | c9b4dccd7eaeed68ce96cf5167916417d0baa6a7 (patch) | |
| tree | 0b381ee72a1486ccb22fe22158b5d7d3edaf3f99 /azalea-block/azalea-block-macros/src | |
| parent | aa78491ee09ec0c6879e6edde349ca67cf809daf (diff) | |
| download | azalea-drasl-c9b4dccd7eaeed68ce96cf5167916417d0baa6a7.tar.xz | |
All block shapes & collisions (#22)
* start adding shapes
* add more collision stuff
* DiscreteCubeMerger
* more mergers
* start adding BitSetDiscreteVoxelShape::join
* i love rust :smiley: :smiley: :smiley:
* r
* IT COMPILES????
* fix warning
* fix error
* fix more clippy issues
* add box_shape
* more shape stuff
* make DiscreteVoxelShape an enum
* Update shape.rs
* also make VoxelShape an enum
* implement BitSet::clear
* add more missing things
* it compiles
W
* start block shape codegen
* optimize shape codegen
* make az-block/blocks.rs look better (broken)
* almost new block macro
* make the codegen not generate 'type'
* try to fix
* work more on the blocks macro
* wait it compiles
* fix clippy issues
* shapes codegen works
* well it's almost working
* simplify some shape codegen
* enum type names are correct
* W it compiles
* cargo check no longer warns
* fix some clippy issues
* start making it so the shape impl is on BlockStates
* insane code
* new impl compiles
* fix wrong find_bits + TESTS PASS!
* add a test for slab collision
* fix clippy issues
* ok rust
* fix error that happens when on stairs
* add test for top slabs
* start adding join_is_not_empty
* add more to join_is_not_empty
* top slabs still don't work!!
* x..=0 doesn't work in rust :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley: :smiley:
* remove comment since i added more useful names
* remove some printlns
* fix walls in some configurations erroring
* fix some warnings
* change comment to \`\`\`ignore instead of \`\`\`no_run
* players are .6 wide not .8
* fix clippy's complaints
* i missed one clippy warning
Diffstat (limited to 'azalea-block/azalea-block-macros/src')
| -rw-r--r-- | azalea-block/azalea-block-macros/src/lib.rs | 312 | ||||
| -rw-r--r-- | azalea-block/azalea-block-macros/src/utils.rs | 9 |
2 files changed, 212 insertions, 109 deletions
diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs index 85887418..0c226ec6 100644 --- a/azalea-block/azalea-block-macros/src/lib.rs +++ b/azalea-block/azalea-block-macros/src/lib.rs @@ -6,6 +6,7 @@ use std::collections::HashMap; use std::fmt::Write; use syn::{ self, braced, + ext::IdentExt, parse::{Parse, ParseStream, Result}, parse_macro_input, punctuated::Punctuated, @@ -13,38 +14,79 @@ use syn::{ }; use utils::{combinations_of, to_pascal_case}; +enum PropertyType { + /// `Axis { X, Y, Z }` + Enum { + type_name: Ident, + variants: Punctuated<Ident, Token![,]>, + }, + /// `bool` + Boolean, +} + +/// `"snowy" => bool` struct PropertyDefinition { name: LitStr, - struct_name: Ident, - variants: Punctuated<Ident, Token![,]>, + property_type: PropertyType, } + +/// Comma separated PropertyDefinitions (`"snowy" => bool,`) struct PropertyDefinitions { properties: Vec<PropertyDefinition>, } -struct PropertyAndDefault { - struct_name: Ident, - default: Ident, -} +/// `snowy: false` or `axis: Axis::Y` +#[derive(Debug)] struct PropertyWithNameAndDefault { - name: String, - struct_name: Ident, - default: Ident, + name: Ident, + property_type: Ident, + is_enum: bool, + default: proc_macro2::TokenStream, } + +/// ```ignore +/// grass_block => BlockBehavior::default(), { +/// snowy: false, +/// }, +/// ``` struct BlockDefinition { name: Ident, behavior: Expr, - properties_and_defaults: Vec<PropertyAndDefault>, + properties_and_defaults: Vec<PropertyWithNameAndDefault>, } -impl PropertyAndDefault { - fn as_property_with_name_and_default(&self, name: String) -> PropertyWithNameAndDefault { - PropertyWithNameAndDefault { - name, - struct_name: self.struct_name.clone(), - default: self.default.clone(), - } +impl Parse for PropertyWithNameAndDefault { + fn parse(input: ParseStream) -> Result<Self> { + // `snowy: false` or `axis: Axis::Y` + let property_name = input.parse()?; + input.parse::<Token![:]>()?; + + let first_ident = input.call(Ident::parse_any)?; + let first_ident_string = first_ident.to_string(); + let mut property_default = quote! { #first_ident }; + + let property_type: Ident; + let mut is_enum = false; + + if input.parse::<Token![::]>().is_ok() { + is_enum = true; + property_type = first_ident; + let variant = input.parse::<Ident>()?; + property_default.extend(quote! { ::#variant }) + } else if first_ident_string == "true" || first_ident_string == "false" { + property_type = Ident::new("bool", first_ident.span()); + } else { + return Err(input.error("Expected a boolean or an enum variant")); + }; + + Ok(PropertyWithNameAndDefault { + name: property_name, + property_type, + is_enum, + default: property_default, + }) } } + struct BlockDefinitions { blocks: Vec<BlockDefinition>, } @@ -53,6 +95,26 @@ struct MakeBlockStates { block_definitions: BlockDefinitions, } +impl Parse for PropertyType { + fn parse(input: ParseStream) -> Result<Self> { + // like `Axis { X, Y, Z }` or `bool` + + let keyword = Ident::parse(input)?; + let keyword_string = keyword.to_string(); + if keyword_string == "bool" { + Ok(Self::Boolean) + } else { + let content; + braced!(content in input); + let variants = content.parse_terminated(Ident::parse)?; + Ok(Self::Enum { + type_name: keyword, + variants, + }) + } + } +} + impl Parse for PropertyDefinition { fn parse(input: ParseStream) -> Result<Self> { // "face" => Face { @@ -66,17 +128,12 @@ impl Parse for PropertyDefinition { // syntax error let name = input.parse()?; input.parse::<Token![=>]>()?; - let struct_name = input.parse()?; - - let content; - braced!(content in input); - let variants = content.parse_terminated(Ident::parse)?; + let property_type = input.parse()?; input.parse::<Token![,]>()?; Ok(PropertyDefinition { name, - struct_name, - variants, + property_type, }) } } @@ -100,7 +157,7 @@ impl Parse for BlockDefinition { // Facing=North, // Powered=False, // Face=Wall, - // }, + // } let name = input.parse()?; input.parse::<Token![=>]>()?; let behavior = input.parse()?; @@ -111,18 +168,14 @@ impl Parse for BlockDefinition { let mut properties_and_defaults = Vec::new(); - while let Ok(property) = content.parse() { - content.parse::<Token![=]>()?; - let property_default = content.parse()?; - properties_and_defaults.push(PropertyAndDefault { - struct_name: property, - default: property_default, - }); - if content.parse::<Token![,]>().is_err() { - break; - } + // read the things comma-separated + let property_and_default_punctuated: Punctuated<PropertyWithNameAndDefault, Token![,]> = + content.parse_terminated(PropertyWithNameAndDefault::parse)?; + + for property_and_default in property_and_default_punctuated { + properties_and_defaults.push(property_and_default); } - input.parse::<Token![,]>()?; + Ok(BlockDefinition { name, behavior, @@ -134,8 +187,11 @@ impl Parse for BlockDefinition { impl Parse for BlockDefinitions { fn parse(input: ParseStream) -> Result<Self> { let mut blocks = Vec::new(); - while !input.is_empty() { - blocks.push(input.parse()?); + + let block_definitions_punctuated: Punctuated<BlockDefinition, Token![,]> = + input.parse_terminated(BlockDefinition::parse)?; + for block_definition in block_definitions_punctuated { + blocks.push(block_definition); } Ok(BlockDefinitions { blocks }) @@ -179,57 +235,70 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let mut state_id: usize = 0; for property in &input.property_definitions.properties { - let mut property_enum_variants = quote! {}; - let mut property_from_number_variants = quote! {}; - let mut property_enum_variant_names = Vec::new(); - - let property_struct_name = &property.struct_name; - - property_struct_names_to_names.insert( - property_struct_name.to_string(), - property.name.clone().value(), - ); - - for i in 0..property.variants.len() { - let variant = &property.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_enum_variant_names.push(variant.to_string()); - } + let property_type_name: Ident; + let mut property_variant_types = Vec::new(); + + match &property.property_type { + PropertyType::Enum { + type_name, + variants, + } => { + let mut property_enum_variants = quote! {}; + let mut property_from_number_variants = quote! {}; + + property_type_name = type_name.clone(); + + property_struct_names_to_names.insert( + property_type_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_type_name::#variant, + }); + + property_variant_types.push(variant.to_string()); + } - property_enums.extend(quote! { - #[derive(Debug, Clone, Copy)] - pub enum #property_struct_name { - #property_enum_variants - } + property_enums.extend(quote! { + #[derive(Debug, Clone, Copy)] + pub enum #property_type_name { + #property_enum_variants + } - impl From<usize> for #property_struct_name { - fn from(value: usize) -> Self { - match value { - #property_from_number_variants - _ => panic!("Invalid property value: {}", value), + impl From<usize> for #property_type_name { + fn from(value: usize) -> Self { + match value { + #property_from_number_variants + _ => panic!("Invalid property value: {}", value), + } + } } - } + }); } - }); - properties_map.insert( - property_struct_name.to_string(), - property_enum_variant_names, - ); + PropertyType::Boolean => { + property_type_name = Ident::new("bool", proc_macro2::Span::call_site()); + // property_type_name = + // Ident::new(&property.name.value(), proc_macro2::Span::call_site()); + property_variant_types = vec!["true".to_string(), "false".to_string()]; + } + } + properties_map.insert(property_type_name.to_string(), property_variant_types); + // properties_map.insert(property.name.value(), property_variant_types); } let mut block_state_enum_variants = quote! {}; @@ -239,10 +308,13 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let block_property_names = &block .properties_and_defaults .iter() - .map(|p| p.struct_name.to_string()) + .map(|p| p.property_type.to_string()) .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 '{}' not found", property_name)) @@ -252,34 +324,46 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let mut properties_with_name: Vec<PropertyWithNameAndDefault> = Vec::with_capacity(block.properties_and_defaults.len()); + // Used to determine the index of the property so we can optionally add a number to it + let mut previous_names: Vec<String> = Vec::new(); for property in &block.properties_and_defaults { let index: Option<usize> = if block .properties_and_defaults .iter() - .filter(|p| p.struct_name == property.struct_name) + .filter(|p| p.name == property.name) .count() > 1 { Some( - properties_with_name + previous_names .iter() - .filter(|p| p.struct_name == property.struct_name) + .filter(|&p| p == &property.name.to_string()) .count(), ) } else { None }; + // let mut property_name = property_struct_names_to_names + // .get(&property.property_type.to_string()) + // .unwrap_or_else(|| panic!("Property '{}' is bad", property.property_type)) + // .clone(); let mut property_name = property_struct_names_to_names - .get(&property.struct_name.to_string()) - .unwrap_or_else(|| panic!("Property '{}' is bad", property.struct_name)) - .clone(); + .get(&property.name.to_string()) + .cloned() + .unwrap_or_else(|| property.name.to_string()); + previous_names.push(property_name.clone()); if let Some(index) = index { // property_name.push_str(&format!("_{}", &index.to_string())); write!(property_name, "_{}", index).unwrap(); } - properties_with_name - .push(property.as_property_with_name_and_default(property_name.clone())); + properties_with_name.push(PropertyWithNameAndDefault { + name: Ident::new(&property_name, proc_macro2::Span::call_site()), + property_type: property.property_type.clone(), + is_enum: property.is_enum, + default: property.default.clone(), + }); } + drop(previous_names); // pub face: properties::Face, // pub facing: properties::Facing, @@ -290,14 +374,15 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { // pub has_bottle_2: HasBottle, let mut block_struct_fields = quote! {}; for PropertyWithNameAndDefault { - struct_name, name, .. + property_type: struct_name, + name, + .. } in &properties_with_name { // let property_name_snake = // Ident::new(&property.to_string(), proc_macro2::Span::call_site()); - let name_ident = Ident::new(name, proc_macro2::Span::call_site()); block_struct_fields.extend(quote! { - pub #name_ident: #struct_name, + pub #name: #struct_name, }) } @@ -329,7 +414,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { block_name_pascal_case, combination .iter() - .map(|v| v.to_string()) + .map(|v| v[0..1].to_uppercase() + &v[1..]) .collect::<Vec<String>>() .join("") ), @@ -346,13 +431,18 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { for i in 0..properties_with_name.len() { let property = &properties_with_name[i]; let property_name = &property.name; - let property_name_ident = Ident::new(property_name, proc_macro2::Span::call_site()); - let property_struct_name_ident = &property.struct_name; + let property_struct_name_ident = &property.property_type; let variant = Ident::new(&combination[i].to_string(), proc_macro2::Span::call_site()); + let property_type = if property.is_enum { + quote! {#property_struct_name_ident::#variant} + } else { + quote! {#variant} + }; + from_block_to_state_combination_match_inner.extend(quote! { - #property_name_ident: #property_struct_name_ident::#variant, + #property_name: #property_type, }); } @@ -375,16 +465,23 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let mut division = 1usize; for i in (0..properties_with_name.len()).rev() { let PropertyWithNameAndDefault { - struct_name: property_struct_name_ident, + property_type: property_struct_name_ident, name: property_name, .. } = &properties_with_name[i]; let property_variants = &block_properties_vec[i]; let property_variants_count = property_variants.len(); - let property_name_ident = Ident::new(property_name, proc_macro2::Span::call_site()); + let conversion_code = { + if &property_struct_name_ident.to_string() == "bool" { + assert_eq!(property_variants_count, 2); + quote! {(b / #division) % #property_variants_count != 0} + } else { + quote! {#property_struct_name_ident::from((b / #division) % #property_variants_count)} + } + }; from_state_to_block_inner.extend(quote! { - #property_name_ident: #property_struct_name_ident::from((b / #division) % #property_variants_count), + #property_name: #conversion_code, }); division *= property_variants_count; @@ -402,15 +499,12 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let mut block_default_fields = quote! {}; for PropertyWithNameAndDefault { - struct_name: struct_name_ident, name, default: property_default, + .. } in properties_with_name { - let name_ident = Ident::new(&name, proc_macro2::Span::call_site()); - block_default_fields.extend(quote! { - #name_ident: #struct_name_ident::#property_default, - }) + block_default_fields.extend(quote! {#name: #property_default,}) } let block_behavior = &block.behavior; diff --git a/azalea-block/azalea-block-macros/src/utils.rs b/azalea-block/azalea-block-macros/src/utils.rs index 82095d86..6e0acc61 100644 --- a/azalea-block/azalea-block-macros/src/utils.rs +++ b/azalea-block/azalea-block-macros/src/utils.rs @@ -23,8 +23,17 @@ pub fn combinations_of<T: Clone>(items: &[Vec<T>]) -> Vec<Vec<T>> { } pub fn to_pascal_case(s: &str) -> String { + // we get the first item later so this is to make it impossible for that + // to error + if s.is_empty() { + return String::new(); + } + let mut result = String::new(); let mut prev_was_underscore = true; // set to true by default so the first character is capitalized + if s.chars().next().unwrap().is_numeric() { + result.push('_'); + } for c in s.chars() { if c == '_' { prev_was_underscore = true; |
