aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--azalea-client/src/attack.rs138
-rw-r--r--azalea-client/src/client.rs4
-rw-r--r--azalea-client/src/interact.rs83
-rw-r--r--azalea-client/src/lib.rs1
-rw-r--r--azalea-client/src/mining.rs10
-rw-r--r--azalea-client/src/packet_handling.rs10
-rw-r--r--azalea-entity/src/attributes.rs19
-rw-r--r--azalea-entity/src/lib.rs1
-rw-r--r--azalea/examples/testbot.rs52
-rw-r--r--azalea/src/auto_respawn.rs1
-rw-r--r--azalea/src/bot.rs50
-rw-r--r--azalea/src/container.rs10
-rw-r--r--azalea/src/lib.rs1
-rw-r--r--azalea/src/mining.rs40
-rw-r--r--azalea/src/prelude.rs4
15 files changed, 359 insertions, 65 deletions
diff --git a/azalea-client/src/attack.rs b/azalea-client/src/attack.rs
new file mode 100644
index 00000000..34083ffc
--- /dev/null
+++ b/azalea-client/src/attack.rs
@@ -0,0 +1,138 @@
+use azalea_core::GameMode;
+use azalea_entity::{
+ metadata::{ShiftKeyDown, Sprinting},
+ Attributes, Physics,
+};
+use azalea_protocol::packets::game::serverbound_interact_packet::{
+ self, ServerboundInteractPacket,
+};
+use azalea_world::MinecraftEntityId;
+use bevy_app::{App, FixedUpdate, Plugin, Update};
+use bevy_ecs::prelude::*;
+use derive_more::{Deref, DerefMut};
+
+use crate::{
+ interact::SwingArmEvent,
+ local_player::{LocalGameMode, SendPacketEvent},
+ Client,
+};
+
+pub struct AttackPlugin;
+impl Plugin for AttackPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_event::<AttackEvent>()
+ .add_systems(Update, handle_attack_event)
+ .add_systems(
+ FixedUpdate,
+ (
+ increment_ticks_since_last_attack,
+ update_attack_strength_scale,
+ )
+ .chain(),
+ );
+ }
+}
+
+impl Client {
+ /// Attack the entity with the given id.
+ pub fn attack(&mut self, entity_id: MinecraftEntityId) {
+ self.ecs.lock().send_event(AttackEvent {
+ entity: self.entity,
+ target: entity_id,
+ });
+ }
+
+ /// Whether the player has an attack cooldown.
+ pub fn has_attack_cooldown(&self) -> bool {
+ let ticks_since_last_attack = *self.component::<AttackStrengthScale>();
+ ticks_since_last_attack < 1.0
+ }
+}
+
+#[derive(Event)]
+pub struct AttackEvent {
+ pub entity: Entity,
+ pub target: MinecraftEntityId,
+}
+pub fn handle_attack_event(
+ mut events: EventReader<AttackEvent>,
+ mut query: Query<(
+ &LocalGameMode,
+ &mut TicksSinceLastAttack,
+ &mut Physics,
+ &mut Sprinting,
+ &mut ShiftKeyDown,
+ )>,
+ mut send_packet_events: EventWriter<SendPacketEvent>,
+ mut swing_arm_event: EventWriter<SwingArmEvent>,
+) {
+ for event in events.iter() {
+ let (game_mode, mut ticks_since_last_attack, mut physics, mut sprinting, sneaking) =
+ query.get_mut(event.entity).unwrap();
+
+ swing_arm_event.send(SwingArmEvent {
+ entity: event.entity,
+ });
+ send_packet_events.send(SendPacketEvent {
+ entity: event.entity,
+ packet: ServerboundInteractPacket {
+ entity_id: *event.target,
+ action: serverbound_interact_packet::ActionType::Attack,
+ using_secondary_action: **sneaking,
+ }
+ .get(),
+ });
+
+ // we can't attack if we're in spectator mode but it still sends the attack
+ // packet
+ if game_mode.current == GameMode::Spectator {
+ continue;
+ };
+
+ ticks_since_last_attack.0 = 0;
+
+ physics.delta = physics.delta.multiply(0.6, 1.0, 0.6);
+ **sprinting = false;
+ }
+}
+
+#[derive(Default, Bundle)]
+pub struct AttackBundle {
+ pub ticks_since_last_attack: TicksSinceLastAttack,
+ pub attack_strength_scale: AttackStrengthScale,
+}
+
+#[derive(Default, Component, Clone, Deref, DerefMut)]
+pub struct TicksSinceLastAttack(pub u32);
+pub fn increment_ticks_since_last_attack(mut query: Query<&mut TicksSinceLastAttack>) {
+ for mut ticks_since_last_attack in query.iter_mut() {
+ **ticks_since_last_attack += 1;
+ }
+}
+
+#[derive(Default, Component, Clone, Deref, DerefMut)]
+pub struct AttackStrengthScale(pub f32);
+pub fn update_attack_strength_scale(
+ mut query: Query<(&TicksSinceLastAttack, &Attributes, &mut AttackStrengthScale)>,
+) {
+ for (ticks_since_last_attack, attributes, mut attack_strength_scale) in query.iter_mut() {
+ // look 0.5 ticks into the future because that's what vanilla does
+ **attack_strength_scale =
+ get_attack_strength_scale(ticks_since_last_attack.0, attributes, 0.5);
+ }
+}
+
+/// Returns how long it takes for the attack cooldown to reset (in ticks).
+pub fn get_attack_strength_delay(attributes: &Attributes) -> f32 {
+ ((1. / attributes.attack_speed.calculate()) * 20.) as f32
+}
+
+pub fn get_attack_strength_scale(
+ ticks_since_last_attack: u32,
+ attributes: &Attributes,
+ in_ticks: f32,
+) -> f32 {
+ let attack_strength_delay = get_attack_strength_delay(attributes);
+ let attack_strength = (ticks_since_last_attack as f32 + in_ticks) / attack_strength_delay;
+ attack_strength.clamp(0., 1.)
+}
diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs
index 301d9197..f7fcb16c 100644
--- a/azalea-client/src/client.rs
+++ b/azalea-client/src/client.rs
@@ -1,4 +1,5 @@
use crate::{
+ attack::{self, AttackPlugin},
chat::ChatPlugin,
disconnect::{DisconnectEvent, DisconnectPlugin},
events::{Event, EventPlugin, LocalPlayerEvents},
@@ -306,6 +307,7 @@ impl Client {
abilities: PlayerAbilities::default(),
permission_level: PermissionLevel::default(),
mining: mining::MineBundle::default(),
+ attack: attack::AttackBundle::default(),
_local: Local,
});
@@ -574,6 +576,7 @@ pub struct JoinedClientBundle {
pub permission_level: PermissionLevel,
pub mining: mining::MineBundle,
+ pub attack: attack::AttackBundle,
pub _local: Local,
}
@@ -722,6 +725,7 @@ impl PluginGroup for DefaultPlugins {
.add(InteractPlugin)
.add(RespawnPlugin)
.add(MinePlugin)
+ .add(AttackPlugin)
.add(TickBroadcastPlugin);
#[cfg(feature = "log")]
{
diff --git a/azalea-client/src/interact.rs b/azalea-client/src/interact.rs
index dc0213b0..3c9428ff 100644
--- a/azalea-client/src/interact.rs
+++ b/azalea-client/src/interact.rs
@@ -2,7 +2,9 @@ use std::ops::AddAssign;
use azalea_block::BlockState;
use azalea_core::{BlockHitResult, BlockPos, Direction, GameMode, Vec3};
-use azalea_entity::{clamp_look_direction, view_vector, EyeHeight, LookDirection, Position};
+use azalea_entity::{
+ clamp_look_direction, view_vector, Attributes, EyeHeight, Local, LookDirection, Position,
+};
use azalea_inventory::{ItemSlot, ItemSlotData};
use azalea_nbt::NbtList;
use azalea_physics::clip::{BlockShapeType, ClipContext, FluidPickType};
@@ -17,6 +19,7 @@ use bevy_ecs::{
component::Component,
entity::Entity,
event::{Event, EventReader, EventWriter},
+ query::{Changed, With},
schedule::IntoSystemConfigs,
system::{Commands, Query, Res},
};
@@ -39,12 +42,15 @@ impl Plugin for InteractPlugin {
.add_systems(
Update,
(
- update_hit_result_component.after(clamp_look_direction),
- handle_block_interact_event,
- handle_swing_arm_event,
- )
- .before(handle_send_packet_event)
- .chain(),
+ (
+ update_hit_result_component.after(clamp_look_direction),
+ handle_block_interact_event,
+ handle_swing_arm_event,
+ )
+ .before(handle_send_packet_event)
+ .chain(),
+ update_modifiers_for_held_item,
+ ),
);
}
}
@@ -305,3 +311,66 @@ fn handle_swing_arm_event(
});
}
}
+
+#[allow(clippy::type_complexity)]
+fn update_modifiers_for_held_item(
+ mut query: Query<
+ (&mut Attributes, &InventoryComponent),
+ (With<Local>, Changed<InventoryComponent>),
+ >,
+) {
+ for (mut attributes, inventory) in &mut query {
+ let held_item = inventory.held_item();
+
+ use azalea_registry::Item;
+ let added_attack_speed = match held_item.kind() {
+ Item::WoodenSword => -2.4,
+ Item::WoodenShovel => -3.0,
+ Item::WoodenPickaxe => -2.8,
+ Item::WoodenAxe => -3.2,
+ Item::WoodenHoe => -3.0,
+
+ Item::StoneSword => -2.4,
+ Item::StoneShovel => -3.0,
+ Item::StonePickaxe => -2.8,
+ Item::StoneAxe => -3.2,
+ Item::StoneHoe => -2.0,
+
+ Item::GoldenSword => -2.4,
+ Item::GoldenShovel => -3.0,
+ Item::GoldenPickaxe => -2.8,
+ Item::GoldenAxe => -3.0,
+ Item::GoldenHoe => -3.0,
+
+ Item::IronSword => -2.4,
+ Item::IronShovel => -3.0,
+ Item::IronPickaxe => -2.8,
+ Item::IronAxe => -3.1,
+ Item::IronHoe => -1.0,
+
+ Item::DiamondSword => -2.4,
+ Item::DiamondShovel => -3.0,
+ Item::DiamondPickaxe => -2.8,
+ Item::DiamondAxe => -3.0,
+ Item::DiamondHoe => 0.0,
+
+ Item::NetheriteSword => -2.4,
+ Item::NetheriteShovel => -3.0,
+ Item::NetheritePickaxe => -2.8,
+ Item::NetheriteAxe => -3.0,
+ Item::NetheriteHoe => 0.0,
+
+ Item::Trident => -2.9,
+ _ => 0.,
+ };
+ attributes
+ .attack_speed
+ .remove(&azalea_entity::attributes::BASE_ATTACK_SPEED_UUID);
+ attributes
+ .attack_speed
+ .insert(azalea_entity::attributes::tool_attack_speed_modifier(
+ added_attack_speed,
+ ))
+ .unwrap();
+ }
+}
diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs
index e36cb846..e8698fc7 100644
--- a/azalea-client/src/lib.rs
+++ b/azalea-client/src/lib.rs
@@ -12,6 +12,7 @@
#![feature(type_alias_impl_trait)]
mod account;
+pub mod attack;
pub mod chat;
mod client;
pub mod disconnect;
diff --git a/azalea-client/src/mining.rs b/azalea-client/src/mining.rs
index 049bc859..c087c467 100644
--- a/azalea-client/src/mining.rs
+++ b/azalea-client/src/mining.rs
@@ -18,6 +18,7 @@ use crate::{
},
inventory::InventoryComponent,
local_player::{LocalGameMode, SendPacketEvent},
+ Client,
};
/// A plugin that allows clients to break blocks in the world.
@@ -44,6 +45,15 @@ impl Plugin for MinePlugin {
}
}
+impl Client {
+ pub fn start_mining(&mut self, position: BlockPos) {
+ self.ecs.lock().send_event(StartMiningBlockEvent {
+ entity: self.entity,
+ position,
+ });
+ }
+}
+
/// Information about the block we're currently mining. This is only present if
/// we're currently mining a block.
#[derive(Component)]
diff --git a/azalea-client/src/packet_handling.rs b/azalea-client/src/packet_handling.rs
index 4ceb3999..b4f4e045 100644
--- a/azalea-client/src/packet_handling.rs
+++ b/azalea-client/src/packet_handling.rs
@@ -574,13 +574,14 @@ fn process_packet_events(ecs: &mut World) {
ClientboundGamePacket::AddEntity(p) => {
debug!("Got add entity packet {:?}", p);
+ #[allow(clippy::type_complexity)]
let mut system_state: SystemState<(
Commands,
Query<Option<&InstanceName>>,
- ResMut<InstanceContainer>,
+ Res<InstanceContainer>,
ResMut<EntityInfos>,
)> = SystemState::new(ecs);
- let (mut commands, mut query, mut instance_container, mut entity_infos) =
+ let (mut commands, mut query, instance_container, mut entity_infos) =
system_state.get_mut(ecs);
let instance_name = query.get_mut(player_entity).unwrap();
@@ -694,9 +695,8 @@ fn process_packet_events(ecs: &mut World) {
ClientboundGamePacket::SetHealth(p) => {
debug!("Got set health packet {:?}", p);
- let mut system_state: SystemState<(Query<&mut Health>, EventWriter<DeathEvent>)> =
- SystemState::new(ecs);
- let (mut query, mut death_events) = system_state.get_mut(ecs);
+ let mut system_state: SystemState<Query<&mut Health>> = SystemState::new(ecs);
+ let mut query = system_state.get_mut(ecs);
let mut health = query.get_mut(player_entity).unwrap();
**health = p.health;
diff --git a/azalea-entity/src/attributes.rs b/azalea-entity/src/attributes.rs
index 97b890dc..18bbc348 100644
--- a/azalea-entity/src/attributes.rs
+++ b/azalea-entity/src/attributes.rs
@@ -13,6 +13,7 @@ use uuid::{uuid, Uuid};
#[derive(Clone, Debug, Component)]
pub struct Attributes {
pub speed: AttributeInstance,
+ pub attack_speed: AttributeInstance,
}
#[derive(Clone, Debug)]
@@ -92,6 +93,24 @@ pub fn sprinting_modifier() -> AttributeModifier {
}
}
+pub static BASE_ATTACK_SPEED_UUID: Uuid = uuid!("FA233E1C-4180-4865-B01B-BCCE9785ACA3");
+pub fn weapon_attack_speed_modifier(amount: f64) -> AttributeModifier {
+ AttributeModifier {
+ uuid: BASE_ATTACK_SPEED_UUID,
+ name: "Weapon modifier".to_string(),
+ amount,
+ operation: AttributeModifierOperation::Addition,
+ }
+}
+pub fn tool_attack_speed_modifier(amount: f64) -> AttributeModifier {
+ AttributeModifier {
+ uuid: BASE_ATTACK_SPEED_UUID,
+ name: "Tool modifier".to_string(),
+ amount,
+ operation: AttributeModifierOperation::Addition,
+ }
+}
+
impl McBufReadable for AttributeModifier {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
let uuid = Uuid::read_from(buf)?;
diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs
index 53e8bfdb..76c5220a 100644
--- a/azalea-entity/src/lib.rs
+++ b/azalea-entity/src/lib.rs
@@ -344,6 +344,7 @@ impl EntityBundle {
// TODO: do the correct defaults for everything, some
// entities have different defaults
speed: AttributeInstance::new(0.1),
+ attack_speed: AttributeInstance::new(4.0),
},
jumping: Jumping(false),
diff --git a/azalea/examples/testbot.rs b/azalea/examples/testbot.rs
index 337ac6ec..75a6ca67 100644
--- a/azalea/examples/testbot.rs
+++ b/azalea/examples/testbot.rs
@@ -11,6 +11,8 @@ use azalea::pathfinder::BlockPosGoal;
use azalea::protocol::packets::game::ClientboundGamePacket;
use azalea::{prelude::*, swarm::prelude::*, BlockPos, GameProfileComponent, WalkDirection};
use azalea::{Account, Client, Event};
+use azalea_core::Vec3;
+use azalea_world::{InstanceName, MinecraftEntityId};
use std::time::Duration;
#[derive(Default, Clone, Component)]
@@ -220,6 +222,56 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
println!("no container found");
}
}
+ "attack" => {
+ let mut nearest_entity = None;
+ let mut nearest_distance = f64::INFINITY;
+ let mut nearest_pos = Vec3::default();
+ let bot_position = bot.position();
+ let bot_entity = bot.entity;
+ let bot_instance_name = bot.component::<InstanceName>();
+ {
+ let mut ecs = bot.ecs.lock();
+ let mut query = ecs.query_filtered::<(
+ azalea::ecs::entity::Entity,
+ &MinecraftEntityId,
+ &Position,
+ &InstanceName,
+ &EyeHeight,
+ ), With<MinecraftEntityId>>(
+ );
+ for (entity, &entity_id, position, instance_name, eye_height) in
+ query.iter(&ecs)
+ {
+ if entity == bot_entity {
+ continue;
+ }
+ if instance_name != &bot_instance_name {
+ continue;
+ }
+
+ let distance = bot_position.distance_to(position);
+ if distance < 4.0 && distance < nearest_distance {
+ nearest_entity = Some(entity_id);
+ nearest_distance = distance;
+ nearest_pos = position.up(**eye_height as f64);
+ }
+ }
+ }
+ if let Some(nearest_entity) = nearest_entity {
+ bot.look_at(nearest_pos);
+ bot.attack(nearest_entity);
+ bot.chat("attacking");
+ let mut ticks = bot.get_tick_broadcaster();
+ while ticks.recv().await.is_ok() {
+ if !bot.has_attack_cooldown() {
+ break;
+ }
+ }
+ bot.chat("finished attacking");
+ } else {
+ bot.chat("no entities found");
+ }
+ }
_ => {}
}
}
diff --git a/azalea/src/auto_respawn.rs b/azalea/src/auto_respawn.rs
index 6d9c5954..77a75b4b 100644
--- a/azalea/src/auto_respawn.rs
+++ b/azalea/src/auto_respawn.rs
@@ -21,6 +21,5 @@ fn auto_respawn(
perform_respawn_events.send(PerformRespawnEvent {
entity: event.entity,
});
- println!("auto respawning");
}
}
diff --git a/azalea/src/bot.rs b/azalea/src/bot.rs
index 7940e7b0..71d96c4d 100644
--- a/azalea/src/bot.rs
+++ b/azalea/src/bot.rs
@@ -8,7 +8,10 @@ use crate::ecs::{
query::{With, Without},
system::{Commands, Query},
};
-use azalea_core::Vec3;
+use azalea_client::interact::SwingArmEvent;
+use azalea_client::mining::Mining;
+use azalea_client::TickBroadcast;
+use azalea_core::{BlockPos, Vec3};
use azalea_entity::{
clamp_look_direction, metadata::Player, EyeHeight, Jumping, Local, LookDirection, Position,
};
@@ -67,18 +70,25 @@ fn stop_jumping(mut query: Query<(&mut Jumping, &mut Bot)>) {
}
pub trait BotClientExt {
+ /// Queue a jump for the next tick.
fn jump(&mut self);
+ /// Turn the bot's head to look at the coordinate in the world.
fn look_at(&mut self, pos: Vec3);
+ /// Get a receiver that will receive a message every tick.
+ fn get_tick_broadcaster(&self) -> tokio::sync::broadcast::Receiver<()>;
+ /// Mine a block. This won't turn the bot's head towards the block, so if
+ /// that's necessary you'll have to do that yourself with [`look_at`].
+ ///
+ /// [`look_at`]: crate::prelude::BotClientExt::look_at
+ async fn mine(&mut self, position: BlockPos);
}
impl BotClientExt for azalea_client::Client {
- /// Queue a jump for the next tick.
fn jump(&mut self) {
let mut ecs = self.ecs.lock();
ecs.send_event(JumpEvent(self.entity));
}
- /// Turn the bot's head to look at the coordinate in the world.
fn look_at(&mut self, position: Vec3) {
let mut ecs = self.ecs.lock();
ecs.send_event(LookAtEvent {
@@ -86,6 +96,40 @@ impl BotClientExt for azalea_client::Client {
position,
});
}
+
+ /// ```
+ /// # use azalea::prelude::*;
+ /// # async fn example(mut bot: azalea::Client) {
+ /// let mut ticks = self.get_tick_broadcaster();
+ /// while ticks.recv().await.is_ok() {
+ /// let ecs = bot.ecs.lock();
+ /// if ecs.get::<WaitingForInventoryOpen>(self.entity).is_none() {
+ /// break;
+ /// }
+ /// }
+ /// # }
+ /// ```
+ fn get_tick_broadcaster(&self) -> tokio::sync::broadcast::Receiver<()> {
+ let ecs = self.ecs.lock();
+ let tick_broadcast = ecs.resource::<TickBroadcast>();
+ tick_broadcast.subscribe()
+ }
+
+ async fn mine(&mut self, position: BlockPos) {
+ self.start_mining(position);
+ // vanilla sends an extra swing arm packet when we start mining
+ self.ecs.lock().send_event(SwingArmEvent {
+ entity: self.entity,
+ });
+
+ let mut receiver = self.get_tick_broadcaster();
+ while receiver.recv().await.is_ok() {
+ let ecs = self.ecs.lock();
+ if ecs.get::<Mining>(self.entity).is_none() {
+ break;
+ }
+ }
+ }
}
/// Event to jump once.
diff --git a/azalea/src/container.rs b/azalea/src/container.rs
index dc0ba169..2261469d 100644
--- a/azalea/src/container.rs
+++ b/azalea/src/container.rs
@@ -3,7 +3,7 @@ use std::fmt::Formatter;
use azalea_client::{
inventory::{CloseContainerEvent, ContainerClickEvent, InventoryComponent},
packet_handling::PacketEvent,
- Client, TickBroadcast,
+ Client,
};
use azalea_core::BlockPos;
use azalea_inventory::{operations::ClickOperation, ItemSlot, Menu};
@@ -12,6 +12,8 @@ use bevy_app::{App, Plugin, Update};
use bevy_ecs::{component::Component, prelude::EventReader, system::Commands};
use std::fmt::Debug;
+use crate::bot::BotClientExt;
+
pub struct ContainerPlugin;
impl Plugin for ContainerPlugin {
fn build(&self, app: &mut App) {
@@ -49,11 +51,7 @@ impl ContainerClientExt for Client {
.insert(WaitingForInventoryOpen);
self.block_interact(pos);
- let mut receiver = {
- let ecs = self.ecs.lock();
- let tick_broadcast = ecs.resource::<TickBroadcast>();
- tick_broadcast.subscribe()
- };
+ let mut receiver = self.get_tick_broadcaster();
while receiver.recv().await.is_ok() {
let ecs = self.ecs.lock();
if ecs.get::<WaitingForInventoryOpen>(self.entity).is_none() {
diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs
index 297199a0..0aff2a56 100644
--- a/azalea/src/lib.rs
+++ b/azalea/src/lib.rs
@@ -6,7 +6,6 @@
mod auto_respawn;
mod bot;
mod container;
-pub mod mining;
pub mod pathfinder;
pub mod prelude;
pub mod swarm;
diff --git a/azalea/src/mining.rs b/azalea/src/mining.rs
deleted file mode 100644
index 8ba16436..00000000
--- a/azalea/src/mining.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use azalea_client::{
- interact::SwingArmEvent,
- mining::{Mining, StartMiningBlockEvent},
- Client, TickBroadcast,
-};
-use azalea_core::BlockPos;
-
-pub trait MiningExt {
- /// Start mining a block.
- async fn mine(&mut self, position: BlockPos);
-}
-
-impl MiningExt for Client {
- /// Start mining a block. This won't turn the bot's head towards the block,
- /// so you'll have to do that yourself with [`look_at`].
- ///
- /// [`look_at`]: crate::prelude::BotClientExt::look_at
- async fn mine(&mut self, position: BlockPos) {
- self.ecs.lock().send_event(StartMiningBlockEvent {
- entity: self.entity,
- position,
- });
- // vanilla sends an extra swing arm packet when we start mining
- self.ecs.lock().send_event(SwingArmEvent {
- entity: self.entity,
- });
-
- let mut receiver = {
- let ecs = self.ecs.lock();
- let tick_broadcast = ecs.resource::<TickBroadcast>();
- tick_broadcast.subscribe()
- };
- while receiver.recv().await.is_ok() {
- let ecs = self.ecs.lock();
- if ecs.get::<Mining>(self.entity).is_none() {
- break;
- }
- }
- }
-}
diff --git a/azalea/src/prelude.rs b/azalea/src/prelude.rs
index ff3c11de..87cb0b53 100644
--- a/azalea/src/prelude.rs
+++ b/azalea/src/prelude.rs
@@ -2,8 +2,8 @@
//! re-exported here.
pub use crate::{
- bot::BotClientExt, container::ContainerClientExt, mining::MiningExt,
- pathfinder::PathfinderClientExt, ClientBuilder,
+ bot::BotClientExt, container::ContainerClientExt, pathfinder::PathfinderClientExt,
+ ClientBuilder,
};
pub use azalea_client::{Account, Client, Event};
// this is necessary to make the macros that reference bevy_ecs work