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, GameProfileComponent, InConfigState, connection::RawConnection,
21    declare_packet_handlers, disconnect::DisconnectEvent,
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_targets(
63                ReceiveHelloEvent {
64                    account: account.clone(),
65                    packet: p.clone(),
66                },
67                self.player,
68            );
69        });
70    }
71    pub fn login_disconnect(&mut self, p: &ClientboundLoginDisconnect) {
72        debug!("Got disconnect {:?}", p);
73
74        as_system::<EventWriter<_>>(self.ecs, |mut events| {
75            events.write(DisconnectEvent {
76                entity: self.player,
77                reason: Some(p.reason.clone()),
78            });
79        });
80    }
81    pub fn login_finished(&mut self, p: &ClientboundLoginFinished) {
82        debug!(
83            "Got profile {:?}. login is finished and we're now switching to the config state",
84            p.game_profile
85        );
86
87        as_system::<(Commands, Query<&mut RawConnection>)>(
88            self.ecs,
89            |(mut commands, mut query)| {
90                commands.trigger(SendLoginPacketEvent::new(
91                    self.player,
92                    ServerboundLoginAcknowledged,
93                ));
94
95                commands
96                    .entity(self.player)
97                    .remove::<InLoginState>()
98                    .insert(InConfigState)
99                    .insert(GameProfileComponent(p.game_profile.clone()));
100
101                let mut conn = query
102                    .get_mut(self.player)
103                    .expect("RawConnection component should be present when receiving packets");
104                conn.state = ConnectionProtocol::Configuration;
105            },
106        );
107    }
108    pub fn login_compression(&mut self, p: &ClientboundLoginCompression) {
109        debug!("Got compression request {p:?}");
110
111        as_system::<Query<&mut RawConnection>>(self.ecs, |mut query| {
112            let mut conn = query
113                .get_mut(self.player)
114                .expect("RawConnection component should be present when receiving packets");
115            if let Some(net_conn) = &mut conn.net_conn() {
116                net_conn.set_compression_threshold(Some(p.compression_threshold as u32));
117            }
118        })
119    }
120    pub fn custom_query(&mut self, p: &ClientboundCustomQuery) {
121        debug!("Got custom query {p:?}");
122
123        as_system::<EventWriter<ReceiveCustomQueryEvent>>(self.ecs, |mut events| {
124            events.write(ReceiveCustomQueryEvent {
125                entity: self.player,
126                packet: p.clone(),
127                disabled: false,
128            });
129        });
130    }
131    pub fn cookie_request(&mut self, p: &ClientboundCookieRequest) {
132        debug!("Got cookie request {p:?}");
133
134        as_system::<Commands>(self.ecs, |mut commands| {
135            commands.trigger(SendLoginPacketEvent::new(
136                self.player,
137                ServerboundCookieResponse {
138                    key: p.key.clone(),
139                    // cookies aren't implemented
140                    payload: None,
141                },
142            ));
143        });
144    }
145}