azalea_client/packet_handling/
login.rs

1// login packets aren't actually handled here because compression/encryption
2// would make packet handling a lot messier
3
4use std::{collections::HashSet, sync::Arc};
5
6use azalea_protocol::packets::{
7    login::{
8        s_custom_query_answer::ServerboundCustomQueryAnswer, ClientboundLoginPacket,
9        ServerboundLoginPacket,
10    },
11    Packet,
12};
13use bevy_ecs::{prelude::*, system::SystemState};
14use derive_more::{Deref, DerefMut};
15use tokio::sync::mpsc;
16use tracing::error;
17
18// this struct is defined here anyways though so it's consistent with the other
19// ones
20
21/// An event that's sent when we receive a login packet from the server. Note
22/// that if you want to handle this in a system, you must add
23/// `.before(azalea::packet_handling::login::process_packet_events)` to it
24/// because that system clears the events.
25#[derive(Event, Debug, Clone)]
26pub struct LoginPacketEvent {
27    /// The client entity that received the packet.
28    pub entity: Entity,
29    /// The packet that was actually received.
30    pub packet: Arc<ClientboundLoginPacket>,
31}
32
33/// Event for sending a login packet to the server.
34#[derive(Event)]
35pub struct SendLoginPacketEvent {
36    pub entity: Entity,
37    pub packet: ServerboundLoginPacket,
38}
39impl SendLoginPacketEvent {
40    pub fn new(entity: Entity, packet: impl Packet<ServerboundLoginPacket>) -> Self {
41        let packet = packet.into_variant();
42        Self { entity, packet }
43    }
44}
45
46#[derive(Component)]
47pub struct LoginSendPacketQueue {
48    pub tx: mpsc::UnboundedSender<ServerboundLoginPacket>,
49}
50
51pub fn handle_send_packet_event(
52    mut send_packet_events: EventReader<SendLoginPacketEvent>,
53    mut query: Query<&mut LoginSendPacketQueue>,
54) {
55    for event in send_packet_events.read() {
56        if let Ok(queue) = query.get_mut(event.entity) {
57            let _ = queue.tx.send(event.packet.clone());
58        } else {
59            error!("Sent SendPacketEvent for entity that doesn't have a LoginSendPacketQueue");
60        }
61    }
62}
63
64/// Plugins can add to this set if they want to handle a custom query packet
65/// themselves. This component removed after the login state ends.
66#[derive(Component, Default, Debug, Deref, DerefMut)]
67pub struct IgnoreQueryIds(HashSet<u32>);
68
69pub fn process_packet_events(ecs: &mut World) {
70    let mut events_owned = Vec::new();
71    let mut system_state: SystemState<ResMut<Events<LoginPacketEvent>>> = SystemState::new(ecs);
72    let mut events = system_state.get_mut(ecs);
73    for LoginPacketEvent {
74        entity: player_entity,
75        packet,
76    } in events.drain()
77    {
78        // we do this so `ecs` isn't borrowed for the whole loop
79        events_owned.push((player_entity, packet));
80    }
81    for (player_entity, packet) in events_owned {
82        #[allow(clippy::single_match)]
83        match packet.as_ref() {
84            ClientboundLoginPacket::CustomQuery(p) => {
85                let mut system_state: SystemState<(
86                    EventWriter<SendLoginPacketEvent>,
87                    Query<&IgnoreQueryIds>,
88                )> = SystemState::new(ecs);
89                let (mut send_packet_events, query) = system_state.get_mut(ecs);
90
91                let ignore_query_ids = query.get(player_entity).ok().map(|x| x.0.clone());
92                if let Some(ignore_query_ids) = ignore_query_ids {
93                    if ignore_query_ids.contains(&p.transaction_id) {
94                        continue;
95                    }
96                }
97
98                send_packet_events.send(SendLoginPacketEvent::new(
99                    player_entity,
100                    ServerboundCustomQueryAnswer {
101                        transaction_id: p.transaction_id,
102                        data: None,
103                    },
104                ));
105            }
106            _ => {}
107        }
108    }
109}