Skip to content

Commit

Permalink
Add mining to the pathfinder (#122)
Browse files Browse the repository at this point in the history
* basic pathfinder mining poc

* mining descending and autotool

* pathfinder mining descending

* pathfinder fixes

* allow disabling pathfinder miner and other fixes

* small optimization to avoid chunk vec iter lookup sometimes

* seeded rng in pathfinder bench

* consistently use f32::INFINITY

this brings performance much closer to how it was before

* astar heuristic optimization from baritone

* add downward_move

* fix downward move execute

* avoid liquids and falling blocks when mining

* fix COST_HEURISTIC

* fix to not path through flowing liquids

* only reset pathfinder timeout while mining if the block is close enough

* cache mining costs of block positions

* fix mine_while_at_start and move PathfinderDebugParticles to its own module

* add ReachBlockPosGoal

in other news: azalea's sin/cos functions were broken this whole time and i never noticed

* clippy and add things that i accidentally didn't commit

* improve wording on doc for azalea::pathfinder
  • Loading branch information
mat-1 authored Dec 15, 2023
1 parent 59e140d commit a707e2e
Show file tree
Hide file tree
Showing 22 changed files with 1,457 additions and 359 deletions.
15 changes: 14 additions & 1 deletion azalea-block/src/range.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::{collections::HashSet, ops::RangeInclusive};
use std::{
collections::HashSet,
ops::{Add, RangeInclusive},
};

use crate::BlockState;

Expand Down Expand Up @@ -31,3 +34,13 @@ impl BlockStates {
self.set.contains(state)
}
}

impl Add for BlockStates {
type Output = Self;

fn add(self, rhs: Self) -> Self::Output {
Self {
set: self.set.union(&rhs.set).copied().collect(),
}
}
}
6 changes: 3 additions & 3 deletions azalea-client/src/interact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ pub fn update_hit_result_component(
};
let instance = instance_lock.read();

let hit_result = pick(look_direction, &eye_position, &instance, pick_range);
let hit_result = pick(look_direction, &eye_position, &instance.chunks, pick_range);
if let Some(mut hit_result_ref) = hit_result_ref {
**hit_result_ref = hit_result;
} else {
Expand All @@ -212,13 +212,13 @@ pub fn update_hit_result_component(
pub fn pick(
look_direction: &LookDirection,
eye_position: &Vec3,
instance: &Instance,
chunks: &azalea_world::ChunkStorage,
pick_range: f64,
) -> BlockHitResult {
let view_vector = view_vector(look_direction);
let end_position = eye_position + &(view_vector * pick_range);
azalea_physics::clip::clip(
&instance.chunks,
chunks,
ClipContext {
from: *eye_position,
to: end_position,
Expand Down
33 changes: 33 additions & 0 deletions azalea-client/src/inventory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use azalea_inventory::{
use azalea_protocol::packets::game::{
serverbound_container_click_packet::ServerboundContainerClickPacket,
serverbound_container_close_packet::ServerboundContainerClosePacket,
serverbound_set_carried_item_packet::ServerboundSetCarriedItemPacket,
};
use azalea_registry::MenuKind;
use bevy_app::{App, Plugin, Update};
Expand Down Expand Up @@ -40,9 +41,11 @@ impl Plugin for InventoryPlugin {
.add_event::<CloseContainerEvent>()
.add_event::<ContainerClickEvent>()
.add_event::<SetContainerContentEvent>()
.add_event::<SetSelectedHotbarSlotEvent>()
.add_systems(
Update,
(
handle_set_selected_hotbar_slot_event,
handle_menu_opened_event,
handle_set_container_content_event,
handle_container_click_event,
Expand Down Expand Up @@ -734,3 +737,33 @@ fn handle_set_container_content_event(
}
}
}

#[derive(Event)]
pub struct SetSelectedHotbarSlotEvent {
pub entity: Entity,
/// The hotbar slot to select. This should be in the range 0..=8.
pub slot: u8,
}
fn handle_set_selected_hotbar_slot_event(
mut events: EventReader<SetSelectedHotbarSlotEvent>,
mut send_packet_events: EventWriter<SendPacketEvent>,
mut query: Query<&mut InventoryComponent>,
) {
for event in events.read() {
let mut inventory = query.get_mut(event.entity).unwrap();

// if the slot is already selected, don't send a packet
if inventory.selected_hotbar_slot == event.slot {
continue;
}

inventory.selected_hotbar_slot = event.slot;
send_packet_events.send(SendPacketEvent {
entity: event.entity,
packet: ServerboundSetCarriedItemPacket {
slot: event.slot as u16,
}
.get(),
});
}
}
15 changes: 14 additions & 1 deletion azalea-client/src/mining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,20 @@ fn handle_start_mining_block_with_direction_event(
.get_block_state(&event.position)
.unwrap_or_default();
*sequence_number += 1;
let block_is_solid = !target_block_state.is_air();
let target_registry_block = azalea_registry::Block::from(target_block_state);

// we can't break blocks if they don't have a bounding box

// TODO: So right now azalea doesn't differenciate between different types of
// bounding boxes. See ClipContext::block_shape for more info. Ideally this
// should just call ClipContext::block_shape and check if it's empty.
let block_is_solid = !target_block_state.is_air()
// this is a hack to make sure we can't break water or lava
&& !matches!(
target_registry_block,
azalea_registry::Block::Water | azalea_registry::Block::Lava
);

if block_is_solid && **mine_progress == 0. {
// interact with the block (like note block left click) here
attack_block_events.send(AttackBlockEvent {
Expand Down
28 changes: 24 additions & 4 deletions azalea-core/src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ pub static SIN: LazyLock<[f32; 65536]> = LazyLock::new(|| {
/// A sine function that uses a lookup table.
pub fn sin(x: f32) -> f32 {
let x = x * 10430.378;
let x = x as usize;
SIN[x & 65535]
let x = x as i32 as usize & 65535;
SIN[x]
}

/// A cosine function that uses a lookup table.
pub fn cos(x: f32) -> f32 {
let x = x * 10430.378 + 16384.0;
let x = x as usize;
SIN[x & 65535]
let x = x as i32 as usize & 65535;
SIN[x]
}

// TODO: make this generic
Expand Down Expand Up @@ -83,4 +83,24 @@ mod tests {
assert_eq!(gcd(12, 7), 1);
assert_eq!(gcd(7, 12), 1);
}

#[test]
fn test_sin() {
const PI: f32 = std::f32::consts::PI;
// check that they're close enough
fn assert_sin_eq_enough(number: f32) {
let a = sin(number);
let b = f32::sin(number);
assert!((a - b).abs() < 0.01, "sin({number}) failed, {a} != {b}");
}
assert_sin_eq_enough(0.0);
assert_sin_eq_enough(PI / 2.0);
assert_sin_eq_enough(PI);
assert_sin_eq_enough(PI * 2.0);
assert_sin_eq_enough(PI * 3.0 / 2.0);
assert_sin_eq_enough(-PI / 2.0);
assert_sin_eq_enough(-PI);
assert_sin_eq_enough(-PI * 2.0);
assert_sin_eq_enough(-PI * 3.0 / 2.0);
}
}
45 changes: 45 additions & 0 deletions azalea-core/src/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
use std::{
fmt,
hash::Hash,
io::{Cursor, Write},
ops::{Add, AddAssign, Mul, Rem, Sub},
Expand Down Expand Up @@ -65,6 +66,43 @@ macro_rules! vec3_impl {
}
}

/// Return a new instance of this position with the z coordinate subtracted
/// by the given number.
pub fn north(&self, z: $type) -> Self {
Self {
x: self.x,
y: self.y,
z: self.z - z,
}
}
/// Return a new instance of this position with the x coordinate increased
/// by the given number.
pub fn east(&self, x: $type) -> Self {
Self {
x: self.x + x,
y: self.y,
z: self.z,
}
}
/// Return a new instance of this position with the z coordinate increased
/// by the given number.
pub fn south(&self, z: $type) -> Self {
Self {
x: self.x,
y: self.y,
z: self.z + z,
}
}
/// Return a new instance of this position with the x coordinate subtracted
/// by the given number.
pub fn west(&self, x: $type) -> Self {
Self {
x: self.x - x,
y: self.y,
z: self.z,
}
}

#[inline]
pub fn dot(&self, other: Self) -> $type {
self.x * other.x + self.y * other.y + self.z * other.z
Expand Down Expand Up @@ -501,6 +539,13 @@ impl From<Vec3> for ChunkBlockPos {
}
}

impl fmt::Display for BlockPos {
/// Display a block position as `x y z`.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {} {}", self.x, self.y, self.z)
}
}

const PACKED_X_LENGTH: u64 = 1 + 25; // minecraft does something a bit more complicated to get this 25
const PACKED_Z_LENGTH: u64 = PACKED_X_LENGTH;
const PACKED_Y_LENGTH: u64 = 64 - PACKED_X_LENGTH - PACKED_Z_LENGTH;
Expand Down
9 changes: 7 additions & 2 deletions azalea-world/src/chunk_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct PartialChunkStorage {
/// A storage for chunks where they're only stored weakly, so if they're not
/// actively being used somewhere else they'll be forgotten. This is used for
/// shared worlds.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ChunkStorage {
pub height: u32,
pub min_y: i32,
Expand Down Expand Up @@ -514,7 +514,12 @@ impl Default for ChunkStorage {
/// and the minimum y coordinate of the world.
#[inline]
pub fn section_index(y: i32, min_y: i32) -> u32 {
assert!(y >= min_y, "y ({y}) must be at least {min_y}");
if y < min_y {
#[cfg(debug_assertions)]
panic!("y ({y}) must be at most {min_y}");
#[cfg(not(debug_assertions))]
tracing::error!("y ({y}) must be at least {min_y}")
};
let min_section_index = min_y >> 4;
((y >> 4) - min_section_index) as u32
}
Expand Down
Loading

0 comments on commit a707e2e

Please sign in to comment.