1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

add BlockState::property

This commit is contained in:
mat 2023-12-09 21:45:36 -06:00
parent 10ee7e147d
commit f15f0325c0
5 changed files with 155 additions and 33 deletions

View file

@ -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
}
};
generated.extend(quote! {
#property_impls
}
pub mod blocks {
use super::*;

View file

@ -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! {
},
}
}

View file

@ -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,

View file

@ -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 {

View file

@ -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() {