Skip to content

Commit

Permalink
fix sometimes being able to mine blocks through walls
Browse files Browse the repository at this point in the history
  • Loading branch information
mat-1 committed Aug 15, 2024
1 parent dec544a commit 73091d8
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 46 deletions.
6 changes: 3 additions & 3 deletions azalea-client/src/mining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,16 +396,16 @@ impl MineProgress {
/// A component that stores the number of ticks that we've been mining the same
/// block for. This is a float even though it should only ever be a round
/// number.
#[derive(Component, Debug, Default, Deref, DerefMut)]
#[derive(Component, Clone, Debug, Default, Deref, DerefMut)]
pub struct MineTicks(pub f32);

/// A component that stores the position of the block we're currently mining.
#[derive(Component, Debug, Default, Deref, DerefMut)]
#[derive(Component, Clone, Debug, Default, Deref, DerefMut)]
pub struct MineBlockPos(pub Option<BlockPos>);

/// A component that contains the item we're currently using to mine. If we're
/// not mining anything, it'll be [`ItemSlot::Empty`].
#[derive(Component, Debug, Default, Deref, DerefMut)]
#[derive(Component, Clone, Debug, Default, Deref, DerefMut)]
pub struct MineItem(pub ItemSlot);

/// Sent when we completed mining a block.
Expand Down
6 changes: 6 additions & 0 deletions azalea-core/src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ pub fn ceil_log2(x: u32) -> u32 {
u32::BITS - x.leading_zeros()
}

pub fn fract(x: f64) -> f64 {
let x_int = x as i64 as f64;
let floor = if x < x_int { x_int - 1. } else { x_int };
x - floor
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
26 changes: 14 additions & 12 deletions azalea-physics/src/clip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use azalea_block::BlockState;
use azalea_core::{
block_hit_result::BlockHitResult,
direction::Direction,
math::{lerp, EPSILON},
math::{self, lerp, EPSILON},
position::{BlockPos, Vec3},
};
use azalea_inventory::ItemSlot;
Expand Down Expand Up @@ -111,13 +111,12 @@ fn clip_with_interaction_override(
block_state: &BlockState,
) -> Option<BlockHitResult> {
let block_hit_result = block_shape.clip(from, to, block_pos);
println!("block_hit_result: {block_hit_result:?}");
if let Some(block_hit_result) = block_hit_result {
// TODO: minecraft calls .getInteractionShape here
// are there even any blocks that have a physics shape different from the
// interaction shape???
// (if not then you can delete this comment)
// (if there are then you have to implement BlockState::interaction_shape, lol
// have fun)
// some blocks (like tall grass) have a physics shape that's different from the
// interaction shape, so we need to implement BlockState::interaction_shape. lol
// have fun
let interaction_shape = block_state.shape();
let interaction_hit_result = interaction_shape.clip(from, to, block_pos);
if let Some(interaction_hit_result) = interaction_hit_result {
Expand Down Expand Up @@ -191,24 +190,27 @@ pub fn traverse_blocks<C, T>(
let mut percentage = Vec3 {
x: percentage_step.x
* if vec_sign.x > 0. {
1. - right_before_start.x.fract()
1. - math::fract(right_before_start.x)
} else {
right_before_start.x.fract().abs()
math::fract(right_before_start.x)
},
y: percentage_step.y
* if vec_sign.y > 0. {
1. - right_before_start.y.fract()
1. - math::fract(right_before_start.y)
} else {
right_before_start.y.fract().abs()
math::fract(right_before_start.y)
},
z: percentage_step.z
* if vec_sign.z > 0. {
1. - right_before_start.z.fract()
1. - math::fract(right_before_start.z)
} else {
right_before_start.z.fract().abs()
math::fract(right_before_start.z)
},
};

println!("percentage_step: {percentage_step:?}");
println!("percentage: {percentage:?}");

loop {
if percentage.x > 1. && percentage.y > 1. && percentage.z > 1. {
return get_miss_result(&context);
Expand Down
153 changes: 153 additions & 0 deletions azalea/src/pathfinder/extras/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//! Random utility functions that are useful for bots.
use azalea_core::position::{BlockPos, Vec3};
use azalea_entity::direction_looking_at;
use azalea_world::ChunkStorage;

/// Get a vec of block positions that we can reach from this position.
pub fn get_reachable_blocks_around_player(
player_position: BlockPos,
chunk_storage: &ChunkStorage,
) -> Vec<BlockPos> {
// check a 12x12x12 area around the player
let mut blocks = Vec::new();

for x in -6..=6 {
// y is 1 up to somewhat offset for the eye height
for y in -5..=7 {
for z in -6..=6 {
let block_pos = player_position + BlockPos::new(x, y, z);
let block_state = chunk_storage
.get_block_state(&block_pos)
.unwrap_or_default();

if block_state.is_air() {
// fast path, skip if it's air
continue;
}

if can_reach_block(chunk_storage, player_position, block_pos) {
blocks.push(block_pos);
}
}
}
}

blocks
}

pub fn pick_closest_block(position: BlockPos, blocks: &[BlockPos]) -> Option<BlockPos> {
// pick the closest one and mine it
let mut closest_block_pos = None;
let mut closest_distance = i32::MAX;
for block_pos in &blocks[1..] {
if block_pos.y < position.y {
// skip blocks below us at first
continue;
}
let distance = block_pos.distance_squared_to(&position);
if distance < closest_distance {
closest_block_pos = Some(*block_pos);
closest_distance = distance;
}
}

if closest_block_pos.is_none() {
// ok now check every block if the only ones around us are below
for block_pos in blocks {
let distance = block_pos.distance_squared_to(&position);
if distance < closest_distance {
closest_block_pos = Some(*block_pos);
closest_distance = distance;
}
}
}

closest_block_pos
}

/// Return the block that we'd be looking at if we were at a given position and
/// looking at a given block.
///
/// This is useful for telling if we'd be able to reach a block from a certain
/// position, like for the pathfinder's [`ReachBlockPosGoal`].
///
/// Also see [`get_hit_result_while_looking_at_with_eye_position`].
///
/// [`ReachBlockPosGoal`]: crate::pathfinder::goals::ReachBlockPosGoal
pub fn get_hit_result_while_looking_at(
chunk_storage: &ChunkStorage,
player_position: BlockPos,
look_target: BlockPos,
) -> BlockPos {
let eye_position = Vec3 {
x: player_position.x as f64 + 0.5,
y: player_position.y as f64 + 1.53,
z: player_position.z as f64 + 0.5,
};
get_hit_result_while_looking_at_with_eye_position(chunk_storage, eye_position, look_target)
}

pub fn can_reach_block(
chunk_storage: &ChunkStorage,
player_position: BlockPos,
look_target: BlockPos,
) -> bool {
let hit_result = get_hit_result_while_looking_at(chunk_storage, player_position, look_target);
hit_result == look_target
}

/// Return the block that we'd be looking at if our eyes are at a given position
/// and looking at a given block.
///
/// This is called by [`get_hit_result_while_looking_at`].
pub fn get_hit_result_while_looking_at_with_eye_position(
chunk_storage: &azalea_world::ChunkStorage,
eye_position: Vec3,
look_target: BlockPos,
) -> BlockPos {
let look_direction = direction_looking_at(&eye_position, &look_target.center());
let block_hit_result =
azalea_client::interact::pick(&look_direction, &eye_position, chunk_storage, 4.5);
block_hit_result.block_pos
}

#[cfg(test)]
mod tests {
use azalea_core::position::ChunkPos;
use azalea_world::{Chunk, PartialInstance};

use super::*;

#[test]
fn test_cannot_reach_block_through_wall_when_y_is_negative() {
let mut partial_world = PartialInstance::default();
let mut world = ChunkStorage::default();
partial_world
.chunks
.set(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()), &mut world);

let set_solid_block_at = |x, y, z| {
partial_world.chunks.set_block_state(
&BlockPos::new(x, y, z),
azalea_registry::Block::Stone.into(),
&world,
);
};

let y_offset = -8;

// walls
set_solid_block_at(1, y_offset, 0);
set_solid_block_at(1, y_offset + 1, 0);
set_solid_block_at(0, y_offset, 1);
set_solid_block_at(0, y_offset + 1, 1);
// target
set_solid_block_at(1, y_offset, 1);

let player_position = BlockPos::new(0, y_offset, 0);
let look_target = BlockPos::new(1, y_offset, 1);

assert!(!can_reach_block(&world, player_position, look_target));
}
}
Loading

0 comments on commit 73091d8

Please sign in to comment.