diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2023-03-07 22:09:56 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-07 22:09:56 -0600 |
| commit | 5dd35c7ed82c38ef36ca28f630e8d05c5db2cbea (patch) | |
| tree | 72719e46479e7884ea535c768ab7c244ce048063 /azalea-block | |
| parent | 719379a8a76ab0685f2bd14bebe2f0cd1e97f06b (diff) | |
| download | azalea-drasl-5dd35c7ed82c38ef36ca28f630e8d05c5db2cbea.tar.xz | |
Add World::find_block (#80)
* start adding World::find_block
* keep working on find_block
* BlockStates
* fix sorting
* update examples that use find_one_block
* azalea_block::properties
* fix tests
* add a gotoblock command to testbot
Diffstat (limited to 'azalea-block')
| -rwxr-xr-x | azalea-block/README.md | 12 | ||||
| -rwxr-xr-x | azalea-block/azalea-block-macros/src/lib.rs | 110 | ||||
| -rwxr-xr-x | azalea-block/src/generated.rs (renamed from azalea-block/src/blocks.rs) | 15 | ||||
| -rwxr-xr-x | azalea-block/src/lib.rs | 60 | ||||
| -rw-r--r-- | azalea-block/src/range.rs | 33 |
5 files changed, 147 insertions, 83 deletions
diff --git a/azalea-block/README.md b/azalea-block/README.md index e4b6357b..9be4c79b 100755 --- a/azalea-block/README.md +++ b/azalea-block/README.md @@ -8,11 +8,11 @@ There's three block types, used for different things. You can (mostly) convert b ``` # use azalea_block::BlockState; -let block_state: BlockState = azalea_block::CobblestoneWallBlock { - east: azalea_block::EastWall::Low, - north: azalea_block::NorthWall::Low, - south: azalea_block::SouthWall::Low, - west: azalea_block::WestWall::Low, +let block_state: BlockState = azalea_block::blocks::CobblestoneWall { + east: azalea_block::properties::EastWall::Low, + north: azalea_block::properties::NorthWall::Low, + south: azalea_block::properties::SouthWall::Low, + west: azalea_block::properties::WestWall::Low, up: false, waterlogged: false, } @@ -36,7 +36,7 @@ let block = Box::<dyn Block>::from(block_state); ``` # use azalea_block::{Block, BlockState}; # let block_state: BlockState = azalea_registry::Block::Jukebox.into(); -if let Some(jukebox) = Box::<dyn Block>::from(block_state).downcast_ref::<azalea_block::JukeboxBlock>() { +if let Some(jukebox) = Box::<dyn Block>::from(block_state).downcast_ref::<azalea_block::blocks::Jukebox>() { // ... } ``` diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs index b69ebd06..a8739e7c 100755 --- a/azalea-block/azalea-block-macros/src/lib.rs +++ b/azalea-block/azalea-block-macros/src/lib.rs @@ -38,7 +38,7 @@ struct PropertyDefinitions { properties: Vec<PropertyDefinition>, } -/// `snowy: false` or `axis: Axis::Y` +/// `snowy: false` or `axis: properties::Axis::Y` #[derive(Debug)] struct PropertyWithNameAndDefault { name: Ident, @@ -59,7 +59,7 @@ struct BlockDefinition { } impl Parse for PropertyWithNameAndDefault { fn parse(input: ParseStream) -> Result<Self> { - // `snowy: false` or `axis: Axis::Y` + // `snowy: false` or `axis: properties::Axis::Y` let property_name = input.parse()?; input.parse::<Token![:]>()?; @@ -74,7 +74,7 @@ impl Parse for PropertyWithNameAndDefault { is_enum = true; property_type = first_ident; let variant = input.parse::<Ident>()?; - property_default.extend(quote! { ::#variant }); + property_default = quote! { properties::#property_default::#variant }; } else if first_ident_string == "true" || first_ident_string == "false" { property_type = Ident::new("bool", first_ident.span()); } else { @@ -310,6 +310,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let mut from_state_to_block_match = quote! {}; let mut from_registry_block_to_block_match = quote! {}; let mut from_registry_block_to_blockstate_match = quote! {}; + let mut from_registry_block_to_blockstates_match = quote! {}; for block in &input.block_definitions.blocks { let block_property_names = &block @@ -386,13 +387,16 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { for PropertyWithNameAndDefault { property_type: struct_name, name, + is_enum, .. } in &properties_with_name { // let property_name_snake = // Ident::new(&property.to_string(), proc_macro2::Span::call_site()); - block_struct_fields.extend(quote! { - pub #name: #struct_name, + block_struct_fields.extend(if *is_enum { + quote! { pub #name: properties::#struct_name, } + } else { + quote! { pub #name: #struct_name, } }); } @@ -400,10 +404,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { &to_pascal_case(&block.name.to_string()), proc_macro2::Span::call_site(), ); - let block_struct_name = Ident::new( - &format!("{block_name_pascal_case}Block"), - proc_macro2::Span::call_site(), - ); + let block_struct_name = Ident::new(&block_name_pascal_case.to_string(), proc_macro2::Span::call_site()); let mut from_block_to_state_match_inner = quote! {}; @@ -445,7 +446,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { } let property_type = if property.is_enum { - quote! {#property_struct_name_ident::#variant} + quote! {properties::#property_struct_name_ident::#variant} } else { quote! {#variant} }; @@ -476,9 +477,9 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { // 7035..=7058 => { // let b = b - 7035; // &AcaciaButtonBlock { - // powered: Powered::from((b / 1) % 2), - // facing: Facing::from((b / 2) % 4), - // face: Face::from((b / 8) % 3), + // powered: properties::Powered::from((b / 1) % 2), + // facing: properties::Facing::from((b / 2) % 4), + // face: properties::Face::from((b / 8) % 3), // } // } let mut from_state_to_block_inner = quote! {}; @@ -498,7 +499,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { // this is not a mistake, it starts with true for some reason quote! {(b / #division) % #property_variants_count == 0} } else { - quote! {#property_struct_name_ident::from((b / #division) % #property_variants_count)} + quote! {properties::#property_struct_name_ident::from((b / #division) % #property_variants_count)} } }; from_state_to_block_inner.extend(quote! { @@ -523,6 +524,9 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { from_registry_block_to_blockstate_match.extend(quote! { azalea_registry::Block::#block_name_pascal_case => BlockState { id: #default_state_id }, }); + from_registry_block_to_blockstates_match.extend(quote! { + azalea_registry::Block::#block_name_pascal_case => BlockStates::from(#first_state_id..=#last_state_id), + }); let mut block_default_fields = quote! {}; for PropertyWithNameAndDefault { @@ -560,14 +564,14 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { fn id(&self) -> &'static str { #block_id } - fn as_blockstate(&self) -> BlockState { + fn as_block_state(&self) -> BlockState { #from_block_to_state_match } } impl From<#block_struct_name> for BlockState { fn from(b: #block_struct_name) -> Self { - b.as_blockstate() + b.as_block_state() } } @@ -585,21 +589,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let last_state_id = state_id - 1; let mut generated = quote! { - #property_enums - - /// A representation of a state a block can be in. (for example, a stone - /// block only has one state but each possible stair rotation is a - /// different state). - #[derive(Copy, Clone, PartialEq, Eq, Default)] - pub struct BlockState { - /// The protocol ID for the block state. IDs may change every - /// version, so you shouldn't hard-code them or store them in databases. - pub id: u32 - } - impl BlockState { - pub const AIR: BlockState = BlockState { id: 0 }; - /// Returns the highest possible state ID. #[inline] pub fn max_state() -> u32 { @@ -607,38 +597,50 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { } } - impl std::fmt::Debug for BlockState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "BlockState(id: {}, {:?})", self.id, Box::<dyn Block>::from(*self)) - } + pub mod properties { + use super::*; + + #property_enums } }; generated.extend(quote! { - #block_structs - - impl From<BlockState> for Box<dyn Block> { - fn from(block_state: BlockState) -> Self { - let b = block_state.id; - match b { - #from_state_to_block_match - _ => panic!("Invalid block state: {}", b), + pub mod blocks { + use super::*; + + #block_structs + + impl From<BlockState> for Box<dyn Block> { + fn from(block_state: BlockState) -> Self { + let b = block_state.id; + match b { + #from_state_to_block_match + _ => panic!("Invalid block state: {}", b), + } } } - } - impl From<azalea_registry::Block> for Box<dyn Block> { - fn from(block: azalea_registry::Block) -> Self { - match block { - #from_registry_block_to_block_match - _ => unreachable!("There should always be a block struct for every azalea_registry::Block variant") + impl From<azalea_registry::Block> for Box<dyn Block> { + fn from(block: azalea_registry::Block) -> Self { + match block { + #from_registry_block_to_block_match + _ => unreachable!("There should always be a block struct for every azalea_registry::Block variant") + } } } - } - impl From<azalea_registry::Block> for BlockState { - fn from(block: azalea_registry::Block) -> Self { - match block { - #from_registry_block_to_blockstate_match - _ => unreachable!("There should always be a block state for every azalea_registry::Block variant") + impl From<azalea_registry::Block> for BlockState { + fn from(block: azalea_registry::Block) -> Self { + match block { + #from_registry_block_to_blockstate_match + _ => unreachable!("There should always be a block state for every azalea_registry::Block variant") + } + } + } + impl From<azalea_registry::Block> for BlockStates { + fn from(block: azalea_registry::Block) -> Self { + match block { + #from_registry_block_to_blockstates_match + _ => unreachable!("There should always be a block state for every azalea_registry::Block variant") + } } } } diff --git a/azalea-block/src/blocks.rs b/azalea-block/src/generated.rs index e6923d59..afe6dfda 100755 --- a/azalea-block/src/blocks.rs +++ b/azalea-block/src/generated.rs @@ -1,20 +1,7 @@ -use std::any::Any; - -use crate::BlockBehavior; +use crate::{Block, BlockBehavior, BlockState, BlockStates}; use azalea_block_macros::make_block_states; use std::fmt::Debug; -pub trait Block: Debug + Any { - fn behavior(&self) -> BlockBehavior; - fn id(&self) -> &'static str; - fn as_blockstate(&self) -> BlockState; -} -impl dyn Block { - pub fn downcast_ref<T: Block>(&self) -> Option<&T> { - (self as &dyn Any).downcast_ref::<T>() - } -} - make_block_states! { Properties => { "snowy" => bool, diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs index 7a62e588..43099db5 100755 --- a/azalea-block/src/lib.rs +++ b/azalea-block/src/lib.rs @@ -2,14 +2,49 @@ #![feature(trait_upcasting)] mod behavior; -mod blocks; +mod generated; +mod range; + +pub use generated::{blocks, properties}; use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable}; pub use behavior::BlockBehavior; -pub use blocks::*; -use std::io::{Cursor, Write}; +use core::fmt::Debug; +pub use range::BlockStates; +use std::{ + any::Any, + io::{Cursor, Write}, +}; + +pub trait Block: Debug + Any { + fn behavior(&self) -> BlockBehavior; + /// Get the Minecraft 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. + fn as_block_state(&self) -> BlockState; +} +impl dyn Block { + pub fn downcast_ref<T: Block>(&self) -> Option<&T> { + (self as &dyn Any).downcast_ref::<T>() + } +} + +/// A representation of a state a block can be in. +/// +/// For example, a stone block only has one state but each possible stair +/// rotation is a different state. +#[derive(Copy, Clone, PartialEq, Eq, Default, Hash)] +pub struct BlockState { + /// The protocol ID for the block state. IDs may change every + /// version, so you shouldn't hard-code them or store them in databases. + pub id: u32, +} impl BlockState { + pub const AIR: BlockState = BlockState { id: 0 }; + /// Transmutes a u32 to a block state. /// /// # Safety @@ -52,6 +87,17 @@ impl McBufWritable for BlockState { } } +impl std::fmt::Debug for BlockState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "BlockState(id: {}, {:?})", + self.id, + Box::<dyn Block>::from(*self) + ) + } +} + #[cfg(test)] mod tests { use super::*; @@ -80,18 +126,14 @@ mod tests { "{:?}", BlockState::from(azalea_registry::Block::FloweringAzalea) ); - assert!( - formatted.ends_with(", FloweringAzaleaBlock)"), - "{}", - formatted - ); + assert!(formatted.ends_with(", FloweringAzalea)"), "{}", formatted); let formatted = format!( "{:?}", BlockState::from(azalea_registry::Block::BigDripleafStem) ); assert!( - formatted.ends_with(", BigDripleafStemBlock { facing: North, waterlogged: false })"), + formatted.ends_with(", BigDripleafStem { facing: North, waterlogged: false })"), "{}", formatted ); diff --git a/azalea-block/src/range.rs b/azalea-block/src/range.rs new file mode 100644 index 00000000..6ccf4152 --- /dev/null +++ b/azalea-block/src/range.rs @@ -0,0 +1,33 @@ +use std::{collections::HashSet, ops::RangeInclusive}; + +use crate::BlockState; + +#[derive(Debug, Clone)] +pub struct BlockStates { + pub set: HashSet<BlockState>, +} + +impl From<RangeInclusive<u32>> for BlockStates { + fn from(range: RangeInclusive<u32>) -> Self { + let mut set = HashSet::with_capacity((range.end() - range.start() + 1) as usize); + for id in range { + set.insert(BlockState { id }); + } + Self { set } + } +} + +impl IntoIterator for BlockStates { + type Item = BlockState; + type IntoIter = std::collections::hash_set::IntoIter<BlockState>; + + fn into_iter(self) -> Self::IntoIter { + self.set.into_iter() + } +} + +impl BlockStates { + pub fn contains(&self, state: &BlockState) -> bool { + self.set.contains(state) + } +} |
