diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2023-12-15 11:26:40 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-15 11:26:40 -0600 |
| commit | a707e2eb82b74994a16083b31fa4576332cf1995 (patch) | |
| tree | db6c2ac94dd73590befd68a9b1b0ef960410b0df /azalea/src/pathfinder/moves | |
| parent | 59e140ddd655c7dc6e35109b91286118c51bcc06 (diff) | |
| download | azalea-drasl-a707e2eb82b74994a16083b31fa4576332cf1995.tar.xz | |
Add mining to the pathfinder (#122)
* 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
Diffstat (limited to 'azalea/src/pathfinder/moves')
| -rw-r--r-- | azalea/src/pathfinder/moves/basic.rs | 141 | ||||
| -rw-r--r-- | azalea/src/pathfinder/moves/mod.rs | 108 |
2 files changed, 224 insertions, 25 deletions
diff --git a/azalea/src/pathfinder/moves/basic.rs b/azalea/src/pathfinder/moves/basic.rs index 957e24c6..54a6dc6a 100644 --- a/azalea/src/pathfinder/moves/basic.rs +++ b/azalea/src/pathfinder/moves/basic.rs @@ -16,17 +16,20 @@ pub fn basic_move(ctx: &mut PathfinderCtx, node: BlockPos) { descend_move(ctx, node); diagonal_move(ctx, node); descend_forward_1_move(ctx, node); + downward_move(ctx, node); } fn forward_move(ctx: &mut PathfinderCtx, pos: BlockPos) { for dir in CardinalDirection::iter() { let offset = BlockPos::new(dir.x(), 0, dir.z()); - if !ctx.world.is_standable(pos + offset) { + let mut cost = SPRINT_ONE_BLOCK_COST; + + let break_cost = ctx.world.cost_for_standing(pos + offset, ctx.mining_cache); + if break_cost == f32::INFINITY { continue; } - - let cost = SPRINT_ONE_BLOCK_COST; + cost += break_cost; ctx.edges.push(Edge { movement: astar::Movement { @@ -43,6 +46,14 @@ fn forward_move(ctx: &mut PathfinderCtx, pos: BlockPos) { fn execute_forward_move(mut ctx: ExecuteCtx) { let center = ctx.target.center(); + + if ctx.mine_while_at_start(ctx.target.up(1)) { + return; + } + if ctx.mine_while_at_start(ctx.target) { + return; + } + ctx.look_at(center); ctx.sprint(SprintDirection::Forward); } @@ -51,14 +62,22 @@ fn ascend_move(ctx: &mut PathfinderCtx, pos: BlockPos) { for dir in CardinalDirection::iter() { let offset = BlockPos::new(dir.x(), 1, dir.z()); - if !ctx.world.is_block_passable(pos.up(2)) { + let break_cost_1 = ctx + .world + .cost_for_breaking_block(pos.up(2), ctx.mining_cache); + if break_cost_1 == f32::INFINITY { continue; } - if !ctx.world.is_standable(pos + offset) { + let break_cost_2 = ctx.world.cost_for_standing(pos + offset, ctx.mining_cache); + if break_cost_2 == f32::INFINITY { continue; } - let cost = SPRINT_ONE_BLOCK_COST + JUMP_PENALTY + *JUMP_ONE_BLOCK_COST; + let cost = SPRINT_ONE_BLOCK_COST + + JUMP_PENALTY + + *JUMP_ONE_BLOCK_COST + + break_cost_1 + + break_cost_2; ctx.edges.push(Edge { movement: astar::Movement { @@ -81,6 +100,16 @@ fn execute_ascend_move(mut ctx: ExecuteCtx) { .. } = ctx; + if ctx.mine_while_at_start(start.up(2)) { + return; + } + if ctx.mine_while_at_start(target) { + return; + } + if ctx.mine_while_at_start(target.up(1)) { + return; + } + let target_center = target.center(); ctx.look_at(target_center); @@ -123,19 +152,39 @@ fn descend_move(ctx: &mut PathfinderCtx, pos: BlockPos) { for dir in CardinalDirection::iter() { let dir_delta = BlockPos::new(dir.x(), 0, dir.z()); let new_horizontal_position = pos + dir_delta; - let fall_distance = ctx.world.fall_distance(new_horizontal_position); - if fall_distance == 0 || fall_distance > 3 { + + let break_cost_1 = ctx + .world + .cost_for_passing(new_horizontal_position, ctx.mining_cache); + if break_cost_1 == f32::INFINITY { continue; } - let new_position = new_horizontal_position.down(fall_distance as i32); - // check whether 3 blocks vertically forward are passable - if !ctx.world.is_passable(new_horizontal_position) { + let mut fall_distance = ctx.world.fall_distance(new_horizontal_position); + if fall_distance > 3 { continue; } - // check whether we can stand on the target position - if !ctx.world.is_standable(new_position) { - continue; + + if fall_distance == 0 { + // if the fall distance is 0, set it to 1 so we try mining + fall_distance = 1 + } + + let new_position = new_horizontal_position.down(fall_distance as i32); + + // only mine if we're descending 1 block + let break_cost_2; + if fall_distance == 1 { + break_cost_2 = ctx.world.cost_for_standing(new_position, ctx.mining_cache); + if break_cost_2 == f32::INFINITY { + continue; + } + } else { + // check whether we can stand on the target position + if !ctx.world.is_standable(new_position) { + continue; + } + break_cost_2 = 0.; } let cost = WALK_OFF_BLOCK_COST @@ -145,9 +194,11 @@ fn descend_move(ctx: &mut PathfinderCtx, pos: BlockPos) { .copied() // avoid panicking if we fall more than the size of FALL_N_BLOCKS_COST // probably not possible but just in case - .unwrap_or(f32::MAX), + .unwrap_or(f32::INFINITY), CENTER_AFTER_FALL_COST, - ); + ) + + break_cost_1 + + break_cost_2; ctx.edges.push(Edge { movement: astar::Movement { @@ -169,6 +220,12 @@ fn execute_descend_move(mut ctx: ExecuteCtx) { .. } = ctx; + for i in (0..=(start.y - target.y + 1)).rev() { + if ctx.mine_while_at_start(target.up(i)) { + return; + } + } + let start_center = start.center(); let center = target.center(); @@ -249,7 +306,7 @@ fn descend_forward_1_move(ctx: &mut PathfinderCtx, pos: BlockPos) { .copied() // avoid panicking if we fall more than the size of FALL_N_BLOCKS_COST // probably not possible but just in case - .unwrap_or(f32::MAX), + .unwrap_or(f32::INFINITY), CENTER_AFTER_FALL_COST, ); @@ -310,3 +367,53 @@ fn execute_diagonal_move(mut ctx: ExecuteCtx) { ctx.look_at(target_center); ctx.sprint(SprintDirection::Forward); } + +/// Go directly down, usually by mining. +fn downward_move(ctx: &mut PathfinderCtx, pos: BlockPos) { + // make sure we land on a solid block after breaking the one below us + if !ctx.world.is_block_solid(pos.down(2)) { + return; + } + + let break_cost = ctx + .world + .cost_for_breaking_block(pos.down(1), ctx.mining_cache); + if break_cost == f32::INFINITY { + return; + } + + let cost = FALL_N_BLOCKS_COST[1] + break_cost; + + ctx.edges.push(Edge { + movement: astar::Movement { + target: pos.down(1), + data: MoveData { + execute: &execute_downward_move, + is_reached: &default_is_reached, + }, + }, + cost, + }) +} +fn execute_downward_move(mut ctx: ExecuteCtx) { + let ExecuteCtx { + target, position, .. + } = ctx; + + let target_center = target.center(); + + let horizontal_distance_from_target = + (target_center - position).horizontal_distance_sqr().sqrt(); + + if horizontal_distance_from_target > 0.25 { + ctx.look_at(target_center); + ctx.walk(WalkDirection::Forward); + } else if ctx.mine_while_at_start(target) { + ctx.walk(WalkDirection::None); + } else if BlockPos::from(position) != target { + ctx.look_at(target_center); + ctx.walk(WalkDirection::Forward); + } else { + ctx.walk(WalkDirection::None); + } +} diff --git a/azalea/src/pathfinder/moves/mod.rs b/azalea/src/pathfinder/moves/mod.rs index e5b837ea..bb10b192 100644 --- a/azalea/src/pathfinder/moves/mod.rs +++ b/azalea/src/pathfinder/moves/mod.rs @@ -1,14 +1,24 @@ pub mod basic; pub mod parkour; -use std::fmt::Debug; - -use crate::{JumpEvent, LookAtEvent}; - -use super::{astar, mining::MiningCache, world::CachedWorld}; -use azalea_client::{SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection}; +use std::{fmt::Debug, sync::Arc}; + +use crate::{auto_tool::best_tool_in_hotbar_for_block, JumpEvent, LookAtEvent}; + +use super::{ + astar, + mining::MiningCache, + world::{is_block_state_passable, CachedWorld}, +}; +use azalea_client::{ + inventory::SetSelectedHotbarSlotEvent, mining::StartMiningBlockEvent, SprintDirection, + StartSprintEvent, StartWalkEvent, WalkDirection, +}; use azalea_core::position::{BlockPos, Vec3}; +use azalea_inventory::Menu; +use azalea_world::Instance; use bevy_ecs::{entity::Entity, event::EventWriter}; +use parking_lot::RwLock; type Edge = astar::Edge<BlockPos, MoveData>; @@ -35,7 +45,7 @@ impl Debug for MoveData { } } -pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'a> { +pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'w5, 'w6, 'a> { pub entity: Entity, /// The node that we're trying to reach. pub target: BlockPos, @@ -43,14 +53,19 @@ pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'a> { pub start: BlockPos, pub position: Vec3, pub physics: &'a azalea_entity::Physics, + pub is_currently_mining: bool, + pub instance: Arc<RwLock<Instance>>, + pub menu: Menu, 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 start_mining_events: &'a mut EventWriter<'w5, StartMiningBlockEvent>, + pub set_selected_hotbar_slot_events: &'a mut EventWriter<'w6, SetSelectedHotbarSlotEvent>, } -impl ExecuteCtx<'_, '_, '_, '_, '_> { +impl ExecuteCtx<'_, '_, '_, '_, '_, '_, '_> { pub fn look_at(&mut self, position: Vec3) { self.look_at_events.send(LookAtEvent { entity: self.entity, @@ -63,6 +78,13 @@ impl ExecuteCtx<'_, '_, '_, '_, '_> { }); } + pub fn look_at_exact(&mut self, position: Vec3) { + self.look_at_events.send(LookAtEvent { + entity: self.entity, + position, + }); + } + pub fn sprint(&mut self, direction: SprintDirection) { self.sprint_events.send(StartSprintEvent { entity: self.entity, @@ -82,6 +104,76 @@ impl ExecuteCtx<'_, '_, '_, '_, '_> { entity: self.entity, }); } + + /// Returns whether this block could be mined. + pub fn should_mine(&mut self, block: BlockPos) -> bool { + let block_state = self + .instance + .read() + .get_block_state(&block) + .unwrap_or_default(); + if is_block_state_passable(block_state) { + // block is already passable, no need to mine it + return false; + } + + true + } + + /// Mine the block at the given position. Returns whether the block is being + /// mined. + pub fn mine(&mut self, block: BlockPos) -> bool { + let block_state = self + .instance + .read() + .get_block_state(&block) + .unwrap_or_default(); + if is_block_state_passable(block_state) { + // block is already passable, no need to mine it + return false; + } + + let best_tool_result = best_tool_in_hotbar_for_block(block_state, &self.menu); + + self.set_selected_hotbar_slot_events + .send(SetSelectedHotbarSlotEvent { + entity: self.entity, + slot: best_tool_result.index as u8, + }); + + self.is_currently_mining = true; + + self.walk(WalkDirection::None); + self.look_at_exact(block.center()); + self.start_mining_events.send(StartMiningBlockEvent { + entity: self.entity, + position: block, + }); + + true + } + + /// Mine the given block, but make sure the player is standing at the start + /// of the current node first. + pub fn mine_while_at_start(&mut self, block: BlockPos) -> bool { + let horizontal_distance_from_start = (self.start.center() - self.position) + .horizontal_distance_sqr() + .sqrt(); + let at_start_position = + BlockPos::from(self.position) == self.start && horizontal_distance_from_start < 0.25; + + if self.should_mine(block) { + if at_start_position { + self.mine(block); + } else { + self.look_at(self.start.center()); + self.walk(WalkDirection::Forward); + } + true + } else { + false + } + } } pub struct IsReachedCtx<'a> { |
