Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add World::find_block #80

Merged
merged 10 commits into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions azalea-block/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand All @@ -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>() {
// ...
}
```
Expand Down
110 changes: 56 additions & 54 deletions azalea-block/azalea-block-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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![:]>()?;

Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -386,24 +387,24 @@ 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, }
});
}

let block_name_pascal_case = Ident::new(
&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! {};

Expand Down Expand Up @@ -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}
};
Expand Down Expand Up @@ -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! {};
Expand All @@ -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! {
Expand All @@ -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 {
Expand Down Expand Up @@ -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()
}
}

Expand All @@ -585,60 +589,58 @@ 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 {
#last_state_id
}
}

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")
}
}
}
}
Expand Down
15 changes: 1 addition & 14 deletions azalea-block/src/blocks.rs → azalea-block/src/generated.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
60 changes: 51 additions & 9 deletions azalea-block/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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::*;
Expand Down Expand Up @@ -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
);
Expand Down
33 changes: 33 additions & 0 deletions azalea-block/src/range.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
Loading