diff options
| author | mat <git@matdoes.dev> | 2023-08-26 18:08:13 -0500 |
|---|---|---|
| committer | mat <git@matdoes.dev> | 2023-08-26 18:08:13 -0500 |
| commit | dea717b68e2945777c68d44ce321639cf09ea226 (patch) | |
| tree | f3909a036730343def45978f0d59f0a82673b1c2 | |
| parent | 5d7669f72b02c749a02bf034d382028e62509540 (diff) | |
| download | azalea-drasl-dea717b68e2945777c68d44ce321639cf09ea226.tar.xz | |
simplify pathfinder more
| -rw-r--r-- | azalea/src/pathfinder/mod.rs | 43 | ||||
| -rw-r--r-- | azalea/src/pathfinder/moves.rs | 420 | ||||
| -rw-r--r-- | azalea/src/pathfinder/moves/basic.rs | 209 | ||||
| -rw-r--r-- | azalea/src/pathfinder/moves/mod.rs | 182 |
4 files changed, 397 insertions, 457 deletions
diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs index f6971155..76d901c3 100644 --- a/azalea/src/pathfinder/mod.rs +++ b/azalea/src/pathfinder/mod.rs @@ -5,7 +5,6 @@ pub mod simulation; use crate::bot::{JumpEvent, LookAtEvent}; use crate::pathfinder::astar::a_star; -use crate::pathfinder::moves::DefaultMoves; use crate::WalkDirection; use crate::app::{App, Plugin}; @@ -18,7 +17,7 @@ use crate::ecs::{ }; use azalea_client::movement::walk_listener; use azalea_client::{StartSprintEvent, StartWalkEvent}; -use azalea_core::{BlockPos, CardinalDirection}; +use azalea_core::BlockPos; use azalea_entity::metadata::Player; use azalea_entity::Local; use azalea_entity::{Physics, Position}; @@ -119,6 +118,8 @@ fn goto_listener( mut query: Query<(&Position, &InstanceName)>, instance_container: Res<InstanceContainer>, ) { + let successors_fn = moves::basic::basic_move; + let thread_pool = AsyncComputeTaskPool::get(); for event in events.iter() { @@ -138,40 +139,9 @@ fn goto_listener( let task = thread_pool.spawn(async move { debug!("start: {start:?}, end: {end:?}"); - let possible_moves: Vec<DefaultMoves> = vec![ - DefaultMoves::Forward(CardinalDirection::North), - DefaultMoves::Forward(CardinalDirection::East), - DefaultMoves::Forward(CardinalDirection::South), - DefaultMoves::Forward(CardinalDirection::West), - // - DefaultMoves::Ascend(CardinalDirection::North), - DefaultMoves::Ascend(CardinalDirection::East), - DefaultMoves::Ascend(CardinalDirection::South), - DefaultMoves::Ascend(CardinalDirection::West), - // - DefaultMoves::Descend(CardinalDirection::North), - DefaultMoves::Descend(CardinalDirection::East), - DefaultMoves::Descend(CardinalDirection::South), - DefaultMoves::Descend(CardinalDirection::West), - // - DefaultMoves::Diagonal(CardinalDirection::North), - DefaultMoves::Diagonal(CardinalDirection::East), - DefaultMoves::Diagonal(CardinalDirection::South), - DefaultMoves::Diagonal(CardinalDirection::West), - ]; - let successors = |pos: BlockPos| { - let mut edges = Vec::new(); - let world = world_lock.read(); - for possible_move in &possible_moves { - let move_result = possible_move.get(&world, pos); - if let Some(edge) = move_result { - edges.push(edge); - } - } - - edges + successors_fn(&world, pos) }; let start_time = std::time::Instant::now(); @@ -183,7 +153,6 @@ fn goto_listener( Duration::from_secs(1), ); let end_time = std::time::Instant::now(); - debug!("movements: {movements:?}"); debug!("partial: {partial:?}"); debug!("time: {:?}", end_time - start_time); @@ -268,8 +237,8 @@ fn tick_execute_path( walk_events: &mut walk_events, jump_events: &mut jump_events, }; - trace!("executing move {:?}", movement.data.move_kind); - movement.data.move_kind.execute(ctx); + trace!("executing move"); + (movement.data.execute)(ctx); break; } } diff --git a/azalea/src/pathfinder/moves.rs b/azalea/src/pathfinder/moves.rs deleted file mode 100644 index 787d6246..00000000 --- a/azalea/src/pathfinder/moves.rs +++ /dev/null @@ -1,420 +0,0 @@ -use crate::{JumpEvent, LookAtEvent}; - -use super::astar; -use azalea_client::{SprintDirection, StartSprintEvent, StartWalkEvent}; -use azalea_core::{BlockPos, CardinalDirection, Vec3}; -use azalea_physics::collision::{self, BlockWithShape}; -use azalea_world::Instance; -use bevy_ecs::{entity::Entity, event::EventWriter}; - -type Edge = astar::Edge<BlockPos, MoveData>; - -#[derive(Debug, Clone)] -pub struct MoveData { - pub move_kind: DefaultMoves, -} - -/// whether this block is passable -fn is_block_passable(pos: &BlockPos, world: &Instance) -> bool { - if let Some(block) = world.chunks.get_block_state(pos) { - if block.shape() != &collision::empty_shape() { - return false; - } - if block == azalea_registry::Block::Water.into() { - return false; - } - if block.waterlogged() { - return false; - } - block.shape() == &collision::empty_shape() - } else { - false - } -} - -/// whether this block has a solid hitbox (i.e. we can stand on it) -fn is_block_solid(pos: &BlockPos, world: &Instance) -> bool { - if let Some(block) = world.chunks.get_block_state(pos) { - block.shape() == &collision::block_shape() - } else { - false - } -} - -/// Whether this block and the block above are passable -fn is_passable(pos: &BlockPos, world: &Instance) -> bool { - is_block_passable(pos, world) && is_block_passable(&pos.up(1), world) -} - -/// Whether we can stand in this position. Checks if the block below is solid, -/// and that the two blocks above that are passable. - -fn is_standable(pos: &BlockPos, world: &Instance) -> bool { - is_block_solid(&pos.down(1), world) && is_passable(pos, world) -} - -/// Get the amount of air blocks until the next solid block below this one. -fn fall_distance(pos: &BlockPos, world: &Instance) -> u32 { - let mut distance = 0; - let mut current_pos = pos.down(1); - while is_block_passable(¤t_pos, world) { - distance += 1; - current_pos = current_pos.down(1); - - if current_pos.y < world.chunks.min_y { - return u32::MAX; - } - } - distance -} - -const JUMP_COST: f32 = 0.5; -const WALK_ONE_BLOCK_COST: f32 = 1.0; -const FALL_ONE_BLOCK_COST: f32 = 0.5; - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub enum DefaultMoves { - Forward(CardinalDirection), - Ascend(CardinalDirection), - Descend(CardinalDirection), - Diagonal(CardinalDirection), -} - -impl DefaultMoves { - pub fn get(self, world: &Instance, node: BlockPos) -> Option<Edge> { - match self { - DefaultMoves::Forward(dir) => ForwardMove(dir).get(world, node), - DefaultMoves::Ascend(dir) => AscendMove(dir).get(world, node), - DefaultMoves::Descend(dir) => DescendMove(dir).get(world, node), - DefaultMoves::Diagonal(dir) => DiagonalMove(dir).get(world, node), - } - } - - pub fn execute(self, ctx: ExecuteCtx) { - match self { - DefaultMoves::Forward(_) => ForwardMove::execute(ctx), - DefaultMoves::Ascend(_) => AscendMove::execute(ctx), - DefaultMoves::Descend(_) => DescendMove::execute(ctx), - DefaultMoves::Diagonal(_) => DiagonalMove::execute(ctx), - } - } -} - -pub trait MoveImpl: Send + Sync { - fn get(&self, world: &Instance, node: BlockPos) -> Option<Edge>; - fn execute(ctx: ExecuteCtx); -} - -pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'a> { - pub entity: Entity, - pub target: BlockPos, - pub position: Vec3, - - pub look_at_events: &'a mut EventWriter<'w1, LookAtEvent>, - pub sprint_events: &'a mut EventWriter<'w2, StartSprintEvent>, - pub walk_events: &'a mut EventWriter<'w3, StartWalkEvent>, - pub jump_events: &'a mut EventWriter<'w4, JumpEvent>, -} - -pub struct ForwardMove(pub CardinalDirection); -impl MoveImpl for ForwardMove { - fn get(&self, world: &Instance, pos: BlockPos) -> Option<Edge> { - let offset = BlockPos::new(self.0.x(), 0, self.0.z()); - - if !is_standable(&(pos + offset), world) { - return None; - } - - let cost = WALK_ONE_BLOCK_COST; - - Some(Edge { - movement: astar::Movement { - target: pos + offset, - data: MoveData { - move_kind: DefaultMoves::Forward(self.0), - }, - }, - cost, - }) - } - - fn execute( - ExecuteCtx { - entity, - target, - look_at_events, - sprint_events, - .. - }: ExecuteCtx, - ) { - let center = target.center(); - look_at_events.send(LookAtEvent { - entity, - position: center, - }); - sprint_events.send(StartSprintEvent { - entity, - direction: SprintDirection::Forward, - }); - } -} - -pub struct AscendMove(pub CardinalDirection); -impl MoveImpl for AscendMove { - fn get(&self, world: &Instance, pos: BlockPos) -> Option<Edge> { - let offset = BlockPos::new(self.0.x(), 1, self.0.z()); - - if !is_block_passable(&pos.up(2), world) || !is_standable(&(pos + offset), world) { - return None; - } - - let cost = WALK_ONE_BLOCK_COST + JUMP_COST; - - // Some(MoveResult { - // node: Node { - // pos: node.pos + offset, - // vertical_vel: VerticalVel::None, - // }, - // cost, - // }) - Some(Edge { - movement: astar::Movement { - target: pos + offset, - data: MoveData { - move_kind: DefaultMoves::Ascend(self.0), - }, - }, - cost, - }) - } - - fn execute( - ExecuteCtx { - entity, - target, - look_at_events, - sprint_events, - jump_events, - .. - }: ExecuteCtx, - ) { - let center = target.center(); - look_at_events.send(LookAtEvent { - entity, - position: center, - }); - jump_events.send(JumpEvent { entity }); - sprint_events.send(StartSprintEvent { - entity, - direction: SprintDirection::Forward, - }); - } -} -pub struct DescendMove(pub CardinalDirection); -impl MoveImpl for DescendMove { - fn get(&self, world: &Instance, pos: BlockPos) -> Option<Edge> { - let new_horizontal_position = pos + BlockPos::new(self.0.x(), 0, self.0.z()); - let fall_distance = fall_distance(&new_horizontal_position, world); - if fall_distance == 0 { - return None; - } - if fall_distance > 3 { - return None; - } - let new_position = new_horizontal_position.down(fall_distance as i32); - - // check whether 3 blocks vertically forward are passable - if !is_passable(&new_horizontal_position, world) { - return None; - } - - let cost = WALK_ONE_BLOCK_COST + FALL_ONE_BLOCK_COST * fall_distance as f32; - - Some(Edge { - movement: astar::Movement { - target: new_position, - data: MoveData { - move_kind: DefaultMoves::Descend(self.0), - }, - }, - cost, - }) - } - - fn execute( - ExecuteCtx { - entity, - target, - look_at_events, - sprint_events, - .. - }: ExecuteCtx, - ) { - let center = target.center(); - look_at_events.send(LookAtEvent { - entity, - position: center, - }); - sprint_events.send(StartSprintEvent { - entity, - direction: SprintDirection::Forward, - }); - } -} -pub struct DiagonalMove(pub CardinalDirection); -impl MoveImpl for DiagonalMove { - fn get(&self, world: &Instance, pos: BlockPos) -> Option<Edge> { - let right = self.0.right(); - let offset = BlockPos::new(self.0.x() + right.x(), 0, self.0.z() + right.z()); - - if !is_passable( - &BlockPos::new(pos.x + self.0.x(), pos.y, pos.z + self.0.z()), - world, - ) && !is_passable( - &BlockPos::new( - pos.x + self.0.right().x(), - pos.y, - pos.z + self.0.right().z(), - ), - world, - ) { - return None; - } - if !is_standable(&(pos + offset), world) { - return None; - } - let cost = WALK_ONE_BLOCK_COST * 1.4; - - // Some(MoveResult { - // node: Node { - // pos: node.pos + offset, - // vertical_vel: VerticalVel::None, - // }, - // cost, - // }) - Some(Edge { - movement: astar::Movement { - target: pos + offset, - data: MoveData { - move_kind: DefaultMoves::Diagonal(self.0), - }, - }, - cost, - }) - } - - fn execute( - ExecuteCtx { - entity, - target, - look_at_events, - sprint_events, - .. - }: ExecuteCtx, - ) { - let center = target.center(); - look_at_events.send(LookAtEvent { - entity, - position: center, - }); - sprint_events.send(StartSprintEvent { - entity, - direction: SprintDirection::Forward, - }); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use azalea_block::BlockState; - use azalea_core::ChunkPos; - use azalea_world::{Chunk, ChunkStorage, PartialInstance}; - - #[test] - fn test_is_passable() { - let mut partial_world = PartialInstance::default(); - let mut chunk_storage = ChunkStorage::default(); - - partial_world.chunks.set( - &ChunkPos { x: 0, z: 0 }, - Some(Chunk::default()), - &mut chunk_storage, - ); - partial_world.chunks.set_block_state( - &BlockPos::new(0, 0, 0), - azalea_registry::Block::Stone.into(), - &chunk_storage, - ); - partial_world.chunks.set_block_state( - &BlockPos::new(0, 1, 0), - BlockState::AIR, - &chunk_storage, - ); - - let world = chunk_storage.into(); - assert!(!is_block_passable(&BlockPos::new(0, 0, 0), &world)); - assert!(is_block_passable(&BlockPos::new(0, 1, 0), &world)); - } - - #[test] - fn test_is_solid() { - let mut partial_world = PartialInstance::default(); - let mut chunk_storage = ChunkStorage::default(); - partial_world.chunks.set( - &ChunkPos { x: 0, z: 0 }, - Some(Chunk::default()), - &mut chunk_storage, - ); - partial_world.chunks.set_block_state( - &BlockPos::new(0, 0, 0), - azalea_registry::Block::Stone.into(), - &chunk_storage, - ); - partial_world.chunks.set_block_state( - &BlockPos::new(0, 1, 0), - BlockState::AIR, - &chunk_storage, - ); - - let world = chunk_storage.into(); - assert!(is_block_solid(&BlockPos::new(0, 0, 0), &world)); - assert!(!is_block_solid(&BlockPos::new(0, 1, 0), &world)); - } - - #[test] - fn test_is_standable() { - let mut partial_world = PartialInstance::default(); - let mut chunk_storage = ChunkStorage::default(); - partial_world.chunks.set( - &ChunkPos { x: 0, z: 0 }, - Some(Chunk::default()), - &mut chunk_storage, - ); - partial_world.chunks.set_block_state( - &BlockPos::new(0, 0, 0), - azalea_registry::Block::Stone.into(), - &chunk_storage, - ); - partial_world.chunks.set_block_state( - &BlockPos::new(0, 1, 0), - BlockState::AIR, - &chunk_storage, - ); - partial_world.chunks.set_block_state( - &BlockPos::new(0, 2, 0), - BlockState::AIR, - &chunk_storage, - ); - partial_world.chunks.set_block_state( - &BlockPos::new(0, 3, 0), - BlockState::AIR, - &chunk_storage, - ); - - let world = chunk_storage.into(); - assert!(is_standable(&BlockPos::new(0, 1, 0), &world)); - assert!(!is_standable(&BlockPos::new(0, 0, 0), &world)); - assert!(!is_standable(&BlockPos::new(0, 2, 0), &world)); - } -} diff --git a/azalea/src/pathfinder/moves/basic.rs b/azalea/src/pathfinder/moves/basic.rs new file mode 100644 index 00000000..2d0c47c2 --- /dev/null +++ b/azalea/src/pathfinder/moves/basic.rs @@ -0,0 +1,209 @@ +use azalea_client::{SprintDirection, StartSprintEvent}; +use azalea_core::{BlockPos, CardinalDirection}; +use azalea_world::Instance; + +use crate::{pathfinder::astar, JumpEvent, LookAtEvent}; + +use super::{ + fall_distance, is_block_passable, is_passable, is_standable, Edge, ExecuteCtx, MoveData, + FALL_ONE_BLOCK_COST, JUMP_COST, WALK_ONE_BLOCK_COST, +}; + +pub fn basic_move(world: &Instance, node: BlockPos) -> Vec<Edge> { + let mut edges = Vec::new(); + edges.extend(forward_move(world, node)); + edges.extend(ascend_move(world, node)); + edges.extend(descend_move(world, node)); + edges.extend(diagonal_move(world, node)); + edges +} + +fn forward_move(world: &Instance, pos: BlockPos) -> Vec<Edge> { + let mut edges = Vec::new(); + for dir in CardinalDirection::iter() { + let offset = BlockPos::new(dir.x(), 0, dir.z()); + + if !is_standable(&(pos + offset), world) { + continue; + } + + let cost = WALK_ONE_BLOCK_COST; + + edges.push(Edge { + movement: astar::Movement { + target: pos + offset, + data: MoveData { + execute: &execute_forward_move, + }, + }, + cost, + }) + } + + edges +} + +fn execute_forward_move( + ExecuteCtx { + entity, + target, + look_at_events, + sprint_events, + .. + }: ExecuteCtx, +) { + let center = target.center(); + look_at_events.send(LookAtEvent { + entity, + position: center, + }); + sprint_events.send(StartSprintEvent { + entity, + direction: SprintDirection::Forward, + }); +} + +fn ascend_move(world: &Instance, pos: BlockPos) -> Vec<Edge> { + let mut edges = Vec::new(); + for dir in CardinalDirection::iter() { + let offset = BlockPos::new(dir.x(), 1, dir.z()); + + if !is_block_passable(&pos.up(2), world) || !is_standable(&(pos + offset), world) { + continue; + } + + let cost = WALK_ONE_BLOCK_COST + JUMP_COST; + + edges.push(Edge { + movement: astar::Movement { + target: pos + offset, + data: MoveData { + execute: &execute_ascend_move, + }, + }, + cost, + }) + } + edges +} +fn execute_ascend_move( + ExecuteCtx { + entity, + target, + look_at_events, + sprint_events, + jump_events, + .. + }: ExecuteCtx, +) { + let center = target.center(); + look_at_events.send(LookAtEvent { + entity, + position: center, + }); + jump_events.send(JumpEvent { entity }); + sprint_events.send(StartSprintEvent { + entity, + direction: SprintDirection::Forward, + }); +} +fn descend_move(world: &Instance, pos: BlockPos) -> Vec<Edge> { + let mut edges = Vec::new(); + for dir in CardinalDirection::iter() { + let new_horizontal_position = pos + BlockPos::new(dir.x(), 0, dir.z()); + let fall_distance = fall_distance(&new_horizontal_position, world); + if fall_distance == 0 || fall_distance > 3 { + continue; + } + let new_position = new_horizontal_position.down(fall_distance as i32); + + // check whether 3 blocks vertically forward are passable + if !is_passable(&new_horizontal_position, world) { + continue; + } + + let cost = WALK_ONE_BLOCK_COST + FALL_ONE_BLOCK_COST * fall_distance as f32; + + edges.push(Edge { + movement: astar::Movement { + target: new_position, + data: MoveData { + execute: &execute_descend_move, + }, + }, + cost, + }) + } + edges +} +fn execute_descend_move( + ExecuteCtx { + entity, + target, + look_at_events, + sprint_events, + .. + }: ExecuteCtx, +) { + let center = target.center(); + look_at_events.send(LookAtEvent { + entity, + position: center, + }); + sprint_events.send(StartSprintEvent { + entity, + direction: SprintDirection::Forward, + }); +} + +fn diagonal_move(world: &Instance, pos: BlockPos) -> Vec<Edge> { + let mut edges = Vec::new(); + for dir in CardinalDirection::iter() { + let right = dir.right(); + let offset = BlockPos::new(dir.x() + right.x(), 0, dir.z() + right.z()); + + if !is_passable( + &BlockPos::new(pos.x + dir.x(), pos.y, pos.z + dir.z()), + world, + ) && !is_passable( + &BlockPos::new(pos.x + dir.right().x(), pos.y, pos.z + dir.right().z()), + world, + ) { + continue; + } + if !is_standable(&(pos + offset), world) { + continue; + } + let cost = WALK_ONE_BLOCK_COST * 1.4; + + edges.push(Edge { + movement: astar::Movement { + target: pos + offset, + data: MoveData { + execute: &execute_diagonal_move, + }, + }, + cost, + }) + } + edges +} +fn execute_diagonal_move( + ExecuteCtx { + entity, + target, + look_at_events, + sprint_events, + .. + }: ExecuteCtx, +) { + let center = target.center(); + look_at_events.send(LookAtEvent { + entity, + position: center, + }); + sprint_events.send(StartSprintEvent { + entity, + direction: SprintDirection::Forward, + }); +} diff --git a/azalea/src/pathfinder/moves/mod.rs b/azalea/src/pathfinder/moves/mod.rs new file mode 100644 index 00000000..63b74945 --- /dev/null +++ b/azalea/src/pathfinder/moves/mod.rs @@ -0,0 +1,182 @@ +pub mod basic; + +use crate::{JumpEvent, LookAtEvent}; + +use super::astar; +use azalea_client::{StartSprintEvent, StartWalkEvent}; +use azalea_core::{BlockPos, Vec3}; +use azalea_physics::collision::{self, BlockWithShape}; +use azalea_world::Instance; +use bevy_ecs::{entity::Entity, event::EventWriter}; + +type Edge = astar::Edge<BlockPos, MoveData>; + +#[derive(Clone)] +pub struct MoveData { + // pub move_kind: BasicMoves, + pub execute: &'static (dyn Fn(ExecuteCtx) + Send + Sync), +} + +/// whether this block is passable +fn is_block_passable(pos: &BlockPos, world: &Instance) -> bool { + if let Some(block) = world.chunks.get_block_state(pos) { + if block.shape() != &collision::empty_shape() { + return false; + } + if block == azalea_registry::Block::Water.into() { + return false; + } + if block.waterlogged() { + return false; + } + block.shape() == &collision::empty_shape() + } else { + false + } +} + +/// whether this block has a solid hitbox (i.e. we can stand on it) +fn is_block_solid(pos: &BlockPos, world: &Instance) -> bool { + if let Some(block) = world.chunks.get_block_state(pos) { + block.shape() == &collision::block_shape() + } else { + false + } +} + +/// Whether this block and the block above are passable +fn is_passable(pos: &BlockPos, world: &Instance) -> bool { + is_block_passable(pos, world) && is_block_passable(&pos.up(1), world) +} + +/// Whether we can stand in this position. Checks if the block below is solid, +/// and that the two blocks above that are passable. + +fn is_standable(pos: &BlockPos, world: &Instance) -> bool { + is_block_solid(&pos.down(1), world) && is_passable(pos, world) +} + +/// Get the amount of air blocks until the next solid block below this one. +fn fall_distance(pos: &BlockPos, world: &Instance) -> u32 { + let mut distance = 0; + let mut current_pos = pos.down(1); + while is_block_passable(¤t_pos, world) { + distance += 1; + current_pos = current_pos.down(1); + + if current_pos.y < world.chunks.min_y { + return u32::MAX; + } + } + distance +} + +const JUMP_COST: f32 = 0.5; +const WALK_ONE_BLOCK_COST: f32 = 1.0; +const FALL_ONE_BLOCK_COST: f32 = 0.5; + +pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'a> { + pub entity: Entity, + pub target: BlockPos, + pub position: Vec3, + + pub look_at_events: &'a mut EventWriter<'w1, LookAtEvent>, + pub sprint_events: &'a mut EventWriter<'w2, StartSprintEvent>, + pub walk_events: &'a mut EventWriter<'w3, StartWalkEvent>, + pub jump_events: &'a mut EventWriter<'w4, JumpEvent>, +} + +#[cfg(test)] +mod tests { + use super::*; + use azalea_block::BlockState; + use azalea_core::ChunkPos; + use azalea_world::{Chunk, ChunkStorage, PartialInstance}; + + #[test] + fn test_is_passable() { + let mut partial_world = PartialInstance::default(); + let mut chunk_storage = ChunkStorage::default(); + + partial_world.chunks.set( + &ChunkPos { x: 0, z: 0 }, + Some(Chunk::default()), + &mut chunk_storage, + ); + partial_world.chunks.set_block_state( + &BlockPos::new(0, 0, 0), + azalea_registry::Block::Stone.into(), + &chunk_storage, + ); + partial_world.chunks.set_block_state( + &BlockPos::new(0, 1, 0), + BlockState::AIR, + &chunk_storage, + ); + + let world = chunk_storage.into(); + assert!(!is_block_passable(&BlockPos::new(0, 0, 0), &world)); + assert!(is_block_passable(&BlockPos::new(0, 1, 0), &world)); + } + + #[test] + fn test_is_solid() { + let mut partial_world = PartialInstance::default(); + let mut chunk_storage = ChunkStorage::default(); + partial_world.chunks.set( + &ChunkPos { x: 0, z: 0 }, + Some(Chunk::default()), + &mut chunk_storage, + ); + partial_world.chunks.set_block_state( + &BlockPos::new(0, 0, 0), + azalea_registry::Block::Stone.into(), + &chunk_storage, + ); + partial_world.chunks.set_block_state( + &BlockPos::new(0, 1, 0), + BlockState::AIR, + &chunk_storage, + ); + + let world = chunk_storage.into(); + assert!(is_block_solid(&BlockPos::new(0, 0, 0), &world)); + assert!(!is_block_solid(&BlockPos::new(0, 1, 0), &world)); + } + + #[test] + fn test_is_standable() { + let mut partial_world = PartialInstance::default(); + let mut chunk_storage = ChunkStorage::default(); + partial_world.chunks.set( + &ChunkPos { x: 0, z: 0 }, + Some(Chunk::default()), + &mut chunk_storage, + ); + partial_world.chunks.set_block_state( + &BlockPos::new(0, 0, 0), + azalea_registry::Block::Stone.into(), + &chunk_storage, + ); + partial_world.chunks.set_block_state( + &BlockPos::new(0, 1, 0), + BlockState::AIR, + &chunk_storage, + ); + partial_world.chunks.set_block_state( + &BlockPos::new(0, 2, 0), + BlockState::AIR, + &chunk_storage, + ); + partial_world.chunks.set_block_state( + &BlockPos::new(0, 3, 0), + BlockState::AIR, + &chunk_storage, + ); + + let world = chunk_storage.into(); + assert!(is_standable(&BlockPos::new(0, 1, 0), &world)); + assert!(!is_standable(&BlockPos::new(0, 0, 0), &world)); + assert!(!is_standable(&BlockPos::new(0, 2, 0), &world)); + } +} |
