mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
improve packet_order test, add BlockUpdatePlugin, fix packet order for sprinting
This commit is contained in:
parent
f9e4b65713
commit
08c409d048
9 changed files with 157 additions and 39 deletions
|
@ -10,17 +10,20 @@ is breaking anyways, semantic versioning is not followed.
|
|||
|
||||
### Added
|
||||
|
||||
- Update to Minecraft 1.21.6.
|
||||
- `HitResult` now contains the entity that's being looked at.
|
||||
- A `QueuedServerBlockUpdates` component that keeps track of block updates per `Update`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Update to Minecraft 1.21.6.
|
||||
- Renamed `azalea_entity::EntityKind` to `EntityKindComponent` to disambiguate with `azalea_registry::EntityKind`.
|
||||
- Moved functions and types related to hit results from `azalea::interact` to `azalea::interact::pick`.
|
||||
- `Client::attack` now takes `Entity` instead of `MinecraftEntityId`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix packet order for loading (`PlayerLoaded`/`MovePlayerPos`) and sprinting (`PlayerInput`/`PlayerCommand`).
|
||||
|
||||
## [0.13.0+mc1.21.5] - 2025-06-15
|
||||
|
||||
### Added
|
||||
|
|
|
@ -47,6 +47,7 @@ use uuid::Uuid;
|
|||
use crate::{
|
||||
Account, DefaultPlugins,
|
||||
attack::{self},
|
||||
block_update::QueuedServerBlockUpdates,
|
||||
chunks::ChunkBatchInfo,
|
||||
connection::RawConnection,
|
||||
disconnect::DisconnectEvent,
|
||||
|
@ -586,7 +587,8 @@ pub struct JoinedClientBundle {
|
|||
pub physics_state: PhysicsState,
|
||||
pub inventory: Inventory,
|
||||
pub tab_list: TabList,
|
||||
pub current_sequence_number: BlockStatePredictionHandler,
|
||||
pub block_state_prediction_handler: BlockStatePredictionHandler,
|
||||
pub queued_server_block_updates: QueuedServerBlockUpdates,
|
||||
pub last_sent_direction: LastSentLookDirection,
|
||||
pub abilities: PlayerAbilities,
|
||||
pub permission_level: PermissionLevel,
|
||||
|
|
49
azalea-client/src/plugins/block_update.rs
Normal file
49
azalea-client/src/plugins/block_update.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use azalea_block::BlockState;
|
||||
use azalea_core::position::BlockPos;
|
||||
use bevy_app::{App, Plugin, Update};
|
||||
use bevy_ecs::prelude::*;
|
||||
|
||||
use crate::{
|
||||
chunks::handle_receive_chunk_event, interact::BlockStatePredictionHandler,
|
||||
local_player::InstanceHolder,
|
||||
};
|
||||
|
||||
pub struct BlockUpdatePlugin;
|
||||
impl Plugin for BlockUpdatePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
// has to be after ReceiveChunkEvent is handled so if we get chunk+blockupdate in one
|
||||
// Update then the block update actually gets applied
|
||||
handle_block_update_event.after(handle_receive_chunk_event),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A component that holds the list of block updates that need to be handled.
|
||||
///
|
||||
/// This is updated by `read_packets` (in `PreUpdate`) and handled/cleared by
|
||||
/// [`handle_block_update_event`] (`Update`).
|
||||
///
|
||||
/// This is a component instead of an ECS event for performance reasons.
|
||||
#[derive(Component, Debug, Clone, Default)]
|
||||
pub struct QueuedServerBlockUpdates {
|
||||
pub list: Vec<(BlockPos, BlockState)>,
|
||||
}
|
||||
|
||||
pub fn handle_block_update_event(
|
||||
mut query: Query<(
|
||||
&mut QueuedServerBlockUpdates,
|
||||
&InstanceHolder,
|
||||
&mut BlockStatePredictionHandler,
|
||||
)>,
|
||||
) {
|
||||
for (mut queued, instance_holder, mut prediction_handler) in query.iter_mut() {
|
||||
let world = instance_holder.instance.read();
|
||||
for (pos, block_state) in queued.list.drain(..) {
|
||||
if !prediction_handler.update_known_server_state(pos, block_state) {
|
||||
world.chunks.set_block_state(pos, block_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ impl Plugin for ChunksPlugin {
|
|||
Update,
|
||||
(
|
||||
handle_chunk_batch_start_event,
|
||||
handle_receive_chunk_events,
|
||||
handle_receive_chunk_event,
|
||||
handle_chunk_batch_finished_event,
|
||||
)
|
||||
.chain()
|
||||
|
@ -65,7 +65,7 @@ pub struct ChunkBatchFinishedEvent {
|
|||
pub batch_size: u32,
|
||||
}
|
||||
|
||||
pub fn handle_receive_chunk_events(
|
||||
pub fn handle_receive_chunk_event(
|
||||
mut events: EventReader<ReceiveChunkEvent>,
|
||||
mut query: Query<&InstanceHolder>,
|
||||
) {
|
||||
|
|
|
@ -56,6 +56,7 @@ pub struct DisconnectEvent {
|
|||
#[derive(Bundle)]
|
||||
pub struct RemoveOnDisconnectBundle {
|
||||
pub joined_client: JoinedClientBundle,
|
||||
|
||||
pub entity: EntityBundle,
|
||||
pub minecraft_entity_id: MinecraftEntityId,
|
||||
pub instance_holder: InstanceHolder,
|
||||
|
@ -69,7 +70,7 @@ pub struct RemoveOnDisconnectBundle {
|
|||
pub chat_signing_session: chat_signing::ChatSigningSession,
|
||||
/// They're not authenticated anymore if they disconnected.
|
||||
pub is_authenticated: IsAuthenticated,
|
||||
// send ServerboundPlayerLoaded next time we join
|
||||
// send ServerboundPlayerLoaded next time we join.
|
||||
pub has_client_loaded: HasClientLoaded,
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ use bevy_app::{PluginGroup, PluginGroupBuilder};
|
|||
|
||||
pub mod attack;
|
||||
pub mod auto_reconnect;
|
||||
pub mod block_update;
|
||||
pub mod brand;
|
||||
pub mod chat;
|
||||
pub mod chat_signing;
|
||||
|
@ -48,6 +49,7 @@ impl PluginGroup for DefaultPlugins {
|
|||
.add(mining::MiningPlugin)
|
||||
.add(attack::AttackPlugin)
|
||||
.add(chunks::ChunksPlugin)
|
||||
.add(block_update::BlockUpdatePlugin)
|
||||
.add(tick_end::TickEndPlugin)
|
||||
.add(loading::PlayerLoadedPlugin)
|
||||
.add(brand::BrandPlugin)
|
||||
|
|
|
@ -65,8 +65,8 @@ impl Plugin for MovementPlugin {
|
|||
.in_set(PhysicsSet)
|
||||
.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_sprinting_if_needed.after(azalea_entity::update_in_loaded_chunk),
|
||||
send_position.after(PhysicsSet),
|
||||
)
|
||||
.chain(),
|
||||
|
|
|
@ -23,6 +23,7 @@ use tracing::{debug, error, trace, warn};
|
|||
|
||||
use crate::{
|
||||
ClientInformation,
|
||||
block_update::QueuedServerBlockUpdates,
|
||||
chat::{ChatPacket, ChatReceivedEvent},
|
||||
chunks,
|
||||
connection::RawConnection,
|
||||
|
@ -1058,17 +1059,10 @@ impl GamePacketHandler<'_> {
|
|||
pub fn block_update(&mut self, p: &ClientboundBlockUpdate) {
|
||||
debug!("Got block update packet {p:?}");
|
||||
|
||||
as_system::<Query<(&InstanceHolder, &mut BlockStatePredictionHandler)>>(
|
||||
self.ecs,
|
||||
|mut query| {
|
||||
let (local_player, mut prediction_handler) = query.get_mut(self.player).unwrap();
|
||||
|
||||
let world = local_player.instance.read();
|
||||
if !prediction_handler.update_known_server_state(p.pos, p.block_state) {
|
||||
world.chunks.set_block_state(p.pos, p.block_state);
|
||||
}
|
||||
},
|
||||
);
|
||||
as_system::<Query<&mut QueuedServerBlockUpdates>>(self.ecs, |mut query| {
|
||||
let mut queued = query.get_mut(self.player).unwrap();
|
||||
queued.list.push((p.pos, p.block_state));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn animate(&mut self, p: &ClientboundAnimate) {
|
||||
|
@ -1078,19 +1072,13 @@ impl GamePacketHandler<'_> {
|
|||
pub fn section_blocks_update(&mut self, p: &ClientboundSectionBlocksUpdate) {
|
||||
debug!("Got section blocks update packet {p:?}");
|
||||
|
||||
as_system::<Query<(&InstanceHolder, &mut BlockStatePredictionHandler)>>(
|
||||
self.ecs,
|
||||
|mut query| {
|
||||
let (local_player, mut prediction_handler) = query.get_mut(self.player).unwrap();
|
||||
let world = local_player.instance.read();
|
||||
for new_state in &p.states {
|
||||
let pos = p.section_pos + new_state.pos;
|
||||
if !prediction_handler.update_known_server_state(pos, new_state.state) {
|
||||
world.chunks.set_block_state(pos, new_state.state);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
as_system::<Query<&mut QueuedServerBlockUpdates>>(self.ecs, |mut query| {
|
||||
let mut queued = query.get_mut(self.player).unwrap();
|
||||
for new_state in &p.states {
|
||||
let pos = p.section_pos + new_state.pos;
|
||||
queued.list.push((pos, new_state.state));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn game_event(&mut self, p: &ClientboundGameEvent) {
|
||||
|
|
|
@ -1,26 +1,32 @@
|
|||
use std::{collections::VecDeque, sync::Arc};
|
||||
|
||||
use azalea_client::{packet::game::SendPacketEvent, test_utils::prelude::*};
|
||||
use azalea_client::{
|
||||
SprintDirection, StartSprintEvent, packet::game::SendPacketEvent, test_utils::prelude::*,
|
||||
};
|
||||
use azalea_core::{
|
||||
position::{ChunkPos, Vec3},
|
||||
position::{BlockPos, ChunkPos, Vec3},
|
||||
resource_location::ResourceLocation,
|
||||
};
|
||||
use azalea_entity::LookDirection;
|
||||
use azalea_protocol::{
|
||||
common::movements::{PositionMoveRotation, RelativeMovements},
|
||||
common::movements::{MoveFlags, PositionMoveRotation, RelativeMovements},
|
||||
packets::{
|
||||
ConnectionProtocol,
|
||||
config::{ClientboundFinishConfiguration, ClientboundRegistryData},
|
||||
game::{ClientboundPlayerPosition, ServerboundAcceptTeleportation, ServerboundGamePacket},
|
||||
game::{
|
||||
ClientboundBlockUpdate, ClientboundPlayerPosition, ServerboundAcceptTeleportation,
|
||||
ServerboundGamePacket, ServerboundMovePlayerPos, ServerboundMovePlayerPosRot,
|
||||
ServerboundMovePlayerStatusOnly,
|
||||
},
|
||||
},
|
||||
};
|
||||
use azalea_registry::{DataRegistry, DimensionType};
|
||||
use azalea_registry::{Block, DataRegistry, DimensionType};
|
||||
use bevy_ecs::observer::Trigger;
|
||||
use parking_lot::Mutex;
|
||||
use simdnbt::owned::{NbtCompound, NbtTag};
|
||||
|
||||
#[test]
|
||||
fn test_set_health_before_login() {
|
||||
fn test_packet_order() {
|
||||
init_tracing();
|
||||
|
||||
let mut simulation = Simulation::new(ConnectionProtocol::Configuration);
|
||||
|
@ -53,16 +59,26 @@ fn test_set_health_before_login() {
|
|||
|
||||
// receive a chunk so the player is "loaded" now
|
||||
simulation.receive_packet(make_basic_empty_chunk(ChunkPos::new(0, 0), (384 + 64) / 16));
|
||||
simulation.receive_packet(ClientboundBlockUpdate {
|
||||
pos: BlockPos::new(1, 1, 3),
|
||||
block_state: Block::Stone.into(),
|
||||
});
|
||||
simulation.receive_packet(ClientboundPlayerPosition {
|
||||
id: 1,
|
||||
change: PositionMoveRotation {
|
||||
pos: Vec3::new(1., 2., 3.),
|
||||
pos: Vec3::new(1.5, 2., 3.5),
|
||||
delta: Vec3::ZERO,
|
||||
look_direction: LookDirection::default(),
|
||||
},
|
||||
relative: RelativeMovements::all_absolute(),
|
||||
});
|
||||
simulation.tick();
|
||||
|
||||
assert_eq!(
|
||||
simulation.get_block_state(BlockPos::new(1, 1, 3)),
|
||||
Some(Block::Stone.into())
|
||||
);
|
||||
|
||||
println!("sent_packets: {:?}", sent_packets.list.lock());
|
||||
sent_packets.expect("AcceptTeleportation", |p| {
|
||||
matches!(
|
||||
|
@ -71,7 +87,16 @@ fn test_set_health_before_login() {
|
|||
)
|
||||
});
|
||||
sent_packets.expect("MovePlayerPosRot", |p| {
|
||||
matches!(p, ServerboundGamePacket::MovePlayerPosRot(_))
|
||||
matches!(
|
||||
p,
|
||||
ServerboundGamePacket::MovePlayerPosRot(ServerboundMovePlayerPosRot {
|
||||
flags: MoveFlags {
|
||||
on_ground: false,
|
||||
horizontal_collision: false
|
||||
},
|
||||
..
|
||||
})
|
||||
)
|
||||
});
|
||||
|
||||
// in vanilla these might be sent in a later tick (depending on how long it
|
||||
|
@ -81,11 +106,59 @@ fn test_set_health_before_login() {
|
|||
matches!(p, ServerboundGamePacket::PlayerLoaded(_))
|
||||
});
|
||||
sent_packets.expect("MovePlayerPos", |p| {
|
||||
matches!(p, ServerboundGamePacket::MovePlayerPos(_))
|
||||
matches!(
|
||||
p,
|
||||
ServerboundGamePacket::MovePlayerPos(ServerboundMovePlayerPos {
|
||||
flags: MoveFlags {
|
||||
on_ground: false,
|
||||
horizontal_collision: false
|
||||
},
|
||||
..
|
||||
})
|
||||
)
|
||||
});
|
||||
|
||||
sent_packets.expect_tick_end();
|
||||
sent_packets.expect_empty();
|
||||
|
||||
// it takes a tick for on_ground to be true
|
||||
simulation.tick();
|
||||
sent_packets.expect("MovePlayerStatusOnly", |p| {
|
||||
matches!(
|
||||
p,
|
||||
ServerboundGamePacket::MovePlayerStatusOnly(ServerboundMovePlayerStatusOnly {
|
||||
flags: MoveFlags {
|
||||
on_ground: true,
|
||||
horizontal_collision: false
|
||||
}
|
||||
})
|
||||
)
|
||||
});
|
||||
sent_packets.expect_tick_end();
|
||||
sent_packets.expect_empty();
|
||||
|
||||
// make sure nothing happens now
|
||||
simulation.tick();
|
||||
sent_packets.expect_tick_end();
|
||||
sent_packets.expect_empty();
|
||||
|
||||
// now sprint for a tick
|
||||
simulation.send_event(StartSprintEvent {
|
||||
entity: simulation.entity,
|
||||
direction: SprintDirection::Forward,
|
||||
});
|
||||
simulation.tick();
|
||||
sent_packets.expect("PlayerInput", |p| {
|
||||
matches!(p, ServerboundGamePacket::PlayerInput(_))
|
||||
});
|
||||
sent_packets.expect("PlayerCommand", |p| {
|
||||
matches!(p, ServerboundGamePacket::PlayerCommand(_))
|
||||
});
|
||||
sent_packets.expect("MovePlayerPos", |p| {
|
||||
matches!(p, ServerboundGamePacket::MovePlayerPos(_))
|
||||
});
|
||||
sent_packets.expect_tick_end();
|
||||
sent_packets.expect_empty();
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
Loading…
Add table
Reference in a new issue