From d26b41fc5c2b73300c4a76d5bf36aefc144c7e77 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 26 Feb 2023 22:19:39 -0600 Subject: [PATCH 1/8] start adding World::find_block --- azalea-block/azalea-block-macros/src/lib.rs | 37 ++--- azalea-block/src/blocks.rs | 15 +- azalea-block/src/lib.rs | 50 ++++++- azalea-block/src/range.rs | 6 + azalea-core/src/position.rs | 11 +- .../game/clientbound_chunks_biomes_packet.rs | 7 + azalea-world/src/iterators.rs | 140 ++++++++++++++++++ azalea-world/src/lib.rs | 1 + azalea-world/src/palette.rs | 65 ++++---- azalea-world/src/world.rs | 31 +++- azalea/examples/testbot.rs | 7 + 11 files changed, 295 insertions(+), 75 deletions(-) create mode 100644 azalea-block/src/range.rs create mode 100644 azalea-protocol/src/packets/game/clientbound_chunks_biomes_packet.rs create mode 100644 azalea-world/src/iterators.rs diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs index b69ebd06a..d4c6ac58e 100755 --- a/azalea-block/azalea-block-macros/src/lib.rs +++ b/azalea-block/azalea-block-macros/src/lib.rs @@ -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_blockstate_range_match = quote! {}; for block in &input.block_definitions.blocks { let block_property_names = &block @@ -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_blockstate_range_match.extend(quote! { + azalea_registry::Block::#block_name_pascal_case => BlockStateRange { id: #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,11 +597,8 @@ 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::::from(*self)) - } - } + #property_enums + }; generated.extend(quote! { @@ -642,6 +629,14 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { } } } + impl From for BlockStateRange { + fn from(block: azalea_registry::Block) -> Self { + match block { + #from_registry_block_to_blockstate_range_match + _ => unreachable!("There should always be a block state for every azalea_registry::Block variant") + } + } + } }); generated.into() diff --git a/azalea-block/src/blocks.rs b/azalea-block/src/blocks.rs index e6923d59d..d1e621672 100755 --- a/azalea-block/src/blocks.rs +++ b/azalea-block/src/blocks.rs @@ -1,20 +1,7 @@ -use std::any::Any; - -use crate::BlockBehavior; +use crate::{Block, BlockBehavior, BlockState, BlockStateRange}; 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(&self) -> Option<&T> { - (self as &dyn Any).downcast_ref::() - } -} - make_block_states! { Properties => { "snowy" => bool, diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs index 7a62e5883..c5d6b4b8d 100755 --- a/azalea-block/src/lib.rs +++ b/azalea-block/src/lib.rs @@ -2,14 +2,47 @@ #![feature(trait_upcasting)] mod behavior; -mod blocks; +pub mod blocks; +mod range; 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::BlockStateRange; +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(&self) -> Option<&T> { + (self as &dyn Any).downcast_ref::() + } +} + +/// 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 +85,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::::from(*self) + ) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-block/src/range.rs b/azalea-block/src/range.rs new file mode 100644 index 000000000..abffbefea --- /dev/null +++ b/azalea-block/src/range.rs @@ -0,0 +1,6 @@ +use std::ops::RangeInclusive; + +#[derive(Debug, Clone, )] +pub struct BlockStateRange { + pub id: RangeInclusive, +} diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index 5ba7143ed..bcfbdfb44 100755 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -148,12 +148,21 @@ pub struct ChunkPos { pub x: i32, pub z: i32, } - impl ChunkPos { pub fn new(x: i32, z: i32) -> Self { ChunkPos { x, z } } } +impl Add for ChunkPos { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self { + x: self.x + rhs.x, + z: self.z + rhs.z, + } + } +} /// The coordinates of a chunk section in the world. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] diff --git a/azalea-protocol/src/packets/game/clientbound_chunks_biomes_packet.rs b/azalea-protocol/src/packets/game/clientbound_chunks_biomes_packet.rs new file mode 100644 index 000000000..9ad242a4a --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_chunks_biomes_packet.rs @@ -0,0 +1,7 @@ +use azalea_protocol_macros::ClientboundGamePacket; +use azalea_buf::McBuf; + +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] +pub struct ClientboundChunksBiomesPacket { +pub chunk_biome_data: todo!(), +} \ No newline at end of file diff --git a/azalea-world/src/iterators.rs b/azalea-world/src/iterators.rs new file mode 100644 index 000000000..f4a49afbe --- /dev/null +++ b/azalea-world/src/iterators.rs @@ -0,0 +1,140 @@ +//! Iterators, based on [prismarine-world's iterators](https://github.com/PrismarineJS/prismarine-world/blob/master/src/iterators.js). + +use azalea_core::{BlockPos, ChunkPos}; + +/// An octahedron iterator, useful for iterating over blocks in a world. +/// +/// ``` +/// let mut iter = OctahedronIterator::new(BlockPos::default(), 4); +/// for block_pos in iter { +/// println!("{:?}", block_pos); +/// } +/// ``` +pub struct BlockIterator { + start: BlockPos, + max_distance: u32, + + pos: BlockPos, + apothem: u32, + left: i32, + right: i32, +} +impl BlockIterator { + pub fn new(start: BlockPos, max_distance: u32) -> Self { + Self { + start, + max_distance, + + pos: BlockPos { + x: -1, + y: -1, + z: -1, + }, + apothem: 1, + left: 1, + right: 2, + } + } +} + +impl Iterator for BlockIterator { + type Item = BlockPos; + + fn next(&mut self) -> Option { + if self.apothem > self.max_distance { + return None; + } + + self.right -= 1; + if self.right < 0 { + self.left -= 1; + if self.left < 0 { + self.pos.z += 2; + if self.pos.z > 1 { + self.pos.y += 2; + if self.pos.y > 1 { + self.pos.x += 2; + if self.pos.x > 1 { + self.apothem += 1; + self.pos.x = -1; + } + self.pos.y = -1; + } + self.pos.z = -1; + } + self.left = self.apothem as i32; + } + self.right = self.left; + } + let x = self.pos.x * self.right; + let y = self.pos.y * ((self.apothem as i32) - self.left); + let z = self.pos.z * ((self.apothem as i32) - (i32::abs(x) + i32::abs(y))); + Some(BlockPos { x: x, y, z } + self.start) + } +} + +/// A spiral iterator, useful for iterating over chunks in a world. +/// +/// ``` +/// let mut iter = ChunkIterator::new(ChunkPos::default(), 4); +/// for chunk_pos in iter { +/// println!("{:?}", chunk_pos); +/// } +/// ``` +pub struct ChunkIterator { + start: ChunkPos, + number_of_points: u32, + + dir: ChunkPos, + + segment_len: u32, + pos: ChunkPos, + segment_passed: u32, + current_iter: u32, +} +impl ChunkIterator { + pub fn new(start: ChunkPos, max_distance: u32) -> Self { + Self { + start, + number_of_points: u32::pow(max_distance * 2 - 1, 2), + + dir: ChunkPos { x: 1, z: 0 }, + + segment_len: 1, + pos: ChunkPos::default(), + segment_passed: 0, + current_iter: 0, + } + } +} +impl Iterator for ChunkIterator { + type Item = ChunkPos; + + fn next(&mut self) -> Option { + if self.current_iter > self.number_of_points { + return None; + } + + let output = self.start + self.dir; + + // make a step, add the direction to the current position + self.pos.x += self.dir.x; + self.pos.z += self.dir.z; + self.segment_passed += 1; + + if self.segment_passed == self.segment_len { + // done with current segment + self.segment_passed = 0; + + // rotate directions + (self.dir.x, self.dir.z) = (-self.dir.z, self.dir.x); + + // increase segment length if necessary + if self.dir.z == 0 { + self.segment_len += 1; + } + } + self.current_iter += 1; + Some(output) + } +} diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index 1a419c3ac..fb6e85170 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -7,6 +7,7 @@ mod bit_storage; mod chunk_storage; mod container; pub mod entity; +mod iterators; pub mod palette; mod world; diff --git a/azalea-world/src/palette.rs b/azalea-world/src/palette.rs index d97f61a35..44f1f3aae 100755 --- a/azalea-world/src/palette.rs +++ b/azalea-world/src/palette.rs @@ -37,7 +37,7 @@ impl PalettedContainer { container_type: &'static PalettedContainerType, ) -> Result { let bits_per_entry = u8::read_from(buf)?; - let palette_type = PaletteType::from_bits_and_type(bits_per_entry, container_type); + let palette_type = PaletteKind::from_bits_and_type(bits_per_entry, container_type); let palette = palette_type.read(buf)?; let size = container_type.size(); @@ -97,7 +97,7 @@ impl PalettedContainer { fn create_or_reuse_data(&self, bits_per_entry: u8) -> PalettedContainer { let new_palette_type = - PaletteType::from_bits_and_type(bits_per_entry, &self.container_type); + PaletteKind::from_bits_and_type(bits_per_entry, &self.container_type); // note for whoever is trying to optimize this: vanilla has this // but it causes a stack overflow since it's not changing the bits per entry // i don't know how to fix this properly so glhf @@ -188,13 +188,14 @@ impl McBufWritable for PalettedContainer { } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum PaletteType { +pub enum PaletteKind { SingleValue, Linear, Hashmap, Global, } +/// A representation of the different types of chunk palettes Minecraft uses. #[derive(Clone, Debug)] pub enum Palette { /// ID of the corresponding entry in its global palette @@ -211,13 +212,7 @@ impl Palette { match self { Palette::SingleValue(v) => *v, Palette::Linear(v) => v[id], - Palette::Hashmap(v) => { - if id >= v.len() { - 0 - } else { - v[id] - } - } + Palette::Hashmap(v) => v.get(id).copied().unwrap_or_default(), Palette::Global => id as u32, } } @@ -241,49 +236,49 @@ impl McBufWritable for Palette { } } -impl PaletteType { +impl PaletteKind { pub fn from_bits_and_type(bits_per_entry: u8, container_type: &PalettedContainerType) -> Self { match container_type { PalettedContainerType::BlockStates => match bits_per_entry { - 0 => PaletteType::SingleValue, - 1..=4 => PaletteType::Linear, - 5..=8 => PaletteType::Hashmap, - _ => PaletteType::Global, + 0 => PaletteKind::SingleValue, + 1..=4 => PaletteKind::Linear, + 5..=8 => PaletteKind::Hashmap, + _ => PaletteKind::Global, }, PalettedContainerType::Biomes => match bits_per_entry { - 0 => PaletteType::SingleValue, - 1..=3 => PaletteType::Linear, - _ => PaletteType::Global, + 0 => PaletteKind::SingleValue, + 1..=3 => PaletteKind::Linear, + _ => PaletteKind::Global, }, } } pub fn read(&self, buf: &mut Cursor<&[u8]>) -> Result { Ok(match self { - PaletteType::SingleValue => Palette::SingleValue(u32::var_read_from(buf)?), - PaletteType::Linear => Palette::Linear(Vec::::var_read_from(buf)?), - PaletteType::Hashmap => Palette::Hashmap(Vec::::var_read_from(buf)?), - PaletteType::Global => Palette::Global, + PaletteKind::SingleValue => Palette::SingleValue(u32::var_read_from(buf)?), + PaletteKind::Linear => Palette::Linear(Vec::::var_read_from(buf)?), + PaletteKind::Hashmap => Palette::Hashmap(Vec::::var_read_from(buf)?), + PaletteKind::Global => Palette::Global, }) } pub fn as_empty_palette(&self) -> Palette { match self { - PaletteType::SingleValue => Palette::SingleValue(0), - PaletteType::Linear => Palette::Linear(Vec::new()), - PaletteType::Hashmap => Palette::Hashmap(Vec::new()), - PaletteType::Global => Palette::Global, + PaletteKind::SingleValue => Palette::SingleValue(0), + PaletteKind::Linear => Palette::Linear(Vec::new()), + PaletteKind::Hashmap => Palette::Hashmap(Vec::new()), + PaletteKind::Global => Palette::Global, } } } -impl From<&Palette> for PaletteType { +impl From<&Palette> for PaletteKind { fn from(palette: &Palette) -> Self { match palette { - Palette::SingleValue(_) => PaletteType::SingleValue, - Palette::Linear(_) => PaletteType::Linear, - Palette::Hashmap(_) => PaletteType::Hashmap, - Palette::Global => PaletteType::Global, + Palette::SingleValue(_) => PaletteKind::SingleValue, + Palette::Linear(_) => PaletteKind::Linear, + Palette::Hashmap(_) => PaletteKind::Hashmap, + Palette::Global => PaletteKind::Global, } } } @@ -313,14 +308,14 @@ mod tests { assert_eq!(palette_container.bits_per_entry, 0); assert_eq!(palette_container.get_at_index(0), 0); assert_eq!( - PaletteType::from(&palette_container.palette), - PaletteType::SingleValue + PaletteKind::from(&palette_container.palette), + PaletteKind::SingleValue ); palette_container.set_at_index(0, 1); assert_eq!(palette_container.get_at_index(0), 1); assert_eq!( - PaletteType::from(&palette_container.palette), - PaletteType::Linear + PaletteKind::from(&palette_container.palette), + PaletteKind::Linear ); } diff --git a/azalea-world/src/world.rs b/azalea-world/src/world.rs index 8f1b2179d..da78558ca 100644 --- a/azalea-world/src/world.rs +++ b/azalea-world/src/world.rs @@ -2,9 +2,12 @@ use crate::{ entity::{ EntityInfos, EntityUuid, LoadedBy, Local, MinecraftEntityId, PartialEntityInfos, WorldName, }, + iterators::{BlockIterator, ChunkIterator}, + palette::Palette, ChunkStorage, PartialChunkStorage, WorldContainer, }; -use azalea_core::ChunkPos; +use azalea_block::BlockState; +use azalea_core::{BlockPos, ChunkPos}; use azalea_ecs::{ entity::Entity, query::{Changed, With, Without}, @@ -187,6 +190,32 @@ impl World { pub fn entity_by_id(&self, entity_id: &MinecraftEntityId) -> Option { self.entity_by_id.get(entity_id).copied() } + + pub fn find_block(&self, nearest_to: impl Into, block_states: HashSet) { + // iterate over every chunk in a 3d spiral pattern + // and then check the palette for the block state + // borrowed from https://github.com/PrismarineJS/prismarine-world/blob/master/src/iterators.js#L65 + + let nearest_to: BlockPos = nearest_to.into(); + let start_chunk: ChunkPos = (&nearest_to).into(); + let iter = ChunkIterator::new(start_chunk, 32); + + for chunk_pos in iter { + let chunk = self.chunks.get(&chunk_pos).unwrap(); + for section in &chunk.read().sections { + let maybe_has_block = match section.states.palette { + Palette::SingleValue(id) => block_states.contains(&BlockState { id }), + Palette::Linear(ids) => ids + .iter() + .any(|id| block_states.contains(&BlockState { id })), + Palette::Hashmap(ids) => ids + .iter() + .any(|id| block_states.contains(&BlockState { id })), + Palette::Global => true, + }; + } + } + } } impl Debug for PartialWorld { diff --git a/azalea/examples/testbot.rs b/azalea/examples/testbot.rs index 55c440a23..c5028af5c 100644 --- a/azalea/examples/testbot.rs +++ b/azalea/examples/testbot.rs @@ -140,6 +140,13 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< "lag" => { std::thread::sleep(Duration::from_millis(1000)); } + "findblock" => { + let target_pos = bot.world().read().find_block( + bot.component::(), + azalea_registry::Block::DiamondBlock.into(), + ); + bot.chat(&format!("target_pos: {target_pos:?}",)); + } _ => {} } } From 7d20b8c6945ae79e207305f087a3a79a41daa188 Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 8 Mar 2023 01:37:37 +0000 Subject: [PATCH 2/8] keep working on find_block --- azalea-block/src/range.rs | 2 +- azalea-core/src/position.rs | 2 +- azalea-physics/src/lib.rs | 28 ++++---- azalea-world/src/bit_storage.rs | 12 ++-- azalea-world/src/iterators.rs | 30 ++++++++- azalea-world/src/lib.rs | 2 +- azalea-world/src/palette.rs | 49 ++++++++++++-- azalea-world/src/world.rs | 64 ++++++++++++++++--- .../{ => todo}/craft_dig_straight_down.rs | 0 azalea/examples/{ => todo}/mine_a_chunk.rs | 0 azalea/examples/{ => todo}/pvp.rs | 0 11 files changed, 152 insertions(+), 37 deletions(-) rename azalea/examples/{ => todo}/craft_dig_straight_down.rs (100%) mode change 100755 => 100644 rename azalea/examples/{ => todo}/mine_a_chunk.rs (100%) rename azalea/examples/{ => todo}/pvp.rs (100%) mode change 100755 => 100644 diff --git a/azalea-block/src/range.rs b/azalea-block/src/range.rs index abffbefea..2f75c2ab2 100644 --- a/azalea-block/src/range.rs +++ b/azalea-block/src/range.rs @@ -1,6 +1,6 @@ use std::ops::RangeInclusive; -#[derive(Debug, Clone, )] +#[derive(Debug, Clone)] pub struct BlockStateRange { pub id: RangeInclusive, } diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index bcfbdfb44..ee2359bc7 100755 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -153,7 +153,7 @@ impl ChunkPos { ChunkPos { x, z } } } -impl Add for ChunkPos { +impl Add for ChunkPos { type Output = Self; fn add(self, rhs: Self) -> Self::Output { diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs index 0a8f8a8bc..f9de13106 100644 --- a/azalea-physics/src/lib.rs +++ b/azalea-physics/src/lib.rs @@ -463,8 +463,8 @@ mod tests { .id(); let block_state = partial_world.chunks.set_block_state( &BlockPos { x: 0, y: 69, z: 0 }, - azalea_block::StoneSlabBlock { - kind: azalea_block::Type::Bottom, + azalea_block::blocks::StoneSlabBlock { + kind: azalea_block::blocks::Type::Bottom, waterlogged: false, } .into(), @@ -516,8 +516,8 @@ mod tests { .id(); let block_state = world_lock.write().chunks.set_block_state( &BlockPos { x: 0, y: 69, z: 0 }, - azalea_block::StoneSlabBlock { - kind: azalea_block::Type::Top, + azalea_block::blocks::StoneSlabBlock { + kind: azalea_block::blocks::Type::Top, waterlogged: false, } .into(), @@ -568,11 +568,11 @@ mod tests { .id(); let block_state = world_lock.write().chunks.set_block_state( &BlockPos { x: 0, y: 69, z: 0 }, - azalea_block::CobblestoneWallBlock { - east: azalea_block::EastWall::Low, - north: azalea_block::NorthWall::Low, - south: azalea_block::SouthWall::Low, - west: azalea_block::WestWall::Low, + azalea_block::blocks::CobblestoneWallBlock { + east: azalea_block::blocks::EastWall::Low, + north: azalea_block::blocks::NorthWall::Low, + south: azalea_block::blocks::SouthWall::Low, + west: azalea_block::blocks::WestWall::Low, up: false, waterlogged: false, } @@ -629,11 +629,11 @@ mod tests { y: 69, z: -8, }, - azalea_block::CobblestoneWallBlock { - east: azalea_block::EastWall::Low, - north: azalea_block::NorthWall::Low, - south: azalea_block::SouthWall::Low, - west: azalea_block::WestWall::Low, + azalea_block::blocks::CobblestoneWallBlock { + east: azalea_block::blocks::EastWall::Low, + north: azalea_block::blocks::NorthWall::Low, + south: azalea_block::blocks::SouthWall::Low, + west: azalea_block::blocks::WestWall::Low, up: false, waterlogged: false, } diff --git a/azalea-world/src/bit_storage.rs b/azalea-world/src/bit_storage.rs index f6ca4cd67..09b68fae9 100755 --- a/azalea-world/src/bit_storage.rs +++ b/azalea-world/src/bit_storage.rs @@ -158,13 +158,13 @@ impl BitStorage { .unwrap() } + /// Get the data at the given index. + /// + /// # Panics + /// + /// This function will panic if the given index is greater than or equal to + /// the size of this storage. pub fn get(&self, index: usize) -> u64 { - // Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)var1); - // int var2 = this.cellIndex(var1); - // long var3 = this.data[var2]; - // int var5 = (var1 - var2 * this.valuesPerLong) * this.bits; - // return (int)(var3 >> var5 & this.mask); - assert!( index < self.size, "Index {} out of bounds (must be less than {})", diff --git a/azalea-world/src/iterators.rs b/azalea-world/src/iterators.rs index f4a49afbe..9f66b4d5a 100644 --- a/azalea-world/src/iterators.rs +++ b/azalea-world/src/iterators.rs @@ -1,11 +1,15 @@ -//! Iterators, based on [prismarine-world's iterators](https://github.com/PrismarineJS/prismarine-world/blob/master/src/iterators.js). +//! Iterators for iterating over Minecraft blocks and chunks, based on +//! [prismarine-world's iterators](https://github.com/PrismarineJS/prismarine-world/blob/master/src/iterators.js). use azalea_core::{BlockPos, ChunkPos}; /// An octahedron iterator, useful for iterating over blocks in a world. /// /// ``` -/// let mut iter = OctahedronIterator::new(BlockPos::default(), 4); +/// # use azalea_core::BlockPos; +/// # use azalea_world::iterators::BlockIterator; +/// +/// let mut iter = BlockIterator::new(BlockPos::default(), 4); /// for block_pos in iter { /// println!("{:?}", block_pos); /// } @@ -76,6 +80,9 @@ impl Iterator for BlockIterator { /// A spiral iterator, useful for iterating over chunks in a world. /// /// ``` +/// # use azalea_core::ChunkPos; +/// # use azalea_world::iterators::ChunkIterator; +/// /// let mut iter = ChunkIterator::new(ChunkPos::default(), 4); /// for chunk_pos in iter { /// println!("{:?}", chunk_pos); @@ -106,6 +113,25 @@ impl ChunkIterator { current_iter: 0, } } + + /// Change the distance that this iterator won't go past. + /// + /// ``` + /// # use azalea_core::ChunkPos; + /// # use azalea_world::iterators::ChunkIterator; + /// + /// let mut iter = ChunkIterator::new(ChunkPos::default(), 2); + /// for chunk_pos in iter { + /// println!("{:?}", chunk_pos); + /// } + /// iter.set_max_distance(4); + /// for chunk_pos in iter { + /// println!("{:?}", chunk_pos); + /// } + /// ``` + pub fn set_max_distance(&mut self, max_distance: u32) { + self.number_of_points = u32::pow(max_distance * 2 - 1, 2); + } } impl Iterator for ChunkIterator { type Item = ChunkPos; diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index fb6e85170..77498efd4 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -7,7 +7,7 @@ mod bit_storage; mod chunk_storage; mod container; pub mod entity; -mod iterators; +pub mod iterators; pub mod palette; mod world; diff --git a/azalea-world/src/palette.rs b/azalea-world/src/palette.rs index 44f1f3aae..d10357ad3 100755 --- a/azalea-world/src/palette.rs +++ b/azalea-world/src/palette.rs @@ -12,6 +12,11 @@ pub enum PalettedContainerType { #[derive(Clone, Debug)] pub struct PalettedContainer { pub bits_per_entry: u8, + /// This is usually a list of unique values that appear in the container so + /// they can be indexed by the bit storage. + /// + /// Sometimes it doesn't contain anything if there's too many unique items + /// in the bit storage, though. pub palette: Palette, /// Compacted list of indices pointing to entry IDs in the Palette. pub storage: BitStorage, @@ -57,15 +62,33 @@ impl PalettedContainer { } /// Calculates the index of the given coordinates. - pub fn get_index(&self, x: usize, y: usize, z: usize) -> usize { + pub fn index_from_coords(&self, x: usize, y: usize, z: usize) -> usize { let size_bits = self.container_type.size_bits(); (((y << size_bits) | z) << size_bits) | x } + pub fn coords_from_index(&self, index: usize) -> (usize, usize, usize) { + let size_bits = self.container_type.size_bits(); + let mask = (1 << size_bits) - 1; + ( + index & mask, + (index >> size_bits >> size_bits) & mask, + (index >> size_bits) & mask, + ) + } + /// Returns the value at the given index. + /// + /// # Panics + /// + /// This function panics if the index is greater than or equal to the number + /// of things in the storage. (So for block states, it must be less than + /// 4096). pub fn get_at_index(&self, index: usize) -> u32 { + // first get the pallete id let paletted_value = self.storage.get(index); + // and then get the value from that id self.palette.value_for(paletted_value as usize) } @@ -73,14 +96,14 @@ impl PalettedContainer { pub fn get(&self, x: usize, y: usize, z: usize) -> u32 { // let paletted_value = self.storage.get(self.get_index(x, y, z)); // self.palette.value_for(paletted_value as usize) - self.get_at_index(self.get_index(x, y, z)) + self.get_at_index(self.index_from_coords(x, y, z)) } /// Sets the id at the given coordinates and return the previous id pub fn get_and_set(&mut self, x: usize, y: usize, z: usize, value: u32) -> u32 { let paletted_value = self.id_for(value); self.storage - .get_and_set(self.get_index(x, y, z), paletted_value as u64) as u32 + .get_and_set(self.index_from_coords(x, y, z), paletted_value as u64) as u32 } /// Sets the id at the given index and return the previous id. You probably @@ -92,7 +115,7 @@ impl PalettedContainer { /// Sets the id at the given coordinates and return the previous id pub fn set(&mut self, x: usize, y: usize, z: usize, value: u32) { - self.set_at_index(self.get_index(x, y, z), value); + self.set_at_index(self.index_from_coords(x, y, z), value); } fn create_or_reuse_data(&self, bits_per_entry: u8) -> PalettedContainer { @@ -354,4 +377,22 @@ mod tests { palette_container.set_at_index(16, 16); // 5 bits assert_eq!(palette_container.bits_per_entry, 5); } + + #[test] + fn test_coords_from_index() { + let palette_container = + PalettedContainer::new(&PalettedContainerType::BlockStates).unwrap(); + + for x in 0..15 { + for y in 0..15 { + for z in 0..15 { + assert_eq!( + palette_container + .coords_from_index(palette_container.index_from_coords(x, y, z)), + (x, y, z) + ); + } + } + } + } } diff --git a/azalea-world/src/world.rs b/azalea-world/src/world.rs index da78558ca..70a1c70a7 100644 --- a/azalea-world/src/world.rs +++ b/azalea-world/src/world.rs @@ -2,7 +2,7 @@ use crate::{ entity::{ EntityInfos, EntityUuid, LoadedBy, Local, MinecraftEntityId, PartialEntityInfos, WorldName, }, - iterators::{BlockIterator, ChunkIterator}, + iterators::ChunkIterator, palette::Palette, ChunkStorage, PartialChunkStorage, WorldContainer, }; @@ -191,30 +191,78 @@ impl World { self.entity_by_id.get(entity_id).copied() } - pub fn find_block(&self, nearest_to: impl Into, block_states: HashSet) { + /// Find the coordinates of a block in the world. + /// + /// Note that this is sorted by `x+y+z` and not `x^2+y^2+z^2`, for + /// optimization purposes. + pub fn find_block( + &self, + nearest_to: impl Into, + block_states: impl Into>, + ) -> Option { // iterate over every chunk in a 3d spiral pattern // and then check the palette for the block state - // borrowed from https://github.com/PrismarineJS/prismarine-world/blob/master/src/iterators.js#L65 + let block_states = block_states.into(); let nearest_to: BlockPos = nearest_to.into(); let start_chunk: ChunkPos = (&nearest_to).into(); + // todo (correctness): rename this to something like SquareChunkIterator and + // also have another one that iterates in a diagonal shape and use that + // here let iter = ChunkIterator::new(start_chunk, 32); for chunk_pos in iter { let chunk = self.chunks.get(&chunk_pos).unwrap(); - for section in &chunk.read().sections { - let maybe_has_block = match section.states.palette { - Palette::SingleValue(id) => block_states.contains(&BlockState { id }), + + let mut nearest_found_pos: Option = None; + + for (section_index, section) in chunk.read().sections.iter().enumerate() { + let maybe_has_block = match §ion.states.palette { + Palette::SingleValue(id) => block_states.contains(&BlockState { id: *id }), Palette::Linear(ids) => ids .iter() - .any(|id| block_states.contains(&BlockState { id })), + .any(|&id| block_states.contains(&BlockState { id })), Palette::Hashmap(ids) => ids .iter() - .any(|id| block_states.contains(&BlockState { id })), + .any(|&id| block_states.contains(&BlockState { id })), Palette::Global => true, }; + if !maybe_has_block { + continue; + } + + for i in 0..4096 { + let block_state = section.states.get_at_index(i); + let block_state = BlockState { id: block_state }; + + if block_states.contains(&block_state) { + let (section_x, section_y, section_z) = section.states.coords_from_index(i); + let (x, y, z) = ( + chunk_pos.x * 16 + (section_x as i32), + self.chunks.min_y + (section_index * 16) as i32 + section_y as i32, + chunk_pos.z * 16 + (section_z as i32), + ); + let this_block_pos = BlockPos { x, y, z }; + // only update if it's closer + if let Some(nearest_found_pos) = nearest_found_pos { + if this_block_pos.x + this_block_pos.y + this_block_pos.z + >= nearest_found_pos.x + nearest_found_pos.y + nearest_found_pos.z + { + continue; + } + } + nearest_found_pos = Some(this_block_pos); + } + } + } + + // if we found the position, return it + if nearest_found_pos.is_some() { + return nearest_found_pos; } } + + None } } diff --git a/azalea/examples/craft_dig_straight_down.rs b/azalea/examples/todo/craft_dig_straight_down.rs old mode 100755 new mode 100644 similarity index 100% rename from azalea/examples/craft_dig_straight_down.rs rename to azalea/examples/todo/craft_dig_straight_down.rs diff --git a/azalea/examples/mine_a_chunk.rs b/azalea/examples/todo/mine_a_chunk.rs similarity index 100% rename from azalea/examples/mine_a_chunk.rs rename to azalea/examples/todo/mine_a_chunk.rs diff --git a/azalea/examples/pvp.rs b/azalea/examples/todo/pvp.rs old mode 100755 new mode 100644 similarity index 100% rename from azalea/examples/pvp.rs rename to azalea/examples/todo/pvp.rs From 683de171211f403a120b5600cff10762d8151c3a Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 7 Mar 2023 20:09:29 -0600 Subject: [PATCH 3/8] BlockStates --- azalea-block/azalea-block-macros/src/lib.rs | 10 +++---- azalea-block/src/blocks.rs | 2 +- azalea-block/src/lib.rs | 2 +- azalea-block/src/range.rs | 33 +++++++++++++++++++-- azalea-world/src/world.rs | 5 ++-- azalea/examples/testbot.rs | 2 +- 6 files changed, 40 insertions(+), 14 deletions(-) diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs index d4c6ac58e..5195dc3c2 100755 --- a/azalea-block/azalea-block-macros/src/lib.rs +++ b/azalea-block/azalea-block-macros/src/lib.rs @@ -310,7 +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_blockstate_range_match = quote! {}; + let mut from_registry_block_to_blockstates_match = quote! {}; for block in &input.block_definitions.blocks { let block_property_names = &block @@ -524,8 +524,8 @@ 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_blockstate_range_match.extend(quote! { - azalea_registry::Block::#block_name_pascal_case => BlockStateRange { id: #first_state_id..=#last_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! {}; @@ -629,10 +629,10 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { } } } - impl From for BlockStateRange { + impl From for BlockStates { fn from(block: azalea_registry::Block) -> Self { match block { - #from_registry_block_to_blockstate_range_match + #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/blocks.rs index d1e621672..afe6dfdaf 100755 --- a/azalea-block/src/blocks.rs +++ b/azalea-block/src/blocks.rs @@ -1,4 +1,4 @@ -use crate::{Block, BlockBehavior, BlockState, BlockStateRange}; +use crate::{Block, BlockBehavior, BlockState, BlockStates}; use azalea_block_macros::make_block_states; use std::fmt::Debug; diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs index c5d6b4b8d..f7361476f 100755 --- a/azalea-block/src/lib.rs +++ b/azalea-block/src/lib.rs @@ -8,7 +8,7 @@ mod range; use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable}; pub use behavior::BlockBehavior; use core::fmt::Debug; -pub use range::BlockStateRange; +pub use range::BlockStates; use std::{ any::Any, io::{Cursor, Write}, diff --git a/azalea-block/src/range.rs b/azalea-block/src/range.rs index 2f75c2ab2..6ccf4152e 100644 --- a/azalea-block/src/range.rs +++ b/azalea-block/src/range.rs @@ -1,6 +1,33 @@ -use std::ops::RangeInclusive; +use std::{collections::HashSet, ops::RangeInclusive}; + +use crate::BlockState; #[derive(Debug, Clone)] -pub struct BlockStateRange { - pub id: RangeInclusive, +pub struct BlockStates { + pub set: HashSet, +} + +impl From> for BlockStates { + fn from(range: RangeInclusive) -> 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; + + fn into_iter(self) -> Self::IntoIter { + self.set.into_iter() + } +} + +impl BlockStates { + pub fn contains(&self, state: &BlockState) -> bool { + self.set.contains(state) + } } diff --git a/azalea-world/src/world.rs b/azalea-world/src/world.rs index 70a1c70a7..31ce80cf1 100644 --- a/azalea-world/src/world.rs +++ b/azalea-world/src/world.rs @@ -6,7 +6,7 @@ use crate::{ palette::Palette, ChunkStorage, PartialChunkStorage, WorldContainer, }; -use azalea_block::BlockState; +use azalea_block::{BlockState, BlockStates}; use azalea_core::{BlockPos, ChunkPos}; use azalea_ecs::{ entity::Entity, @@ -198,12 +198,11 @@ impl World { pub fn find_block( &self, nearest_to: impl Into, - block_states: impl Into>, + block_states: &BlockStates, ) -> Option { // iterate over every chunk in a 3d spiral pattern // and then check the palette for the block state - let block_states = block_states.into(); let nearest_to: BlockPos = nearest_to.into(); let start_chunk: ChunkPos = (&nearest_to).into(); // todo (correctness): rename this to something like SquareChunkIterator and diff --git a/azalea/examples/testbot.rs b/azalea/examples/testbot.rs index 55eba118f..6777cd2d3 100644 --- a/azalea/examples/testbot.rs +++ b/azalea/examples/testbot.rs @@ -143,7 +143,7 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< "findblock" => { let target_pos = bot.world().read().find_block( bot.component::(), - azalea_registry::Block::DiamondBlock.into(), + &azalea_registry::Block::DiamondBlock.into(), ); bot.chat(&format!("target_pos: {target_pos:?}",)); } From 32ca2429f309bdde944539c0e3f30d52e9ecb17e Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 7 Mar 2023 20:31:26 -0600 Subject: [PATCH 4/8] fix sorting --- azalea-core/src/position.rs | 7 +++ azalea-world/src/iterators.rs | 93 ++++++++++++++++++++++++++++++++--- azalea-world/src/world.rs | 17 +++---- 3 files changed, 101 insertions(+), 16 deletions(-) diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index ee2359bc7..3c452d3af 100755 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -12,6 +12,8 @@ macro_rules! vec3_impl { Self { x, y, z } } + /// Get the distance of this vector to the origin by doing `x^2 + y^2 + + /// z^2`. pub fn length_sqr(&self) -> $type { self.x * self.x + self.y * self.y + self.z * self.z } @@ -139,6 +141,11 @@ impl BlockPos { z: self.z as f64 + 0.5, } } + + /// Get the distance of this vector from the origin by doing `x + y + z`. + pub fn length_manhattan(&self) -> u32 { + (self.x.abs() + self.y.abs() + self.z.abs()) as u32 + } } /// Chunk coordinates are used to represent where a chunk is in the world. You diff --git a/azalea-world/src/iterators.rs b/azalea-world/src/iterators.rs index 9f66b4d5a..ca1536cb0 100644 --- a/azalea-world/src/iterators.rs +++ b/azalea-world/src/iterators.rs @@ -77,18 +77,19 @@ impl Iterator for BlockIterator { } } -/// A spiral iterator, useful for iterating over chunks in a world. +/// A spiral iterator, useful for iterating over chunks in a world. Use +/// `ChunkIterator` to sort by x+y+z (Manhattan) distance. /// /// ``` /// # use azalea_core::ChunkPos; -/// # use azalea_world::iterators::ChunkIterator; +/// # use azalea_world::iterators::SquareChunkIterator; /// -/// let mut iter = ChunkIterator::new(ChunkPos::default(), 4); +/// let mut iter = SquareChunkIterator::new(ChunkPos::default(), 4); /// for chunk_pos in iter { /// println!("{:?}", chunk_pos); /// } /// ``` -pub struct ChunkIterator { +pub struct SquareChunkIterator { start: ChunkPos, number_of_points: u32, @@ -99,7 +100,7 @@ pub struct ChunkIterator { segment_passed: u32, current_iter: u32, } -impl ChunkIterator { +impl SquareChunkIterator { pub fn new(start: ChunkPos, max_distance: u32) -> Self { Self { start, @@ -133,7 +134,7 @@ impl ChunkIterator { self.number_of_points = u32::pow(max_distance * 2 - 1, 2); } } -impl Iterator for ChunkIterator { +impl Iterator for SquareChunkIterator { type Item = ChunkPos; fn next(&mut self) -> Option { @@ -164,3 +165,83 @@ impl Iterator for ChunkIterator { Some(output) } } + +/// A diagonal spiral iterator, useful for iterating over chunks in a world. +/// +/// ``` +/// # use azalea_core::ChunkPos; +/// # use azalea_world::iterators::ChunkIterator; +/// +/// let mut iter = ChunkIterator::new(ChunkPos::default(), 4); +/// for chunk_pos in iter { +/// println!("{:?}", chunk_pos); +/// } +/// ``` +pub struct ChunkIterator { + pub max_distance: u32, + pub start: ChunkPos, + pub pos: ChunkPos, + pub layer: i32, + pub leg: i32, +} +impl ChunkIterator { + pub fn new(start: ChunkPos, max_distance: u32) -> Self { + Self { + max_distance, + start, + pos: ChunkPos { x: 2, z: -1 }, + layer: 1, + leg: -1, + } + } +} +impl Iterator for ChunkIterator { + type Item = ChunkPos; + + fn next(&mut self) -> Option { + match self.leg { + -1 => { + self.leg = 0; + return Some(self.start); + } + 0 => { + if self.max_distance == 1 { + return None; + } + self.pos.x -= 1; + self.pos.z += 1; + if self.pos.x == 0 { + self.leg = 1; + } + } + 1 => { + self.pos.x -= 1; + self.pos.z -= 1; + if self.pos.z == 0 { + self.leg = 2; + } + } + 2 => { + self.pos.x += 1; + self.pos.z -= 1; + if self.pos.x == 0 { + self.leg = 3; + } + } + 3 => { + self.pos.x += 1; + self.pos.z += 1; + if self.pos.z == 0 { + self.pos.x += 1; + self.leg = 0; + self.layer += 1; + if self.layer == self.max_distance as i32 { + return None; + } + } + } + _ => unreachable!(), + } + Some(self.start + self.pos) + } +} diff --git a/azalea-world/src/world.rs b/azalea-world/src/world.rs index 31ce80cf1..4c0d104bf 100644 --- a/azalea-world/src/world.rs +++ b/azalea-world/src/world.rs @@ -205,15 +205,13 @@ impl World { let nearest_to: BlockPos = nearest_to.into(); let start_chunk: ChunkPos = (&nearest_to).into(); - // todo (correctness): rename this to something like SquareChunkIterator and - // also have another one that iterates in a diagonal shape and use that - // here let iter = ChunkIterator::new(start_chunk, 32); for chunk_pos in iter { let chunk = self.chunks.get(&chunk_pos).unwrap(); let mut nearest_found_pos: Option = None; + let mut nearest_found_distance = 0; for (section_index, section) in chunk.read().sections.iter().enumerate() { let maybe_has_block = match §ion.states.palette { @@ -242,15 +240,14 @@ impl World { chunk_pos.z * 16 + (section_z as i32), ); let this_block_pos = BlockPos { x, y, z }; + let this_block_distance = (nearest_to - this_block_pos).length_manhattan(); // only update if it's closer - if let Some(nearest_found_pos) = nearest_found_pos { - if this_block_pos.x + this_block_pos.y + this_block_pos.z - >= nearest_found_pos.x + nearest_found_pos.y + nearest_found_pos.z - { - continue; - } + if !nearest_found_pos.is_some() + || this_block_distance < nearest_found_distance + { + nearest_found_pos = Some(this_block_pos); + nearest_found_distance = this_block_distance; } - nearest_found_pos = Some(this_block_pos); } } } From 796bce0c60893be3e98040ba7c3600cb72bbeccb Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 7 Mar 2023 20:34:59 -0600 Subject: [PATCH 5/8] update examples that use find_one_block --- azalea/examples/todo/craft_dig_straight_down.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/azalea/examples/todo/craft_dig_straight_down.rs b/azalea/examples/todo/craft_dig_straight_down.rs index 0632776e2..4c980ccff 100644 --- a/azalea/examples/todo/craft_dig_straight_down.rs +++ b/azalea/examples/todo/craft_dig_straight_down.rs @@ -38,7 +38,7 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> { bot.goto(pathfinder::Goals::NearXZ(5, azalea::BlockXZ(0, 0))) .await; let chest = bot - .open_container(&bot.world().find_one_block(|b| b.id == "minecraft:chest")) + .open_container(&bot.world().find_block(azalea_registry::Block::Chest)) .await .unwrap(); bot.take_amount(&chest, 5, |i| i.id == "#minecraft:planks") @@ -47,8 +47,7 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> { let crafting_table = bot .open_crafting_table( - &bot.world - .find_one_block(|b| b.id == "minecraft:crafting_table"), + &bot.world.find_block(azalea_registry::Block::CraftingTable), ) .await .unwrap(); From 599c398f2e7014dfdf6bded5dc5df05b87f31daf Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 7 Mar 2023 21:42:33 -0600 Subject: [PATCH 6/8] azalea_block::properties --- azalea-block/azalea-block-macros/src/lib.rs | 84 +++++++++++--------- azalea-block/src/{blocks.rs => generated.rs} | 0 azalea-block/src/lib.rs | 4 +- azalea/examples/todo/README.md | 1 + 4 files changed, 51 insertions(+), 38 deletions(-) rename azalea-block/src/{blocks.rs => generated.rs} (100%) create mode 100644 azalea/examples/todo/README.md diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs index 5195dc3c2..2f37b70eb 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, } -/// `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 { - // `snowy: false` or `axis: Axis::Y` + // `snowy: false` or `axis: properties::Axis::Y` let property_name = input.parse()?; input.parse::()?; @@ -74,7 +74,7 @@ impl Parse for PropertyWithNameAndDefault { is_enum = true; property_type = first_ident; let variant = input.parse::()?; - 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 { @@ -387,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, } }); } @@ -446,7 +449,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} }; @@ -477,9 +480,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! {}; @@ -499,7 +502,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! { @@ -597,43 +600,50 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { } } - #property_enums + pub mod properties { + use super::*; + #property_enums + } }; generated.extend(quote! { - #block_structs - - impl From for Box { - 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 for Box { + 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 for Box { - 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 for Box { + 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 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 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 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") + impl From 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 similarity index 100% rename from azalea-block/src/blocks.rs rename to azalea-block/src/generated.rs diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs index f7361476f..4f2b37ae2 100755 --- a/azalea-block/src/lib.rs +++ b/azalea-block/src/lib.rs @@ -2,9 +2,11 @@ #![feature(trait_upcasting)] mod behavior; -pub mod blocks; +mod generated; mod range; +pub use generated::{blocks, properties}; + use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable}; pub use behavior::BlockBehavior; use core::fmt::Debug; diff --git a/azalea/examples/todo/README.md b/azalea/examples/todo/README.md new file mode 100644 index 000000000..ab31cf22d --- /dev/null +++ b/azalea/examples/todo/README.md @@ -0,0 +1 @@ +These examples don't work yet and were only written to help design APIs. They will work in the future (probably with minor changes). From fb38ad6d6e94bd780d3dc69314f2a49183d112f3 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 7 Mar 2023 22:06:08 -0600 Subject: [PATCH 7/8] fix tests --- azalea-block/README.md | 12 ++++----- azalea-block/azalea-block-macros/src/lib.rs | 5 +--- azalea-block/src/lib.rs | 8 ++---- azalea-physics/src/lib.rs | 28 ++++++++++----------- azalea-world/src/iterators.rs | 8 +++--- 5 files changed, 27 insertions(+), 34 deletions(-) diff --git a/azalea-block/README.md b/azalea-block/README.md index e4b6357b4..9be4c79bc 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::::from(block_state); ``` # use azalea_block::{Block, BlockState}; # let block_state: BlockState = azalea_registry::Block::Jukebox.into(); -if let Some(jukebox) = Box::::from(block_state).downcast_ref::() { +if let Some(jukebox) = Box::::from(block_state).downcast_ref::() { // ... } ``` diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs index 2f37b70eb..a8739e7ca 100755 --- a/azalea-block/azalea-block-macros/src/lib.rs +++ b/azalea-block/azalea-block-macros/src/lib.rs @@ -404,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! {}; diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs index 4f2b37ae2..43099db58 100755 --- a/azalea-block/src/lib.rs +++ b/azalea-block/src/lib.rs @@ -126,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-physics/src/lib.rs b/azalea-physics/src/lib.rs index 3855354e8..1736b8fb8 100644 --- a/azalea-physics/src/lib.rs +++ b/azalea-physics/src/lib.rs @@ -467,8 +467,8 @@ mod tests { .id(); let block_state = partial_world.chunks.set_block_state( &BlockPos { x: 0, y: 69, z: 0 }, - azalea_block::blocks::StoneSlabBlock { - kind: azalea_block::blocks::Type::Bottom, + azalea_block::blocks::StoneSlab { + kind: azalea_block::properties::Type::Bottom, waterlogged: false, } .into(), @@ -521,8 +521,8 @@ mod tests { .id(); let block_state = world_lock.write().chunks.set_block_state( &BlockPos { x: 0, y: 69, z: 0 }, - azalea_block::blocks::StoneSlabBlock { - kind: azalea_block::blocks::Type::Top, + azalea_block::blocks::StoneSlab { + kind: azalea_block::properties::Type::Top, waterlogged: false, } .into(), @@ -574,11 +574,11 @@ mod tests { .id(); let block_state = world_lock.write().chunks.set_block_state( &BlockPos { x: 0, y: 69, z: 0 }, - azalea_block::blocks::CobblestoneWallBlock { - east: azalea_block::blocks::EastWall::Low, - north: azalea_block::blocks::NorthWall::Low, - south: azalea_block::blocks::SouthWall::Low, - west: azalea_block::blocks::WestWall::Low, + 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, } @@ -636,11 +636,11 @@ mod tests { y: 69, z: -8, }, - azalea_block::blocks::CobblestoneWallBlock { - east: azalea_block::blocks::EastWall::Low, - north: azalea_block::blocks::NorthWall::Low, - south: azalea_block::blocks::SouthWall::Low, - west: azalea_block::blocks::WestWall::Low, + 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, } diff --git a/azalea-world/src/iterators.rs b/azalea-world/src/iterators.rs index ca1536cb0..53a94898c 100644 --- a/azalea-world/src/iterators.rs +++ b/azalea-world/src/iterators.rs @@ -119,14 +119,14 @@ impl SquareChunkIterator { /// /// ``` /// # use azalea_core::ChunkPos; - /// # use azalea_world::iterators::ChunkIterator; + /// # use azalea_world::iterators::SquareChunkIterator; /// - /// let mut iter = ChunkIterator::new(ChunkPos::default(), 2); - /// for chunk_pos in iter { + /// let mut iter = SquareChunkIterator::new(ChunkPos::default(), 2); + /// while let Some(chunk_pos) = iter.next() { /// println!("{:?}", chunk_pos); /// } /// iter.set_max_distance(4); - /// for chunk_pos in iter { + /// while let Some(chunk_pos) = iter.next() { /// println!("{:?}", chunk_pos); /// } /// ``` From c7d086ff25c5aeb65b9c03e27133ffe929f9fb96 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 7 Mar 2023 22:10:06 -0600 Subject: [PATCH 8/8] add a gotoblock command to testbot --- azalea/examples/testbot.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/azalea/examples/testbot.rs b/azalea/examples/testbot.rs index 6777cd2d3..a25b28e3d 100644 --- a/azalea/examples/testbot.rs +++ b/azalea/examples/testbot.rs @@ -147,6 +147,18 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< ); bot.chat(&format!("target_pos: {target_pos:?}",)); } + "gotoblock" => { + let target_pos = bot.world().read().find_block( + bot.component::(), + &azalea_registry::Block::DiamondBlock.into(), + ); + if let Some(target_pos) = target_pos { + // +1 to stand on top of the block + bot.goto(BlockPosGoal::from(target_pos.up(1))); + } else { + bot.chat("no diamond block found"); + } + } _ => {} } }