azalea_client/plugins/packet/login/
mod.rs

1// login packets aren't actually handled here because compression/encryption
2// would make packet handling a lot messier
3
4mod events;
5
6use azalea_protocol::packets::{
7    ConnectionProtocol,
8    login::{
9        ClientboundCookieRequest, ClientboundCustomQuery, ClientboundHello,
10        ClientboundLoginCompression, ClientboundLoginDisconnect, ClientboundLoginFinished,
11        ClientboundLoginPacket, ServerboundCookieResponse, ServerboundLoginAcknowledged,
12    },
13};
14use bevy_ecs::prelude::*;
15pub use events::*;
16use tracing::{debug, error};
17
18use super::as_system;
19use crate::{
20    Account, InConfigState, connection::RawConnection, disconnect::DisconnectEvent,
21    packet::declare_packet_handlers, player::GameProfileComponent,
22};
23
24pub fn process_packet(ecs: &mut World, player: Entity, packet: &ClientboundLoginPacket) {
25    let mut handler = LoginPacketHandler { player, ecs };
26
27    declare_packet_handlers!(
28        ClientboundLoginPacket,
29        packet,
30        handler,
31        [
32            hello,
33            login_disconnect,
34            login_finished,
35            login_compression,
36            custom_query,
37            cookie_request
38        ]
39    );
40}
41
42/// A marker component for local players that are currently in the
43/// `login` state.
44#[derive(Component, Clone, Debug)]
45pub struct InLoginState;
46
47pub struct LoginPacketHandler<'a> {
48    pub ecs: &'a mut World,
49    pub player: Entity,
50}
51impl LoginPacketHandler<'_> {
52    pub fn hello(&mut self, p: &ClientboundHello) {
53        debug!("Got encryption request {p:?}");
54
55        as_system::<(Commands, Query<&Account>)>(self.ecs, |(mut commands, query)| {
56            let Ok(account) = query.get(self.player) else {
57                error!(
58                    "Expected Account component to be present on player when receiving hello packet."
59                );
60                return;
61            };
62            commands.trigger(ReceiveHelloEvent {
63                entity: self.player,
64                account: account.clone(),
65                packet: p.clone(),
66            });
67        });
68    }
69    pub fn login_disconnect(&mut self, p: &ClientboundLoginDisconnect) {
70        debug!("Got disconnect {:?}", p);
71
72        as_system::<MessageWriter<_>>(self.ecs, |mut events| {
73            events.write(DisconnectEvent {
74                entity: self.player,
75                reason: Some(p.reason.clone()),
76            });
77        });
78    }
79    pub fn login_finished(&mut self, p: &ClientboundLoginFinished) {
80        debug!(
81            "Got profile {:?}. login is finished and we're now switching to the config state",
82            p.game_profile
83        );
84
85        as_system::<(Commands, Query<&mut RawConnection>)>(
86            self.ecs,
87            |(mut commands, mut query)| {
88                commands.trigger(SendLoginPacketEvent::new(
89                    self.player,
90                    ServerboundLoginAcknowledged,
91                ));
92
93                commands
94                    .entity(self.player)
95                    .remove::<InLoginState>()
96                    .insert(InConfigState)
97                    .insert(GameProfileComponent(p.game_profile.clone()));
98
99                let mut conn = query
100                    .get_mut(self.player)
101                    .expect("RawConnection component should be present when receiving packets");
102                conn.state = ConnectionProtocol::Configuration;
103            },
104        );
105    }
106    pub fn login_compression(&mut self, p: &ClientboundLoginCompression) {
107        debug!("Got compression request {p:?}");
108
109        as_system::<Query<&mut RawConnection>>(self.ecs, |mut query| {
110            let mut conn = query
111                .get_mut(self.player)
112                .expect("RawConnection component should be present when receiving packets");
113            if let Some(net_conn) = &mut conn.net_conn() {
114                net_conn.set_compression_threshold(Some(p.compression_threshold as u32));
115            }
116        })
117    }
118    pub fn custom_query(&mut self, p: &ClientboundCustomQuery) {
119        debug!("Got custom query {p:?}");
120
121        as_system::<MessageWriter<ReceiveCustomQueryEvent>>(self.ecs, |mut events| {
122            events.write(ReceiveCustomQueryEvent {
123                entity: self.player,
124                packet: p.clone(),
125                disabled: false,
126            });
127        });
128    }
129    pub fn cookie_request(&mut self, p: &ClientboundCookieRequest) {
130        debug!("Got cookie request {p:?}");
131
132        as_system::<Commands>(self.ecs, |mut commands| {
133            commands.trigger(SendLoginPacketEvent::new(
134                self.player,
135                ServerboundCookieResponse {
136                    key: p.key.clone(),
137                    // cookies aren't implemented
138                    payload: None,
139                },
140            ));
141        });
142    }
143}