aboutsummaryrefslogtreecommitdiff
path: root/azalea-client/tests
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 /azalea-client/tests
parentaf1ef9310093aa3c8dfd5054eb6d0b8c7c0d0b31 (diff)
downloadazalea-drasl-f9e4b65713bbacabcd54416a388a92b90f56ab47.tar.xz
start adding packet_order test
Diffstat (limited to 'azalea-client/tests')
-rw-r--r--azalea-client/tests/packet_order.rs150
1 files changed, 150 insertions, 0 deletions
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()
+ }
+}