diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 662a1294..cfef34d1 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -78,6 +78,7 @@ use crate::{ player::retroactively_add_game_profile_component, raw_connection::RawConnection, respawn::RespawnPlugin, + send_client_end::TickEndPlugin, task_pool::TaskPoolPlugin, Account, PlayerInfo, }; @@ -927,6 +928,7 @@ impl PluginGroup for DefaultPlugins { .add(MinePlugin) .add(AttackPlugin) .add(ChunkPlugin) + .add(TickEndPlugin) .add(ConfigurationPlugin) .add(TickBroadcastPlugin); #[cfg(feature = "log")] diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index 3e4a48aa..68354c87 100644 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -27,6 +27,7 @@ pub mod ping; mod player; pub mod raw_connection; pub mod respawn; +pub mod send_client_end; pub mod task_pool; pub use account::{Account, AccountOpts}; diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index ec5b751e..dfb35532 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -5,7 +5,7 @@ use azalea_core::tick::GameTick; use azalea_entity::{metadata::Sprinting, Attributes, Jumping}; use azalea_entity::{InLoadedChunk, LastSentPosition, LookDirection, Physics, Position}; use azalea_physics::{ai_step, PhysicsSet}; -use azalea_protocol::packets::game::ServerboundPlayerCommand; +use azalea_protocol::packets::game::{ServerboundPlayerCommand, ServerboundPlayerInput}; use azalea_protocol::packets::{ game::{ s_move_player_pos::ServerboundMovePlayerPos, @@ -19,6 +19,7 @@ use azalea_world::{MinecraftEntityId, MoveEntityError}; use bevy_app::{App, Plugin, Update}; use bevy_ecs::prelude::{Event, EventWriter}; use bevy_ecs::schedule::SystemSet; +use bevy_ecs::system::Commands; use bevy_ecs::{ component::Component, entity::Entity, event::EventReader, query::With, schedule::IntoSystemConfigs, system::Query, @@ -68,6 +69,7 @@ impl Plugin for PlayerMovePlugin { .before(ai_step) .before(azalea_physics::fluids::update_in_water_state_and_do_fluid_pushing), send_sprinting_if_needed.after(azalea_entity::update_in_loaded_chunk), + send_player_input_packet, send_position.after(PhysicsSet), ) .chain(), @@ -251,6 +253,40 @@ pub fn send_position( } } +#[derive(Debug, Default, Component, Clone, PartialEq, Eq)] +pub struct LastSentInput(pub ServerboundPlayerInput); +pub fn send_player_input_packet( + mut query: Query<(Entity, &PhysicsState, &Jumping, Option<&LastSentInput>)>, + mut send_packet_events: EventWriter, + mut commands: Commands, +) { + for (entity, physics_state, jumping, last_sent_input) in query.iter_mut() { + let input = ServerboundPlayerInput { + forward: physics_state.move_direction == WalkDirection::Forward, + backward: physics_state.move_direction == WalkDirection::Backward, + left: physics_state.move_direction == WalkDirection::Left, + right: physics_state.move_direction == WalkDirection::Right, + jump: **jumping, + // TODO: implement sneaking + shift: false, + sprint: physics_state.trying_to_sprint, + }; + + // if LastSentInput isn't present, we default to assuming we're not pressing any + // keys and insert it anyways every time it changes + let last_sent_input = last_sent_input.cloned().unwrap_or_default(); + + if input != last_sent_input.0 { + send_packet_events.send(SendPacketEvent { + sent_by: entity, + packet: input.clone().into_variant(), + }); + println!("sent input packet {:?}", input); + commands.entity(entity).insert(LastSentInput(input)); + } + } +} + fn send_sprinting_if_needed( mut query: Query<(Entity, &MinecraftEntityId, &Sprinting, &mut PhysicsState)>, mut send_packet_events: EventWriter, @@ -510,7 +546,7 @@ pub fn handle_knockback(mut query: Query<&mut Physics>, mut events: EventReader< } } -#[derive(Clone, Copy, Debug, Default)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum WalkDirection { #[default] None, diff --git a/azalea-client/src/send_client_end.rs b/azalea-client/src/send_client_end.rs new file mode 100644 index 00000000..cb3d5e74 --- /dev/null +++ b/azalea-client/src/send_client_end.rs @@ -0,0 +1,36 @@ +//! Clients send a [`ServerboundClientTickEnd`] packet every tick. + +use azalea_core::tick::GameTick; +use azalea_entity::LocalEntity; +use azalea_physics::PhysicsSet; +use azalea_protocol::packets::game::ServerboundClientTickEnd; +use azalea_world::InstanceName; +use bevy_app::{App, Plugin}; +use bevy_ecs::prelude::*; + +use crate::{mining::MiningSet, packet_handling::game::SendPacketEvent}; + +/// A plugin that makes clients send a [`ServerboundClientTickEnd`] packet every +/// tick. +pub struct TickEndPlugin; +impl Plugin for TickEndPlugin { + fn build(&self, app: &mut App) { + app.add_systems( + GameTick, + // this has to happen after every other event that might send packets + game_tick_packet + .after(PhysicsSet) + .after(MiningSet) + .after(crate::movement::send_position), + ); + } +} + +pub fn game_tick_packet( + query: Query, With)>, + mut send_packets: EventWriter, +) { + for entity in query.iter() { + send_packets.send(SendPacketEvent::new(entity, ServerboundClientTickEnd)); + } +} diff --git a/azalea-protocol/src/packets/game/s_player_input.rs b/azalea-protocol/src/packets/game/s_player_input.rs index 6dbd47cd..a16a62d0 100755 --- a/azalea-protocol/src/packets/game/s_player_input.rs +++ b/azalea-protocol/src/packets/game/s_player_input.rs @@ -5,7 +5,7 @@ use azalea_buf::{AzaleaRead, AzaleaWrite}; use azalea_core::bitset::FixedBitSet; use azalea_protocol_macros::ServerboundGamePacket; -#[derive(Clone, Debug, ServerboundGamePacket)] +#[derive(Clone, Debug, Default, PartialEq, Eq, ServerboundGamePacket)] pub struct ServerboundPlayerInput { pub forward: bool, pub backward: bool, diff --git a/azalea/src/bot.rs b/azalea/src/bot.rs index 522f99eb..539a5281 100644 --- a/azalea/src/bot.rs +++ b/azalea/src/bot.rs @@ -43,7 +43,12 @@ impl Plugin for BotPlugin { jump_listener, ), ) - .add_systems(GameTick, stop_jumping.after(PhysicsSet)); + .add_systems( + GameTick, + stop_jumping + .after(PhysicsSet) + .after(azalea_client::movement::send_player_input_packet), + ); } }