make blockstate good

This commit is contained in:
Ubuntu 2023-02-10 01:56:45 +00:00
parent 48b2a37aa0
commit 9d4f738d4e
20 changed files with 6407 additions and 20554 deletions

1
Cargo.lock generated
View file

@ -215,6 +215,7 @@ version = "0.5.0"
dependencies = [
"azalea-block-macros",
"azalea-buf",
"azalea-registry",
]
[[package]]

View file

@ -6,13 +6,11 @@ name = "azalea-block"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-block"
version = "0.5.0"
[features]
full-debug = ["azalea-block-macros/full-debug"]
[lib]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-block-macros = {path = "./azalea-block-macros", version = "^0.5.0" }
azalea-buf = {path = "../azalea-buf", version = "^0.5.0" }
azalea-block-macros = { path = "./azalea-block-macros", version = "^0.5.0" }
azalea-buf = { path = "../azalea-buf", version = "^0.5.0" }
azalea-registry = { version = "0.5.0", path = "../azalea-registry" }

View file

@ -1,12 +1,49 @@
# Azalea Block
Representation of Minecraft block states.
There's two main things here, the `BlockState` enum and the `Block` trait.
`BlockState` is a simple enum with every possible block state as variant, and `Block` is a heavier trait which lets you access information about a block more easily.
There's three block types, used for different things. You can (mostly) freely convert between them with `.into()`.
Every block is a struct that implements `Block`. You can freely convert between `BlockState` and `Block` with .into().
## BlockState struct
If you don't want the `Block` trait, set default-features to false.
[`BlockState`] is a struct containing the numerical protocol ID of a block state. This is how blocks are stored in the world.
```
# use azalea_block::BlockState;
let block_state: BlockState = azalea_registry::Block::Jukebox.into();
```
## Block trait
The [`Block`] trait represents a type of a block. With the the [`Block`] trait, you can get some extra things like the string block ID and some information about the block's behavior. Also, the structs that implement the trait contain the block attributes as fields so it's more convenient to get them. Note that this is often used as `Box<dyn Block>`.
If for some reason you don't want the `Block` trait, set default-features to false.
```
# 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>() {
// ...
}
```
```
# 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,
up: false,
waterlogged: false,
}
.into();
```
## azalea_registry::Block enum
This one technically isn't from the `azalea-block` crate, but it's still very relevant. It's an enum that contains every block type as a variant *without* containing any state data (like `BlockState` and the `Block` trait). Converting this into any other block type will use the default state for that block.
```
# use azalea_block::BlockState;
let block_state: BlockState = azalea_registry::Block::Jukebox.into();
```
Also, by default the `Debug` implementation for `BlockState` only logs the name of the block and not the name of the enum variant. If you want that, enable the `full-debug` feature (though it's not recommended).

View file

@ -6,9 +6,6 @@ name = "azalea-block-macros"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-block/azalea-block-macros"
version = "0.5.0"
[features]
full-debug = []
[lib]
proc-macro = true

View file

@ -3,6 +3,7 @@
mod utils;
use proc_macro::TokenStream;
use proc_macro2::TokenTree;
use quote::quote;
use std::collections::HashMap;
use std::fmt::Write;
@ -234,7 +235,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let mut properties_map = HashMap::new();
let mut property_struct_names_to_names = HashMap::new();
let mut state_id: usize = 0;
let mut state_id: u32 = 0;
for property in &input.property_definitions.properties {
let property_type_name: Ident;
@ -282,8 +283,8 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
#property_enum_variants
}
impl From<usize> for #property_type_name {
fn from(value: usize) -> Self {
impl From<u32> for #property_type_name {
fn from(value: u32) -> Self {
match value {
#property_from_number_variants
_ => panic!("Invalid property value: {}", value),
@ -305,7 +306,11 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let mut block_state_enum_variants = quote! {};
let mut block_structs = quote! {};
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! {};
for block in &input.block_definitions.blocks {
let block_property_names = &block
.properties_and_defaults
@ -403,30 +408,18 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let mut from_block_to_state_match_inner = quote! {};
let first_state_id = state_id;
let mut default_state_id = None;
// if there's no properties, then the block is just a single state
if block_properties_vec.is_empty() {
block_state_enum_variants.extend(quote! {
#block_name_pascal_case,
});
default_state_id = Some(state_id);
state_id += 1;
}
for combination in combinations_of(&block_properties_vec) {
state_id += 1;
let variant_name = Ident::new(
&format!(
"{}_{}",
block_name_pascal_case,
combination
.iter()
.map(|v| v[0..1].to_uppercase() + &v[1..])
.collect::<String>()
),
proc_macro2::Span::call_site(),
);
block_state_enum_variants.extend(quote! {
#variant_name,
});
let mut is_default = true;
// face: properties::Face::Floor,
// facing: properties::Facing::North,
@ -439,6 +432,18 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
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) =
property.default.clone().into_iter().last().unwrap()
{
i.to_string()
} else {
panic!()
};
if property_default_as_string != combination[i] {
is_default = false;
}
let property_type = if property.is_enum {
quote! {#property_struct_name_ident::#variant}
} else {
@ -453,10 +458,21 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
from_block_to_state_match_inner.extend(quote! {
#block_struct_name {
#from_block_to_state_combination_match_inner
} => BlockState::#variant_name,
} => BlockState { id: #state_id },
});
if is_default {
default_state_id = Some(state_id);
}
state_id += 1;
}
let Some(default_state_id) = default_state_id else {
let defaults = properties_with_name.iter().map(|p| if let TokenTree::Ident(i) = p.default.clone().into_iter().last().unwrap() { i.to_string() } else { panic!() }).collect::<Vec<_>>();
panic!("Couldn't get default state id for {}, combinations={:?}, defaults={:?}", block_name_pascal_case.to_string(), block_properties_vec, defaults)
};
// 7035..=7058 => {
// let b = b - 7035;
// &AcaciaButtonBlock {
@ -466,7 +482,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
// }
// }
let mut from_state_to_block_inner = quote! {};
let mut division = 1usize;
let mut division = 1u32;
for i in (0..properties_with_name.len()).rev() {
let PropertyWithNameAndDefault {
property_type: property_struct_name_ident,
@ -475,11 +491,12 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
} = &properties_with_name[i];
let property_variants = &block_properties_vec[i];
let property_variants_count = property_variants.len();
let property_variants_count = property_variants.len() as u32;
let conversion_code = {
if &property_struct_name_ident.to_string() == "bool" {
assert_eq!(property_variants_count, 2);
quote! {(b / #division) % #property_variants_count != 0}
// 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)}
}
@ -500,6 +517,12 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
})
},
});
from_registry_block_to_block_match.extend(quote! {
azalea_registry::Block::#block_name_pascal_case => Box::new(#block_struct_name::default()),
});
from_registry_block_to_blockstate_match.extend(quote! {
azalea_registry::Block::#block_name_pascal_case => BlockState { id: #default_state_id },
});
let mut block_default_fields = quote! {};
for PropertyWithNameAndDefault {
@ -515,10 +538,10 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let block_id = block.name.to_string();
let from_block_to_state_match = if block.properties_and_defaults.is_empty() {
quote! { BlockState::#block_name_pascal_case }
quote! { BlockState { id: #first_state_id } }
} else {
quote! {
match b {
match self {
#from_block_to_state_match_inner
}
}
@ -537,11 +560,14 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
fn id(&self) -> &'static str {
#block_id
}
fn as_blockstate(&self) -> BlockState {
#from_block_to_state_match
}
}
impl From<#block_struct_name> for BlockState {
fn from(b: #block_struct_name) -> Self {
#from_block_to_state_match
b.as_blockstate()
}
}
@ -561,26 +587,29 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let mut generated = quote! {
#property_enums
#[repr(u32)]
#[derive(Copy, Clone, PartialEq, Eq)]
// the Debug impl is very large and slows down compilation
#[cfg_attr(feature = "full-debug", derive(Debug))]
pub enum BlockState {
#block_state_enum_variants
/// 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 {
/// Returns the highest possible state
pub const AIR: BlockState = BlockState { id: 0 };
/// Returns the highest possible state ID.
#[inline]
pub fn max_state() -> u32 {
#last_state_id
}
}
#[cfg(not(feature = "full-debug"))]
impl std::fmt::Debug for BlockState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "BlockState ({})", Box::<dyn Block>::from(*self).id())
write!(f, "BlockState(id: {}, {:?})", self.id, Box::<dyn Block>::from(*self))
}
}
};
@ -589,14 +618,30 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
#block_structs
impl From<BlockState> for Box<dyn Block> {
fn from(b: BlockState) -> Self {
let b = b as usize;
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 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")
}
}
}
});
generated.into()

View file

@ -1,9 +1,18 @@
use std::any::Any;
use crate::BlockBehavior;
use azalea_block_macros::make_block_states;
use std::fmt::Debug;
pub trait Block {
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! {

View file

@ -1,4 +1,5 @@
#![doc = include_str!("../README.md")]
#![feature(trait_upcasting)]
mod behavior;
mod blocks;
@ -6,10 +7,7 @@ mod blocks;
use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable};
pub use behavior::BlockBehavior;
pub use blocks::*;
use std::{
io::{Cursor, Write},
mem,
};
use std::io::{Cursor, Write};
impl BlockState {
/// Transmutes a u32 to a block state.
@ -17,8 +15,8 @@ impl BlockState {
/// # Safety
/// The `state_id` should be a valid block state.
#[inline]
pub unsafe fn from_u32_unsafe(state_id: u32) -> Self {
mem::transmute::<u32, BlockState>(state_id)
pub unsafe fn from_u32_unchecked(state_id: u32) -> Self {
BlockState { id: state_id }
}
#[inline]
@ -33,7 +31,7 @@ impl TryFrom<u32> for BlockState {
/// Safely converts a state id to a block state.
fn try_from(state_id: u32) -> Result<Self, Self::Error> {
if Self::is_valid_state(state_id) {
Ok(unsafe { Self::from_u32_unsafe(state_id) })
Ok(unsafe { Self::from_u32_unchecked(state_id) })
} else {
Err(())
}
@ -50,7 +48,7 @@ impl McBufReadable for BlockState {
}
impl McBufWritable for BlockState {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
u32::var_write_into(&(*self as u32), buf)
u32::var_write_into(&self.id, buf)
}
}
@ -60,7 +58,7 @@ mod tests {
#[test]
fn test_from_u32() {
assert_eq!(BlockState::try_from(0).unwrap(), BlockState::Air);
assert_eq!(BlockState::try_from(0).unwrap(), BlockState::AIR);
assert!(BlockState::try_from(BlockState::max_state()).is_ok());
assert!(BlockState::try_from(BlockState::max_state() + 1).is_err());
@ -68,19 +66,34 @@ mod tests {
#[test]
fn test_from_blockstate() {
let block: Box<dyn Block> = Box::<dyn Block>::from(BlockState::Air);
let block: Box<dyn Block> = Box::<dyn Block>::from(BlockState::AIR);
assert_eq!(block.id(), "air");
let block: Box<dyn Block> = Box::<dyn Block>::from(BlockState::FloweringAzalea);
let block: Box<dyn Block> =
Box::<dyn Block>::from(BlockState::from(azalea_registry::Block::FloweringAzalea));
assert_eq!(block.id(), "flowering_azalea");
}
#[cfg(not(feature = "full-debug"))]
#[test]
fn test_debug_blockstate() {
assert_eq!(
format!("{:?}", BlockState::FloweringAzalea),
"BlockState (flowering_azalea)"
let formatted = format!(
"{:?}",
BlockState::from(azalea_registry::Block::FloweringAzalea)
);
assert!(
formatted.ends_with(", FloweringAzaleaBlock)"),
"{}",
formatted
);
let formatted = format!(
"{:?}",
BlockState::from(azalea_registry::Block::BigDripleafStem)
);
assert!(
formatted.ends_with(", BigDripleafStemBlock { facing: North, waterlogged: false })"),
"{}",
formatted
);
}
}

View file

@ -378,7 +378,8 @@ impl Client {
/// # Examples
///
/// ```
/// # fn example(client: &azalea::Client) {
/// # use azalea_world::entity::WorldName;
/// # fn example(client: &azalea_client::Client) {
/// let world_name = client.component::<WorldName>();
/// # }
pub fn component<T: Component + Clone>(&self) -> T {

View file

@ -15,6 +15,7 @@ impl Client {
///
/// # Examples
/// ```
/// # use azalea_world::entity::WorldName;
/// # fn example(mut client: azalea_client::Client) {
/// let is_logged_in = client
/// .query::<Option<&WorldName>>(&mut client.ecs.lock())

File diff suppressed because it is too large Load diff

View file

@ -79,7 +79,7 @@ impl<'a> Iterator for BlockCollisions<'a> {
let block_state: BlockState = chunk
.read()
.get(&(&pos).into(), self.world.chunks.min_y)
.unwrap_or(BlockState::Air);
.unwrap_or(BlockState::AIR);
// TODO: continue if self.only_suffocating_blocks and the block is not
// suffocating

View file

@ -75,7 +75,7 @@ fn travel(
let block_state_below = world
.chunks
.get_block_state(&block_pos_below)
.unwrap_or(BlockState::Air);
.unwrap_or(BlockState::AIR);
let block_below: Box<dyn Block> = block_state_below.into();
let block_friction = block_below.behavior().friction;
@ -412,7 +412,7 @@ mod tests {
.id();
let block_state = partial_world.chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::Stone,
azalea_registry::Block::Stone.into(),
&mut world_lock.write().chunks,
);
assert!(
@ -469,7 +469,11 @@ mod tests {
.id();
let block_state = partial_world.chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::StoneSlab_BottomFalse,
azalea_block::StoneSlabBlock {
kind: azalea_block::Type::Bottom,
waterlogged: false,
}
.into(),
&mut world_lock.write().chunks,
);
assert!(
@ -518,7 +522,11 @@ mod tests {
.id();
let block_state = world_lock.write().chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::StoneSlab_TopFalse,
azalea_block::StoneSlabBlock {
kind: azalea_block::Type::Top,
waterlogged: false,
}
.into(),
);
assert!(
block_state.is_some(),
@ -566,7 +574,15 @@ mod tests {
.id();
let block_state = world_lock.write().chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::CobblestoneWall_LowLowLowFalseFalseLow,
azalea_block::CobblestoneWallBlock {
east: azalea_block::EastWall::Low,
north: azalea_block::NorthWall::Low,
south: azalea_block::SouthWall::Low,
west: azalea_block::WestWall::Low,
up: false,
waterlogged: false,
}
.into(),
);
assert!(
block_state.is_some(),

View file

@ -37,7 +37,7 @@ impl McBufReadable for BlockStateWithPosition {
impl McBufWritable for BlockStateWithPosition {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let data = (self.state as u64) << 12
let data = (self.state.id as u64) << 12
| (u64::from(self.pos.x) << 8 | u64::from(self.pos.z) << 4 | u64::from(self.pos.y));
u64::var_write_into(&data, buf)?;
Ok(())

View file

@ -343,20 +343,20 @@ impl Section {
.states
.get(pos.x as usize, pos.y as usize, pos.z as usize);
// if there's an unknown block assume it's air
BlockState::try_from(state).unwrap_or(BlockState::Air)
BlockState::try_from(state).unwrap_or(BlockState::AIR)
}
fn get_and_set(&mut self, pos: ChunkSectionBlockPos, state: BlockState) -> BlockState {
let previous_state =
self.states
.get_and_set(pos.x as usize, pos.y as usize, pos.z as usize, state as u32);
.get_and_set(pos.x as usize, pos.y as usize, pos.z as usize, state.id);
// if there's an unknown block assume it's air
BlockState::try_from(previous_state).unwrap_or(BlockState::Air)
BlockState::try_from(previous_state).unwrap_or(BlockState::AIR)
}
fn set(&mut self, pos: ChunkSectionBlockPos, state: BlockState) {
self.states
.set(pos.x as usize, pos.y as usize, pos.z as usize, state as u32);
.set(pos.x as usize, pos.y as usize, pos.z as usize, state.id);
}
}

View file

@ -2368,7 +2368,7 @@ impl Default for EndermanMetadataBundle {
},
},
},
carry_state: CarryState(BlockState::Air),
carry_state: CarryState(BlockState::AIR),
creepy: Creepy(false),
stared_at: StaredAt(false),
}

View file

@ -94,7 +94,7 @@ pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: &Position) -> Bloc
// TODO: check if block below is a fence, wall, or fence gate
let block_pos = pos.down(1);
let block_state = chunk_storage.get_block_state(&block_pos);
if block_state == Some(BlockState::Air) {
if block_state == Some(BlockState::AIR) {
let block_pos_below = block_pos.down(1);
let block_state_below = chunk_storage.get_block_state(&block_pos_below);
if let Some(_block_state_below) = block_state_below {

View file

@ -165,12 +165,12 @@ mod tests {
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
BlockState::Stone,
azalea_registry::Block::Stone.into(),
&mut chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 1, 0),
BlockState::Air,
BlockState::AIR,
&mut chunk_storage,
);
@ -190,12 +190,12 @@ mod tests {
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
BlockState::Stone,
azalea_registry::Block::Stone.into(),
&mut chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 1, 0),
BlockState::Air,
BlockState::AIR,
&mut chunk_storage,
);
@ -215,22 +215,22 @@ mod tests {
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
BlockState::Stone,
azalea_registry::Block::Stone.into(),
&mut chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 1, 0),
BlockState::Air,
BlockState::AIR,
&mut chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 2, 0),
BlockState::Air,
BlockState::AIR,
&mut chunk_storage,
);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 3, 0),
BlockState::Air,
BlockState::AIR,
&mut chunk_storage,
);

View file

@ -367,7 +367,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
elif type_name == 'ItemStack':
default = f'Slot::Present({default})' if default != 'Empty' else 'Slot::Empty'
elif type_name == 'BlockState':
default = f'{default}' if default != 'Empty' else 'BlockState::Air'
default = f'{default}' if default != 'Empty' else 'BlockState::AIR'
elif type_name == 'OptionalFormattedText':
default = f'Some({default})' if default != 'Empty' else 'None'
elif type_name == 'CompoundTag':

View file

@ -7,8 +7,8 @@ COLLISION_BLOCKS_RS_DIR = get_dir_location(
'../azalea-physics/src/collision/blocks.rs')
def generate_block_shapes(blocks: dict, shapes: dict, aabbs: dict, block_states_report, block_datas_burger, mappings: Mappings):
blocks, shapes = simplify_shapes(blocks, shapes, aabbs)
def generate_block_shapes(blocks_pixlyzer: dict, shapes: dict, aabbs: dict, block_states_report, block_datas_burger, mappings: Mappings):
blocks, shapes = simplify_shapes(blocks_pixlyzer, shapes, aabbs)
code = generate_block_shapes_code(
blocks, shapes, block_states_report, block_datas_burger, mappings)
@ -28,8 +28,7 @@ def simplify_shapes(blocks: dict, shapes: dict, aabbs: dict):
used_shape_ids = set()
# determine the used shape ids
for block_id, block_data in blocks.items():
block_id = block_id.split(':')[-1]
for _block_id, block_data in blocks.items():
block_shapes = [state.get('collision_shape')
for state in block_data['states'].values()]
for s in block_shapes:
@ -73,9 +72,9 @@ def generate_block_shapes_code(blocks: dict, shapes: dict, block_states_report,
for (shape_id, shape) in sorted(shapes.items(), key=lambda shape: int(shape[0])):
generated_shape_code += generate_code_for_shape(shape_id, shape)
# BlockState::PurpurStairs_NorthTopStraightTrue => &SHAPE24,
# 1..100 | 200..300 => &SHAPE1,
generated_match_inner_code = ''
shape_ids_to_variants = {}
shape_ids_to_block_state_ids = {}
for block_id, shape_ids in blocks.items():
if isinstance(shape_ids, int):
shape_ids = [shape_ids]
@ -83,23 +82,34 @@ def generate_block_shapes_code(blocks: dict, shapes: dict, block_states_report,
block_data_burger = block_datas_burger[block_id]
for possible_state, shape_id in zip(block_report_data['states'], shape_ids):
variant_values = []
for value in tuple(possible_state.get('properties', {}).values()):
variant_values.append(to_camel_case(value))
block_state_id = possible_state['id']
if variant_values == []:
variant_name = to_camel_case(block_id)
else:
variant_name = f'{to_camel_case(block_id)}_{"".join(variant_values)}'
if shape_id not in shape_ids_to_variants:
shape_ids_to_variants[shape_id] = []
shape_ids_to_variants[shape_id].append(
f'BlockState::{variant_name}')
if shape_id not in shape_ids_to_block_state_ids:
shape_ids_to_block_state_ids[shape_id] = []
shape_ids_to_block_state_ids[shape_id].append(block_state_id)
# shape 1 is the most common so we have a _ => &SHAPE1 at the end
del shape_ids_to_variants[1]
for shape_id, variants in shape_ids_to_variants.items():
generated_match_inner_code += f'{"|".join(variants)} => &SHAPE{shape_id},\n'
del shape_ids_to_block_state_ids[1]
for shape_id, block_state_ids in shape_ids_to_block_state_ids.items():
# convert them into ranges (so like 1|2|3 is 1..=3 instead)
block_state_ids_ranges = []
range_start_block_state_id = None
last_block_state_id = None
for block_state_id in sorted(block_state_ids):
if range_start_block_state_id is None:
range_start_block_state_id = block_state_id
if last_block_state_id is not None:
# check if the range is done
if block_state_id - 1 != last_block_state_id:
block_state_ids_ranges.append(f'{range_start_block_state_id}..={last_block_state_id}' if range_start_block_state_id != last_block_state_id else str(range_start_block_state_id))
range_start_block_state_id = block_state_id
last_block_state_id = block_state_id
block_state_ids_ranges.append(f'{range_start_block_state_id}..={last_block_state_id}' if range_start_block_state_id != last_block_state_id else str(range_start_block_state_id))
generated_match_inner_code += f'{"|".join(block_state_ids_ranges)} => &SHAPE{shape_id},\n'
generated_match_inner_code += '_ => &SHAPE1'
return f'''
//! Autogenerated block collisions for every block
@ -123,8 +133,8 @@ pub trait BlockWithShape {{
impl BlockWithShape for BlockState {{
fn shape(&self) -> &'static VoxelShape {{
match self {{
{generated_match_inner_code}_ => &SHAPE1
match self.id {{
{generated_match_inner_code}
}}
}}
}}

View file

@ -4,6 +4,7 @@ from lib.download import get_server_jar, get_burger, get_client_jar, get_pixlyze
from lib.utils import get_dir_location
from zipfile import ZipFile
import subprocess
import requests
import json
import sys
import re
@ -114,7 +115,20 @@ def get_pixlyzer_data(version_id: str, category: str):
target_dir = get_dir_location(f'downloads/pixlyzer-{version_id}')
if not os.path.exists(get_dir_location(target_dir)):
# TODO: right now this False is hard-coded, it should retry with this
# enabled if # initially getting the data fails
if False or (os.path.exists(target_dir) and not os.path.exists(f'{target_dir}/{category}.min.json')):
print('Downloading', category, 'from pixlyzer-data.')
data = requests.get(f'https://gitlab.com/Bixilon/pixlyzer-data/-/raw/master/version/{version_id}/{category}.min.json?inline=false').text
try:
os.mkdir(target_dir)
except:
pass
with open(f'{target_dir}/{category}.min.json', 'w') as f:
f.write(data)
return json.loads(data)
if not os.path.exists(target_dir):
pixlyzer_dir = get_pixlyzer()
# for some reason pixlyzer doesn't work right unless the mvn clean
@ -231,7 +245,6 @@ def get_pixlyzer_data(version_id: str, category: str):
with open(f'{target_dir}/{category}.min.json', 'r') as f:
return json.load(f)
def get_file_from_jar(version_id: str, file_dir: str):
get_client_jar(version_id)
with ZipFile(get_dir_location(f'downloads/client-{version_id}.jar')) as z: