Skip to content

Commit

Permalink
start adding picking up drops for mined blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
mat-1 committed Dec 21, 2023
1 parent 4242cfe commit 68bb32c
Show file tree
Hide file tree
Showing 13 changed files with 279 additions and 37 deletions.
2 changes: 1 addition & 1 deletion azalea/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ azalea-buf = { version = "0.9.0", path = "../azalea-buf" }
bevy_app = "0.12.1"
bevy_ecs = "0.12.1"
bevy_tasks = { version = "0.12.1", features = ["multi-threaded"] }
derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] }
derive_more = { version = "0.99.17" }
futures = "0.3.29"
futures-lite = "2.1.0"
tracing = "0.1.40"
Expand Down
2 changes: 0 additions & 2 deletions azalea/src/bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use crate::ecs::{
query::{With, Without},
system::{Commands, Query},
};
use crate::pathfinder_extras::PathfinderExtrasPlugin;
use azalea_client::interact::SwingArmEvent;
use azalea_client::mining::Mining;
use azalea_client::TickBroadcast;
Expand Down Expand Up @@ -193,7 +192,6 @@ impl PluginGroup for DefaultBotPlugins {
PluginGroupBuilder::start::<Self>()
.add(BotPlugin)
.add(PathfinderPlugin)
.add(PathfinderExtrasPlugin)
.add(ContainerPlugin)
.add(AutoRespawnPlugin)
.add(AcceptResourcePacksPlugin)
Expand Down
1 change: 0 additions & 1 deletion azalea/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ mod bot;
pub mod container;
pub mod nearest_entity;
pub mod pathfinder;
pub mod pathfinder_extras;
pub mod prelude;
pub mod swarm;

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Adds utility functions that all depend on the pathfinder.
pub mod goals;
pub mod pickup;
pub mod process;
pub mod utils;

Expand All @@ -22,11 +23,24 @@ impl Plugin for PathfinderExtrasPlugin {
app.add_event::<SetActiveProcessEvent>()
.add_systems(
Update,
process::set_active_pathfinder_process_listener
.after(crate::pathfinder::stop_pathfinding_on_instance_change)
.before(crate::pathfinder::handle_stop_pathfinding_event),
(
process::set_active_pathfinder_process_listener
.after(crate::pathfinder::stop_pathfinding_on_instance_change)
.before(crate::pathfinder::handle_stop_pathfinding_event),
pickup::add_pickup_components_to_player,
pickup::remove_pickup_components_from_player,
pickup::watch_for_mined_blocks,
pickup::watch_for_item_spawns_from_blocks_we_mined,
),
)
.add_systems(GameTick, process::process_tick.before(PhysicsSet));
.add_systems(
GameTick,
(
pickup::remove_despawned_items_to_pickup,
process::process_tick.before(PhysicsSet),
)
.chain(),
);
}
}

Expand Down
136 changes: 136 additions & 0 deletions azalea/src/pathfinder/extras/pickup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use std::{collections::VecDeque, time::Instant};

use azalea_client::mining::FinishMiningBlockEvent;
use azalea_core::position::BlockPos;
use azalea_entity::Position;
use bevy_ecs::prelude::*;
use derive_more::{Deref, DerefMut};

#[derive(Debug)]
pub struct RecentlyMinedBlock {
pub block: BlockPos,
pub time: Instant,
}

/// A component that contains the blocks that we finished mining recently. When
/// a new item is added, the ones that were added more than 5 seconds ago are
/// removed.
///
/// This is only present when the entity has the
/// [`Process`](super::process::Process) component, since it's currently only
/// used for picking up items we mined while pathfinding.
#[derive(Component, Debug, Default)]
pub struct RecentlyMinedBlocks {
pub blocks: VecDeque<RecentlyMinedBlock>,
}

#[derive(Component, Debug, Default)]
pub struct ItemsToPickup {
pub items: Vec<Entity>,
}

/// This is used internally to recalculate the path when there's a new item to
/// pickup.
#[derive(Component, Debug, Default)]
pub struct LastItemsToPickup {
pub items: Vec<Entity>,
}
/// A component that tracks whether we've acknowledged the items to pickup
/// change.
///
/// This is only used internally for recalculating paths when there's a new item
/// to pick up.
#[derive(Component, Debug, Deref, DerefMut)]
pub struct ItemsToPickupChangeAcknowledged(pub bool);

pub fn add_pickup_components_to_player(
mut commands: Commands,
mut query: Query<Entity, Added<super::process::Process>>,
) {
for entity in &mut query {
commands.entity(entity).insert((
RecentlyMinedBlocks::default(),
ItemsToPickup::default(),
LastItemsToPickup::default(),
ItemsToPickupChangeAcknowledged(true),
));
}
}

pub fn remove_pickup_components_from_player(
mut commands: Commands,
mut query: RemovedComponents<super::process::Process>,
) {
for entity in query.read() {
commands
.entity(entity)
.remove::<RecentlyMinedBlocks>()
.remove::<ItemsToPickup>()
.remove::<LastItemsToPickup>()
.remove::<ItemsToPickupChangeAcknowledged>();
}
}

pub fn watch_for_mined_blocks(
mut finish_mining_block_events: EventReader<FinishMiningBlockEvent>,
mut query: Query<&mut RecentlyMinedBlocks, With<super::process::Process>>,
) {
for event in finish_mining_block_events.read() {
let mut recently_mined_blocks = query.get_mut(event.entity).unwrap();

// remove blocks that are too old
let now = Instant::now();
recently_mined_blocks
.blocks
.retain(|block| now.duration_since(block.time).as_secs_f32() < 5.0);

recently_mined_blocks.blocks.push_back(RecentlyMinedBlock {
block: event.position,
time: now,
});
}
}

pub fn watch_for_item_spawns_from_blocks_we_mined(
mut player_query: Query<(&RecentlyMinedBlocks, &Position, &mut ItemsToPickup)>,
spawned_items_query: Query<(Entity, &Position), Added<azalea_entity::metadata::Item>>,
) {
for (recently_mined_blocks, player_position, mut items_to_pickup) in &mut player_query {
for (entity, position) in &mut spawned_items_query.iter() {
if recently_mined_blocks
.blocks
.iter()
.any(|block| block.block == BlockPos::from(position))
{
// if we're already within 1 block of the item, ignore because we probably
// already picked it up
if (player_position.distance_squared_to(position) < 1.0)
|| (player_position
.up(player_position.y + 1.8)
.distance_squared_to(position)
< 1.0)
{
// this check isn't perfect since minecraft checks with the bounding box, and
// the distance is different vertically, but it's good enough for our purposes
continue;
}

items_to_pickup.items.push(entity);
println!("added item to pickup: {:?}", entity);
}
}
}
}

/// Remove items from [`ItemsToPickup`] that no longer exist. This doesn't need
/// to run super frequently, so it only runs every tick.
pub fn remove_despawned_items_to_pickup(
mut player_query: Query<&mut ItemsToPickup>,
items_query: Query<Entity, With<azalea_entity::metadata::Item>>,
) {
for mut items_to_pickup in &mut player_query {
items_to_pickup
.items
.retain(|entity| items_query.get(*entity).is_ok());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ use tracing::info;
use crate::{
auto_tool::StartMiningBlockWithAutoToolEvent,
ecs::prelude::*,
pathfinder::{self, block_box::BlockBox, goals::Goal, GotoEvent},
pathfinder_extras::{
goals::{ReachBlockPosGoal, ReachBoxGoal},
utils::{get_reachable_blocks_around_player, pick_closest_block},
pathfinder::{
self,
block_box::BlockBox,
extras::{
goals::{ReachBlockPosGoal, ReachBoxGoal},
utils::{get_reachable_blocks_around_player, pick_closest_block},
},
goals::Goal,
GotoEvent,
},
LookAtEvent,
};
Expand All @@ -34,20 +39,19 @@ pub fn mine_area(
pathfinder,
mining,
executing_path,
..
}: ProcessSystemComponents<'_>,
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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
use std::sync::Arc;

use azalea_block::BlockStates;
use azalea_core::position::BlockPos;
use azalea_core::position::{BlockPos, Vec3};
use tracing::info;

use crate::{
auto_tool::StartMiningBlockWithAutoToolEvent,
ecs::prelude::*,
pathfinder::{self, GotoEvent},
pathfinder_extras::{
goals::ReachBlockPosGoal,
utils::{can_reach_block, pick_closest_block},
pathfinder::{
self,
extras::{
goals::ReachBlockPosGoal,
utils::{can_reach_block, pick_closest_block},
},
GotoEvent,
},
LookAtEvent,
};
Expand All @@ -32,22 +35,35 @@ pub fn mine_forever(
pathfinder,
mining,
executing_path,
mut items_to_pickup_change_acknowledged,
}: ProcessSystemComponents<'_>,
items_to_pickup_positions: &[Vec3],
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;
let mut should_force_recalculate_path = false;

if !pathfinder.is_calculating {
if !**items_to_pickup_change_acknowledged {
should_force_recalculate_path = true;
**items_to_pickup_change_acknowledged = true;
println!("items_to_pickup_change_acknowledged = true");
}
}

Check warning on line 53 in azalea/src/pathfinder/extras/process/mine_forever.rs

View workflow job for this annotation

GitHub Actions / clippy

this `if` statement can be collapsed

warning: this `if` statement can be collapsed --> azalea/src/pathfinder/extras/process/mine_forever.rs:47:5 | 47 | / if !pathfinder.is_calculating { 48 | | if !**items_to_pickup_change_acknowledged { 49 | | should_force_recalculate_path = true; 50 | | **items_to_pickup_change_acknowledged = true; 51 | | println!("items_to_pickup_change_acknowledged = true"); 52 | | } 53 | | } | |_____^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if = note: `#[warn(clippy::collapsible_if)]` on by default help: collapse nested if block | 47 ~ if !pathfinder.is_calculating && !**items_to_pickup_change_acknowledged { 48 + should_force_recalculate_path = true; 49 + **items_to_pickup_change_acknowledged = true; 50 + println!("items_to_pickup_change_acknowledged = true"); 51 + } |

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

if pathfinder.goal.is_some() || executing_path.is_some() {
// already pathfinding
return;
}
}

let instance = &instance_holder.instance.read();

let target_blocks = instance
Expand Down Expand Up @@ -82,23 +98,35 @@ pub fn mine_forever(
return;
}

let mut potential_goals = Vec::new();
let mut reach_block_goals = Vec::new();
for target_pos in target_blocks {
potential_goals.push(ReachBlockPosGoal {
reach_block_goals.push(ReachBlockPosGoal {
pos: target_pos,
chunk_storage: chunk_storage.clone(),
});
}

if potential_goals.is_empty() {
let mut reach_item_goals = Vec::new();
for &item_position in items_to_pickup_positions {
println!("item_position: {item_position:?}");
reach_item_goals.push(pathfinder::goals::RadiusGoal {
pos: item_position,
radius: 1.0,
});
}

if reach_block_goals.is_empty() && reach_item_goals.is_empty() {
info!("MineForever process is done, can't find any more blocks to mine");
commands.entity(entity).remove::<Process>();
return;
}

goto_events.send(GotoEvent {
entity,
goal: Arc::new(pathfinder::goals::OrGoals(potential_goals)),
goal: Arc::new(pathfinder::goals::OrGoal(
pathfinder::goals::OrGoals(reach_block_goals),
pathfinder::goals::ScaleGoal(pathfinder::goals::OrGoals(reach_item_goals), 0.5),
)),
successors_fn: pathfinder::moves::default_move,
allow_mining: true,
});
Expand Down
Loading

0 comments on commit 68bb32c

Please sign in to comment.