valence/crates/valence_block/build.rs

972 lines
30 KiB
Rust
Raw Normal View History

2022-04-18 10:04:39 +10:00
use std::collections::BTreeSet;
use heck::{ToPascalCase, ToShoutySnakeCase};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
2022-04-18 10:04:39 +10:00
use serde::Deserialize;
Reorganize Project (#321) ## Description - `valence` and `valence_protocol` have been divided into smaller crates in order to parallelize the build and improve IDE responsiveness. In the process, code architecture has been made clearer by removing circular dependencies between modules. `valence` is now just a shell around the other crates. - `workspace.packages` and `workspace.dependencies` are now used. This makes dependency managements and crate configuration much easier. - `valence_protocol` is no more. Most things from `valence_protocol` ended up in `valence_core`. We won't advertise `valence_core` as a general-purpose protocol library since it contains too much valence-specific stuff. Closes #308. - Networking code (login, initial TCP connection handling, etc.) has been extracted into the `valence_network` crate. The API has been expanded and improved with better defaults. Player counts and initial connections to the server are now tracked separately. Player counts function by default without any user configuration. - Some crates like `valence_anvil`, `valence_network`, `valence_player_list`, `valence_inventory`, etc. are now optional. They can be enabled/disabled with feature flags and `DefaultPlugins` just like bevy. - Whole-server unit tests have been moved to `valence/src/tests` in order to avoid [cyclic dev-dependencies](https://github.com/rust-lang/cargo/issues/4242). - Tools like `valence_stresser` and `packet_inspector` have been moved to a new `tools` directory. Renamed `valence_stresser` to `stresser`. Closes #241. - Moved all benches to `valence/benches/` to make them easier to run and organize. Ignoring transitive dependencies and `valence_core`, here's what the dependency graph looks like now: ```mermaid graph TD network --> client client --> instance biome --> registry dimension --> registry instance --> biome instance --> dimension instance --> entity player_list --> client inventory --> client anvil --> instance entity --> block ``` ### Issues - Inventory tests inspect many private implementation details of the inventory module, forcing us to mark things as `pub` and `#[doc(hidden)]`. It would be ideal if the tests only looked at observable behavior. - Consider moving packets in `valence_core` elsewhere. `Particle` wants to use `BlockState`, but that's defined in `valence_block`, so we can't use it without causing cycles. - Unsure what exactly should go in `valence::prelude`. - This could use some more tests of course, but I'm holding off on that until I'm confident this is the direction we want to take things. ## TODOs - [x] Update examples. - [x] Update benches. - [x] Update main README. - [x] Add short READMEs to crates. - [x] Test new schedule to ensure behavior is the same. - [x] Update tools. - [x] Copy lints to all crates. - [x] Fix docs, clippy, etc.
2023-04-22 07:43:59 +10:00
use valence_build_utils::{ident, rerun_if_changed, write_generated_file};
2022-04-18 10:04:39 +10:00
#[derive(Deserialize, Clone, Debug)]
struct TopLevel {
blocks: Vec<Block>,
2022-08-07 09:46:07 +10:00
shapes: Vec<Shape>,
block_entity_types: Vec<BlockEntityKind>,
}
#[derive(Deserialize, Clone, Debug)]
struct Block {
id: u16,
item_id: u16,
wall_variant_id: Option<u16>,
translation_key: String,
name: String,
properties: Vec<Property>,
default_state_id: u16,
states: Vec<State>,
}
impl Block {
pub fn min_state_id(&self) -> u16 {
self.states.iter().map(|s| s.id).min().unwrap()
}
pub fn max_state_id(&self) -> u16 {
self.states.iter().map(|s| s.id).max().unwrap()
}
}
#[derive(Deserialize, Clone, Debug)]
struct BlockEntityKind {
id: u32,
ident: String,
name: String,
}
#[derive(Deserialize, Clone, Debug)]
struct Property {
name: String,
values: Vec<String>,
}
#[derive(Deserialize, Clone, Debug)]
struct State {
id: u16,
luminance: u8,
opaque: bool,
replaceable: bool,
collision_shapes: Vec<u16>,
block_entity_type: Option<u32>,
}
#[derive(Deserialize, Clone, Debug)]
2022-08-07 09:46:07 +10:00
struct Shape {
min_x: f64,
min_y: f64,
min_z: f64,
max_x: f64,
max_y: f64,
max_z: f64,
}
Reorganize Project (#321) ## Description - `valence` and `valence_protocol` have been divided into smaller crates in order to parallelize the build and improve IDE responsiveness. In the process, code architecture has been made clearer by removing circular dependencies between modules. `valence` is now just a shell around the other crates. - `workspace.packages` and `workspace.dependencies` are now used. This makes dependency managements and crate configuration much easier. - `valence_protocol` is no more. Most things from `valence_protocol` ended up in `valence_core`. We won't advertise `valence_core` as a general-purpose protocol library since it contains too much valence-specific stuff. Closes #308. - Networking code (login, initial TCP connection handling, etc.) has been extracted into the `valence_network` crate. The API has been expanded and improved with better defaults. Player counts and initial connections to the server are now tracked separately. Player counts function by default without any user configuration. - Some crates like `valence_anvil`, `valence_network`, `valence_player_list`, `valence_inventory`, etc. are now optional. They can be enabled/disabled with feature flags and `DefaultPlugins` just like bevy. - Whole-server unit tests have been moved to `valence/src/tests` in order to avoid [cyclic dev-dependencies](https://github.com/rust-lang/cargo/issues/4242). - Tools like `valence_stresser` and `packet_inspector` have been moved to a new `tools` directory. Renamed `valence_stresser` to `stresser`. Closes #241. - Moved all benches to `valence/benches/` to make them easier to run and organize. Ignoring transitive dependencies and `valence_core`, here's what the dependency graph looks like now: ```mermaid graph TD network --> client client --> instance biome --> registry dimension --> registry instance --> biome instance --> dimension instance --> entity player_list --> client inventory --> client anvil --> instance entity --> block ``` ### Issues - Inventory tests inspect many private implementation details of the inventory module, forcing us to mark things as `pub` and `#[doc(hidden)]`. It would be ideal if the tests only looked at observable behavior. - Consider moving packets in `valence_core` elsewhere. `Particle` wants to use `BlockState`, but that's defined in `valence_block`, so we can't use it without causing cycles. - Unsure what exactly should go in `valence::prelude`. - This could use some more tests of course, but I'm holding off on that until I'm confident this is the direction we want to take things. ## TODOs - [x] Update examples. - [x] Update benches. - [x] Update main README. - [x] Add short READMEs to crates. - [x] Test new schedule to ensure behavior is the same. - [x] Update tools. - [x] Copy lints to all crates. - [x] Fix docs, clippy, etc.
2023-04-22 07:43:59 +10:00
pub fn main() -> anyhow::Result<()> {
rerun_if_changed(["../../extracted/blocks.json"]);
write_generated_file(build()?, "block.rs")
}
fn build() -> anyhow::Result<TokenStream> {
let TopLevel {
blocks,
shapes,
block_entity_types,
Reorganize Project (#321) ## Description - `valence` and `valence_protocol` have been divided into smaller crates in order to parallelize the build and improve IDE responsiveness. In the process, code architecture has been made clearer by removing circular dependencies between modules. `valence` is now just a shell around the other crates. - `workspace.packages` and `workspace.dependencies` are now used. This makes dependency managements and crate configuration much easier. - `valence_protocol` is no more. Most things from `valence_protocol` ended up in `valence_core`. We won't advertise `valence_core` as a general-purpose protocol library since it contains too much valence-specific stuff. Closes #308. - Networking code (login, initial TCP connection handling, etc.) has been extracted into the `valence_network` crate. The API has been expanded and improved with better defaults. Player counts and initial connections to the server are now tracked separately. Player counts function by default without any user configuration. - Some crates like `valence_anvil`, `valence_network`, `valence_player_list`, `valence_inventory`, etc. are now optional. They can be enabled/disabled with feature flags and `DefaultPlugins` just like bevy. - Whole-server unit tests have been moved to `valence/src/tests` in order to avoid [cyclic dev-dependencies](https://github.com/rust-lang/cargo/issues/4242). - Tools like `valence_stresser` and `packet_inspector` have been moved to a new `tools` directory. Renamed `valence_stresser` to `stresser`. Closes #241. - Moved all benches to `valence/benches/` to make them easier to run and organize. Ignoring transitive dependencies and `valence_core`, here's what the dependency graph looks like now: ```mermaid graph TD network --> client client --> instance biome --> registry dimension --> registry instance --> biome instance --> dimension instance --> entity player_list --> client inventory --> client anvil --> instance entity --> block ``` ### Issues - Inventory tests inspect many private implementation details of the inventory module, forcing us to mark things as `pub` and `#[doc(hidden)]`. It would be ideal if the tests only looked at observable behavior. - Consider moving packets in `valence_core` elsewhere. `Particle` wants to use `BlockState`, but that's defined in `valence_block`, so we can't use it without causing cycles. - Unsure what exactly should go in `valence::prelude`. - This could use some more tests of course, but I'm holding off on that until I'm confident this is the direction we want to take things. ## TODOs - [x] Update examples. - [x] Update benches. - [x] Update main README. - [x] Add short READMEs to crates. - [x] Test new schedule to ensure behavior is the same. - [x] Update tools. - [x] Copy lints to all crates. - [x] Fix docs, clippy, etc.
2023-04-22 07:43:59 +10:00
} = serde_json::from_str(include_str!("../../extracted/blocks.json"))?;
2022-04-18 10:04:39 +10:00
let max_state_id = blocks.iter().map(|b| b.max_state_id()).max().unwrap();
2022-04-18 10:04:39 +10:00
2022-08-07 09:50:50 +10:00
let kind_to_translation_key_arms = blocks
.iter()
.map(|b| {
let kind = ident(b.name.replace('.', "_").to_pascal_case());
2022-08-07 09:50:50 +10:00
let translation_key = &b.translation_key;
quote! {
Self::#kind => #translation_key,
}
})
.collect::<TokenStream>();
let state_to_kind_arms = blocks
2022-04-18 10:04:39 +10:00
.iter()
.map(|b| {
let name = ident(b.name.replace('.', "_").to_pascal_case());
let mut token_stream = TokenStream::new();
let min_id = b.min_state_id();
let max_id = b.max_state_id();
if min_id == max_id {
quote!(#min_id).to_tokens(&mut token_stream);
} else {
for id in min_id..max_id {
quote!(#id | ).to_tokens(&mut token_stream);
}
quote!(#max_id).to_tokens(&mut token_stream);
2022-04-18 10:04:39 +10:00
}
quote!(=> BlockKind::#name,).to_tokens(&mut token_stream);
token_stream
2022-04-18 10:04:39 +10:00
})
.collect::<TokenStream>();
2022-08-07 09:46:07 +10:00
let state_to_luminance_arms = blocks
.iter()
.flat_map(|b| {
b.states.iter().filter(|s| s.luminance != 0).map(|s| {
let id = s.id;
let luminance = s.luminance;
quote! {
#id => #luminance,
}
})
})
.collect::<TokenStream>();
let state_to_opaque_arms = blocks
.iter()
.flat_map(|b| {
b.states.iter().filter(|s| !s.opaque).map(|s| {
let id = s.id;
quote! {
#id => false,
}
})
})
.collect::<TokenStream>();
let state_to_replaceable_arms = blocks
.iter()
.flat_map(|b| {
b.states.iter().filter(|s| s.replaceable).map(|s| {
let id = s.id;
quote! {
#id => true,
}
})
})
.collect::<TokenStream>();
2022-08-07 09:46:07 +10:00
let shapes = shapes.iter().map(|s| {
let min_x = s.min_x;
let min_y = s.min_y;
let min_z = s.min_z;
let max_x = s.max_x;
let max_y = s.max_y;
let max_z = s.max_z;
quote! {
Aabb {
min: dvec3(#min_x, #min_y, #min_z),
max: dvec3(#max_x, #max_y, #max_z),
}
2022-08-07 09:46:07 +10:00
}
});
let shape_count = shapes.len();
let state_to_collision_shapes_arms = blocks
.iter()
.flat_map(|b| {
b.states.iter().map(|s| {
let id = s.id;
let collision_shapes = &s.collision_shapes;
quote! {
#id => &[#(#collision_shapes),*],
}
})
})
.collect::<TokenStream>();
2022-04-18 10:04:39 +10:00
let get_arms = blocks
.iter()
.filter(|&b| !b.properties.is_empty())
2022-04-18 10:04:39 +10:00
.map(|b| {
let block_kind_name = ident(b.name.replace('.', "_").to_pascal_case());
2022-04-18 10:04:39 +10:00
let arms = b
.properties
2022-04-18 10:04:39 +10:00
.iter()
.map(|p| {
let prop_name = ident(p.name.replace('.', "_").to_pascal_case());
let min_state_id = b.min_state_id();
2022-04-18 10:04:39 +10:00
let product: u16 = b
.properties
2022-04-18 10:04:39 +10:00
.iter()
.rev()
2022-04-18 10:04:39 +10:00
.take_while(|&other| p.name != other.name)
.map(|p| p.values.len() as u16)
2022-04-18 10:04:39 +10:00
.product();
let values_count = p.values.len() as u16;
2022-04-18 10:04:39 +10:00
let arms = p.values.iter().enumerate().map(|(i, v)| {
2022-04-18 10:04:39 +10:00
let value_idx = i as u16;
let value_name = ident(v.replace('.', "_").to_pascal_case());
2022-04-18 10:04:39 +10:00
quote! {
#value_idx => Some(PropValue::#value_name),
}
}).collect::<TokenStream>();
quote! {
2022-04-29 17:48:41 +10:00
PropName::#prop_name => match (self.0 - #min_state_id) / #product % #values_count {
2022-04-18 10:04:39 +10:00
#arms
_ => unreachable!(),
},
}
})
.collect::<TokenStream>();
quote! {
BlockKind::#block_kind_name => match name {
2022-04-18 10:04:39 +10:00
#arms
_ => None,
},
}
})
.collect::<TokenStream>();
let set_arms = blocks
.iter()
.filter(|&b| !b.properties.is_empty())
2022-04-18 10:04:39 +10:00
.map(|b| {
let block_kind_name = ident(b.name.replace('.', "_").to_pascal_case());
2022-04-18 10:04:39 +10:00
let arms = b
.properties
2022-04-18 10:04:39 +10:00
.iter()
.map(|p| {
let prop_name = ident(p.name.replace('.', "_").to_pascal_case());
let min_state_id = b.min_state_id();
2022-04-18 10:04:39 +10:00
let product: u16 = b
.properties
2022-04-18 10:04:39 +10:00
.iter()
.rev()
2022-04-18 10:04:39 +10:00
.take_while(|&other| p.name != other.name)
.map(|p| p.values.len() as u16)
2022-04-18 10:04:39 +10:00
.product();
let values_count = p.values.len() as u16;
2022-04-18 10:04:39 +10:00
let arms = p
.values
2022-04-18 10:04:39 +10:00
.iter()
.enumerate()
.map(|(i, v)| {
let val_idx = i as u16;
let val_name = ident(v.replace('.', "_").to_pascal_case());
2022-04-18 10:04:39 +10:00
quote! {
PropValue::#val_name =>
2022-04-29 17:48:41 +10:00
Self(self.0 - (self.0 - #min_state_id) / #product % #values_count * #product
2022-04-18 10:04:39 +10:00
+ #val_idx * #product),
}
})
.collect::<TokenStream>();
quote! {
PropName::#prop_name => match val {
#arms
_ => self,
},
}
})
.collect::<TokenStream>();
quote! {
BlockKind::#block_kind_name => match name {
2022-04-18 10:04:39 +10:00
#arms
_ => self,
},
}
})
.collect::<TokenStream>();
let default_block_states = blocks
.iter()
.map(|b| {
let name = ident(b.name.replace('.', "_").to_shouty_snake_case());
let state = b.default_state_id;
2022-04-18 10:04:39 +10:00
let doc = format!("The default block state for `{}`.", b.name);
quote! {
#[doc = #doc]
pub const #name: BlockState = BlockState(#state);
}
})
.collect::<TokenStream>();
let state_to_wall_variant_arms = blocks
.iter()
.filter(|b| b.wall_variant_id.is_some())
.map(|b| {
let block_name = ident(b.name.replace('.', "_").to_shouty_snake_case());
let wall_block_name = ident(
blocks[b.wall_variant_id.unwrap() as usize]
.name
.replace('.', "_")
.to_shouty_snake_case(),
);
quote! {
BlockState::#block_name => Some(BlockState::#wall_block_name),
}
})
.collect::<TokenStream>();
let state_to_block_entity_type_arms = blocks
.iter()
.flat_map(|b| {
b.states.iter().filter_map(|s| {
let id = s.id;
let block_entity_type = s.block_entity_type?;
Some(quote! {
#id => Some(#block_entity_type),
})
})
})
.collect::<TokenStream>();
let kind_to_state_arms = blocks
2022-04-18 10:04:39 +10:00
.iter()
.map(|b| {
let kind = ident(b.name.replace('.', "_").to_pascal_case());
let state = ident(b.name.replace('.', "_").to_shouty_snake_case());
2022-04-18 10:04:39 +10:00
quote! {
BlockKind::#kind => BlockState::#state,
2022-04-18 10:04:39 +10:00
}
})
.collect::<TokenStream>();
let block_kind_variants = blocks
2022-04-18 10:04:39 +10:00
.iter()
.map(|b| ident(b.name.replace('.', "_").to_pascal_case()))
2022-04-18 10:04:39 +10:00
.collect::<Vec<_>>();
let block_kind_from_str_arms = blocks
2022-04-18 10:04:39 +10:00
.iter()
.map(|b| {
let name = &b.name;
let name_ident = ident(name.replace('.', "_").to_pascal_case());
2022-04-18 10:04:39 +10:00
quote! {
#name => Some(BlockKind::#name_ident),
2022-04-18 10:04:39 +10:00
}
})
.collect::<TokenStream>();
let block_kind_to_str_arms = blocks
2022-04-18 10:04:39 +10:00
.iter()
.map(|b| {
let name = &b.name;
let name_ident = ident(name.replace('.', "_").to_pascal_case());
2022-04-18 10:04:39 +10:00
quote! {
BlockKind::#name_ident => #name,
2022-04-18 10:04:39 +10:00
}
})
.collect::<TokenStream>();
let block_kind_props_arms = blocks
2022-04-18 10:04:39 +10:00
.iter()
.filter(|&b| !b.properties.is_empty())
2022-04-18 10:04:39 +10:00
.map(|b| {
let name = ident(b.name.replace('.', "_").to_pascal_case());
let prop_names = b
.properties
.iter()
.map(|p| ident(p.name.replace('.', "_").to_pascal_case()));
2022-04-18 10:04:39 +10:00
quote! {
Self::#name => &[#(PropName::#prop_names,)*],
}
})
.collect::<TokenStream>();
let block_kind_to_item_kind_arms = blocks
.iter()
.map(|block| {
let name = ident(block.name.replace('.', "_").to_pascal_case());
let item_id = block.item_id;
quote! {
BlockKind::#name => #item_id,
}
})
.collect::<TokenStream>();
let block_kind_from_item_kind_arms = blocks
.iter()
.filter(|block| block.item_id != 0)
.map(|block| {
let name = ident(block.name.replace('.', "_").to_pascal_case());
let item_id = block.item_id;
quote! {
#item_id => Some(BlockKind::#name),
}
})
.collect::<TokenStream>();
let block_kind_from_raw_arms = blocks
.iter()
.map(|block| {
let name = ident(block.name.replace('.', "_").to_pascal_case());
let id = block.id;
quote! {
#id => Some(BlockKind::#name),
}
})
.collect::<TokenStream>();
let block_entity_kind_variants = block_entity_types
.iter()
.map(|block_entity| {
let name = ident(block_entity.name.replace('.', "_").to_pascal_case());
let doc = format!(
"The block entity type `{}` (ID {}).",
block_entity.name, block_entity.id
);
quote! {
#[doc = #doc]
#name,
}
})
.collect::<TokenStream>();
let block_entity_kind_from_id_arms = block_entity_types
.iter()
.map(|block_entity| {
let id = block_entity.id;
let name = ident(block_entity.name.replace('.', "_").to_pascal_case());
quote! {
#id => Some(Self::#name),
}
})
.collect::<TokenStream>();
let block_entity_kind_to_id_arms = block_entity_types
.iter()
.map(|block_entity| {
let id = block_entity.id;
let name = ident(block_entity.name.replace('.', "_").to_pascal_case());
quote! {
Self::#name => #id,
}
})
.collect::<TokenStream>();
let block_entity_kind_from_ident_arms = block_entity_types
.iter()
.map(|block_entity| {
let name = ident(block_entity.name.replace('.', "_").to_pascal_case());
let ident = &block_entity.ident;
quote! {
#ident => Some(Self::#name),
}
})
.collect::<TokenStream>();
let block_entity_kind_to_ident_arms = block_entity_types
.iter()
.map(|block_entity| {
let name = ident(block_entity.name.replace('.', "_").to_pascal_case());
let ident = &block_entity.ident;
quote! {
Self::#name => ident!(#ident),
}
})
.collect::<TokenStream>();
let block_kind_count = blocks.len();
2022-04-18 10:04:39 +10:00
let prop_names = blocks
.iter()
.flat_map(|b| b.properties.iter().map(|p| p.name.as_str()))
2022-04-18 10:04:39 +10:00
.collect::<BTreeSet<_>>();
let prop_name_variants = prop_names
.iter()
.map(|&name| ident(name.replace('.', "_").to_pascal_case()))
2022-04-18 10:04:39 +10:00
.collect::<Vec<_>>();
let prop_name_from_str_arms = prop_names
.iter()
.map(|&name| {
let ident = ident(name.replace('.', "_").to_pascal_case());
2022-04-18 10:04:39 +10:00
quote! {
#name => Some(PropName::#ident),
}
})
.collect::<TokenStream>();
let prop_name_to_str_arms = prop_names
.iter()
.map(|&name| {
let ident = ident(name.replace('.', "_").to_pascal_case());
2022-04-18 10:04:39 +10:00
quote! {
PropName::#ident => #name,
}
})
.collect::<TokenStream>();
2022-04-29 17:48:41 +10:00
let prop_name_count = prop_names.len();
2022-04-18 10:04:39 +10:00
let prop_values = blocks
.iter()
.flat_map(|b| b.properties.iter().flat_map(|p| &p.values))
.map(|s| s.as_str())
2022-04-18 10:04:39 +10:00
.collect::<BTreeSet<_>>();
let prop_value_variants = prop_values
.iter()
.map(|val| ident(val.replace('.', "_").to_pascal_case()))
2022-04-18 10:04:39 +10:00
.collect::<Vec<_>>();
let prop_value_from_str_arms = prop_values
.iter()
.map(|val| {
let ident = ident(val.replace('.', "_").to_pascal_case());
2022-04-18 10:04:39 +10:00
quote! {
#val => Some(PropValue::#ident),
}
})
.collect::<TokenStream>();
let prop_value_to_str_arms = prop_values
.iter()
.map(|val| {
let ident = ident(val.replace('.', "_").to_pascal_case());
2022-04-18 10:04:39 +10:00
quote! {
PropValue::#ident => #val,
}
})
.collect::<TokenStream>();
let prop_value_from_u16_arms = prop_values
.iter()
.filter_map(|v| v.parse::<u16>().ok())
.map(|n| {
let ident = ident(n.to_string());
quote! {
#n => Some(PropValue::#ident),
}
})
.collect::<TokenStream>();
let prop_value_to_u16_arms = prop_values
.iter()
.filter_map(|v| v.parse::<u16>().ok())
.map(|n| {
let ident = ident(n.to_string());
quote! {
PropValue::#ident => Some(#n),
}
})
.collect::<TokenStream>();
let prop_value_count = prop_values.len();
2022-04-18 10:04:39 +10:00
2022-07-28 00:10:35 +10:00
Ok(quote! {
use valence_core::aabb::Aabb;
use glam::dvec3;
/// Represents the state of a block. This does not include block entity data such as
2022-04-18 10:04:39 +10:00
/// the text on a sign, the design on a banner, or the content of a spawner.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
2022-04-18 10:04:39 +10:00
pub struct BlockState(u16);
impl BlockState {
/// Returns the default block state for a given block type.
pub const fn from_kind(kind: BlockKind) -> Self {
match kind {
#kind_to_state_arms
2022-04-18 10:04:39 +10:00
}
}
/// Constructs a block state from a raw block state ID.
///
/// If the given ID is invalid, `None` is returned.
pub const fn from_raw(id: u16) -> Option<Self> {
if id <= #max_state_id {
2022-04-18 10:04:39 +10:00
Some(Self(id))
} else {
None
}
}
2022-08-07 09:46:07 +10:00
/// Returns the [`BlockKind`] of this block state.
pub const fn to_kind(self) -> BlockKind {
match self.0 {
#state_to_kind_arms
_ => unreachable!(),
}
}
2022-04-18 10:04:39 +10:00
/// Converts this block state to its underlying raw block state ID.
///
/// The original block state can be recovered with [`BlockState::from_raw`].
pub const fn to_raw(self) -> u16 {
self.0
}
2022-06-23 01:06:54 +10:00
/// Returns the maximum block state ID.
pub const fn max_raw() -> u16 {
#max_state_id
2022-06-23 01:06:54 +10:00
}
/// Returns the wall variant of the block state.
///
/// If the given block state doesn't have a wall variant, `None` is returned.
pub const fn wall_block_id(self) -> Option<Self> {
match self {
#state_to_wall_variant_arms
_ => None
}
}
2022-04-18 10:04:39 +10:00
/// Gets the value of the property with the given name from this block.
2022-04-18 10:06:13 +10:00
///
2022-04-18 10:04:39 +10:00
/// If this block does not have the property, then `None` is returned.
pub const fn get(self, name: PropName) -> Option<PropValue> {
match self.to_kind() {
2022-04-18 10:04:39 +10:00
#get_arms
_ => None
}
}
/// Sets the value of a property on this block, returning the modified block.
2022-04-18 10:06:13 +10:00
///
2022-04-18 10:04:39 +10:00
/// If this block does not have the given property or the property value is invalid,
/// then the original block is returned unchanged.
2022-04-18 10:04:39 +10:00
#[must_use]
pub const fn set(self, name: PropName, val: PropValue) -> Self {
match self.to_kind() {
2022-04-18 10:04:39 +10:00
#set_arms
_ => self,
}
}
2022-04-20 16:40:33 +10:00
/// If this block is `air`, `cave_air` or `void_air`.
pub const fn is_air(self) -> bool {
matches!(
self,
BlockState::AIR | BlockState::CAVE_AIR | BlockState::VOID_AIR
2022-04-20 16:40:33 +10:00
)
}
// TODO: is_solid
/// If this block is water or lava.
pub const fn is_liquid(self) -> bool {
matches!(self.to_kind(), BlockKind::Water | BlockKind::Lava)
2022-04-20 16:40:33 +10:00
}
2022-08-07 09:46:07 +10:00
pub const fn is_opaque(self) -> bool {
match self.0 {
#state_to_opaque_arms
_ => true,
}
}
pub const fn is_replaceable(self) -> bool {
match self.0 {
#state_to_replaceable_arms
_ => false,
}
}
const SHAPES: [Aabb; #shape_count] = [
2022-08-07 09:46:07 +10:00
#(#shapes,)*
];
pub fn collision_shapes(self) -> impl ExactSizeIterator<Item = Aabb> + FusedIterator + Clone {
2022-08-07 09:46:07 +10:00
let shape_idxs: &'static [u16] = match self.0 {
#state_to_collision_shapes_arms
_ => &[],
};
shape_idxs.into_iter().map(|idx| Self::SHAPES[*idx as usize])
2022-08-07 09:46:07 +10:00
}
pub const fn luminance(self) -> u8 {
match self.0 {
#state_to_luminance_arms
_ => 0,
}
}
pub const fn block_entity_kind(self) -> Option<BlockEntityKind> {
let kind = match self.0 {
#state_to_block_entity_type_arms
_ => None
};
match kind {
Some(id) => BlockEntityKind::from_id(id),
None => None,
}
}
2022-04-18 10:04:39 +10:00
#default_block_states
}
2022-07-11 22:08:02 +10:00
/// An enumeration of all block kinds.
2022-04-18 10:04:39 +10:00
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum BlockKind {
#(#block_kind_variants,)*
2022-04-18 10:04:39 +10:00
}
impl BlockKind {
2022-07-11 22:08:02 +10:00
/// Construct a block kind from its snake_case name.
2022-04-18 10:04:39 +10:00
///
2022-07-11 22:08:02 +10:00
/// Returns `None` if the name is invalid.
pub fn from_str(name: &str) -> Option<Self> {
2022-04-18 10:04:39 +10:00
match name {
#block_kind_from_str_arms
2022-04-18 10:04:39 +10:00
_ => None
}
}
2022-07-11 22:08:02 +10:00
/// Get the snake_case name of this block kind.
2022-04-18 10:04:39 +10:00
pub const fn to_str(self) -> &'static str {
match self {
#block_kind_to_str_arms
2022-04-18 10:04:39 +10:00
}
}
2022-07-11 22:08:02 +10:00
/// Returns the default block state for a given block kind.
2022-04-18 10:04:39 +10:00
pub const fn to_state(self) -> BlockState {
BlockState::from_kind(self)
2022-04-18 10:04:39 +10:00
}
2022-07-11 22:08:02 +10:00
/// Returns a slice of all properties this block kind has.
2022-04-18 10:04:39 +10:00
pub const fn props(self) -> &'static [PropName] {
match self {
#block_kind_props_arms
2022-04-18 10:04:39 +10:00
_ => &[],
}
}
2022-08-07 09:50:50 +10:00
pub const fn translation_key(self) -> &'static str {
match self {
#kind_to_translation_key_arms
}
}
/// Converts a block kind to its corresponding item kind.
///
/// [`ItemKind::Air`] is used to indicate the absence of an item.
pub const fn to_item_kind(self) -> ItemKind {
let id = match self {
#block_kind_to_item_kind_arms
};
// TODO: unwrap() is not const yet.
match ItemKind::from_raw(id) {
Some(k) => k,
None => unreachable!(),
}
}
/// Constructs a block kind from an item kind.
///
/// If the given item does not have a corresponding block, `None` is returned.
pub const fn from_item_kind(item: ItemKind) -> Option<Self> {
// The "default" blocks are ordered before the other variants.
// For instance, `torch` comes before `wall_torch` so this match
// should do the correct thing.
#[allow(unreachable_patterns)]
match item.to_raw() {
#block_kind_from_item_kind_arms
_ => None,
}
}
/// Constructs a block kind from a raw block kind ID.
///
/// If the given ID is invalid, `None` is returned.
pub const fn from_raw(id: u16) -> Option<Self> {
match id {
#block_kind_from_raw_arms
_ => None,
}
}
/// Converts this block kind to its underlying raw block state ID.
///
/// The original block kind can be recovered with [`BlockKind::from_raw`].
pub const fn to_raw(self) -> u16 {
self as u16
}
2022-07-11 22:08:02 +10:00
/// An array of all block kinds.
pub const ALL: [Self; #block_kind_count] = [#(Self::#block_kind_variants,)*];
2022-04-18 10:04:39 +10:00
}
2022-07-11 22:08:02 +10:00
/// The default block kind is `air`.
impl Default for BlockKind {
2022-04-18 10:04:39 +10:00
fn default() -> Self {
Self::Air
}
}
/// Contains all possible block state property names.
///
/// For example, `waterlogged`, `facing`, and `half` are all property names.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum PropName {
#(#prop_name_variants,)*
}
impl PropName {
/// Construct a property name from its snake_case name.
///
/// Returns `None` if the given name is not valid.
pub fn from_str(name: &str) -> Option<Self> {
// TODO: match on str in const fn.
2022-04-18 10:04:39 +10:00
match name {
#prop_name_from_str_arms
_ => None,
}
}
/// Get the snake_case name of this property name.
pub const fn to_str(self) -> &'static str {
match self {
#prop_name_to_str_arms
}
}
/// An array of all property names.
2022-04-29 17:48:41 +10:00
pub const ALL: [Self; #prop_name_count] = [#(Self::#prop_name_variants,)*];
2022-04-18 10:04:39 +10:00
}
/// Contains all possible values that a block property might have.
///
/// For example, `upper`, `true`, and `2` are all property values.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum PropValue {
#(#prop_value_variants,)*
}
impl PropValue {
/// Construct a property value from its snake_case name.
///
/// Returns `None` if the given name is not valid.
pub fn from_str(name: &str) -> Option<Self> {
match name {
#prop_value_from_str_arms
_ => None,
}
}
/// Get the snake_case name of this property value.
pub const fn to_str(self) -> &'static str {
match self {
#prop_value_to_str_arms
}
}
/// Converts a `u16` into a numeric property value.
/// Returns `None` if the given number does not have a
/// corresponding property value.
pub const fn from_u16(n: u16) -> Option<Self> {
match n {
#prop_value_from_u16_arms
_ => None,
}
}
/// Converts this property value into a `u16` if it is numeric.
/// Returns `None` otherwise.
pub const fn to_u16(self) -> Option<u16> {
match self {
#prop_value_to_u16_arms
_ => None,
}
}
/// Converts a `bool` to a `True` or `False` property value.
2022-04-18 10:04:39 +10:00
pub const fn from_bool(b: bool) -> Self {
if b {
Self::True
} else {
Self::False
}
}
/// Converts a `True` or `False` property value to a `bool`.
///
/// Returns `None` if this property value is not `True` or `False`
2022-04-18 10:04:39 +10:00
pub const fn to_bool(self) -> Option<bool> {
match self {
Self::True => Some(true),
Self::False => Some(false),
_ => None,
}
}
/// An array of all property values.
pub const ALL: [Self; #prop_value_count] = [#(Self::#prop_value_variants,)*];
2022-04-18 10:04:39 +10:00
}
impl From<bool> for PropValue {
fn from(b: bool) -> Self {
Self::from_bool(b)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum BlockEntityKind {
#block_entity_kind_variants
}
impl BlockEntityKind {
pub const fn from_id(num: u32) -> Option<Self> {
match num {
#block_entity_kind_from_id_arms
_ => None
}
}
pub const fn id(self) -> u32 {
match self {
#block_entity_kind_to_id_arms
}
}
pub fn from_ident(ident: Ident<&str>) -> Option<Self> {
match ident.as_str() {
#block_entity_kind_from_ident_arms
_ => None
}
}
pub fn ident(self) -> Ident<&'static str> {
match self {
#block_entity_kind_to_ident_arms
}
}
}
impl Encode for BlockEntityKind {
Reorganize Project (#321) ## Description - `valence` and `valence_protocol` have been divided into smaller crates in order to parallelize the build and improve IDE responsiveness. In the process, code architecture has been made clearer by removing circular dependencies between modules. `valence` is now just a shell around the other crates. - `workspace.packages` and `workspace.dependencies` are now used. This makes dependency managements and crate configuration much easier. - `valence_protocol` is no more. Most things from `valence_protocol` ended up in `valence_core`. We won't advertise `valence_core` as a general-purpose protocol library since it contains too much valence-specific stuff. Closes #308. - Networking code (login, initial TCP connection handling, etc.) has been extracted into the `valence_network` crate. The API has been expanded and improved with better defaults. Player counts and initial connections to the server are now tracked separately. Player counts function by default without any user configuration. - Some crates like `valence_anvil`, `valence_network`, `valence_player_list`, `valence_inventory`, etc. are now optional. They can be enabled/disabled with feature flags and `DefaultPlugins` just like bevy. - Whole-server unit tests have been moved to `valence/src/tests` in order to avoid [cyclic dev-dependencies](https://github.com/rust-lang/cargo/issues/4242). - Tools like `valence_stresser` and `packet_inspector` have been moved to a new `tools` directory. Renamed `valence_stresser` to `stresser`. Closes #241. - Moved all benches to `valence/benches/` to make them easier to run and organize. Ignoring transitive dependencies and `valence_core`, here's what the dependency graph looks like now: ```mermaid graph TD network --> client client --> instance biome --> registry dimension --> registry instance --> biome instance --> dimension instance --> entity player_list --> client inventory --> client anvil --> instance entity --> block ``` ### Issues - Inventory tests inspect many private implementation details of the inventory module, forcing us to mark things as `pub` and `#[doc(hidden)]`. It would be ideal if the tests only looked at observable behavior. - Consider moving packets in `valence_core` elsewhere. `Particle` wants to use `BlockState`, but that's defined in `valence_block`, so we can't use it without causing cycles. - Unsure what exactly should go in `valence::prelude`. - This could use some more tests of course, but I'm holding off on that until I'm confident this is the direction we want to take things. ## TODOs - [x] Update examples. - [x] Update benches. - [x] Update main README. - [x] Add short READMEs to crates. - [x] Test new schedule to ensure behavior is the same. - [x] Update tools. - [x] Copy lints to all crates. - [x] Fix docs, clippy, etc.
2023-04-22 07:43:59 +10:00
fn encode(&self, w: impl Write) -> anyhow::Result<()> {
VarInt(self.id() as i32).encode(w)
}
}
impl<'a> Decode<'a> for BlockEntityKind {
Reorganize Project (#321) ## Description - `valence` and `valence_protocol` have been divided into smaller crates in order to parallelize the build and improve IDE responsiveness. In the process, code architecture has been made clearer by removing circular dependencies between modules. `valence` is now just a shell around the other crates. - `workspace.packages` and `workspace.dependencies` are now used. This makes dependency managements and crate configuration much easier. - `valence_protocol` is no more. Most things from `valence_protocol` ended up in `valence_core`. We won't advertise `valence_core` as a general-purpose protocol library since it contains too much valence-specific stuff. Closes #308. - Networking code (login, initial TCP connection handling, etc.) has been extracted into the `valence_network` crate. The API has been expanded and improved with better defaults. Player counts and initial connections to the server are now tracked separately. Player counts function by default without any user configuration. - Some crates like `valence_anvil`, `valence_network`, `valence_player_list`, `valence_inventory`, etc. are now optional. They can be enabled/disabled with feature flags and `DefaultPlugins` just like bevy. - Whole-server unit tests have been moved to `valence/src/tests` in order to avoid [cyclic dev-dependencies](https://github.com/rust-lang/cargo/issues/4242). - Tools like `valence_stresser` and `packet_inspector` have been moved to a new `tools` directory. Renamed `valence_stresser` to `stresser`. Closes #241. - Moved all benches to `valence/benches/` to make them easier to run and organize. Ignoring transitive dependencies and `valence_core`, here's what the dependency graph looks like now: ```mermaid graph TD network --> client client --> instance biome --> registry dimension --> registry instance --> biome instance --> dimension instance --> entity player_list --> client inventory --> client anvil --> instance entity --> block ``` ### Issues - Inventory tests inspect many private implementation details of the inventory module, forcing us to mark things as `pub` and `#[doc(hidden)]`. It would be ideal if the tests only looked at observable behavior. - Consider moving packets in `valence_core` elsewhere. `Particle` wants to use `BlockState`, but that's defined in `valence_block`, so we can't use it without causing cycles. - Unsure what exactly should go in `valence::prelude`. - This could use some more tests of course, but I'm holding off on that until I'm confident this is the direction we want to take things. ## TODOs - [x] Update examples. - [x] Update benches. - [x] Update main README. - [x] Add short READMEs to crates. - [x] Test new schedule to ensure behavior is the same. - [x] Update tools. - [x] Copy lints to all crates. - [x] Fix docs, clippy, etc.
2023-04-22 07:43:59 +10:00
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
let id = VarInt::decode(r)?;
Self::from_id(id.0 as u32).with_context(|| format!("id {}", id.0))
}
}
2022-07-28 00:10:35 +10:00
})
2022-04-18 10:04:39 +10:00
}