1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

fix errors when switching to Game state and add fast_login test

This commit is contained in:
mat 2025-03-02 05:42:15 +00:00
parent 72d87349fe
commit c9022e8f67
6 changed files with 101 additions and 20 deletions

View file

@ -23,8 +23,8 @@ pub mod test_simulation;
pub use account::{Account, AccountOpts}; pub use account::{Account, AccountOpts};
pub use azalea_protocol::common::client_information::ClientInformation; pub use azalea_protocol::common::client_information::ClientInformation;
pub use client::{ pub use client::{
Client, DefaultPlugins, InConfigState, JoinError, JoinedClientBundle, LocalPlayerBundle, Client, DefaultPlugins, InConfigState, InGameState, JoinError, JoinedClientBundle,
StartClientOpts, TickBroadcast, start_ecs_runner, LocalPlayerBundle, StartClientOpts, TickBroadcast, start_ecs_runner,
}; };
pub use events::Event; pub use events::Event;
pub use local_player::{GameProfileComponent, Hunger, InstanceHolder, TabList}; pub use local_player::{GameProfileComponent, Hunger, InstanceHolder, TabList};

View file

@ -55,7 +55,7 @@ pub fn handle_send_packet_event(
} }
} }
pub fn send_packet_events( pub fn emit_receive_config_packet_events(
query: Query<(Entity, &RawConnection), With<InConfigState>>, query: Query<(Entity, &RawConnection), With<InConfigState>>,
mut packet_events: ResMut<Events<ReceiveConfigPacketEvent>>, mut packet_events: ResMut<Events<ReceiveConfigPacketEvent>>,
) { ) {
@ -67,7 +67,9 @@ pub fn send_packet_events(
let packets_lock = raw_conn.incoming_packet_queue(); let packets_lock = raw_conn.incoming_packet_queue();
let mut packets = packets_lock.lock(); let mut packets = packets_lock.lock();
if !packets.is_empty() { if !packets.is_empty() {
let mut packets_read = 0;
for raw_packet in packets.iter() { for raw_packet in packets.iter() {
packets_read += 1;
let packet = match deserialize_packet::<ClientboundConfigPacket>(&mut Cursor::new( let packet = match deserialize_packet::<ClientboundConfigPacket>(&mut Cursor::new(
raw_packet, raw_packet,
)) { )) {
@ -78,13 +80,32 @@ pub fn send_packet_events(
continue; continue;
} }
}; };
let should_interrupt = packet_interrupts(&packet);
packet_events.send(ReceiveConfigPacketEvent { packet_events.send(ReceiveConfigPacketEvent {
entity: player_entity, entity: player_entity,
packet, packet,
}); });
if should_interrupt {
break;
}
} }
// clear the packets right after we read them packets.drain(0..packets_read);
packets.clear();
} }
} }
} }
/// Whether the given packet should make us stop deserializing the received
/// packets until next update.
///
/// This is used for packets that can switch the client state.
fn packet_interrupts(packet: &ClientboundConfigPacket) -> bool {
matches!(
packet,
ClientboundConfigPacket::FinishConfiguration(_)
| ClientboundConfigPacket::Disconnect(_)
| ClientboundConfigPacket::Transfer(_)
)
}

View file

@ -75,7 +75,7 @@ pub fn handle_outgoing_packets(
} }
} }
pub fn send_receivepacketevent( pub fn emit_receive_packet_events(
query: Query<(Entity, &RawConnection), With<InGameState>>, query: Query<(Entity, &RawConnection), With<InGameState>>,
mut packet_events: ResMut<Events<ReceivePacketEvent>>, mut packet_events: ResMut<Events<ReceivePacketEvent>>,
) { ) {
@ -87,7 +87,9 @@ pub fn send_receivepacketevent(
let packets_lock = raw_connection.incoming_packet_queue(); let packets_lock = raw_connection.incoming_packet_queue();
let mut packets = packets_lock.lock(); let mut packets = packets_lock.lock();
if !packets.is_empty() { if !packets.is_empty() {
let mut packets_read = 0;
for raw_packet in packets.iter() { for raw_packet in packets.iter() {
packets_read += 1;
let packet = let packet =
match deserialize_packet::<ClientboundGamePacket>(&mut Cursor::new(raw_packet)) match deserialize_packet::<ClientboundGamePacket>(&mut Cursor::new(raw_packet))
{ {
@ -98,17 +100,36 @@ pub fn send_receivepacketevent(
continue; continue;
} }
}; };
let should_interrupt = packet_interrupts(&packet);
packet_events.send(ReceivePacketEvent { packet_events.send(ReceivePacketEvent {
entity: player_entity, entity: player_entity,
packet: Arc::new(packet), packet: Arc::new(packet),
}); });
if should_interrupt {
break;
}
} }
// clear the packets right after we read them packets.drain(0..packets_read);
packets.clear();
} }
} }
} }
/// Whether the given packet should make us stop deserializing the received
/// packets until next update.
///
/// This is used for packets that can switch the client state.
fn packet_interrupts(packet: &ClientboundGamePacket) -> bool {
matches!(
packet,
ClientboundGamePacket::StartConfiguration(_)
| ClientboundGamePacket::Disconnect(_)
| ClientboundGamePacket::Transfer(_)
)
}
/// A player joined the game (or more specifically, was added to the tab /// A player joined the game (or more specifically, was added to the tab
/// list of a local player). /// list of a local player).
#[derive(Event, Debug, Clone)] #[derive(Event, Debug, Clone)]

View file

@ -38,7 +38,10 @@ impl Plugin for PacketPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems( app.add_systems(
First, First,
(game::send_receivepacketevent, config::send_packet_events), (
game::emit_receive_packet_events,
config::emit_receive_config_packet_events,
),
) )
.add_systems( .add_systems(
PreUpdate, PreUpdate,

View file

@ -1,10 +1,9 @@
use azalea_client::{InConfigState, test_simulation::*}; use azalea_client::{InConfigState, InGameState, test_simulation::*};
use azalea_core::{position::ChunkPos, resource_location::ResourceLocation}; use azalea_core::{position::ChunkPos, resource_location::ResourceLocation};
use azalea_entity::{LocalEntity, metadata::Health}; use azalea_entity::LocalEntity;
use azalea_protocol::packets::{ use azalea_protocol::packets::{
ConnectionProtocol, ConnectionProtocol,
config::{ClientboundFinishConfiguration, ClientboundRegistryData}, config::{ClientboundFinishConfiguration, ClientboundRegistryData},
game::ClientboundSetHealth,
}; };
use azalea_registry::DimensionType; use azalea_registry::DimensionType;
use azalea_world::InstanceName; use azalea_world::InstanceName;
@ -17,6 +16,7 @@ fn test_change_dimension_to_nether_and_back() {
let mut simulation = Simulation::new(ConnectionProtocol::Configuration); let mut simulation = Simulation::new(ConnectionProtocol::Configuration);
assert!(simulation.has_component::<InConfigState>()); assert!(simulation.has_component::<InConfigState>());
assert!(!simulation.has_component::<InGameState>());
simulation.receive_packet(ClientboundRegistryData { simulation.receive_packet(ClientboundRegistryData {
registry_id: ResourceLocation::new("minecraft:dimension_type"), registry_id: ResourceLocation::new("minecraft:dimension_type"),
@ -53,16 +53,9 @@ fn test_change_dimension_to_nether_and_back() {
simulation.tick(); simulation.tick();
assert!(!simulation.has_component::<InConfigState>()); assert!(!simulation.has_component::<InConfigState>());
assert!(simulation.has_component::<InGameState>());
assert!(simulation.has_component::<LocalEntity>()); assert!(simulation.has_component::<LocalEntity>());
simulation.receive_packet(ClientboundSetHealth {
health: 15.,
food: 20,
saturation: 20.,
});
simulation.tick();
assert_eq!(*simulation.component::<Health>(), 15.);
// //
// OVERWORLD // OVERWORLD
// //

View file

@ -0,0 +1,43 @@
use azalea_client::{InConfigState, test_simulation::*};
use azalea_core::resource_location::ResourceLocation;
use azalea_entity::metadata::Health;
use azalea_protocol::packets::{
ConnectionProtocol,
config::{ClientboundFinishConfiguration, ClientboundRegistryData},
game::ClientboundSetHealth,
};
use bevy_log::tracing_subscriber;
use simdnbt::owned::{NbtCompound, NbtTag};
#[test]
fn test_fast_login() {
let _ = tracing_subscriber::fmt::try_init();
let mut simulation = Simulation::new(ConnectionProtocol::Configuration);
assert!(simulation.has_component::<InConfigState>());
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.receive_packet(ClientboundFinishConfiguration);
// note that there's no simulation tick here
simulation.receive_packet(ClientboundSetHealth {
health: 15.,
food: 20,
saturation: 20.,
});
simulation.tick();
// we need a second tick to handle the state switch properly
simulation.tick();
assert_eq!(*simulation.component::<Health>(), 15.);
}