aboutsummaryrefslogtreecommitdiff
path: root/azalea-block/azalea-block-macros/src
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2022-10-02 12:29:47 -0500
committerGitHub <noreply@github.com>2022-10-02 12:29:47 -0500
commitc9b4dccd7eaeed68ce96cf5167916417d0baa6a7 (patch)
tree0b381ee72a1486ccb22fe22158b5d7d3edaf3f99 /azalea-block/azalea-block-macros/src
parentaa78491ee09ec0c6879e6edde349ca67cf809daf (diff)
downloadazalea-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.rs312
-rw-r--r--azalea-block/azalea-block-macros/src/utils.rs9
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;