aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2025-06-26 12:32:01 +0930
committermat <git@matdoes.dev>2025-06-26 10:05:58 +0700
commitf9e4b65713bbacabcd54416a388a92b90f56ab47 (patch)
tree25d392d60836351311e9f498d40277c226c1e32c
parentaf1ef9310093aa3c8dfd5054eb6d0b8c7c0d0b31 (diff)
downloadazalea-drasl-f9e4b65713bbacabcd54416a388a92b90f56ab47.tar.xz
start adding packet_order test
-rw-r--r--azalea-client/src/lib.rs1
-rw-r--r--azalea-client/src/plugins/attack.rs1
-rw-r--r--azalea-client/src/plugins/loading.rs9
-rw-r--r--azalea-client/src/plugins/movement.rs36
-rw-r--r--azalea-client/src/plugins/packet/game/mod.rs17
-rw-r--r--azalea-client/tests/packet_order.rs150
-rw-r--r--azalea-protocol/src/common/movements.rs30
-rw-r--r--azalea-protocol/src/packets/game/s_move_player_pos.rs4
-rw-r--r--azalea-protocol/src/packets/game/s_move_player_pos_rot.rs4
-rw-r--r--azalea-protocol/src/packets/game/s_move_player_rot.rs4
-rw-r--r--azalea-protocol/src/packets/game/s_move_player_status_only.rs4
11 files changed, 224 insertions, 36 deletions
diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs
index df04a606..19e13253 100644
--- a/azalea-client/src/lib.rs
+++ b/azalea-client/src/lib.rs
@@ -16,6 +16,7 @@ pub mod ping;
pub mod player;
mod plugins;
+#[cfg(feature = "log")]
#[doc(hidden)]
pub mod test_utils;
diff --git a/azalea-client/src/plugins/attack.rs b/azalea-client/src/plugins/attack.rs
index 86ed5de5..ec4337e5 100644
--- a/azalea-client/src/plugins/attack.rs
+++ b/azalea-client/src/plugins/attack.rs
@@ -90,6 +90,7 @@ impl Client {
pub struct AttackQueued {
pub target: Entity,
}
+#[allow(clippy::type_complexity)]
pub fn handle_attack_queued(
mut commands: Commands,
mut query: Query<(
diff --git a/azalea-client/src/plugins/loading.rs b/azalea-client/src/plugins/loading.rs
index 217d6f75..b195868b 100644
--- a/azalea-client/src/plugins/loading.rs
+++ b/azalea-client/src/plugins/loading.rs
@@ -14,8 +14,8 @@ impl Plugin for PlayerLoadedPlugin {
GameTick,
player_loaded_packet
.after(PhysicsSet)
- .after(MiningSet)
- .after(crate::movement::send_position),
+ .before(MiningSet)
+ .before(crate::movement::send_position),
);
}
}
@@ -36,8 +36,11 @@ pub fn player_loaded_packet(
Entity,
(
With<LocalEntity>,
- With<InLoadedChunk>,
Without<HasClientLoaded>,
+ // the vanilla client waits for the chunk mesh to be "compiled" for the renderer (or
+ // some other conditions) before sending PlayerLoaded. see LevelLoadStatusManager.tick
+ // in the decompiled source
+ With<InLoadedChunk>,
),
>,
) {
diff --git a/azalea-client/src/plugins/movement.rs b/azalea-client/src/plugins/movement.rs
index b4649f20..5d43261f 100644
--- a/azalea-client/src/plugins/movement.rs
+++ b/azalea-client/src/plugins/movement.rs
@@ -6,14 +6,17 @@ use azalea_entity::{
metadata::Sprinting,
};
use azalea_physics::{PhysicsSet, ai_step};
-use azalea_protocol::packets::{
- Packet,
- game::{
- ServerboundPlayerCommand, ServerboundPlayerInput,
- s_move_player_pos::ServerboundMovePlayerPos,
- s_move_player_pos_rot::ServerboundMovePlayerPosRot,
- s_move_player_rot::ServerboundMovePlayerRot,
- s_move_player_status_only::ServerboundMovePlayerStatusOnly,
+use azalea_protocol::{
+ common::movements::MoveFlags,
+ packets::{
+ Packet,
+ game::{
+ ServerboundPlayerCommand, ServerboundPlayerInput,
+ s_move_player_pos::ServerboundMovePlayerPos,
+ s_move_player_pos_rot::ServerboundMovePlayerPosRot,
+ s_move_player_rot::ServerboundMovePlayerRot,
+ s_move_player_status_only::ServerboundMovePlayerStatusOnly,
+ },
},
};
use azalea_world::{MinecraftEntityId, MoveEntityError};
@@ -186,12 +189,16 @@ pub fn send_position(
// if self.is_passenger() {
// TODO: posrot packet for being a passenger
// }
+ let flags = MoveFlags {
+ on_ground: physics.on_ground(),
+ horizontal_collision: physics.horizontal_collision,
+ };
let packet = if sending_position && sending_direction {
Some(
ServerboundMovePlayerPosRot {
pos: **position,
look_direction: *direction,
- on_ground: physics.on_ground(),
+ flags,
}
.into_variant(),
)
@@ -199,7 +206,7 @@ pub fn send_position(
Some(
ServerboundMovePlayerPos {
pos: **position,
- on_ground: physics.on_ground(),
+ flags,
}
.into_variant(),
)
@@ -207,17 +214,12 @@ pub fn send_position(
Some(
ServerboundMovePlayerRot {
look_direction: *direction,
- on_ground: physics.on_ground(),
+ flags,
}
.into_variant(),
)
} else if physics.last_on_ground() != physics.on_ground() {
- Some(
- ServerboundMovePlayerStatusOnly {
- on_ground: physics.on_ground(),
- }
- .into_variant(),
- )
+ Some(ServerboundMovePlayerStatusOnly { flags }.into_variant())
} else {
None
};
diff --git a/azalea-client/src/plugins/packet/game/mod.rs b/azalea-client/src/plugins/packet/game/mod.rs
index 1ea4db10..d9940937 100644
--- a/azalea-client/src/plugins/packet/game/mod.rs
+++ b/azalea-client/src/plugins/packet/game/mod.rs
@@ -12,7 +12,10 @@ use azalea_entity::{
indexing::{EntityIdIndex, EntityUuidIndex},
metadata::{Health, apply_metadata},
};
-use azalea_protocol::packets::{ConnectionProtocol, game::*};
+use azalea_protocol::{
+ common::movements::MoveFlags,
+ packets::{ConnectionProtocol, game::*},
+};
use azalea_world::{InstanceContainer, InstanceName, MinecraftEntityId, PartialInstance};
use bevy_ecs::{prelude::*, system::SystemState};
pub use events::*;
@@ -319,15 +322,6 @@ impl GamePacketHandler<'_> {
.entity(self.player)
.insert(LoadedBy(HashSet::from_iter(vec![self.player])));
}
-
- // send the client information that we have set
- debug!(
- "Sending client information because login: {:?}",
- client_information
- );
- commands.trigger(SendPacketEvent::new(self.player,
- azalea_protocol::packets::game::s_client_information::ServerboundClientInformation { client_information: client_information.clone() },
- ));
},
);
}
@@ -443,8 +437,7 @@ impl GamePacketHandler<'_> {
ServerboundMovePlayerPosRot {
pos: **position,
look_direction: *direction,
- // this is always false
- on_ground: false,
+ flags: MoveFlags::default(),
},
));
});
diff --git a/azalea-client/tests/packet_order.rs b/azalea-client/tests/packet_order.rs
new file mode 100644
index 00000000..1d3b29a2
--- /dev/null
+++ b/azalea-client/tests/packet_order.rs
@@ -0,0 +1,150 @@
+use std::{collections::VecDeque, sync::Arc};
+
+use azalea_client::{packet::game::SendPacketEvent, test_utils::prelude::*};
+use azalea_core::{
+ position::{ChunkPos, Vec3},
+ resource_location::ResourceLocation,
+};
+use azalea_entity::LookDirection;
+use azalea_protocol::{
+ common::movements::{PositionMoveRotation, RelativeMovements},
+ packets::{
+ ConnectionProtocol,
+ config::{ClientboundFinishConfiguration, ClientboundRegistryData},
+ game::{ClientboundPlayerPosition, ServerboundAcceptTeleportation, ServerboundGamePacket},
+ },
+};
+use azalea_registry::{DataRegistry, DimensionType};
+use bevy_ecs::observer::Trigger;
+use parking_lot::Mutex;
+use simdnbt::owned::{NbtCompound, NbtTag};
+
+#[test]
+fn test_set_health_before_login() {
+ init_tracing();
+
+ let mut simulation = Simulation::new(ConnectionProtocol::Configuration);
+ let sent_packets = SentPackets::new(&mut simulation);
+
+ simulation.receive_packet(ClientboundRegistryData {
+ registry_id: ResourceLocation::new("minecraft:dimension_type"),
+ entries: vec![(
+ ResourceLocation::new("minecraft:overworld"),
+ Some(NbtCompound::from_values(vec![
+ ("height".into(), NbtTag::Int(384)),
+ ("min_y".into(), NbtTag::Int(-64)),
+ ])),
+ )]
+ .into_iter()
+ .collect(),
+ });
+ simulation.tick();
+ simulation.receive_packet(ClientboundFinishConfiguration);
+ simulation.tick();
+
+ simulation.receive_packet(make_basic_login_packet(
+ DimensionType::new_raw(0), // overworld
+ ResourceLocation::new("minecraft:overworld"),
+ ));
+ simulation.tick();
+
+ sent_packets.expect_tick_end();
+ sent_packets.expect_empty();
+
+ // 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(ClientboundPlayerPosition {
+ id: 1,
+ change: PositionMoveRotation {
+ pos: Vec3::new(1., 2., 3.),
+ delta: Vec3::ZERO,
+ look_direction: LookDirection::default(),
+ },
+ relative: RelativeMovements::all_absolute(),
+ });
+ simulation.tick();
+ println!("sent_packets: {:?}", sent_packets.list.lock());
+ sent_packets.expect("AcceptTeleportation", |p| {
+ matches!(
+ p,
+ ServerboundGamePacket::AcceptTeleportation(ServerboundAcceptTeleportation { id: 1 })
+ )
+ });
+ sent_packets.expect("MovePlayerPosRot", |p| {
+ matches!(p, ServerboundGamePacket::MovePlayerPosRot(_))
+ });
+
+ // in vanilla these might be sent in a later tick (depending on how long it
+ // takes to render the chunks)... see the comment in player_loaded_packet.
+ // this might be worth changing later for better anticheat compat?
+ sent_packets.expect("PlayerLoaded", |p| {
+ matches!(p, ServerboundGamePacket::PlayerLoaded(_))
+ });
+ sent_packets.expect("MovePlayerPos", |p| {
+ matches!(p, ServerboundGamePacket::MovePlayerPos(_))
+ });
+
+ sent_packets.expect_tick_end();
+ sent_packets.expect_empty();
+}
+
+#[derive(Clone)]
+pub struct SentPackets {
+ list: Arc<Mutex<VecDeque<ServerboundGamePacket>>>,
+}
+impl SentPackets {
+ pub fn new(simulation: &mut Simulation) -> Self {
+ let sent_packets = SentPackets {
+ list: Default::default(),
+ };
+
+ let simulation_entity = simulation.entity;
+ let sent_packets_clone = sent_packets.clone();
+ simulation
+ .app
+ .add_observer(move |trigger: Trigger<SendPacketEvent>| {
+ if trigger.sent_by == simulation_entity {
+ sent_packets_clone
+ .list
+ .lock()
+ .push_back(trigger.event().packet.clone())
+ }
+ });
+
+ sent_packets
+ }
+
+ pub fn clear(&self) {
+ self.list.lock().clear();
+ }
+
+ pub fn expect_tick_end(&self) {
+ self.expect("TickEnd", |p| {
+ matches!(p, ServerboundGamePacket::ClientTickEnd(_))
+ });
+ }
+ pub fn expect_empty(&self) {
+ let sent_packet = self.next();
+ if let None = sent_packet {
+ } else {
+ panic!("Expected no packet, got {sent_packet:?}");
+ }
+ }
+ pub fn expect(
+ &self,
+ expected_formatted: &str,
+ check: impl FnOnce(&ServerboundGamePacket) -> bool,
+ ) {
+ let sent_packet = self.next();
+ if let Some(sent_packet) = sent_packet {
+ if !check(&sent_packet) {
+ panic!("Expected {expected_formatted}, got {sent_packet:?}");
+ }
+ } else {
+ panic!("Expected {expected_formatted}, got nothing");
+ }
+ }
+ pub fn next(&self) -> Option<ServerboundGamePacket> {
+ self.list.lock().pop_front()
+ }
+}
diff --git a/azalea-protocol/src/common/movements.rs b/azalea-protocol/src/common/movements.rs
index a70342b3..e88fb87e 100644
--- a/azalea-protocol/src/common/movements.rs
+++ b/azalea-protocol/src/common/movements.rs
@@ -131,3 +131,33 @@ impl AzaleaWrite for RelativeMovements {
set.azalea_write(buf)
}
}
+
+#[derive(Clone, Copy, Debug, Default)]
+pub struct MoveFlags {
+ pub on_ground: bool,
+ pub horizontal_collision: bool,
+}
+impl AzaleaWrite for MoveFlags {
+ fn azalea_write(&self, buf: &mut impl io::Write) -> Result<(), io::Error> {
+ let mut bitset = FixedBitSet::<8>::new();
+ if self.on_ground {
+ bitset.set(0);
+ }
+ if self.horizontal_collision {
+ bitset.set(1);
+ }
+ bitset.azalea_write(buf)?;
+ Ok(())
+ }
+}
+impl AzaleaRead for MoveFlags {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let bitset = FixedBitSet::<8>::azalea_read(buf)?;
+ let on_ground = bitset.index(0);
+ let horizontal_collision = bitset.index(1);
+ Ok(Self {
+ on_ground,
+ horizontal_collision,
+ })
+ }
+}
diff --git a/azalea-protocol/src/packets/game/s_move_player_pos.rs b/azalea-protocol/src/packets/game/s_move_player_pos.rs
index 2daf1d42..fd1f4a9f 100644
--- a/azalea-protocol/src/packets/game/s_move_player_pos.rs
+++ b/azalea-protocol/src/packets/game/s_move_player_pos.rs
@@ -2,8 +2,10 @@ use azalea_buf::AzBuf;
use azalea_core::position::Vec3;
use azalea_protocol_macros::ServerboundGamePacket;
+use crate::common::movements::MoveFlags;
+
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
pub struct ServerboundMovePlayerPos {
pub pos: Vec3,
- pub on_ground: bool,
+ pub flags: MoveFlags,
}
diff --git a/azalea-protocol/src/packets/game/s_move_player_pos_rot.rs b/azalea-protocol/src/packets/game/s_move_player_pos_rot.rs
index 3460c709..feee0137 100644
--- a/azalea-protocol/src/packets/game/s_move_player_pos_rot.rs
+++ b/azalea-protocol/src/packets/game/s_move_player_pos_rot.rs
@@ -3,9 +3,11 @@ use azalea_core::position::Vec3;
use azalea_entity::LookDirection;
use azalea_protocol_macros::ServerboundGamePacket;
+use crate::common::movements::MoveFlags;
+
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
pub struct ServerboundMovePlayerPosRot {
pub pos: Vec3,
pub look_direction: LookDirection,
- pub on_ground: bool,
+ pub flags: MoveFlags,
}
diff --git a/azalea-protocol/src/packets/game/s_move_player_rot.rs b/azalea-protocol/src/packets/game/s_move_player_rot.rs
index 6aef91b0..d158af8c 100644
--- a/azalea-protocol/src/packets/game/s_move_player_rot.rs
+++ b/azalea-protocol/src/packets/game/s_move_player_rot.rs
@@ -2,8 +2,10 @@ use azalea_buf::AzBuf;
use azalea_entity::LookDirection;
use azalea_protocol_macros::ServerboundGamePacket;
+use crate::common::movements::MoveFlags;
+
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
pub struct ServerboundMovePlayerRot {
pub look_direction: LookDirection,
- pub on_ground: bool,
+ pub flags: MoveFlags,
}
diff --git a/azalea-protocol/src/packets/game/s_move_player_status_only.rs b/azalea-protocol/src/packets/game/s_move_player_status_only.rs
index 155841f0..162b6b0e 100644
--- a/azalea-protocol/src/packets/game/s_move_player_status_only.rs
+++ b/azalea-protocol/src/packets/game/s_move_player_status_only.rs
@@ -1,7 +1,9 @@
use azalea_buf::AzBuf;
use azalea_protocol_macros::ServerboundGamePacket;
+use crate::common::movements::MoveFlags;
+
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
pub struct ServerboundMovePlayerStatusOnly {
- pub on_ground: bool,
+ pub flags: MoveFlags,
}