aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2023-10-10 23:21:23 -0500
committermat <git@matdoes.dev>2023-10-10 23:21:23 -0500
commit9a687f0ffebad80441e036aabe14e7cf80c774d3 (patch)
tree2f6ad5a1a14cae23723550c934df395aab1733f6
parent0297b8aacee27d9e86cea781b3751591e32df401 (diff)
downloadazalea-drasl-9a687f0ffebad80441e036aabe14e7cf80c774d3.tar.xz
start adding mining to pathfinder
-rwxr-xr-xazalea-brigadier/src/arguments/argument_type.rs4
-rw-r--r--azalea-brigadier/src/arguments/bool_argument_type.rs6
-rw-r--r--azalea-brigadier/src/arguments/double_argument_type.rs6
-rw-r--r--azalea-brigadier/src/arguments/float_argument_type.rs6
-rw-r--r--azalea-brigadier/src/arguments/integer_argument_type.rs6
-rw-r--r--azalea-brigadier/src/arguments/long_argument_type.rs6
-rw-r--r--azalea-brigadier/src/arguments/string_argument_type.rs6
-rwxr-xr-xazalea-brigadier/src/builder/required_argument_builder.rs4
-rwxr-xr-xazalea-brigadier/src/context/command_context.rs2
-rwxr-xr-xazalea-brigadier/src/context/parsed_argument.rs4
-rw-r--r--azalea-client/src/inventory.rs2
-rwxr-xr-xazalea-core/src/bitset.rs5
-rw-r--r--azalea-entity/src/lib.rs6
-rw-r--r--azalea-entity/src/mining.rs18
-rw-r--r--azalea/benches/pathfinder.rs10
-rw-r--r--azalea/src/auto_tool.rs84
-rw-r--r--azalea/src/bot.rs3
-rw-r--r--azalea/src/container.rs4
-rw-r--r--azalea/src/lib.rs1
-rw-r--r--azalea/src/pathfinder/mining.rs30
-rw-r--r--azalea/src/pathfinder/mod.rs52
-rw-r--r--azalea/src/pathfinder/moves/mod.rs3
-rw-r--r--azalea/src/pathfinder/simulation.rs4
23 files changed, 221 insertions, 51 deletions
diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs
index 029e4696..f44233e1 100755
--- a/azalea-brigadier/src/arguments/argument_type.rs
+++ b/azalea-brigadier/src/arguments/argument_type.rs
@@ -1,7 +1,7 @@
-use std::{any::Any, rc::Rc};
+use std::{any::Any, sync::Arc};
use crate::{exceptions::CommandSyntaxException, string_reader::StringReader};
pub trait ArgumentType {
- fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException>;
+ fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException>;
}
diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs
index 1bff8aa3..2e348c7b 100644
--- a/azalea-brigadier/src/arguments/bool_argument_type.rs
+++ b/azalea-brigadier/src/arguments/bool_argument_type.rs
@@ -1,4 +1,4 @@
-use std::{any::Any, rc::Rc};
+use std::{any::Any, sync::Arc};
use crate::{
context::CommandContext, exceptions::CommandSyntaxException, string_reader::StringReader,
@@ -10,8 +10,8 @@ use super::ArgumentType;
struct Boolean;
impl ArgumentType for Boolean {
- fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> {
- Ok(Rc::new(reader.read_boolean()?))
+ fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
+ Ok(Arc::new(reader.read_boolean()?))
}
}
diff --git a/azalea-brigadier/src/arguments/double_argument_type.rs b/azalea-brigadier/src/arguments/double_argument_type.rs
index d83bfb2a..9502a680 100644
--- a/azalea-brigadier/src/arguments/double_argument_type.rs
+++ b/azalea-brigadier/src/arguments/double_argument_type.rs
@@ -1,4 +1,4 @@
-use std::{any::Any, rc::Rc};
+use std::{any::Any, sync::Arc};
use crate::{
context::CommandContext,
@@ -15,7 +15,7 @@ struct Double {
}
impl ArgumentType for Double {
- fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> {
+ fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
let start = reader.cursor;
let result = reader.read_double()?;
if let Some(minimum) = self.minimum {
@@ -38,7 +38,7 @@ impl ArgumentType for Double {
.create_with_context(reader));
}
}
- Ok(Rc::new(result))
+ Ok(Arc::new(result))
}
}
diff --git a/azalea-brigadier/src/arguments/float_argument_type.rs b/azalea-brigadier/src/arguments/float_argument_type.rs
index 0d194efe..a2831a08 100644
--- a/azalea-brigadier/src/arguments/float_argument_type.rs
+++ b/azalea-brigadier/src/arguments/float_argument_type.rs
@@ -1,4 +1,4 @@
-use std::{any::Any, rc::Rc};
+use std::{any::Any, sync::Arc};
use crate::{
context::CommandContext,
@@ -15,7 +15,7 @@ struct Float {
}
impl ArgumentType for Float {
- fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> {
+ fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
let start = reader.cursor;
let result = reader.read_float()?;
if let Some(minimum) = self.minimum {
@@ -38,7 +38,7 @@ impl ArgumentType for Float {
.create_with_context(reader));
}
}
- Ok(Rc::new(result))
+ Ok(Arc::new(result))
}
}
diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs
index 336046a7..a31a6e70 100644
--- a/azalea-brigadier/src/arguments/integer_argument_type.rs
+++ b/azalea-brigadier/src/arguments/integer_argument_type.rs
@@ -1,4 +1,4 @@
-use std::{any::Any, rc::Rc};
+use std::{any::Any, sync::Arc};
use crate::{
context::CommandContext,
@@ -15,7 +15,7 @@ struct Integer {
}
impl ArgumentType for Integer {
- fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> {
+ fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
let start = reader.cursor;
let result = reader.read_int()?;
if let Some(minimum) = self.minimum {
@@ -38,7 +38,7 @@ impl ArgumentType for Integer {
.create_with_context(reader));
}
}
- Ok(Rc::new(result))
+ Ok(Arc::new(result))
}
}
diff --git a/azalea-brigadier/src/arguments/long_argument_type.rs b/azalea-brigadier/src/arguments/long_argument_type.rs
index 9b3469b6..d557881a 100644
--- a/azalea-brigadier/src/arguments/long_argument_type.rs
+++ b/azalea-brigadier/src/arguments/long_argument_type.rs
@@ -1,4 +1,4 @@
-use std::{any::Any, rc::Rc};
+use std::{any::Any, sync::Arc};
use crate::{
context::CommandContext,
@@ -15,7 +15,7 @@ struct Long {
}
impl ArgumentType for Long {
- fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> {
+ fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
let start = reader.cursor;
let result = reader.read_long()?;
if let Some(minimum) = self.minimum {
@@ -38,7 +38,7 @@ impl ArgumentType for Long {
.create_with_context(reader));
}
}
- Ok(Rc::new(result))
+ Ok(Arc::new(result))
}
}
diff --git a/azalea-brigadier/src/arguments/string_argument_type.rs b/azalea-brigadier/src/arguments/string_argument_type.rs
index 27363bd4..9fd70d13 100644
--- a/azalea-brigadier/src/arguments/string_argument_type.rs
+++ b/azalea-brigadier/src/arguments/string_argument_type.rs
@@ -1,4 +1,4 @@
-use std::{any::Any, rc::Rc};
+use std::{any::Any, sync::Arc};
use crate::{
context::CommandContext, exceptions::CommandSyntaxException, string_reader::StringReader,
@@ -17,7 +17,7 @@ pub enum StringArgument {
}
impl ArgumentType for StringArgument {
- fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> {
+ fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
let result = match self {
StringArgument::SingleWord => reader.read_unquoted_string().to_string(),
StringArgument::QuotablePhrase => reader.read_string()?,
@@ -27,7 +27,7 @@ impl ArgumentType for StringArgument {
text
}
};
- Ok(Rc::new(result))
+ Ok(Arc::new(result))
}
}
diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs
index 0363d204..60fa713f 100755
--- a/azalea-brigadier/src/builder/required_argument_builder.rs
+++ b/azalea-brigadier/src/builder/required_argument_builder.rs
@@ -2,7 +2,7 @@ use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType};
use crate::{
arguments::ArgumentType, exceptions::CommandSyntaxException, string_reader::StringReader,
};
-use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc};
+use std::{any::Any, fmt::Debug, sync::Arc};
/// An argument node type. The `T` type parameter is the type of the argument,
/// which can be anything.
@@ -19,7 +19,7 @@ impl Argument {
}
}
- pub fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> {
+ pub fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
self.parser.parse(reader)
}
}
diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs
index a8efeeeb..f78fe758 100755
--- a/azalea-brigadier/src/context/command_context.rs
+++ b/azalea-brigadier/src/context/command_context.rs
@@ -78,7 +78,7 @@ impl<S> CommandContext<S> {
!self.nodes.is_empty()
}
- pub fn argument(&self, name: &str) -> Option<Rc<dyn Any>> {
+ pub fn argument(&self, name: &str) -> Option<Arc<dyn Any>> {
let argument = self.arguments.get(name);
argument.map(|a| a.result.clone())
}
diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs
index 3302b1be..428851a8 100755
--- a/azalea-brigadier/src/context/parsed_argument.rs
+++ b/azalea-brigadier/src/context/parsed_argument.rs
@@ -1,8 +1,8 @@
use super::string_range::StringRange;
-use std::{any::Any, rc::Rc};
+use std::{any::Any, sync::Arc};
#[derive(Clone)]
pub struct ParsedArgument {
pub range: StringRange,
- pub result: Rc<dyn Any>,
+ pub result: Arc<dyn Any>,
}
diff --git a/azalea-client/src/inventory.rs b/azalea-client/src/inventory.rs
index 2e8478d4..0e4b9da3 100644
--- a/azalea-client/src/inventory.rs
+++ b/azalea-client/src/inventory.rs
@@ -69,7 +69,7 @@ impl Client {
}
/// A component present on all local players that have an inventory.
-#[derive(Component, Debug)]
+#[derive(Component, Debug, Clone)]
pub struct InventoryComponent {
/// A component that contains the player's inventory menu. This is
/// guaranteed to be a `Menu::Player`.
diff --git a/azalea-core/src/bitset.rs b/azalea-core/src/bitset.rs
index 912216a8..52a34154 100755
--- a/azalea-core/src/bitset.rs
+++ b/azalea-core/src/bitset.rs
@@ -125,6 +125,11 @@ impl From<Vec<u8>> for BitSet {
}
/// A list of bits with a known fixed size.
+///
+/// Note that this is primarily meant for fast serialization and deserialization
+/// Minecraft, if you don't need that you should use the `fixedbitset` crate
+/// since it's approximately 20% faster (since it stores the data as usizes
+/// instead of u8s)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FixedBitSet<const N: usize>
where
diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs
index 45213fc1..9b673945 100644
--- a/azalea-entity/src/lib.rs
+++ b/azalea-entity/src/lib.rs
@@ -366,6 +366,12 @@ pub struct LocalEntity;
#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
pub struct FluidOnEyes(azalea_registry::Fluid);
+impl FluidOnEyes {
+ pub fn new(fluid: azalea_registry::Fluid) -> Self {
+ Self(fluid)
+ }
+}
+
// #[cfg(test)]
// mod tests {
// use super::*;
diff --git a/azalea-entity/src/mining.rs b/azalea-entity/src/mining.rs
index a2f869cf..ee5c961b 100644
--- a/azalea-entity/src/mining.rs
+++ b/azalea-entity/src/mining.rs
@@ -4,6 +4,14 @@ use azalea_registry as registry;
use crate::{effects, enchantments, FluidOnEyes, Physics};
+/// How much progress is made towards mining the block per tick, as a
+/// percentage. If this is 1 then the block gets broken instantly.
+///
+/// You can divide 1 by this and then round up to get the number of ticks it
+/// takes to mine the block.
+///
+/// The player inventory is needed to check your armor and offhand for modifiers
+/// to your mining speed.
pub fn get_mine_progress(
block: &dyn Block,
held_item: registry::Item,
@@ -11,16 +19,6 @@ pub fn get_mine_progress(
fluid_on_eyes: &FluidOnEyes,
physics: &Physics,
) -> f32 {
- // public float getDestroyProgress(BlockState blockState, Player player,
- // BlockGetter world, BlockPos blockPos) { float destroySpeed =
- // blockState.getDestroySpeed(world, blockPos); if (destroySpeed ==
- // -1.0F) { return 0.0F;
- // } else {
- // int divider = player.hasCorrectToolForDrops(blockState) ? 30 : 100;
- // return player.getDestroySpeed(blockState) / destroySpeed /
- // (float)divider; }
- // }
-
let block_behavior: BlockBehavior = block.behavior();
let destroy_time = block_behavior.destroy_time;
diff --git a/azalea/benches/pathfinder.rs b/azalea/benches/pathfinder.rs
index 3fb1c7b2..406b726d 100644
--- a/azalea/benches/pathfinder.rs
+++ b/azalea/benches/pathfinder.rs
@@ -4,12 +4,14 @@ use azalea::{
pathfinder::{
astar::{self, a_star},
goals::BlockPosGoal,
+ mining::MiningCache,
world::CachedWorld,
Goal,
},
BlockPos,
};
use azalea_core::position::{ChunkBlockPos, ChunkPos};
+use azalea_inventory::Menu;
use azalea_world::{Chunk, ChunkStorage, PartialChunkStorage};
use criterion::{criterion_group, criterion_main, Criterion};
use parking_lot::RwLock;
@@ -80,10 +82,16 @@ fn bench_pathfinder(c: &mut Criterion) {
b.iter(|| {
let (world, start, end) = generate_bedrock_world(&mut partial_chunks, 4);
let cached_world = CachedWorld::new(Arc::new(RwLock::new(world.into())));
+ let mining_cache = MiningCache::new(Menu::Player(azalea_inventory::Player::default()));
let goal = BlockPosGoal(end);
let successors = |pos: BlockPos| {
- azalea::pathfinder::call_successors_fn(&cached_world, successors_fn, pos)
+ azalea::pathfinder::call_successors_fn(
+ &cached_world,
+ &mining_cache,
+ successors_fn,
+ pos,
+ )
};
let astar::Path { movements, partial } = a_star(
diff --git a/azalea/src/auto_tool.rs b/azalea/src/auto_tool.rs
new file mode 100644
index 00000000..718ed6a7
--- /dev/null
+++ b/azalea/src/auto_tool.rs
@@ -0,0 +1,84 @@
+use azalea_block::{Block, BlockState};
+use azalea_client::{inventory::InventoryComponent, Client};
+use azalea_entity::{FluidOnEyes, Physics};
+use azalea_inventory::{ItemSlot, Menu};
+use azalea_registry::Fluid;
+
+pub struct BestToolResult {
+ pub index: usize,
+ pub percentage_per_tick: f32,
+}
+
+pub trait AutoToolClientExt {
+ fn best_tool_in_hotbar_for_block(&self, block: BlockState) -> BestToolResult;
+}
+
+impl AutoToolClientExt for Client {
+ fn best_tool_in_hotbar_for_block(&self, block: BlockState) -> BestToolResult {
+ let mut ecs = self.ecs.lock();
+ let (inventory, physics, fluid_on_eyes) =
+ self.query::<(&InventoryComponent, &Physics, &FluidOnEyes)>(&mut ecs);
+ let menu = &inventory.inventory_menu;
+
+ accurate_best_tool_in_hotbar_for_block(block, menu, physics, fluid_on_eyes)
+ }
+}
+
+/// Returns the best tool in the hotbar for the given block.
+///
+/// Note that this doesn't take into account whether the player is on the ground
+/// or in water, use [`accurate_best_tool_in_hotbar_for_block`] instead if you
+/// care about those things.
+pub fn best_tool_in_hotbar_for_block(block: BlockState, menu: &Menu) -> BestToolResult {
+ accurate_best_tool_in_hotbar_for_block(
+ block,
+ menu,
+ &Physics {
+ on_ground: true,
+ delta: Default::default(),
+ xxa: Default::default(),
+ yya: Default::default(),
+ zza: Default::default(),
+ last_on_ground: Default::default(),
+ dimensions: Default::default(),
+ bounding_box: Default::default(),
+ has_impulse: Default::default(),
+ },
+ &FluidOnEyes::new(Fluid::Empty),
+ )
+}
+
+pub fn accurate_best_tool_in_hotbar_for_block(
+ block: BlockState,
+ menu: &Menu,
+ physics: &Physics,
+ fluid_on_eyes: &FluidOnEyes,
+) -> BestToolResult {
+ let hotbar_slots = &menu.slots()[menu.hotbar_slots_range()];
+
+ let mut best_speed = 0.;
+ let mut best_slot = None;
+
+ let block = Box::<dyn Block>::from(block);
+
+ for (i, item_slot) in hotbar_slots.iter().enumerate() {
+ if let ItemSlot::Present(item_slot) = item_slot {
+ let this_item_speed = azalea_entity::mining::get_mine_progress(
+ block.as_ref(),
+ item_slot.kind,
+ &menu,
+ fluid_on_eyes,
+ physics,
+ );
+ if this_item_speed > best_speed {
+ best_slot = Some(i);
+ best_speed = this_item_speed;
+ }
+ }
+ }
+
+ BestToolResult {
+ index: best_slot.unwrap_or(0),
+ percentage_per_tick: best_speed,
+ }
+}
diff --git a/azalea/src/bot.rs b/azalea/src/bot.rs
index 352eda59..768ae767 100644
--- a/azalea/src/bot.rs
+++ b/azalea/src/bot.rs
@@ -44,7 +44,8 @@ impl Plugin for BotPlugin {
}
}
-/// Component for all bots.
+/// A component that clients with [`BotPlugin`] will have. If you just want to
+/// check if an entity is one of our bots, you should use [`LocalEntity`].
#[derive(Default, Component)]
pub struct Bot {
jumping_once: bool,
diff --git a/azalea/src/container.rs b/azalea/src/container.rs
index ef6fdcf6..34f86715 100644
--- a/azalea/src/container.rs
+++ b/azalea/src/container.rs
@@ -79,6 +79,10 @@ impl ContainerClientExt for Client {
/// Note that this will send a packet to the server once it's dropped. Also,
/// due to how it's implemented, you could call this function multiple times
/// while another inventory handle already exists (but you shouldn't).
+ ///
+ /// If you just want to get the items in the player's inventory without
+ /// sending any packets, use [`Client::menu`], [`Menu::player_slots_range`],
+ /// and [`Menu::slots`].
fn open_inventory(&mut self) -> Option<ContainerHandle> {
let ecs = self.ecs.lock();
let inventory = ecs
diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs
index 3b9d3e6f..c788434b 100644
--- a/azalea/src/lib.rs
+++ b/azalea/src/lib.rs
@@ -8,6 +8,7 @@
pub mod accept_resource_packs;
mod auto_respawn;
+pub mod auto_tool;
mod bot;
pub mod container;
pub mod nearest_entity;
diff --git a/azalea/src/pathfinder/mining.rs b/azalea/src/pathfinder/mining.rs
new file mode 100644
index 00000000..d5977973
--- /dev/null
+++ b/azalea/src/pathfinder/mining.rs
@@ -0,0 +1,30 @@
+use azalea_block::BlockState;
+use azalea_inventory::Menu;
+use nohash_hasher::IntMap;
+
+use crate::auto_tool::best_tool_in_hotbar_for_block;
+
+pub struct MiningCache {
+ block_state_id_costs: IntMap<u32, f32>,
+ inventory_menu: Menu,
+}
+
+impl MiningCache {
+ pub fn new(inventory_menu: Menu) -> Self {
+ Self {
+ block_state_id_costs: IntMap::default(),
+ inventory_menu,
+ }
+ }
+
+ pub fn cost_for(&mut self, block: BlockState) -> f32 {
+ if let Some(cost) = self.block_state_id_costs.get(&block.id) {
+ *cost
+ } else {
+ let best_tool_result = best_tool_in_hotbar_for_block(block, &self.inventory_menu);
+ let cost = 1. / best_tool_result.percentage_per_tick;
+ self.block_state_id_costs.insert(block.id, cost);
+ cost
+ }
+ }
+}
diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs
index e92457b8..73ce2967 100644
--- a/azalea/src/pathfinder/mod.rs
+++ b/azalea/src/pathfinder/mod.rs
@@ -4,6 +4,7 @@
pub mod astar;
pub mod costs;
pub mod goals;
+pub mod mining;
pub mod moves;
pub mod simulation;
pub mod world;
@@ -23,6 +24,7 @@ use crate::ecs::{
use crate::pathfinder::moves::PathfinderCtx;
use crate::pathfinder::world::CachedWorld;
use azalea_client::chat::SendChatEvent;
+use azalea_client::inventory::{InventoryComponent, InventorySet};
use azalea_client::movement::walk_listener;
use azalea_client::{StartSprintEvent, StartWalkEvent};
use azalea_core::position::{BlockPos, Vec3};
@@ -45,6 +47,7 @@ use std::sync::atomic::{self, AtomicUsize};
use std::sync::Arc;
use std::time::{Duration, Instant};
+use self::mining::MiningCache;
use self::moves::{ExecuteCtx, IsReachedCtx, SuccessorsFn};
#[derive(Clone, Default)]
@@ -82,7 +85,8 @@ impl Plugin for PathfinderPlugin {
handle_stop_pathfinding_event,
)
.chain()
- .before(walk_listener),
+ .before(walk_listener)
+ .before(InventorySet),
);
}
}
@@ -116,7 +120,7 @@ pub struct GotoEvent {
/// `pathfinder::moves::default_move`
pub successors_fn: SuccessorsFn,
}
-#[derive(Event)]
+#[derive(Event, Clone)]
pub struct PathFoundEvent {
pub entity: Entity,
pub start: BlockPos,
@@ -175,13 +179,14 @@ fn goto_listener(
Option<&ExecutingPath>,
&Position,
&InstanceName,
+ &InventoryComponent,
)>,
instance_container: Res<InstanceContainer>,
) {
let thread_pool = AsyncComputeTaskPool::get();
for event in events.iter() {
- let (mut pathfinder, executing_path, position, instance_name) = query
+ let (mut pathfinder, executing_path, position, instance_name, inventory) = query
.get_mut(event.entity)
.expect("Called goto on an entity that's not in the world");
@@ -217,12 +222,15 @@ fn goto_listener(
let goto_id_atomic = pathfinder.goto_id.clone();
let goto_id = goto_id_atomic.fetch_add(1, atomic::Ordering::Relaxed) + 1;
+ let mining_cache = MiningCache::new(inventory.inventory_menu.clone());
let task = thread_pool.spawn(async move {
debug!("start: {start:?}");
let cached_world = CachedWorld::new(world_lock);
- let successors = |pos: BlockPos| call_successors_fn(&cached_world, successors_fn, pos);
+ let successors = |pos: BlockPos| {
+ call_successors_fn(&cached_world, &mining_cache, successors_fn, pos)
+ };
let mut attempt_number = 0;
@@ -311,12 +319,17 @@ fn handle_tasks(
// set the path for the target entity when we get the PathFoundEvent
fn path_found_listener(
mut events: EventReader<PathFoundEvent>,
- mut query: Query<(&mut Pathfinder, Option<&mut ExecutingPath>, &InstanceName)>,
+ mut query: Query<(
+ &mut Pathfinder,
+ Option<&mut ExecutingPath>,
+ &InstanceName,
+ &InventoryComponent,
+ )>,
instance_container: Res<InstanceContainer>,
mut commands: Commands,
) {
for event in events.iter() {
- let (mut pathfinder, executing_path, instance_name) = query
+ let (mut pathfinder, executing_path, instance_name, inventory) = query
.get_mut(event.entity)
.expect("Path found for an entity that doesn't have a pathfinder");
if let Some(path) = &event.path {
@@ -331,8 +344,10 @@ fn path_found_listener(
.expect("Entity tried to pathfind but the entity isn't in a valid world");
let successors_fn: moves::SuccessorsFn = event.successors_fn;
let cached_world = CachedWorld::new(world_lock);
- let successors =
- |pos: BlockPos| call_successors_fn(&cached_world, successors_fn, pos);
+ let mining_cache = MiningCache::new(inventory.inventory_menu.clone());
+ let successors = |pos: BlockPos| {
+ call_successors_fn(&cached_world, &mining_cache, successors_fn, pos)
+ };
if let Some(first_node_of_new_path) = path.front() {
if successors(last_node_of_current_path.target)
@@ -503,10 +518,15 @@ fn check_node_reached(
}
fn check_for_path_obstruction(
- mut query: Query<(&Pathfinder, &mut ExecutingPath, &InstanceName)>,
+ mut query: Query<(
+ &Pathfinder,
+ &mut ExecutingPath,
+ &InstanceName,
+ &InventoryComponent,
+ )>,
instance_container: Res<InstanceContainer>,
) {
- for (pathfinder, mut executing_path, instance_name) in &mut query {
+ for (pathfinder, mut executing_path, instance_name, inventory) in &mut query {
let Some(successors_fn) = pathfinder.successors_fn else {
continue;
};
@@ -517,7 +537,9 @@ fn check_for_path_obstruction(
// obstruction check (the path we're executing isn't possible anymore)
let cached_world = CachedWorld::new(world_lock);
- let successors = |pos: BlockPos| call_successors_fn(&cached_world, successors_fn, pos);
+ let mining_cache = MiningCache::new(inventory.inventory_menu.clone());
+ let successors =
+ |pos: BlockPos| call_successors_fn(&cached_world, &mining_cache, successors_fn, pos);
if let Some(obstructed_index) = check_path_obstructed(
executing_path.last_reached_node,
@@ -694,6 +716,11 @@ fn stop_pathfinding_on_instance_change(
/// permissions, and it'll make them spam *a lot* of commands.
///
/// ```
+/// # use azalea::prelude::*;
+/// # use azalea::pathfinder::PathfinderDebugParticles;
+/// # #[derive(Component, Clone, Default)]
+/// # pub struct State;
+///
/// async fn handle(mut bot: Client, event: azalea::Event, state: State) -> anyhow::Result<()> {
/// match event {
/// azalea::Event::Init => {
@@ -704,6 +731,7 @@ fn stop_pathfinding_on_instance_change(
/// }
/// _ => {}
/// }
+/// Ok(())
/// }
/// ```
#[derive(Component)]
@@ -809,6 +837,7 @@ where
pub fn call_successors_fn(
cached_world: &CachedWorld,
+ mining_cache: &MiningCache,
successors_fn: SuccessorsFn,
pos: BlockPos,
) -> Vec<astar::Edge<BlockPos, moves::MoveData>> {
@@ -816,6 +845,7 @@ pub fn call_successors_fn(
let mut ctx = PathfinderCtx {
edges: &mut edges,
world: cached_world,
+ mining_cache,
};
successors_fn(&mut ctx, pos);
edges
diff --git a/azalea/src/pathfinder/moves/mod.rs b/azalea/src/pathfinder/moves/mod.rs
index bf1fc5f4..e5b837ea 100644
--- a/azalea/src/pathfinder/moves/mod.rs
+++ b/azalea/src/pathfinder/moves/mod.rs
@@ -5,7 +5,7 @@ use std::fmt::Debug;
use crate::{JumpEvent, LookAtEvent};
-use super::{astar, world::CachedWorld};
+use super::{astar, mining::MiningCache, world::CachedWorld};
use azalea_client::{SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection};
use azalea_core::position::{BlockPos, Vec3};
use bevy_ecs::{entity::Entity, event::EventWriter};
@@ -107,4 +107,5 @@ pub fn default_is_reached(
pub struct PathfinderCtx<'a> {
pub edges: &'a mut Vec<Edge>,
pub world: &'a CachedWorld,
+ pub mining_cache: &'a MiningCache,
}
diff --git a/azalea/src/pathfinder/simulation.rs b/azalea/src/pathfinder/simulation.rs
index 2b1bfd42..cc077985 100644
--- a/azalea/src/pathfinder/simulation.rs
+++ b/azalea/src/pathfinder/simulation.rs
@@ -2,7 +2,7 @@
use std::{sync::Arc, time::Duration};
-use azalea_client::PhysicsState;
+use azalea_client::{inventory::InventoryComponent, PhysicsState};
use azalea_core::{position::Vec3, resource_location::ResourceLocation};
use azalea_entity::{
attributes::AttributeInstance, metadata::Sprinting, Attributes, EntityDimensions, Physics,
@@ -20,6 +20,7 @@ pub struct SimulatedPlayerBundle {
pub physics: Physics,
pub physics_state: PhysicsState,
pub attributes: Attributes,
+ pub inventory: InventoryComponent,
}
impl SimulatedPlayerBundle {
@@ -37,6 +38,7 @@ impl SimulatedPlayerBundle {
speed: AttributeInstance::new(0.1),
attack_speed: AttributeInstance::new(4.0),
},
+ inventory: InventoryComponent::default(),
}
}
}