mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
fix issues related to pathfinder mining
This commit is contained in:
parent
d7cd305059
commit
0569862a1b
23 changed files with 239 additions and 158 deletions
|
@ -45,7 +45,7 @@ use tracing::{debug, error, info, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Account, DefaultPlugins, PlayerInfo,
|
Account, DefaultPlugins,
|
||||||
attack::{self},
|
attack::{self},
|
||||||
chunks::ChunkBatchInfo,
|
chunks::ChunkBatchInfo,
|
||||||
connection::RawConnection,
|
connection::RawConnection,
|
||||||
|
@ -54,13 +54,11 @@ use crate::{
|
||||||
interact::CurrentSequenceNumber,
|
interact::CurrentSequenceNumber,
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
join::{ConnectOpts, StartJoinServerEvent},
|
join::{ConnectOpts, StartJoinServerEvent},
|
||||||
local_player::{
|
local_player::{Hunger, InstanceHolder, PermissionLevel, PlayerAbilities, TabList},
|
||||||
GameProfileComponent, Hunger, InstanceHolder, PermissionLevel, PlayerAbilities, TabList,
|
|
||||||
},
|
|
||||||
mining::{self},
|
mining::{self},
|
||||||
movement::{LastSentLookDirection, PhysicsState},
|
movement::{LastSentLookDirection, PhysicsState},
|
||||||
packet::game::SendPacketEvent,
|
packet::game::SendPacketEvent,
|
||||||
player::retroactively_add_game_profile_component,
|
player::{GameProfileComponent, PlayerInfo, retroactively_add_game_profile_component},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// `Client` has the things that a user interacting with the library will want.
|
/// `Client` has the things that a user interacting with the library will want.
|
||||||
|
@ -324,7 +322,7 @@ impl Client {
|
||||||
/// This will panic if the component doesn't exist on the client.
|
/// This will panic if the component doesn't exist on the client.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use azalea_client::{Client, Hunger};
|
/// # use azalea_client::{Client, local_player::Hunger};
|
||||||
/// # fn example(bot: &Client) {
|
/// # fn example(bot: &Client) {
|
||||||
/// let hunger = bot.map_component::<Hunger, _>(|h| h.food);
|
/// let hunger = bot.map_component::<Hunger, _>(|h| h.food);
|
||||||
/// # }
|
/// # }
|
||||||
|
|
|
@ -43,7 +43,7 @@ impl Client {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// use azalea_client::{Client, GameProfileComponent};
|
/// use azalea_client::{Client, player::GameProfileComponent};
|
||||||
/// use azalea_entity::{Position, metadata::Player};
|
/// use azalea_entity::{Position, metadata::Player};
|
||||||
/// use bevy_ecs::query::With;
|
/// use bevy_ecs::query::With;
|
||||||
///
|
///
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
mod account;
|
mod account;
|
||||||
mod client;
|
mod client;
|
||||||
mod entity_query;
|
mod entity_query;
|
||||||
mod local_player;
|
pub mod local_player;
|
||||||
pub mod ping;
|
pub mod ping;
|
||||||
mod player;
|
pub mod player;
|
||||||
mod plugins;
|
mod plugins;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -29,9 +29,7 @@ pub use client::{
|
||||||
StartClientOpts, start_ecs_runner,
|
StartClientOpts, start_ecs_runner,
|
||||||
};
|
};
|
||||||
pub use events::Event;
|
pub use events::Event;
|
||||||
pub use local_player::{GameProfileComponent, Hunger, InstanceHolder, TabList};
|
|
||||||
pub use movement::{
|
pub use movement::{
|
||||||
PhysicsState, SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection,
|
PhysicsState, SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection,
|
||||||
};
|
};
|
||||||
pub use player::PlayerInfo;
|
|
||||||
pub use plugins::*;
|
pub use plugins::*;
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::{
|
||||||
sync::{Arc, PoisonError},
|
sync::{Arc, PoisonError},
|
||||||
};
|
};
|
||||||
|
|
||||||
use azalea_auth::game_profile::GameProfile;
|
|
||||||
use azalea_core::game_type::GameMode;
|
use azalea_core::game_type::GameMode;
|
||||||
use azalea_protocol::packets::game::c_player_abilities::ClientboundPlayerAbilities;
|
use azalea_protocol::packets::game::c_player_abilities::ClientboundPlayerAbilities;
|
||||||
use azalea_world::{Instance, PartialInstance};
|
use azalea_world::{Instance, PartialInstance};
|
||||||
|
@ -16,7 +15,7 @@ use tokio::sync::mpsc;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{ClientInformation, PlayerInfo, events::Event as AzaleaEvent};
|
use crate::{ClientInformation, events::Event as AzaleaEvent, player::PlayerInfo};
|
||||||
|
|
||||||
/// A component that keeps strong references to our [`PartialInstance`] and
|
/// A component that keeps strong references to our [`PartialInstance`] and
|
||||||
/// [`Instance`] for local players.
|
/// [`Instance`] for local players.
|
||||||
|
@ -40,14 +39,6 @@ pub struct InstanceHolder {
|
||||||
pub instance: Arc<RwLock<Instance>>,
|
pub instance: Arc<RwLock<Instance>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component only present in players that contains the [`GameProfile`] (which
|
|
||||||
/// you can use to get a player's name).
|
|
||||||
///
|
|
||||||
/// Note that it's possible for this to be missing in a player if the server
|
|
||||||
/// never sent the player info for them (though this is uncommon).
|
|
||||||
#[derive(Component, Clone, Debug, Deref, DerefMut)]
|
|
||||||
pub struct GameProfileComponent(pub GameProfile);
|
|
||||||
|
|
||||||
/// The gamemode of a local player. For a non-local player, you can look up the
|
/// The gamemode of a local player. For a non-local player, you can look up the
|
||||||
/// player in the [`TabList`].
|
/// player in the [`TabList`].
|
||||||
#[derive(Component, Clone, Debug, Copy)]
|
#[derive(Component, Clone, Debug, Copy)]
|
||||||
|
@ -55,6 +46,14 @@ pub struct LocalGameMode {
|
||||||
pub current: GameMode,
|
pub current: GameMode,
|
||||||
pub previous: Option<GameMode>,
|
pub previous: Option<GameMode>,
|
||||||
}
|
}
|
||||||
|
impl From<GameMode> for LocalGameMode {
|
||||||
|
fn from(current: GameMode) -> Self {
|
||||||
|
LocalGameMode {
|
||||||
|
current,
|
||||||
|
previous: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A component that contains the abilities the player has, like flying
|
/// A component that contains the abilities the player has, like flying
|
||||||
/// or instantly breaking blocks. This is only present on local players.
|
/// or instantly breaking blocks. This is only present on local players.
|
||||||
|
@ -92,7 +91,7 @@ pub struct PermissionLevel(pub u8);
|
||||||
/// tab list.
|
/// tab list.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use azalea_client::TabList;
|
/// # use azalea_client::local_player::TabList;
|
||||||
/// # fn example(client: &azalea_client::Client) {
|
/// # fn example(client: &azalea_client::Client) {
|
||||||
/// let tab_list = client.component::<TabList>();
|
/// let tab_list = client.component::<TabList>();
|
||||||
/// println!("Online players:");
|
/// println!("Online players:");
|
||||||
|
|
|
@ -3,12 +3,14 @@ use azalea_chat::FormattedText;
|
||||||
use azalea_core::game_type::GameMode;
|
use azalea_core::game_type::GameMode;
|
||||||
use azalea_entity::indexing::EntityUuidIndex;
|
use azalea_entity::indexing::EntityUuidIndex;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
event::EventReader,
|
event::EventReader,
|
||||||
system::{Commands, Res},
|
system::{Commands, Res},
|
||||||
};
|
};
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{GameProfileComponent, packet::game::AddPlayerEvent};
|
use crate::packet::game::AddPlayerEvent;
|
||||||
|
|
||||||
/// A player in the tab list.
|
/// A player in the tab list.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -29,6 +31,14 @@ pub struct PlayerInfo {
|
||||||
pub display_name: Option<Box<FormattedText>>,
|
pub display_name: Option<Box<FormattedText>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A component only present in players that contains the [`GameProfile`] (which
|
||||||
|
/// you can use to get a player's name).
|
||||||
|
///
|
||||||
|
/// Note that it's possible for this to be missing in a player if the server
|
||||||
|
/// never sent the player info for them (though this is uncommon).
|
||||||
|
#[derive(Component, Clone, Debug, Deref, DerefMut)]
|
||||||
|
pub struct GameProfileComponent(pub GameProfile);
|
||||||
|
|
||||||
/// Add a [`GameProfileComponent`] when an [`AddPlayerEvent`] is received.
|
/// Add a [`GameProfileComponent`] when an [`AddPlayerEvent`] is received.
|
||||||
/// Usually the `GameProfileComponent` will be added from the
|
/// Usually the `GameProfileComponent` will be added from the
|
||||||
/// `ClientboundGamePacket::AddPlayer` handler though.
|
/// `ClientboundGamePacket::AddPlayer` handler though.
|
||||||
|
|
|
@ -17,7 +17,7 @@ use bevy_ecs::prelude::*;
|
||||||
use tracing::{error, trace};
|
use tracing::{error, trace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
InstanceHolder, interact::handle_start_use_item_queued, inventory::InventorySet,
|
interact::handle_start_use_item_queued, inventory::InventorySet, local_player::InstanceHolder,
|
||||||
packet::game::SendPacketEvent, respawn::perform_respawn,
|
packet::game::SendPacketEvent, respawn::perform_respawn,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,10 @@ use derive_more::Deref;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use super::login::IsAuthenticated;
|
use super::login::IsAuthenticated;
|
||||||
use crate::{InstanceHolder, chat_signing, client::JoinedClientBundle, connection::RawConnection};
|
use crate::{
|
||||||
|
chat_signing, client::JoinedClientBundle, connection::RawConnection,
|
||||||
|
local_player::InstanceHolder,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct DisconnectPlugin;
|
pub struct DisconnectPlugin;
|
||||||
impl Plugin for DisconnectPlugin {
|
impl Plugin for DisconnectPlugin {
|
||||||
|
|
|
@ -14,12 +14,12 @@ use derive_more::{Deref, DerefMut};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
PlayerInfo,
|
|
||||||
chat::{ChatPacket, ChatReceivedEvent},
|
chat::{ChatPacket, ChatReceivedEvent},
|
||||||
disconnect::DisconnectEvent,
|
disconnect::DisconnectEvent,
|
||||||
packet::game::{
|
packet::game::{
|
||||||
AddPlayerEvent, DeathEvent, KeepAliveEvent, RemovePlayerEvent, UpdatePlayerEvent,
|
AddPlayerEvent, DeathEvent, KeepAliveEvent, RemovePlayerEvent, UpdatePlayerEvent,
|
||||||
},
|
},
|
||||||
|
player::PlayerInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
// (for contributors):
|
// (for contributors):
|
||||||
|
|
|
@ -24,7 +24,7 @@ use bevy_ecs::prelude::*;
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use super::mining::{Mining, MiningSet};
|
use super::mining::Mining;
|
||||||
use crate::{
|
use crate::{
|
||||||
Client,
|
Client,
|
||||||
attack::handle_attack_event,
|
attack::handle_attack_event,
|
||||||
|
@ -58,12 +58,7 @@ impl Plugin for InteractPlugin {
|
||||||
.after(MoveEventsSet),
|
.after(MoveEventsSet),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(GameTick, handle_start_use_item_queued.before(PhysicsSet))
|
||||||
GameTick,
|
|
||||||
handle_start_use_item_queued
|
|
||||||
.after(MiningSet)
|
|
||||||
.before(PhysicsSet),
|
|
||||||
)
|
|
||||||
.add_observer(handle_swing_arm_trigger);
|
.add_observer(handle_swing_arm_trigger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,16 @@ use azalea_world::{InstanceContainer, InstanceName};
|
||||||
use bevy_app::{App, Plugin, Update};
|
use bevy_app::{App, Plugin, Update};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use tracing::trace;
|
use tracing::{info, trace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Client, InstanceHolder,
|
Client,
|
||||||
interact::{
|
interact::{
|
||||||
CurrentSequenceNumber, HitResultComponent, SwingArmEvent, can_use_game_master_blocks,
|
CurrentSequenceNumber, HitResultComponent, SwingArmEvent, can_use_game_master_blocks,
|
||||||
check_is_interaction_restricted,
|
check_is_interaction_restricted,
|
||||||
},
|
},
|
||||||
inventory::{Inventory, InventorySet},
|
inventory::{Inventory, InventorySet},
|
||||||
local_player::{LocalGameMode, PermissionLevel, PlayerAbilities},
|
local_player::{InstanceHolder, LocalGameMode, PermissionLevel, PlayerAbilities},
|
||||||
movement::MoveEventsSet,
|
movement::MoveEventsSet,
|
||||||
packet::game::SendPacketEvent,
|
packet::game::SendPacketEvent,
|
||||||
};
|
};
|
||||||
|
@ -40,14 +40,15 @@ impl Plugin for MiningPlugin {
|
||||||
handle_mining_queued,
|
handle_mining_queued,
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.before(PhysicsSet)
|
.after(PhysicsSet)
|
||||||
|
.after(super::movement::send_position)
|
||||||
|
.after(super::attack::handle_attack_queued)
|
||||||
.in_set(MiningSet),
|
.in_set(MiningSet),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
handle_start_mining_block_event,
|
handle_start_mining_block_event,
|
||||||
handle_finish_mining_block_event,
|
|
||||||
handle_stop_mining_block_event,
|
handle_stop_mining_block_event,
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
|
@ -60,7 +61,8 @@ impl Plugin for MiningPlugin {
|
||||||
.after(crate::attack::handle_attack_event)
|
.after(crate::attack::handle_attack_event)
|
||||||
.after(crate::interact::handle_start_use_item_queued)
|
.after(crate::interact::handle_start_use_item_queued)
|
||||||
.before(crate::interact::handle_swing_arm_event),
|
.before(crate::interact::handle_swing_arm_event),
|
||||||
);
|
)
|
||||||
|
.add_observer(handle_finish_mining_block_observer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,13 +155,15 @@ fn handle_auto_mine(
|
||||||
pub struct Mining {
|
pub struct Mining {
|
||||||
pub pos: BlockPos,
|
pub pos: BlockPos,
|
||||||
pub dir: Direction,
|
pub dir: Direction,
|
||||||
|
/// See [`MiningQueued::force`].
|
||||||
|
pub force: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start mining the block at the given position.
|
/// Start mining the block at the given position.
|
||||||
///
|
///
|
||||||
/// If we're looking at the block then the correct direction will be used,
|
/// If we're looking at the block then the correct direction will be used,
|
||||||
/// otherwise it'll be [`Direction::Down`].
|
/// otherwise it'll be [`Direction::Down`].
|
||||||
#[derive(Event)]
|
#[derive(Event, Debug)]
|
||||||
pub struct StartMiningBlockEvent {
|
pub struct StartMiningBlockEvent {
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub position: BlockPos,
|
pub position: BlockPos,
|
||||||
|
@ -170,33 +174,37 @@ fn handle_start_mining_block_event(
|
||||||
mut query: Query<&HitResultComponent>,
|
mut query: Query<&HitResultComponent>,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
|
trace!("{event:?}");
|
||||||
let hit_result = query.get_mut(event.entity).unwrap();
|
let hit_result = query.get_mut(event.entity).unwrap();
|
||||||
let direction = if let Some(block_hit_result) = hit_result.as_block_hit_result_if_not_miss()
|
let (direction, force) = if let Some(block_hit_result) =
|
||||||
|
hit_result.as_block_hit_result_if_not_miss()
|
||||||
&& block_hit_result.block_pos == event.position
|
&& block_hit_result.block_pos == event.position
|
||||||
{
|
{
|
||||||
// we're looking at the block
|
// we're looking at the block
|
||||||
block_hit_result.direction
|
(block_hit_result.direction, false)
|
||||||
} else {
|
} else {
|
||||||
// we're not looking at the block, arbitrary direction
|
// we're not looking at the block, arbitrary direction
|
||||||
Direction::Down
|
(Direction::Down, true)
|
||||||
};
|
};
|
||||||
commands.entity(event.entity).insert(MiningQueued {
|
commands.entity(event.entity).insert(MiningQueued {
|
||||||
position: event.position,
|
position: event.position,
|
||||||
direction,
|
direction,
|
||||||
|
force,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Present on entities when they're going to start mining a block next tick.
|
/// Present on entities when they're going to start mining a block next tick.
|
||||||
#[derive(Component)]
|
#[derive(Component, Debug, Clone)]
|
||||||
pub struct MiningQueued {
|
pub struct MiningQueued {
|
||||||
pub position: BlockPos,
|
pub position: BlockPos,
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
|
/// Whether we should mine the block regardless of whether it's reachable.
|
||||||
|
pub force: bool,
|
||||||
}
|
}
|
||||||
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
||||||
fn handle_mining_queued(
|
fn handle_mining_queued(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut finish_mining_events: EventWriter<FinishMiningBlockEvent>,
|
|
||||||
mut attack_block_events: EventWriter<AttackBlockEvent>,
|
mut attack_block_events: EventWriter<AttackBlockEvent>,
|
||||||
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
||||||
query: Query<(
|
query: Query<(
|
||||||
|
@ -233,6 +241,7 @@ fn handle_mining_queued(
|
||||||
mut current_mining_pos,
|
mut current_mining_pos,
|
||||||
) in query
|
) in query
|
||||||
{
|
{
|
||||||
|
info!("mining_queued: {mining_queued:?}");
|
||||||
commands.entity(entity).remove::<MiningQueued>();
|
commands.entity(entity).remove::<MiningQueued>();
|
||||||
|
|
||||||
let instance = instance_holder.instance.read();
|
let instance = instance_holder.instance.read();
|
||||||
|
@ -248,10 +257,12 @@ fn handle_mining_queued(
|
||||||
// is outside of the worldborder
|
// is outside of the worldborder
|
||||||
|
|
||||||
if game_mode.current == GameMode::Creative {
|
if game_mode.current == GameMode::Creative {
|
||||||
finish_mining_events.write(FinishMiningBlockEvent {
|
commands.trigger_targets(
|
||||||
|
FinishMiningBlockEvent {
|
||||||
|
position: mining_queued.position,
|
||||||
|
},
|
||||||
entity,
|
entity,
|
||||||
position: mining_queued.position,
|
);
|
||||||
});
|
|
||||||
**mine_delay = 5;
|
**mine_delay = 5;
|
||||||
} else if mining.is_none()
|
} else if mining.is_none()
|
||||||
|| !is_same_mining_target(
|
|| !is_same_mining_target(
|
||||||
|
@ -304,14 +315,17 @@ fn handle_mining_queued(
|
||||||
) >= 1.
|
) >= 1.
|
||||||
{
|
{
|
||||||
// block was broken instantly
|
// block was broken instantly
|
||||||
finish_mining_events.write(FinishMiningBlockEvent {
|
commands.trigger_targets(
|
||||||
|
FinishMiningBlockEvent {
|
||||||
|
position: mining_queued.position,
|
||||||
|
},
|
||||||
entity,
|
entity,
|
||||||
position: mining_queued.position,
|
);
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
let mining = Mining {
|
let mining = Mining {
|
||||||
pos: mining_queued.position,
|
pos: mining_queued.position,
|
||||||
dir: mining_queued.direction,
|
dir: mining_queued.direction,
|
||||||
|
force: mining_queued.force,
|
||||||
};
|
};
|
||||||
trace!("inserting mining component {mining:?} for entity {entity:?}");
|
trace!("inserting mining component {mining:?} for entity {entity:?}");
|
||||||
commands.entity(entity).insert(mining);
|
commands.entity(entity).insert(mining);
|
||||||
|
@ -370,7 +384,7 @@ fn is_same_mining_target(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component bundle for players that can mine blocks.
|
/// A component bundle for players that can mine blocks.
|
||||||
#[derive(Bundle, Default)]
|
#[derive(Bundle, Default, Clone)]
|
||||||
pub struct MineBundle {
|
pub struct MineBundle {
|
||||||
pub delay: MineDelay,
|
pub delay: MineDelay,
|
||||||
pub progress: MineProgress,
|
pub progress: MineProgress,
|
||||||
|
@ -380,12 +394,12 @@ pub struct MineBundle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component that counts down until we start mining the next block.
|
/// A component that counts down until we start mining the next block.
|
||||||
#[derive(Component, Debug, Default, Deref, DerefMut)]
|
#[derive(Component, Debug, Default, Deref, DerefMut, Clone)]
|
||||||
pub struct MineDelay(pub u32);
|
pub struct MineDelay(pub u32);
|
||||||
|
|
||||||
/// A component that stores the progress of the current mining operation. This
|
/// A component that stores the progress of the current mining operation. This
|
||||||
/// is a value between 0 and 1.
|
/// is a value between 0 and 1.
|
||||||
#[derive(Component, Debug, Default, Deref, DerefMut)]
|
#[derive(Component, Debug, Default, Deref, DerefMut, Clone)]
|
||||||
pub struct MineProgress(pub f32);
|
pub struct MineProgress(pub f32);
|
||||||
|
|
||||||
impl MineProgress {
|
impl MineProgress {
|
||||||
|
@ -413,15 +427,14 @@ pub struct MineBlockPos(pub Option<BlockPos>);
|
||||||
#[derive(Component, Clone, Debug, Default, Deref, DerefMut)]
|
#[derive(Component, Clone, Debug, Default, Deref, DerefMut)]
|
||||||
pub struct MineItem(pub ItemStack);
|
pub struct MineItem(pub ItemStack);
|
||||||
|
|
||||||
/// Sent when we completed mining a block.
|
/// A trigger that's sent when we completed mining a block.
|
||||||
#[derive(Event)]
|
#[derive(Event)]
|
||||||
pub struct FinishMiningBlockEvent {
|
pub struct FinishMiningBlockEvent {
|
||||||
pub entity: Entity,
|
|
||||||
pub position: BlockPos,
|
pub position: BlockPos,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_finish_mining_block_event(
|
pub fn handle_finish_mining_block_observer(
|
||||||
mut events: EventReader<FinishMiningBlockEvent>,
|
trigger: Trigger<FinishMiningBlockEvent>,
|
||||||
mut query: Query<(
|
mut query: Query<(
|
||||||
&InstanceName,
|
&InstanceName,
|
||||||
&LocalGameMode,
|
&LocalGameMode,
|
||||||
|
@ -432,53 +445,48 @@ pub fn handle_finish_mining_block_event(
|
||||||
)>,
|
)>,
|
||||||
instances: Res<InstanceContainer>,
|
instances: Res<InstanceContainer>,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
let event = trigger.event();
|
||||||
let (instance_name, game_mode, inventory, abilities, permission_level, _sequence_number) =
|
|
||||||
query.get_mut(event.entity).unwrap();
|
|
||||||
let instance_lock = instances.get(instance_name).unwrap();
|
|
||||||
let instance = instance_lock.read();
|
|
||||||
if check_is_interaction_restricted(
|
|
||||||
&instance,
|
|
||||||
&event.position,
|
|
||||||
&game_mode.current,
|
|
||||||
inventory,
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if game_mode.current == GameMode::Creative {
|
let (instance_name, game_mode, inventory, abilities, permission_level, _sequence_number) =
|
||||||
let held_item = inventory.held_item().kind();
|
query.get_mut(trigger.target()).unwrap();
|
||||||
if matches!(
|
let instance_lock = instances.get(instance_name).unwrap();
|
||||||
held_item,
|
let instance = instance_lock.read();
|
||||||
azalea_registry::Item::Trident | azalea_registry::Item::DebugStick
|
if check_is_interaction_restricted(&instance, &event.position, &game_mode.current, inventory) {
|
||||||
) || azalea_registry::tags::items::SWORDS.contains(&held_item)
|
return;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(block_state) = instance.get_block_state(&event.position) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let registry_block = Box::<dyn Block>::from(block_state).as_registry_block();
|
|
||||||
if !can_use_game_master_blocks(abilities, permission_level)
|
|
||||||
&& matches!(
|
|
||||||
registry_block,
|
|
||||||
azalea_registry::Block::CommandBlock | azalea_registry::Block::StructureBlock
|
|
||||||
)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if block_state == BlockState::AIR {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// when we break a waterlogged block we want to keep the water there
|
|
||||||
let fluid_state = FluidState::from(block_state);
|
|
||||||
let block_state_for_fluid = BlockState::from(fluid_state);
|
|
||||||
instance.set_block_state(&event.position, block_state_for_fluid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if game_mode.current == GameMode::Creative {
|
||||||
|
let held_item = inventory.held_item().kind();
|
||||||
|
if matches!(
|
||||||
|
held_item,
|
||||||
|
azalea_registry::Item::Trident | azalea_registry::Item::DebugStick
|
||||||
|
) || azalea_registry::tags::items::SWORDS.contains(&held_item)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(block_state) = instance.get_block_state(&event.position) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let registry_block = Box::<dyn Block>::from(block_state).as_registry_block();
|
||||||
|
if !can_use_game_master_blocks(abilities, permission_level)
|
||||||
|
&& matches!(
|
||||||
|
registry_block,
|
||||||
|
azalea_registry::Block::CommandBlock | azalea_registry::Block::StructureBlock
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if block_state == BlockState::AIR {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when we break a waterlogged block we want to keep the water there
|
||||||
|
let fluid_state = FluidState::from(block_state);
|
||||||
|
let block_state_for_fluid = BlockState::from(fluid_state);
|
||||||
|
instance.set_block_state(&event.position, block_state_for_fluid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Abort mining a block.
|
/// Abort mining a block.
|
||||||
|
@ -535,7 +543,6 @@ pub fn continue_mining_block(
|
||||||
)>,
|
)>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
||||||
mut finish_mining_events: EventWriter<FinishMiningBlockEvent>,
|
|
||||||
instances: Res<InstanceContainer>,
|
instances: Res<InstanceContainer>,
|
||||||
) {
|
) {
|
||||||
for (
|
for (
|
||||||
|
@ -562,10 +569,12 @@ pub fn continue_mining_block(
|
||||||
if game_mode.current == GameMode::Creative {
|
if game_mode.current == GameMode::Creative {
|
||||||
// TODO: worldborder check
|
// TODO: worldborder check
|
||||||
**mine_delay = 5;
|
**mine_delay = 5;
|
||||||
finish_mining_events.write(FinishMiningBlockEvent {
|
commands.trigger_targets(
|
||||||
|
FinishMiningBlockEvent {
|
||||||
|
position: mining.pos,
|
||||||
|
},
|
||||||
entity,
|
entity,
|
||||||
position: mining.pos,
|
);
|
||||||
});
|
|
||||||
commands.trigger(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
entity,
|
entity,
|
||||||
ServerboundPlayerAction {
|
ServerboundPlayerAction {
|
||||||
|
@ -576,12 +585,14 @@ pub fn continue_mining_block(
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
commands.trigger(SwingArmEvent { entity });
|
commands.trigger(SwingArmEvent { entity });
|
||||||
} else if is_same_mining_target(
|
} else if mining.force
|
||||||
mining.pos,
|
|| is_same_mining_target(
|
||||||
inventory,
|
mining.pos,
|
||||||
current_mining_pos,
|
inventory,
|
||||||
current_mining_item,
|
current_mining_pos,
|
||||||
) {
|
current_mining_item,
|
||||||
|
)
|
||||||
|
{
|
||||||
trace!("continue mining block at {:?}", mining.pos);
|
trace!("continue mining block at {:?}", mining.pos);
|
||||||
let instance_lock = instances.get(instance_name).unwrap();
|
let instance_lock = instances.get(instance_name).unwrap();
|
||||||
let instance = instance_lock.read();
|
let instance = instance_lock.read();
|
||||||
|
@ -612,10 +623,12 @@ pub fn continue_mining_block(
|
||||||
// repeatedly inserts MiningQueued
|
// repeatedly inserts MiningQueued
|
||||||
commands.entity(entity).remove::<(Mining, MiningQueued)>();
|
commands.entity(entity).remove::<(Mining, MiningQueued)>();
|
||||||
trace!("finished mining block at {:?}", mining.pos);
|
trace!("finished mining block at {:?}", mining.pos);
|
||||||
finish_mining_events.write(FinishMiningBlockEvent {
|
commands.trigger_targets(
|
||||||
|
FinishMiningBlockEvent {
|
||||||
|
position: mining.pos,
|
||||||
|
},
|
||||||
entity,
|
entity,
|
||||||
position: mining.pos,
|
);
|
||||||
});
|
|
||||||
commands.trigger(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
entity,
|
entity,
|
||||||
ServerboundPlayerAction {
|
ServerboundPlayerAction {
|
||||||
|
@ -641,6 +654,7 @@ pub fn continue_mining_block(
|
||||||
commands.entity(entity).insert(MiningQueued {
|
commands.entity(entity).insert(MiningQueued {
|
||||||
position: mining.pos,
|
position: mining.pos,
|
||||||
direction: mining.dir,
|
direction: mining.dir,
|
||||||
|
force: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,11 @@ use tracing::{debug, warn};
|
||||||
|
|
||||||
use super::as_system;
|
use super::as_system;
|
||||||
use crate::{
|
use crate::{
|
||||||
InstanceHolder,
|
|
||||||
client::InConfigState,
|
client::InConfigState,
|
||||||
connection::RawConnection,
|
connection::RawConnection,
|
||||||
declare_packet_handlers,
|
declare_packet_handlers,
|
||||||
disconnect::DisconnectEvent,
|
disconnect::DisconnectEvent,
|
||||||
|
local_player::InstanceHolder,
|
||||||
packet::game::{KeepAliveEvent, ResourcePackEvent},
|
packet::game::{KeepAliveEvent, ResourcePackEvent},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use parking_lot::RwLock;
|
||||||
use tracing::{error, trace};
|
use tracing::{error, trace};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{PlayerInfo, client::InGameState, connection::RawConnection};
|
use crate::{client::InGameState, connection::RawConnection, player::PlayerInfo};
|
||||||
|
|
||||||
/// An event that's sent when we receive a packet.
|
/// An event that's sent when we receive a packet.
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub use events::*;
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ClientInformation, PlayerInfo,
|
ClientInformation,
|
||||||
chat::{ChatPacket, ChatReceivedEvent},
|
chat::{ChatPacket, ChatReceivedEvent},
|
||||||
chunks,
|
chunks,
|
||||||
connection::RawConnection,
|
connection::RawConnection,
|
||||||
|
@ -29,11 +29,10 @@ use crate::{
|
||||||
inventory::{
|
inventory::{
|
||||||
ClientSideCloseContainerEvent, Inventory, MenuOpenedEvent, SetContainerContentEvent,
|
ClientSideCloseContainerEvent, Inventory, MenuOpenedEvent, SetContainerContentEvent,
|
||||||
},
|
},
|
||||||
local_player::{
|
local_player::{Hunger, InstanceHolder, LocalGameMode, PlayerAbilities, TabList},
|
||||||
GameProfileComponent, Hunger, InstanceHolder, LocalGameMode, PlayerAbilities, TabList,
|
|
||||||
},
|
|
||||||
movement::{KnockbackEvent, KnockbackType},
|
movement::{KnockbackEvent, KnockbackType},
|
||||||
packet::as_system,
|
packet::as_system,
|
||||||
|
player::{GameProfileComponent, PlayerInfo},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn process_packet(ecs: &mut World, player: Entity, packet: &ClientboundGamePacket) {
|
pub fn process_packet(ecs: &mut World, player: Entity, packet: &ClientboundGamePacket) {
|
||||||
|
|
|
@ -17,8 +17,8 @@ use tracing::{debug, error};
|
||||||
|
|
||||||
use super::as_system;
|
use super::as_system;
|
||||||
use crate::{
|
use crate::{
|
||||||
Account, GameProfileComponent, InConfigState, connection::RawConnection,
|
Account, InConfigState, connection::RawConnection, declare_packet_handlers,
|
||||||
declare_packet_handlers, disconnect::DisconnectEvent,
|
disconnect::DisconnectEvent, player::GameProfileComponent,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn process_packet(ecs: &mut World, player: Entity, packet: &ClientboundLoginPacket) {
|
pub fn process_packet(ecs: &mut World, player: Entity, packet: &ClientboundLoginPacket) {
|
||||||
|
|
|
@ -30,8 +30,8 @@ use simdnbt::owned::{NbtCompound, NbtTag};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ClientInformation, GameProfileComponent, InConfigState, InstanceHolder, LocalPlayerBundle,
|
ClientInformation, InConfigState, LocalPlayerBundle, connection::RawConnection,
|
||||||
connection::RawConnection, disconnect::DisconnectEvent,
|
disconnect::DisconnectEvent, local_player::InstanceHolder, player::GameProfileComponent,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A way to simulate a client in a server, used for some internal tests.
|
/// A way to simulate a client in a server, used for some internal tests.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use azalea_client::{InConfigState, InGameState, InstanceHolder, test_simulation::*};
|
use azalea_client::{InConfigState, InGameState, local_player::InstanceHolder, test_simulation::*};
|
||||||
use azalea_core::{position::ChunkPos, resource_location::ResourceLocation};
|
use azalea_core::{position::ChunkPos, resource_location::ResourceLocation};
|
||||||
use azalea_entity::LocalEntity;
|
use azalea_entity::LocalEntity;
|
||||||
use azalea_protocol::packets::{
|
use azalea_protocol::packets::{
|
||||||
|
|
|
@ -4,6 +4,7 @@ use azalea::{
|
||||||
BlockPos,
|
BlockPos,
|
||||||
pathfinder::{
|
pathfinder::{
|
||||||
astar::{self, PathfinderTimeout, WeightedNode, a_star},
|
astar::{self, PathfinderTimeout, WeightedNode, a_star},
|
||||||
|
custom_state::CustomPathfinderStateRef,
|
||||||
goals::{BlockPosGoal, Goal},
|
goals::{BlockPosGoal, Goal},
|
||||||
mining::MiningCache,
|
mining::MiningCache,
|
||||||
rel_block_pos::RelBlockPos,
|
rel_block_pos::RelBlockPos,
|
||||||
|
@ -41,13 +42,13 @@ fn generate_bedrock_world(
|
||||||
let mut chunk = chunk.write();
|
let mut chunk = chunk.write();
|
||||||
for x in 0..16_u8 {
|
for x in 0..16_u8 {
|
||||||
for z in 0..16_u8 {
|
for z in 0..16_u8 {
|
||||||
chunk.set(
|
chunk.set_block_state(
|
||||||
&ChunkBlockPos::new(x, 1, z),
|
&ChunkBlockPos::new(x, 1, z),
|
||||||
azalea_registry::Block::Bedrock.into(),
|
azalea_registry::Block::Bedrock.into(),
|
||||||
chunks.min_y,
|
chunks.min_y,
|
||||||
);
|
);
|
||||||
if rng.gen_bool(0.5) {
|
if rng.gen_bool(0.5) {
|
||||||
chunk.set(
|
chunk.set_block_state(
|
||||||
&ChunkBlockPos::new(x, 2, z),
|
&ChunkBlockPos::new(x, 2, z),
|
||||||
azalea_registry::Block::Bedrock.into(),
|
azalea_registry::Block::Bedrock.into(),
|
||||||
chunks.min_y,
|
chunks.min_y,
|
||||||
|
@ -99,7 +100,7 @@ fn generate_mining_world(
|
||||||
for y in chunks.min_y..(chunks.min_y + chunks.height as i32) {
|
for y in chunks.min_y..(chunks.min_y + chunks.height as i32) {
|
||||||
for x in 0..16_u8 {
|
for x in 0..16_u8 {
|
||||||
for z in 0..16_u8 {
|
for z in 0..16_u8 {
|
||||||
chunk.set(
|
chunk.set_block_state(
|
||||||
&ChunkBlockPos::new(x, y, z),
|
&ChunkBlockPos::new(x, y, z),
|
||||||
azalea_registry::Block::Stone.into(),
|
azalea_registry::Block::Stone.into(),
|
||||||
chunks.min_y,
|
chunks.min_y,
|
||||||
|
@ -134,7 +135,13 @@ fn run_pathfinder_benchmark(
|
||||||
let goal = BlockPosGoal(end);
|
let goal = BlockPosGoal(end);
|
||||||
|
|
||||||
let successors = |pos: RelBlockPos| {
|
let successors = |pos: RelBlockPos| {
|
||||||
azalea::pathfinder::call_successors_fn(&cached_world, &mining_cache, successors_fn, pos)
|
azalea::pathfinder::call_successors_fn(
|
||||||
|
&cached_world,
|
||||||
|
&mining_cache,
|
||||||
|
&CustomPathfinderStateRef::default(),
|
||||||
|
successors_fn,
|
||||||
|
pos,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let astar::Path {
|
let astar::Path {
|
||||||
|
|
|
@ -25,7 +25,7 @@ fn generate_world(partial_chunks: &mut PartialChunkStorage, size: u32) -> ChunkS
|
||||||
let mut chunk = chunk.write();
|
let mut chunk = chunk.write();
|
||||||
for x in 0..16_u8 {
|
for x in 0..16_u8 {
|
||||||
for z in 0..16_u8 {
|
for z in 0..16_u8 {
|
||||||
chunk.set(
|
chunk.set_block_state(
|
||||||
&ChunkBlockPos::new(x, 1, z),
|
&ChunkBlockPos::new(x, 1, z),
|
||||||
azalea_registry::Block::OakFence.into(),
|
azalea_registry::Block::OakFence.into(),
|
||||||
chunks.min_y,
|
chunks.min_y,
|
||||||
|
|
|
@ -3,8 +3,8 @@ pub mod debug;
|
||||||
pub mod movement;
|
pub mod movement;
|
||||||
|
|
||||||
use azalea::{
|
use azalea::{
|
||||||
Client, GameProfileComponent, brigadier::prelude::*, chat::ChatPacket, ecs::prelude::*,
|
Client, brigadier::prelude::*, chat::ChatPacket, ecs::prelude::*, entity::metadata::Player,
|
||||||
entity::metadata::Player,
|
player::GameProfileComponent,
|
||||||
};
|
};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use azalea_client::{InstanceHolder, chat::SendChatEvent};
|
use azalea_client::{chat::SendChatEvent, local_player::InstanceHolder};
|
||||||
use azalea_core::position::Vec3;
|
use azalea_core::position::Vec3;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,10 @@ use std::{
|
||||||
|
|
||||||
use astar::{Edge, PathfinderTimeout};
|
use astar::{Edge, PathfinderTimeout};
|
||||||
use azalea_client::{
|
use azalea_client::{
|
||||||
InstanceHolder, StartSprintEvent, StartWalkEvent,
|
StartSprintEvent, StartWalkEvent,
|
||||||
inventory::{Inventory, InventorySet, SetSelectedHotbarSlotEvent},
|
inventory::{Inventory, InventorySet, SetSelectedHotbarSlotEvent},
|
||||||
mining::{Mining, StartMiningBlockEvent},
|
local_player::InstanceHolder,
|
||||||
|
mining::{Mining, MiningSet, StartMiningBlockEvent},
|
||||||
movement::MoveEventsSet,
|
movement::MoveEventsSet,
|
||||||
};
|
};
|
||||||
use azalea_core::{position::BlockPos, tick::GameTick};
|
use azalea_core::{position::BlockPos, tick::GameTick};
|
||||||
|
@ -89,7 +90,8 @@ impl Plugin for PathfinderPlugin {
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.after(PhysicsSet)
|
.after(PhysicsSet)
|
||||||
.after(azalea_client::movement::send_position),
|
.after(azalea_client::movement::send_position)
|
||||||
|
.after(MiningSet),
|
||||||
)
|
)
|
||||||
.add_systems(PreUpdate, add_default_pathfinder)
|
.add_systems(PreUpdate, add_default_pathfinder)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
|
@ -1271,6 +1273,7 @@ mod tests {
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use azalea_block::BlockState;
|
||||||
use azalea_core::position::{BlockPos, ChunkPos, Vec3};
|
use azalea_core::position::{BlockPos, ChunkPos, Vec3};
|
||||||
use azalea_world::{Chunk, ChunkStorage, PartialChunkStorage};
|
use azalea_world::{Chunk, ChunkStorage, PartialChunkStorage};
|
||||||
|
|
||||||
|
@ -1286,9 +1289,9 @@ mod tests {
|
||||||
partial_chunks: &mut PartialChunkStorage,
|
partial_chunks: &mut PartialChunkStorage,
|
||||||
start_pos: BlockPos,
|
start_pos: BlockPos,
|
||||||
end_pos: BlockPos,
|
end_pos: BlockPos,
|
||||||
solid_blocks: Vec<BlockPos>,
|
solid_blocks: &[BlockPos],
|
||||||
) -> Simulation {
|
) -> Simulation {
|
||||||
let mut simulation = setup_simulation_world(partial_chunks, start_pos, solid_blocks);
|
let mut simulation = setup_simulation_world(partial_chunks, start_pos, solid_blocks, &[]);
|
||||||
|
|
||||||
// you can uncomment this while debugging tests to get trace logs
|
// you can uncomment this while debugging tests to get trace logs
|
||||||
// simulation.app.add_plugins(bevy_log::LogPlugin {
|
// simulation.app.add_plugins(bevy_log::LogPlugin {
|
||||||
|
@ -1311,10 +1314,14 @@ mod tests {
|
||||||
fn setup_simulation_world(
|
fn setup_simulation_world(
|
||||||
partial_chunks: &mut PartialChunkStorage,
|
partial_chunks: &mut PartialChunkStorage,
|
||||||
start_pos: BlockPos,
|
start_pos: BlockPos,
|
||||||
solid_blocks: Vec<BlockPos>,
|
solid_blocks: &[BlockPos],
|
||||||
|
extra_blocks: &[(BlockPos, BlockState)],
|
||||||
) -> Simulation {
|
) -> Simulation {
|
||||||
let mut chunk_positions = HashSet::new();
|
let mut chunk_positions = HashSet::new();
|
||||||
for block_pos in &solid_blocks {
|
for block_pos in solid_blocks {
|
||||||
|
chunk_positions.insert(ChunkPos::from(block_pos));
|
||||||
|
}
|
||||||
|
for (block_pos, _) in extra_blocks {
|
||||||
chunk_positions.insert(ChunkPos::from(block_pos));
|
chunk_positions.insert(ChunkPos::from(block_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1323,8 +1330,12 @@ mod tests {
|
||||||
partial_chunks.set(&chunk_pos, Some(Chunk::default()), &mut chunks);
|
partial_chunks.set(&chunk_pos, Some(Chunk::default()), &mut chunks);
|
||||||
}
|
}
|
||||||
for block_pos in solid_blocks {
|
for block_pos in solid_blocks {
|
||||||
chunks.set_block_state(&block_pos, azalea_registry::Block::Stone.into());
|
chunks.set_block_state(block_pos, azalea_registry::Block::Stone.into());
|
||||||
}
|
}
|
||||||
|
for (block_pos, block_state) in extra_blocks {
|
||||||
|
chunks.set_block_state(block_pos, *block_state);
|
||||||
|
}
|
||||||
|
|
||||||
let player = SimulatedPlayerBundle::new(Vec3::new(
|
let player = SimulatedPlayerBundle::new(Vec3::new(
|
||||||
start_pos.x as f64 + 0.5,
|
start_pos.x as f64 + 0.5,
|
||||||
start_pos.y as f64,
|
start_pos.y as f64,
|
||||||
|
@ -1360,7 +1371,7 @@ mod tests {
|
||||||
&mut partial_chunks,
|
&mut partial_chunks,
|
||||||
BlockPos::new(0, 71, 0),
|
BlockPos::new(0, 71, 0),
|
||||||
BlockPos::new(0, 71, 1),
|
BlockPos::new(0, 71, 1),
|
||||||
vec![BlockPos::new(0, 70, 0), BlockPos::new(0, 70, 1)],
|
&[BlockPos::new(0, 70, 0), BlockPos::new(0, 70, 1)],
|
||||||
);
|
);
|
||||||
assert_simulation_reaches(&mut simulation, 20, BlockPos::new(0, 71, 1));
|
assert_simulation_reaches(&mut simulation, 20, BlockPos::new(0, 71, 1));
|
||||||
}
|
}
|
||||||
|
@ -1372,7 +1383,7 @@ mod tests {
|
||||||
&mut partial_chunks,
|
&mut partial_chunks,
|
||||||
BlockPos::new(0, 71, 0),
|
BlockPos::new(0, 71, 0),
|
||||||
BlockPos::new(2, 71, 2),
|
BlockPos::new(2, 71, 2),
|
||||||
vec![
|
&[
|
||||||
BlockPos::new(0, 70, 0),
|
BlockPos::new(0, 70, 0),
|
||||||
BlockPos::new(1, 70, 1),
|
BlockPos::new(1, 70, 1),
|
||||||
BlockPos::new(2, 70, 2),
|
BlockPos::new(2, 70, 2),
|
||||||
|
@ -1390,7 +1401,7 @@ mod tests {
|
||||||
&mut partial_chunks,
|
&mut partial_chunks,
|
||||||
BlockPos::new(0, 71, 3),
|
BlockPos::new(0, 71, 3),
|
||||||
BlockPos::new(5, 76, 0),
|
BlockPos::new(5, 76, 0),
|
||||||
vec![
|
&[
|
||||||
BlockPos::new(0, 70, 3),
|
BlockPos::new(0, 70, 3),
|
||||||
BlockPos::new(0, 70, 2),
|
BlockPos::new(0, 70, 2),
|
||||||
BlockPos::new(0, 70, 1),
|
BlockPos::new(0, 70, 1),
|
||||||
|
@ -1412,7 +1423,7 @@ mod tests {
|
||||||
&mut partial_chunks,
|
&mut partial_chunks,
|
||||||
BlockPos::new(0, 71, 0),
|
BlockPos::new(0, 71, 0),
|
||||||
BlockPos::new(0, 71, 3),
|
BlockPos::new(0, 71, 3),
|
||||||
vec![BlockPos::new(0, 70, 0), BlockPos::new(0, 70, 3)],
|
&[BlockPos::new(0, 70, 0), BlockPos::new(0, 70, 3)],
|
||||||
);
|
);
|
||||||
assert_simulation_reaches(&mut simulation, 40, BlockPos::new(0, 71, 3));
|
assert_simulation_reaches(&mut simulation, 40, BlockPos::new(0, 71, 3));
|
||||||
}
|
}
|
||||||
|
@ -1424,7 +1435,7 @@ mod tests {
|
||||||
&mut partial_chunks,
|
&mut partial_chunks,
|
||||||
BlockPos::new(0, 71, 0),
|
BlockPos::new(0, 71, 0),
|
||||||
BlockPos::new(3, 67, 4),
|
BlockPos::new(3, 67, 4),
|
||||||
vec![
|
&[
|
||||||
BlockPos::new(0, 70, 0),
|
BlockPos::new(0, 70, 0),
|
||||||
BlockPos::new(0, 69, 1),
|
BlockPos::new(0, 69, 1),
|
||||||
BlockPos::new(0, 68, 2),
|
BlockPos::new(0, 68, 2),
|
||||||
|
@ -1443,7 +1454,7 @@ mod tests {
|
||||||
&mut partial_chunks,
|
&mut partial_chunks,
|
||||||
BlockPos::new(0, 71, 0),
|
BlockPos::new(0, 71, 0),
|
||||||
BlockPos::new(0, 70, 5),
|
BlockPos::new(0, 70, 5),
|
||||||
vec![
|
&[
|
||||||
BlockPos::new(0, 70, 0),
|
BlockPos::new(0, 70, 0),
|
||||||
BlockPos::new(0, 70, 1),
|
BlockPos::new(0, 70, 1),
|
||||||
BlockPos::new(0, 69, 2),
|
BlockPos::new(0, 69, 2),
|
||||||
|
@ -1460,7 +1471,7 @@ mod tests {
|
||||||
&mut partial_chunks,
|
&mut partial_chunks,
|
||||||
BlockPos::new(0, 71, 0),
|
BlockPos::new(0, 71, 0),
|
||||||
BlockPos::new(0, 68, 3),
|
BlockPos::new(0, 68, 3),
|
||||||
vec![
|
&[
|
||||||
BlockPos::new(0, 70, 0),
|
BlockPos::new(0, 70, 0),
|
||||||
BlockPos::new(0, 69, 1),
|
BlockPos::new(0, 69, 1),
|
||||||
BlockPos::new(0, 68, 2),
|
BlockPos::new(0, 68, 2),
|
||||||
|
@ -1477,7 +1488,7 @@ mod tests {
|
||||||
&mut partial_chunks,
|
&mut partial_chunks,
|
||||||
BlockPos::new(0, 71, 0),
|
BlockPos::new(0, 71, 0),
|
||||||
BlockPos::new(3, 74, 0),
|
BlockPos::new(3, 74, 0),
|
||||||
vec![
|
&[
|
||||||
BlockPos::new(0, 70, 0),
|
BlockPos::new(0, 70, 0),
|
||||||
BlockPos::new(0, 71, 3),
|
BlockPos::new(0, 71, 3),
|
||||||
BlockPos::new(3, 72, 3),
|
BlockPos::new(3, 72, 3),
|
||||||
|
@ -1494,7 +1505,7 @@ mod tests {
|
||||||
&mut partial_chunks,
|
&mut partial_chunks,
|
||||||
BlockPos::new(0, 71, 0),
|
BlockPos::new(0, 71, 0),
|
||||||
BlockPos::new(4, 71, 12),
|
BlockPos::new(4, 71, 12),
|
||||||
vec![
|
&[
|
||||||
BlockPos::new(0, 70, 0),
|
BlockPos::new(0, 70, 0),
|
||||||
BlockPos::new(0, 70, 4),
|
BlockPos::new(0, 70, 4),
|
||||||
BlockPos::new(0, 70, 8),
|
BlockPos::new(0, 70, 8),
|
||||||
|
@ -1512,7 +1523,7 @@ mod tests {
|
||||||
&mut partial_chunks,
|
&mut partial_chunks,
|
||||||
BlockPos::new(0, 71, 0),
|
BlockPos::new(0, 71, 0),
|
||||||
BlockPos::new(4, 74, 9),
|
BlockPos::new(4, 74, 9),
|
||||||
vec![
|
&[
|
||||||
BlockPos::new(0, 70, 0),
|
BlockPos::new(0, 70, 0),
|
||||||
BlockPos::new(0, 70, 1),
|
BlockPos::new(0, 70, 1),
|
||||||
BlockPos::new(0, 70, 2),
|
BlockPos::new(0, 70, 2),
|
||||||
|
@ -1526,4 +1537,41 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_simulation_reaches(&mut simulation, 80, BlockPos::new(4, 74, 9));
|
assert_simulation_reaches(&mut simulation, 80, BlockPos::new(4, 74, 9));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mine_through_non_colliding_block() {
|
||||||
|
let mut partial_chunks = PartialChunkStorage::default();
|
||||||
|
|
||||||
|
let mut simulation = setup_simulation_world(
|
||||||
|
&mut partial_chunks,
|
||||||
|
// the pathfinder can't actually dig straight down, so we start a block to the side so
|
||||||
|
// it can descend correctly
|
||||||
|
BlockPos::new(0, 72, 1),
|
||||||
|
&[BlockPos::new(0, 71, 1)],
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
BlockPos::new(0, 71, 0),
|
||||||
|
azalea_registry::Block::SculkVein.into(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
BlockPos::new(0, 70, 0),
|
||||||
|
azalea_registry::Block::GrassBlock.into(),
|
||||||
|
),
|
||||||
|
// this is an extra check to make sure that we don't accidentally break the block
|
||||||
|
// below (since tnt will break instantly)
|
||||||
|
(BlockPos::new(0, 69, 0), azalea_registry::Block::Tnt.into()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
simulation.app.world_mut().send_event(GotoEvent {
|
||||||
|
entity: simulation.entity,
|
||||||
|
goal: Arc::new(BlockPosGoal(BlockPos::new(0, 70, 0))),
|
||||||
|
successors_fn: moves::default_move,
|
||||||
|
allow_mining: true,
|
||||||
|
min_timeout: PathfinderTimeout::Nodes(1_000_000),
|
||||||
|
max_timeout: PathfinderTimeout::Nodes(5_000_000),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_simulation_reaches(&mut simulation, 200, BlockPos::new(0, 70, 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,13 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use azalea_client::{PhysicsState, inventory::Inventory, packet::game::SendPacketEvent};
|
use azalea_client::{
|
||||||
use azalea_core::{position::Vec3, resource_location::ResourceLocation, tick::GameTick};
|
PhysicsState, interact::CurrentSequenceNumber, inventory::Inventory,
|
||||||
|
local_player::LocalGameMode, mining::MineBundle, packet::game::SendPacketEvent,
|
||||||
|
};
|
||||||
|
use azalea_core::{
|
||||||
|
game_type::GameMode, position::Vec3, resource_location::ResourceLocation, tick::GameTick,
|
||||||
|
};
|
||||||
use azalea_entity::{
|
use azalea_entity::{
|
||||||
Attributes, EntityDimensions, LookDirection, Physics, Position, attributes::AttributeInstance,
|
Attributes, EntityDimensions, LookDirection, Physics, Position, attributes::AttributeInstance,
|
||||||
};
|
};
|
||||||
|
@ -87,7 +92,7 @@ fn create_simulation_instance(chunks: ChunkStorage) -> (App, Arc<RwLock<Instance
|
||||||
fn create_simulation_player_complete_bundle(
|
fn create_simulation_player_complete_bundle(
|
||||||
instance: Arc<RwLock<Instance>>,
|
instance: Arc<RwLock<Instance>>,
|
||||||
player: &SimulatedPlayerBundle,
|
player: &SimulatedPlayerBundle,
|
||||||
) -> impl Bundle + use<> {
|
) -> impl Bundle {
|
||||||
let instance_name = simulation_instance_name();
|
let instance_name = simulation_instance_name();
|
||||||
|
|
||||||
(
|
(
|
||||||
|
@ -100,12 +105,17 @@ fn create_simulation_player_complete_bundle(
|
||||||
azalea_registry::EntityKind::Player,
|
azalea_registry::EntityKind::Player,
|
||||||
instance_name,
|
instance_name,
|
||||||
),
|
),
|
||||||
azalea_client::InstanceHolder {
|
azalea_client::local_player::InstanceHolder {
|
||||||
// partial_instance is never actually used by the pathfinder so
|
// partial_instance is never actually used by the pathfinder so
|
||||||
partial_instance: Arc::new(RwLock::new(PartialInstance::default())),
|
partial_instance: Arc::new(RwLock::new(PartialInstance::default())),
|
||||||
instance: instance.clone(),
|
instance: instance.clone(),
|
||||||
},
|
},
|
||||||
Inventory::default(),
|
Inventory::default(),
|
||||||
|
LocalGameMode::from(GameMode::Survival),
|
||||||
|
MineBundle::default(),
|
||||||
|
CurrentSequenceNumber::default(),
|
||||||
|
azalea_client::local_player::PermissionLevel::default(),
|
||||||
|
azalea_client::local_player::PlayerAbilities::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use azalea_client::InstanceHolder;
|
use azalea_client::local_player::InstanceHolder;
|
||||||
use azalea_world::MinecraftEntityId;
|
use azalea_world::MinecraftEntityId;
|
||||||
use bevy_app::{App, Plugin, Update};
|
use bevy_app::{App, Plugin, Update};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
|
Loading…
Add table
Reference in a new issue