Skip to content

Commit

Permalink
start adding sneaking
Browse files Browse the repository at this point in the history
  • Loading branch information
mat-1 committed Mar 6, 2024
1 parent 737af37 commit 83dc3dd
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 43 deletions.
3 changes: 2 additions & 1 deletion azalea-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use azalea_core::{position::Vec3, tick::GameTick};
use azalea_entity::{
indexing::{EntityIdIndex, EntityUuidIndex},
metadata::Health,
EntityPlugin, EntityUpdateSet, EyeHeight, LocalEntity, Position,
EntityPlugin, EntityUpdateSet, EyeHeight, LocalEntity, Position, Sneaking,
};
use azalea_physics::PhysicsPlugin;
use azalea_protocol::{
Expand Down Expand Up @@ -640,6 +640,7 @@ pub struct JoinedClientBundle {
pub permission_level: PermissionLevel,
pub chunk_batch_info: ChunkBatchInfo,
pub hunger: Hunger,
pub sneaking: Sneaking,

pub entity_id_index: EntityIdIndex,

Expand Down
15 changes: 0 additions & 15 deletions azalea-client/src/local_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,6 @@ pub struct LocalGameMode {
pub previous: Option<GameMode>,
}

/// A component that contains the abilities the player has, like flying
/// or instantly breaking blocks. This is only present on local players.
#[derive(Clone, Debug, Component, Default)]
pub struct PlayerAbilities {
pub invulnerable: bool,
pub flying: bool,
pub can_fly: bool,
/// Whether the player can instantly break blocks and can duplicate blocks
/// in their inventory.
pub instant_break: bool,

pub flying_speed: f32,
/// Used for the fov
pub walking_speed: f32,
}
impl From<&ClientboundPlayerAbilitiesPacket> for PlayerAbilities {
fn from(packet: &ClientboundPlayerAbilitiesPacket) -> Self {
Self {
Expand Down
162 changes: 149 additions & 13 deletions azalea-client/src/movement.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use crate::client::Client;
use crate::inventory::InventoryComponent;
use crate::local_player::PlayerAbilities;
use crate::packet_handling::game::SendPacketEvent;
use azalea_core::position::Vec3;
use azalea_core::tick::GameTick;
use azalea_entity::metadata::{ShiftKeyDown, Sleeping, SleepingPos, Swimming};
use azalea_entity::{metadata::Sprinting, Attributes, Jumping};
use azalea_entity::{InLoadedChunk, LastSentPosition, LookDirection, Physics, Position};
use azalea_entity::{InLoadedChunk, LastSentPosition, LookDirection, Physics, Position, Sneaking};
use azalea_physics::{ai_step, PhysicsSet};
use azalea_protocol::packets::game::serverbound_player_command_packet::ServerboundPlayerCommandPacket;
use azalea_protocol::packets::game::{
Expand All @@ -12,10 +15,11 @@ use azalea_protocol::packets::game::{
serverbound_move_player_rot_packet::ServerboundMovePlayerRotPacket,
serverbound_move_player_status_only_packet::ServerboundMovePlayerStatusOnlyPacket,
};
use azalea_world::{MinecraftEntityId, MoveEntityError};
use azalea_world::{InstanceContainer, InstanceName, MinecraftEntityId, MoveEntityError};
use bevy_app::{App, Plugin, Update};
use bevy_ecs::prelude::{Event, EventWriter};
use bevy_ecs::schedule::SystemSet;
use bevy_ecs::system::Res;
use bevy_ecs::{
component::Component, entity::Entity, event::EventReader, query::With,
schedule::IntoSystemConfigs, system::Query,
Expand Down Expand Up @@ -57,11 +61,13 @@ impl Plugin for PlayerMovePlugin {
.add_systems(
GameTick,
(
(tick_controls, local_player_ai_step)
(update_sneaking, tick_controls, local_player_ai_step)
.chain()
.in_set(PhysicsSet)
.before(ai_step),
send_sprinting_if_needed.after(azalea_entity::update_in_loaded_chunk),
(send_sprinting_if_needed, send_shift_key_down_if_needed)
.chain()
.after(azalea_entity::update_in_loaded_chunk),
send_position.after(PhysicsSet),
)
.chain(),
Expand Down Expand Up @@ -117,7 +123,7 @@ pub struct LastSentLookDirection {
pub y_rot: f32,
}

/// Component for entities that can move and sprint. Usually only in
/// Component for entities that can move, sprint, and sneak. Usually only in
/// [`LocalEntity`]s.
///
/// [`LocalEntity`]: azalea_entity::LocalEntity
Expand All @@ -126,7 +132,15 @@ pub struct PhysicsState {
/// Minecraft only sends a movement packet either after 20 ticks or if the
/// player moved enough. This is that tick counter.
pub position_remainder: u32,

pub was_sprinting: bool,

/// Whether the player was sneaking last tick. You shouldn't modify this.
///
/// If you want to check or change the player's sneak state from the ECS,
/// use the [`ShiftKeyDown`] component.
pub was_sneaking: bool,

// Whether we're going to try to start sprinting this tick. Equivalent to
// holding down ctrl for a tick.
pub trying_to_sprint: bool,
Expand Down Expand Up @@ -251,33 +265,134 @@ fn send_sprinting_if_needed(
mut query: Query<(Entity, &MinecraftEntityId, &Sprinting, &mut PhysicsState)>,
mut send_packet_events: EventWriter<SendPacketEvent>,
) {
for (entity, minecraft_entity_id, sprinting, mut physics_state) in query.iter_mut() {
for (entity, &minecraft_entity_id, &Sprinting(sprinting), mut physics_state) in query.iter_mut()
{
let was_sprinting = physics_state.was_sprinting;
if **sprinting != was_sprinting {
let sprinting_action = if **sprinting {
if sprinting != was_sprinting {
let sprinting_action = if sprinting {
azalea_protocol::packets::game::serverbound_player_command_packet::Action::StartSprinting
} else {
azalea_protocol::packets::game::serverbound_player_command_packet::Action::StopSprinting
};
send_packet_events.send(SendPacketEvent {
entity,
packet: ServerboundPlayerCommandPacket {
id: **minecraft_entity_id,
id: *minecraft_entity_id,
action: sprinting_action,
data: 0,
}
.get(),
});
physics_state.was_sprinting = **sprinting;
physics_state.was_sprinting = sprinting;
}
}
}

fn send_shift_key_down_if_needed(
mut query: Query<(Entity, &MinecraftEntityId, &ShiftKeyDown, &mut PhysicsState)>,
mut send_packet_events: EventWriter<SendPacketEvent>,
) {
for (entity, &minecraft_entity_id, &ShiftKeyDown(sneaking), mut physics_state) in
query.iter_mut()
{
let was_sneaking = physics_state.was_sneaking;
if sneaking != was_sneaking {
let sneaking_action = if sneaking {
azalea_protocol::packets::game::serverbound_player_command_packet::Action::PressShiftKey
} else {
azalea_protocol::packets::game::serverbound_player_command_packet::Action::ReleaseShiftKey
};
send_packet_events.send(SendPacketEvent {
entity,
packet: ServerboundPlayerCommandPacket {
id: *minecraft_entity_id,
action: sneaking_action,
data: 0,
}
.get(),
});
physics_state.was_sneaking = sneaking;
}
}
}

fn can_player_fit_within_blocks_and_entities_when(
_pose: azalea_entity::Pose,
_position: Vec3,
_instance: &azalea_world::Instance,
) -> bool {
// TODO
true
}

pub fn update_sneaking(
mut query: Query<(
&PlayerAbilities,
&Swimming,
// TODO: isPassenger
&ShiftKeyDown,
&SleepingPos,
&Position,
&InstanceName,
&mut Sneaking,
)>,
instances: Res<InstanceContainer>,
) {
for (
player_abilities,
&Swimming(swimming),
&ShiftKeyDown(shift_key_down),
&SleepingPos(sleeping_pos),
&Position(position),
instance_name,
mut sneaking,
) in query.iter_mut()
{
let Some(instance) = instances.get(instance_name) else {
continue;
};
// this.crouching = !this.getAbilities().flying
// && !this.isSwimming()
// && !this.isPassenger()
// && this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.CROUCHING)
// && (this.isShiftKeyDown()
// || !this.isSleeping() && !this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.STANDING));

let instance = instance.read();

**sneaking = !player_abilities.flying
&& !swimming
// && !isPassenger
// && this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.CROUCHING)
&& can_player_fit_within_blocks_and_entities_when(
azalea_entity::Pose::Crouching,
position,
&instance,
)
&& (shift_key_down
|| (
sleeping_pos.is_none()
&& !can_player_fit_within_blocks_and_entities_when(azalea_entity::Pose::Standing, position, &instance)
)
);
}
}

fn is_moving_slowly(sneaking: bool, is_visually_crawling: bool) -> bool {
sneaking || is_visually_crawling
}

/// Update the impulse from self.move_direction. The multiplier is used for
/// sneaking.
pub(crate) fn tick_controls(mut query: Query<&mut PhysicsState>) {
for mut physics_state in query.iter_mut() {
let multiplier: Option<f32> = None;
pub(crate) fn tick_controls(mut query: Query<(&mut PhysicsState, &Sneaking, &InventoryComponent)>) {
for (mut physics_state, &Sneaking(sneaking), inventory) in query.iter_mut() {
let multiplier: Option<f32> = if is_moving_slowly(sneaking, false) {
let sneaking_speed_bonus =
azalea_entity::enchantments::get_sneaking_speed_bonus(&inventory.inventory_menu);
Some(f32::clamp(0.3 + sneaking_speed_bonus, 0., 1.))
} else {
None
};

let mut forward_impulse: f32 = 0.;
let mut left_impulse: f32 = 0.;
Expand Down Expand Up @@ -397,6 +512,27 @@ impl Client {
direction,
});
}

/// Start or stop sneaking.
///
/// ```rust,no_run
/// # use azalea_client::{Client, WalkDirection, SprintDirection};
/// # use std::time::Duration;
/// # async fn example(mut bot: Client) {
/// // toggle sneak
/// bot.sneak(!bot.sneaking());
/// # }
/// ```
pub fn sneak(&mut self, sneaking: bool) {
let mut ecs = self.ecs.lock();
let mut sneaking_component = self.query::<&mut ShiftKeyDown>(&mut ecs);
**sneaking_component = sneaking;
}

/// Returns whether the player is sneaking.
pub fn sneaking(&self) -> bool {
*self.component::<ShiftKeyDown>()
}
}

/// An event sent when the client starts walking. This does not get sent for
Expand Down
3 changes: 2 additions & 1 deletion azalea-client/src/packet_handling/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ pub fn process_packet_events(ecs: &mut World) {
last_sent_direction: crate::movement::LastSentLookDirection::default(),
abilities: crate::local_player::PlayerAbilities::default(),
permission_level: crate::local_player::PermissionLevel::default(),
hunger: Hunger::default(),
chunk_batch_info: crate::chunks::ChunkBatchInfo::default(),
hunger: Hunger::default(),
sneaking: azalea_entity::Sneaking::default(),

entity_id_index: EntityIdIndex::default(),

Expand Down
12 changes: 11 additions & 1 deletion azalea-entity/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,19 @@ pub enum Pose {
Sleeping,
Swimming,
SpinAttack,
Sneaking,
Crouching,
LongJumping,
Dying,
Croaking,
UsingTongue,
Sitting,
Roaring,
Sniffing,
Emerging,
Digging,
Sliding,
Shooting,
Inhaling,
}

#[derive(Debug, Clone, McBuf)]
Expand Down
8 changes: 7 additions & 1 deletion azalea-entity/src/enchantments.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
use azalea_registry::Enchantment;

pub fn get_enchant_level(
_enchantment: azalea_registry::Enchantment,
_enchantment: Enchantment,
_player_inventory: &azalea_inventory::Menu,
) -> u32 {
// TODO

0
}

pub fn get_sneaking_speed_bonus(player_inventory: &azalea_inventory::Menu) -> f32 {
get_enchant_level(Enchantment::SwiftSneak, player_inventory) as f32 * 0.15
}
24 changes: 22 additions & 2 deletions azalea-entity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod attributes;
mod data;
mod dimensions;
mod effects;
mod enchantments;
pub mod enchantments;
pub mod metadata;
pub mod mining;
mod plugin;
Expand Down Expand Up @@ -138,7 +138,7 @@ impl Debug for EntityUuid {
/// You are free to change this; there's systems that update the indexes
/// automatically.
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
pub struct Position(Vec3);
pub struct Position(pub Vec3);
impl Position {
pub fn new(pos: Vec3) -> Self {
Self(pos)
Expand Down Expand Up @@ -425,6 +425,26 @@ impl FluidOnEyes {
#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
pub struct OnClimbable(bool);

/// A component that indicates whether the player is currently sneaking.
#[derive(Component, Clone, Deref, DerefMut, Default)]
pub struct Sneaking(pub bool);

/// A component that contains the abilities the player has, like flying
/// or instantly breaking blocks. This is only present on local players.
#[derive(Clone, Debug, Component, Default)]
pub struct PlayerAbilities {
pub invulnerable: bool,
pub flying: bool,
pub can_fly: bool,
/// Whether the player can instantly break blocks and can duplicate blocks
/// in their inventory.
pub instant_break: bool,

pub flying_speed: f32,
/// Used for the fov
pub walking_speed: f32,
}

// #[cfg(test)]
// mod tests {
// use super::*;
Expand Down
Loading

0 comments on commit 83dc3dd

Please sign in to comment.