Skip to content

Commit

Permalink
move mine_area to its own module
Browse files Browse the repository at this point in the history
  • Loading branch information
mat-1 committed Dec 17, 2023
1 parent 23fadb0 commit 6b90194
Show file tree
Hide file tree
Showing 5 changed files with 285 additions and 215 deletions.
6 changes: 4 additions & 2 deletions azalea/src/auto_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use azalea_client::{
Client, InstanceHolder,
};
use azalea_core::position::BlockPos;
use azalea_entity::{FluidOnEyes, Physics};
use azalea_entity::{update_fluid_on_eyes, FluidOnEyes, Physics};
use azalea_inventory::{ItemSlot, Menu};
use azalea_registry::Fluid;
use bevy_app::{App, Plugin, Update};
Expand All @@ -24,7 +24,9 @@ impl Plugin for AutoToolPlugin {
.add_systems(
Update,
start_mining_block_with_auto_tool_listener
.before(azalea_client::inventory::handle_set_selected_hotbar_slot_event),
.before(azalea_client::inventory::handle_set_selected_hotbar_slot_event)
.after(update_fluid_on_eyes)
.after(azalea_client::chunks::handle_receive_chunk_events),
);
}
}
Expand Down
4 changes: 2 additions & 2 deletions azalea/src/pathfinder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@ pub struct StopPathfindingEvent {
pub force: bool,
}

fn handle_stop_pathfinding_event(
pub fn handle_stop_pathfinding_event(
mut events: EventReader<StopPathfindingEvent>,
mut query: Query<(&mut Pathfinder, &mut ExecutingPath)>,
mut walk_events: EventWriter<StartWalkEvent>,
Expand Down Expand Up @@ -790,7 +790,7 @@ fn handle_stop_pathfinding_event(
}
}

fn stop_pathfinding_on_instance_change(
pub fn stop_pathfinding_on_instance_change(
mut query: Query<(Entity, &mut ExecutingPath), Changed<InstanceName>>,
mut stop_pathfinding_events: EventWriter<StopPathfindingEvent>,
) {
Expand Down
20 changes: 14 additions & 6 deletions azalea/src/pathfinder_extras/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,46 @@
pub mod process;

use crate::ecs::prelude::*;
use azalea_client::Client;
use azalea_core::{position::BlockPos, tick::GameTick};
use azalea_physics::PhysicsSet;
use bevy_app::Update;

use crate::app::{App, Plugin};

use self::process::{Process, SetActiveProcessEvent};
use self::process::{mine_area::MineArea, Process, SetActiveProcessEvent};

pub struct PathfinderExtrasPlugin;

impl Plugin for PathfinderExtrasPlugin {
fn build(&self, app: &mut App) {
app.add_event::<SetActiveProcessEvent>()
.add_systems(Update, process::set_active_pathfinder_process_listener)
.add_systems(GameTick, process::process_tick);
.add_systems(
Update,
process::set_active_pathfinder_process_listener
.after(crate::pathfinder::stop_pathfinding_on_instance_change)
.before(crate::pathfinder::handle_stop_pathfinding_event),
)
.add_systems(GameTick, process::process_tick.before(PhysicsSet));
}
}

pub trait PathfinderExtrasClientExt {
fn set_active_pathfinder_process(&self, process: Process);
fn set_active_pathfinder_process(&self, process: impl Into<Process>);
fn mine_area(&self, corner1: BlockPos, corner2: BlockPos);
}

impl PathfinderExtrasClientExt for Client {
fn set_active_pathfinder_process(&self, process: Process) {
fn set_active_pathfinder_process(&self, process: impl Into<Process>) {
let process = process.into();
self.ecs.lock().send_event(SetActiveProcessEvent {
entity: self.entity,
process,
});
}

fn mine_area(&self, corner1: BlockPos, corner2: BlockPos) {
self.set_active_pathfinder_process(Process::MineArea { corner1, corner2 });
self.set_active_pathfinder_process(MineArea { corner1, corner2 });
}
}
232 changes: 232 additions & 0 deletions azalea/src/pathfinder_extras/process/mine_area.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
use std::sync::Arc;

use azalea_block::BlockState;
use azalea_core::position::BlockPos;
use azalea_world::ChunkStorage;
use tracing::info;

use crate::{
auto_tool::StartMiningBlockWithAutoToolEvent,
ecs::prelude::*,
pathfinder::{
self,
block_box::BlockBox,
goals::{Goal, ReachBlockPosGoal},
GotoEvent,
},
utils::get_reachable_blocks_around_player,
LookAtEvent,
};

use super::{Process, ProcessSystemComponents};

#[derive(Clone, Debug)]
pub struct MineArea {
pub corner1: BlockPos,
pub corner2: BlockPos,
}

pub fn mine_area<'a>(
mine_area: &MineArea,
commands: &mut Commands,
ProcessSystemComponents {
entity,
position,
instance_holder,
pathfinder,
mining,
executing_path,
}: ProcessSystemComponents<'a>,
goto_events: &mut EventWriter<GotoEvent>,
look_at_events: &mut EventWriter<LookAtEvent>,
start_mining_block_events: &mut EventWriter<StartMiningBlockWithAutoToolEvent>,
) {
if pathfinder.goal.is_some() || executing_path.is_some() {
// already pathfinding
println!("currently pathfinding");
return;
}

if mining.is_some() {
// currently mining, so wait for that to finish
println!("currently mining");
return;
}

let bb = BlockBox::new(mine_area.corner1, mine_area.corner2);
let chunk_storage = instance_holder.instance.read().chunks.clone();
let player_position = BlockPos::from(position);

println!("player_position: {player_position}");

// the index is from the top-down, so 0 means the top layer
let layer_index = determine_layer(&bb, &chunk_storage);
let layer_bb = BlockBox::new(
BlockPos::new(
bb.min().x,
i32::max(bb.min().y, bb.max().y - layer_index as i32),
bb.min().z,
),
BlockPos::new(
bb.max().x,
i32::max(bb.min().y, bb.max().y - layer_index as i32),
bb.max().z,
),
);

let reachable_blocks = get_reachable_blocks_around_player(player_position, &chunk_storage);
let mineable_blocks = reachable_blocks
.into_iter()
.filter(|block_pos| {
// must be within box
if !layer_bb.contains(*block_pos) {
return false;
}

// and must be mineable
let block = chunk_storage.get_block_state(block_pos).unwrap_or_default();

is_block_mineable(block)
})
.collect::<Vec<_>>();

println!("mineable_blocks: {:?}", mineable_blocks);

if !mineable_blocks.is_empty() {
// pick the closest one and mine it
let mut closest_block_pos = None;
let mut closest_distance = i32::MAX;
for block_pos in &mineable_blocks[1..] {
if block_pos.y < player_position.y {
// skip blocks below us at first
continue;
}
let distance = block_pos.distance_squared_to(&player_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 &mineable_blocks {
let distance = block_pos.distance_squared_to(&player_position);
if distance < closest_distance {
closest_block_pos = Some(*block_pos);
closest_distance = distance;
}
}
}

let closest_block_pos = closest_block_pos
.expect("there must be a closest block because mineable_blocks wasn't empty");
look_at_events.send(LookAtEvent {
entity,
position: closest_block_pos.center(),
});
start_mining_block_events.send(StartMiningBlockWithAutoToolEvent {
entity,
position: closest_block_pos,
});

println!("start mining block {closest_block_pos:?}");
return;
}

// no mineable blocks, so go towards the blocks that can be mined

let goal: Arc<dyn Goal> = if bb.distance_squared_to(player_position) < 16 * 16 {
// already close enough to the box, path to the closest
// block instead

let mut block_positions_and_distances = Vec::new();
for x in layer_bb.min().x..=layer_bb.max().x {
for y in layer_bb.min().y..=layer_bb.max().y {
for z in layer_bb.min().z..=layer_bb.max().z {
let block_pos = BlockPos::new(x, y, z);

if !is_block_mineable(
chunk_storage
.get_block_state(&block_pos)
.unwrap_or_default(),
) {
continue;
}

let distance = block_pos.distance_squared_to(&player_position);
block_positions_and_distances.push((block_pos, distance));
}
}
}

if block_positions_and_distances.is_empty() {
info!("MineArea process is done, no more blocks to mine!");
commands.entity(entity).remove::<Process>();
return;
}

// use the closest 64 blocks as the goals

block_positions_and_distances.sort_by_key(|(_, distance)| *distance);
let mut goals = Vec::new();
for (block_pos, _) in block_positions_and_distances.into_iter().take(64) {
goals.push(ReachBlockPosGoal {
pos: block_pos,
chunk_storage: chunk_storage.clone(),
});
}

let reach_blocks_goal = pathfinder::goals::OrGoals(goals);

println!("reaching for block");

Arc::new(reach_blocks_goal)
} else {
println!("reaching for box because we're at {player_position}");

let reach_box_goal = pathfinder::goals::ReachBoxGoal {
bb: bb.clone(),
chunk_storage: chunk_storage.clone(),
};

Arc::new(reach_box_goal)
};

goto_events.send(GotoEvent {
entity,
goal,
successors_fn: pathfinder::moves::default_move,
allow_mining: true,
});
}

fn is_block_mineable(block: BlockState) -> bool {
!block.is_air()
}

/// Determine what layer should be mined first. This is from the top-down, so 0
/// means the top layer.
fn determine_layer(bb: &BlockBox, chunks: &ChunkStorage) -> usize {
let mut layer = 0;
let mut y = bb.max().y;
while y >= bb.min().y {
let mut x = bb.min().x;
while x <= bb.max().x {
let mut z = bb.min().z;
while z <= bb.max().z {
let block = chunks
.get_block_state(&BlockPos::new(x, y, z))
.unwrap_or_default();
if is_block_mineable(block) {
return layer;
}
z += 1;
}
x += 1;
}
y -= 1;
layer += 1;
}
layer
}
Loading

0 comments on commit 6b90194

Please sign in to comment.