mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 23:44:38 +00:00
start adding packet_order test
This commit is contained in:
parent
af1ef93100
commit
f9e4b65713
11 changed files with 224 additions and 36 deletions
|
@ -16,6 +16,7 @@ pub mod ping;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
mod plugins;
|
mod plugins;
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod test_utils;
|
pub mod test_utils;
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,7 @@ impl Client {
|
||||||
pub struct AttackQueued {
|
pub struct AttackQueued {
|
||||||
pub target: Entity,
|
pub target: Entity,
|
||||||
}
|
}
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn handle_attack_queued(
|
pub fn handle_attack_queued(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut query: Query<(
|
mut query: Query<(
|
||||||
|
|
|
@ -14,8 +14,8 @@ impl Plugin for PlayerLoadedPlugin {
|
||||||
GameTick,
|
GameTick,
|
||||||
player_loaded_packet
|
player_loaded_packet
|
||||||
.after(PhysicsSet)
|
.after(PhysicsSet)
|
||||||
.after(MiningSet)
|
.before(MiningSet)
|
||||||
.after(crate::movement::send_position),
|
.before(crate::movement::send_position),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,11 @@ pub fn player_loaded_packet(
|
||||||
Entity,
|
Entity,
|
||||||
(
|
(
|
||||||
With<LocalEntity>,
|
With<LocalEntity>,
|
||||||
With<InLoadedChunk>,
|
|
||||||
Without<HasClientLoaded>,
|
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>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -6,14 +6,17 @@ use azalea_entity::{
|
||||||
metadata::Sprinting,
|
metadata::Sprinting,
|
||||||
};
|
};
|
||||||
use azalea_physics::{PhysicsSet, ai_step};
|
use azalea_physics::{PhysicsSet, ai_step};
|
||||||
use azalea_protocol::packets::{
|
use azalea_protocol::{
|
||||||
Packet,
|
common::movements::MoveFlags,
|
||||||
game::{
|
packets::{
|
||||||
ServerboundPlayerCommand, ServerboundPlayerInput,
|
Packet,
|
||||||
s_move_player_pos::ServerboundMovePlayerPos,
|
game::{
|
||||||
s_move_player_pos_rot::ServerboundMovePlayerPosRot,
|
ServerboundPlayerCommand, ServerboundPlayerInput,
|
||||||
s_move_player_rot::ServerboundMovePlayerRot,
|
s_move_player_pos::ServerboundMovePlayerPos,
|
||||||
s_move_player_status_only::ServerboundMovePlayerStatusOnly,
|
s_move_player_pos_rot::ServerboundMovePlayerPosRot,
|
||||||
|
s_move_player_rot::ServerboundMovePlayerRot,
|
||||||
|
s_move_player_status_only::ServerboundMovePlayerStatusOnly,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use azalea_world::{MinecraftEntityId, MoveEntityError};
|
use azalea_world::{MinecraftEntityId, MoveEntityError};
|
||||||
|
@ -186,12 +189,16 @@ pub fn send_position(
|
||||||
// if self.is_passenger() {
|
// if self.is_passenger() {
|
||||||
// TODO: posrot packet for being a 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 {
|
let packet = if sending_position && sending_direction {
|
||||||
Some(
|
Some(
|
||||||
ServerboundMovePlayerPosRot {
|
ServerboundMovePlayerPosRot {
|
||||||
pos: **position,
|
pos: **position,
|
||||||
look_direction: *direction,
|
look_direction: *direction,
|
||||||
on_ground: physics.on_ground(),
|
flags,
|
||||||
}
|
}
|
||||||
.into_variant(),
|
.into_variant(),
|
||||||
)
|
)
|
||||||
|
@ -199,7 +206,7 @@ pub fn send_position(
|
||||||
Some(
|
Some(
|
||||||
ServerboundMovePlayerPos {
|
ServerboundMovePlayerPos {
|
||||||
pos: **position,
|
pos: **position,
|
||||||
on_ground: physics.on_ground(),
|
flags,
|
||||||
}
|
}
|
||||||
.into_variant(),
|
.into_variant(),
|
||||||
)
|
)
|
||||||
|
@ -207,17 +214,12 @@ pub fn send_position(
|
||||||
Some(
|
Some(
|
||||||
ServerboundMovePlayerRot {
|
ServerboundMovePlayerRot {
|
||||||
look_direction: *direction,
|
look_direction: *direction,
|
||||||
on_ground: physics.on_ground(),
|
flags,
|
||||||
}
|
}
|
||||||
.into_variant(),
|
.into_variant(),
|
||||||
)
|
)
|
||||||
} else if physics.last_on_ground() != physics.on_ground() {
|
} else if physics.last_on_ground() != physics.on_ground() {
|
||||||
Some(
|
Some(ServerboundMovePlayerStatusOnly { flags }.into_variant())
|
||||||
ServerboundMovePlayerStatusOnly {
|
|
||||||
on_ground: physics.on_ground(),
|
|
||||||
}
|
|
||||||
.into_variant(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,10 @@ use azalea_entity::{
|
||||||
indexing::{EntityIdIndex, EntityUuidIndex},
|
indexing::{EntityIdIndex, EntityUuidIndex},
|
||||||
metadata::{Health, apply_metadata},
|
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 azalea_world::{InstanceContainer, InstanceName, MinecraftEntityId, PartialInstance};
|
||||||
use bevy_ecs::{prelude::*, system::SystemState};
|
use bevy_ecs::{prelude::*, system::SystemState};
|
||||||
pub use events::*;
|
pub use events::*;
|
||||||
|
@ -319,15 +322,6 @@ impl GamePacketHandler<'_> {
|
||||||
.entity(self.player)
|
.entity(self.player)
|
||||||
.insert(LoadedBy(HashSet::from_iter(vec![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 {
|
ServerboundMovePlayerPosRot {
|
||||||
pos: **position,
|
pos: **position,
|
||||||
look_direction: *direction,
|
look_direction: *direction,
|
||||||
// this is always false
|
flags: MoveFlags::default(),
|
||||||
on_ground: false,
|
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
150
azalea-client/tests/packet_order.rs
Normal file
150
azalea-client/tests/packet_order.rs
Normal file
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -131,3 +131,33 @@ impl AzaleaWrite for RelativeMovements {
|
||||||
set.azalea_write(buf)
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,10 @@ use azalea_buf::AzBuf;
|
||||||
use azalea_core::position::Vec3;
|
use azalea_core::position::Vec3;
|
||||||
use azalea_protocol_macros::ServerboundGamePacket;
|
use azalea_protocol_macros::ServerboundGamePacket;
|
||||||
|
|
||||||
|
use crate::common::movements::MoveFlags;
|
||||||
|
|
||||||
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
||||||
pub struct ServerboundMovePlayerPos {
|
pub struct ServerboundMovePlayerPos {
|
||||||
pub pos: Vec3,
|
pub pos: Vec3,
|
||||||
pub on_ground: bool,
|
pub flags: MoveFlags,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,11 @@ use azalea_core::position::Vec3;
|
||||||
use azalea_entity::LookDirection;
|
use azalea_entity::LookDirection;
|
||||||
use azalea_protocol_macros::ServerboundGamePacket;
|
use azalea_protocol_macros::ServerboundGamePacket;
|
||||||
|
|
||||||
|
use crate::common::movements::MoveFlags;
|
||||||
|
|
||||||
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
||||||
pub struct ServerboundMovePlayerPosRot {
|
pub struct ServerboundMovePlayerPosRot {
|
||||||
pub pos: Vec3,
|
pub pos: Vec3,
|
||||||
pub look_direction: LookDirection,
|
pub look_direction: LookDirection,
|
||||||
pub on_ground: bool,
|
pub flags: MoveFlags,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,10 @@ use azalea_buf::AzBuf;
|
||||||
use azalea_entity::LookDirection;
|
use azalea_entity::LookDirection;
|
||||||
use azalea_protocol_macros::ServerboundGamePacket;
|
use azalea_protocol_macros::ServerboundGamePacket;
|
||||||
|
|
||||||
|
use crate::common::movements::MoveFlags;
|
||||||
|
|
||||||
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
||||||
pub struct ServerboundMovePlayerRot {
|
pub struct ServerboundMovePlayerRot {
|
||||||
pub look_direction: LookDirection,
|
pub look_direction: LookDirection,
|
||||||
pub on_ground: bool,
|
pub flags: MoveFlags,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use azalea_buf::AzBuf;
|
use azalea_buf::AzBuf;
|
||||||
use azalea_protocol_macros::ServerboundGamePacket;
|
use azalea_protocol_macros::ServerboundGamePacket;
|
||||||
|
|
||||||
|
use crate::common::movements::MoveFlags;
|
||||||
|
|
||||||
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
||||||
pub struct ServerboundMovePlayerStatusOnly {
|
pub struct ServerboundMovePlayerStatusOnly {
|
||||||
pub on_ground: bool,
|
pub flags: MoveFlags,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue