aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2023-12-09 21:45:36 -0600
committermat <git@matdoes.dev>2023-12-09 21:45:36 -0600
commitf15f0325c0a9d62f10ea22b891ab7134391b04fb (patch)
tree5ac131e9841378a3d8afd1cefa7a2861f7c9bd46
parent10ee7e147dc56e12e61ac3bd962abe85ddf25934 (diff)
downloadazalea-drasl-f15f0325c0a9d62f10ea22b891ab7134391b04fb.tar.xz
add BlockState::property
-rwxr-xr-xazalea-block/azalea-block-macros/src/lib.rs162
-rwxr-xr-xazalea-block/src/generated.rs3
-rwxr-xr-xazalea-block/src/lib.rs11
-rw-r--r--azalea-world/src/heightmap.rs5
-rw-r--r--azalea/src/pathfinder/world.rs5
5 files changed, 154 insertions, 32 deletions
diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs
index 245b605e..270b49c7 100755
--- a/azalea-block/azalea-block-macros/src/lib.rs
+++ b/azalea-block/azalea-block-macros/src/lib.rs
@@ -42,9 +42,13 @@ struct PropertyDefinitions {
/// `snowy: Snowy(false)` or `axis: properties::Axis::Y`
#[derive(Debug)]
struct PropertyWithNameAndDefault {
+ // "snowy" / "axis"
name: Ident,
+ // Snowy / Axis
property_type: Ident,
+ property_value_type: Ident,
is_enum: bool,
+ // false / properties::Axis::Y
default: proc_macro2::TokenStream,
}
@@ -60,7 +64,7 @@ struct BlockDefinition {
}
impl Parse for PropertyWithNameAndDefault {
fn parse(input: ParseStream) -> Result<Self> {
- // `snowy: false` or `axis: properties::Axis::Y`
+ // `snowy: Snowy(false)` or `axis: properties::Axis::Y`
let property_name = input.parse()?;
input.parse::<Token![:]>()?;
@@ -68,12 +72,14 @@ impl Parse for PropertyWithNameAndDefault {
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;
+ property_type = first_ident.clone();
+ property_value_type = first_ident;
let variant = input.parse::<Ident>()?;
property_default = quote! { properties::#property_default::#variant };
} else {
@@ -86,7 +92,8 @@ impl Parse for PropertyWithNameAndDefault {
let unit_struct_inner_string = unit_struct_inner.to_string();
if matches!(unit_struct_inner_string.as_str(), "true" | "false") {
- property_type = Ident::new("bool", first_ident.span());
+ 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"));
@@ -96,6 +103,7 @@ impl Parse for PropertyWithNameAndDefault {
Ok(PropertyWithNameAndDefault {
name: property_name,
property_type,
+ property_value_type,
is_enum,
default: property_default,
})
@@ -259,6 +267,12 @@ impl Parse for MakeBlockStates {
}
}
+struct PropertyVariantData {
+ pub block_state_ids: Vec<u32>,
+ pub ident: Ident,
+ pub is_enum: bool,
+}
+
#[proc_macro]
pub fn make_block_states(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as MakeBlockStates);
@@ -318,6 +332,12 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
#property_enum_variants
}
+ // impl Property for #property_struct_name {
+ // type Value = Self;
+
+ // fn try_from_block_state
+ // }
+
impl From<u32> for #property_struct_name {
fn from(value: u32) -> Self {
match value {
@@ -337,6 +357,10 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
#[derive(Debug, Clone, Copy)]
pub struct #property_struct_name(pub bool);
+ // impl Property for #property_struct_name {
+ // type Value = bool;
+ // }
+
impl From<u32> for #property_struct_name {
fn from(value: u32) -> Self {
match value {
@@ -360,14 +384,19 @@ 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! {};
- // a list of block state ids that are waterlogged
- let mut waterlogged_state_ids: Vec<u32> = Vec::new();
+ // {
+ // 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();
for block in &input.block_definitions.blocks {
let block_property_names = &block
.properties_and_defaults
.iter()
- .map(|p| p.property_type.to_string())
+ .map(|p| p.property_value_type.to_string())
.collect::<Vec<_>>();
let mut block_properties_vec = Vec::new();
for property_name in block_property_names {
@@ -421,6 +450,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
properties_with_name.push(PropertyWithNameAndDefault {
name: Ident::new(&property_name, proc_macro2::Span::call_site()),
property_type: property.property_type.clone(),
+ property_value_type: property.property_value_type.clone(),
is_enum: property.is_enum,
default: property.default.clone(),
});
@@ -436,7 +466,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
// pub has_bottle_2: HasBottle,
let mut block_struct_fields = quote! {};
for PropertyWithNameAndDefault {
- property_type: struct_name,
+ property_value_type,
name,
is_enum,
..
@@ -445,9 +475,9 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
// let property_name_snake =
// Ident::new(&property.to_string(), proc_macro2::Span::call_site());
block_struct_fields.extend(if *is_enum {
- quote! { pub #name: properties::#struct_name, }
+ quote! { pub #name: properties::#property_value_type, }
} else {
- quote! { pub #name: #struct_name, }
+ quote! { pub #name: #property_value_type, }
});
}
@@ -483,15 +513,15 @@ 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_struct_name_ident = &property.property_type;
+ let property_value_name_ident = &property.property_type;
let variant =
Ident::new(&combination[i].to_string(), proc_macro2::Span::call_site());
// this terrible code just gets the property default as a string
- let property_default_as_string = if let TokenTree::Ident(i) =
+ let property_default_as_string = if let TokenTree::Ident(ident) =
property.default.clone().into_iter().last().unwrap()
{
- i.to_string()
+ ident.to_string()
} else {
panic!()
};
@@ -499,20 +529,31 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
is_default = false;
}
- let property_value = if property.is_enum {
- quote! {properties::#property_struct_name_ident::#variant}
+ let property_variant = if property.is_enum {
+ quote! {properties::#property_value_name_ident::#variant}
} else {
quote! {#variant}
};
from_block_to_state_combination_match_inner.extend(quote! {
- #property_name: #property_value,
+ #property_name: #property_variant,
});
- // if "waterlogged" is a property and it's true for this state then add it to
- // waterlogged_state_ids
- if property_name == "waterlogged" && property_value.to_string() == "true" {
- waterlogged_state_ids.push(state_id)
+ // add to properties_to_state_ids
+ let property_variants = properties_to_state_ids
+ .entry(property_value_name_ident.to_string())
+ .or_insert_with(Vec::new);
+ let property_variant_data = property_variants
+ .iter_mut()
+ .find(|v| v.ident.to_string() == variant.to_string());
+ if let Some(property_variant_data) = property_variant_data {
+ property_variant_data.block_state_ids.push(state_id);
+ } else {
+ property_variants.push(PropertyVariantData {
+ block_state_ids: vec![state_id],
+ ident: variant,
+ is_enum: property.is_enum,
+ });
}
}
@@ -557,13 +598,14 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let PropertyWithNameAndDefault {
property_type: property_struct_name_ident,
name: property_name,
+ property_value_type,
..
} = &properties_with_name[i];
let property_variants = &block_properties_vec[i];
let property_variants_count = property_variants.len() as u32;
let conversion_code = {
- if &property_struct_name_ident.to_string() == "bool" {
+ if &property_value_type.to_string() == "bool" {
assert_eq!(property_variants_count, 2);
// this is not a mistake, it starts with true for some reason
quote! {(b / #division) % #property_variants_count == 0}
@@ -659,8 +701,6 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
block_structs.extend(block_struct);
}
- let waterlogged_state_ids_match = quote! { #(#waterlogged_state_ids)|* };
-
let last_state_id = state_id - 1;
let mut generated = quote! {
impl BlockState {
@@ -670,21 +710,87 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
#last_state_id
}
- /// Whether the given block state is waterlogged. This checks for
- /// the `waterlogged` field, so it'll return false for water.
- pub fn waterlogged(&self) -> bool {
- matches!(self.id, #waterlogged_state_ids_match)
+ /// Get a property from this block state. Will be `None` if the block can't have the property.
+ ///
+ /// ```
+ /// fn is_waterlogged(block_state: azalea_block::BlockState) -> bool {
+ /// block_state.property::<azalea_block::properties::Waterlogged>().unwrap_or_default()
+ /// }
+ /// ```
+ pub fn property<P: Property>(self) -> Option<P::Value> {
+ P::try_from_block_state(self)
}
}
+ };
+
+ // 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 enum_inner_generated = quote! {};
+
+ let mut is_enum_ = false;
+
+ for PropertyVariantData {
+ block_state_ids,
+ ident,
+ is_enum,
+ } in property_values
+ {
+ enum_inner_generated.extend(if is_enum {
+ quote! {
+ #(#block_state_ids)|* => Some(Self::#ident),
+ }
+ } else {
+ quote! {
+ #(#block_state_ids)|* => Some(#ident),
+ }
+ });
+ is_enum_ = is_enum;
+ }
+ let is_enum = is_enum_;
+
+ 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> {
+ match block_state.id {
+ #enum_inner_generated
+ _ => None
+ }
+ }
+ }
+ };
+ property_impls.extend(property_impl);
+ }
+ generated.extend(quote! {
pub mod properties {
use super::*;
#property_enums
+
+ #property_impls
}
- };
- generated.extend(quote! {
pub mod blocks {
use super::*;
diff --git a/azalea-block/src/generated.rs b/azalea-block/src/generated.rs
index 3a903b8d..97236b4b 100755
--- a/azalea-block/src/generated.rs
+++ b/azalea-block/src/generated.rs
@@ -1,4 +1,4 @@
-use crate::{Block, BlockBehavior, BlockState, BlockStates};
+use crate::{Block, BlockBehavior, BlockState, BlockStates, Property};
use azalea_block_macros::make_block_states;
use std::fmt::Debug;
@@ -5380,3 +5380,4 @@ make_block_states! {
},
}
}
+
diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs
index b67a6e76..d590bcea 100755
--- a/azalea-block/src/lib.rs
+++ b/azalea-block/src/lib.rs
@@ -33,6 +33,12 @@ impl dyn Block {
}
}
+pub trait Property {
+ type Value;
+
+ fn try_from_block_state(state: BlockState) -> Option<Self::Value>;
+}
+
/// A representation of a state a block can be in.
///
/// For example, a stone block only has one state but each possible stair
@@ -113,7 +119,10 @@ impl Default for FluidState {
impl From<BlockState> for FluidState {
fn from(state: BlockState) -> Self {
- if state.waterlogged() {
+ if state
+ .property::<crate::properties::Waterlogged>()
+ .unwrap_or_default()
+ {
Self {
fluid: azalea_registry::Fluid::Water,
height: 15,
diff --git a/azalea-world/src/heightmap.rs b/azalea-world/src/heightmap.rs
index 12aaa159..c38f3edc 100644
--- a/azalea-world/src/heightmap.rs
+++ b/azalea-world/src/heightmap.rs
@@ -32,7 +32,10 @@ fn blocks_motion(block_state: BlockState) -> bool {
fn motion_blocking(block_state: BlockState) -> bool {
// TODO
- !block_state.is_air() || block_state.waterlogged()
+ !block_state.is_air()
+ || block_state
+ .property::<azalea_block::properties::Waterlogged>()
+ .unwrap_or_default()
}
impl HeightmapKind {
diff --git a/azalea/src/pathfinder/world.rs b/azalea/src/pathfinder/world.rs
index f73a6641..4b48f921 100644
--- a/azalea/src/pathfinder/world.rs
+++ b/azalea/src/pathfinder/world.rs
@@ -246,7 +246,10 @@ fn is_block_state_passable(block: BlockState) -> bool {
if block == azalea_registry::Block::Water.into() {
return false;
}
- if block.waterlogged() {
+ if block
+ .property::<azalea_block::properties::Waterlogged>()
+ .unwrap_or_default()
+ {
return false;
}
if block == azalea_registry::Block::Lava.into() {