azalea_client/packet_handling/
configuration.rs

1use std::io::Cursor;
2
3use azalea_entity::indexing::EntityIdIndex;
4use azalea_protocol::packets::config::s_finish_configuration::ServerboundFinishConfiguration;
5use azalea_protocol::packets::config::s_keep_alive::ServerboundKeepAlive;
6use azalea_protocol::packets::config::s_select_known_packs::ServerboundSelectKnownPacks;
7use azalea_protocol::packets::config::{
8    self, ClientboundConfigPacket, ServerboundConfigPacket, ServerboundCookieResponse,
9    ServerboundResourcePack,
10};
11use azalea_protocol::packets::{ConnectionProtocol, Packet};
12use azalea_protocol::read::deserialize_packet;
13use bevy_ecs::prelude::*;
14use bevy_ecs::system::SystemState;
15use tracing::{debug, error, warn};
16
17use crate::client::InConfigState;
18use crate::disconnect::DisconnectEvent;
19use crate::local_player::Hunger;
20use crate::packet_handling::game::KeepAliveEvent;
21use crate::raw_connection::RawConnection;
22use crate::InstanceHolder;
23
24#[derive(Event, Debug, Clone)]
25pub struct ConfigurationEvent {
26    /// The client entity that received the packet.
27    pub entity: Entity,
28    /// The packet that was actually received.
29    pub packet: ClientboundConfigPacket,
30}
31
32pub fn send_packet_events(
33    query: Query<(Entity, &RawConnection), With<InConfigState>>,
34    mut packet_events: ResMut<Events<ConfigurationEvent>>,
35) {
36    // we manually clear and send the events at the beginning of each update
37    // since otherwise it'd cause issues with events in process_packet_events
38    // running twice
39    packet_events.clear();
40    for (player_entity, raw_conn) in &query {
41        let packets_lock = raw_conn.incoming_packet_queue();
42        let mut packets = packets_lock.lock();
43        if !packets.is_empty() {
44            for raw_packet in packets.iter() {
45                let packet = match deserialize_packet::<ClientboundConfigPacket>(&mut Cursor::new(
46                    raw_packet,
47                )) {
48                    Ok(packet) => packet,
49                    Err(err) => {
50                        error!("failed to read packet: {err:?}");
51                        debug!("packet bytes: {raw_packet:?}");
52                        continue;
53                    }
54                };
55                packet_events.send(ConfigurationEvent {
56                    entity: player_entity,
57                    packet,
58                });
59            }
60            // clear the packets right after we read them
61            packets.clear();
62        }
63    }
64}
65
66pub fn process_packet_events(ecs: &mut World) {
67    let mut events_owned = Vec::new();
68    let mut system_state: SystemState<EventReader<ConfigurationEvent>> = SystemState::new(ecs);
69    let mut events = system_state.get_mut(ecs);
70    for ConfigurationEvent {
71        entity: player_entity,
72        packet,
73    } in events.read()
74    {
75        // we do this so `ecs` isn't borrowed for the whole loop
76        events_owned.push((*player_entity, packet.clone()));
77    }
78    for (player_entity, packet) in events_owned {
79        match packet {
80            ClientboundConfigPacket::RegistryData(p) => {
81                let mut system_state: SystemState<Query<&mut InstanceHolder>> =
82                    SystemState::new(ecs);
83                let mut query = system_state.get_mut(ecs);
84                let instance_holder = query.get_mut(player_entity).unwrap();
85                let mut instance = instance_holder.instance.write();
86
87                // add the new registry data
88                instance.registries.append(p.registry_id, p.entries);
89            }
90
91            ClientboundConfigPacket::CustomPayload(p) => {
92                debug!("Got custom payload packet {p:?}");
93            }
94            ClientboundConfigPacket::Disconnect(p) => {
95                warn!("Got disconnect packet {p:?}");
96                let mut system_state: SystemState<EventWriter<DisconnectEvent>> =
97                    SystemState::new(ecs);
98                let mut disconnect_events = system_state.get_mut(ecs);
99                disconnect_events.send(DisconnectEvent {
100                    entity: player_entity,
101                    reason: Some(p.reason.clone()),
102                });
103            }
104            ClientboundConfigPacket::FinishConfiguration(p) => {
105                debug!("got FinishConfiguration packet: {p:?}");
106
107                let mut system_state: SystemState<Query<&mut RawConnection>> =
108                    SystemState::new(ecs);
109                let mut query = system_state.get_mut(ecs);
110                let mut raw_conn = query.get_mut(player_entity).unwrap();
111
112                raw_conn
113                    .write_packet(ServerboundFinishConfiguration)
114                    .expect(
115                        "we should be in the right state and encoding this packet shouldn't fail",
116                    );
117                raw_conn.set_state(ConnectionProtocol::Game);
118
119                // these components are added now that we're going to be in the Game state
120                ecs.entity_mut(player_entity)
121                    .remove::<InConfigState>()
122                    .insert(crate::JoinedClientBundle {
123                        physics_state: crate::PhysicsState::default(),
124                        inventory: crate::inventory::Inventory::default(),
125                        tab_list: crate::local_player::TabList::default(),
126                        current_sequence_number: crate::interact::CurrentSequenceNumber::default(),
127                        last_sent_direction: crate::movement::LastSentLookDirection::default(),
128                        abilities: crate::local_player::PlayerAbilities::default(),
129                        permission_level: crate::local_player::PermissionLevel::default(),
130                        hunger: Hunger::default(),
131                        chunk_batch_info: crate::chunks::ChunkBatchInfo::default(),
132
133                        entity_id_index: EntityIdIndex::default(),
134
135                        mining: crate::mining::MineBundle::default(),
136                        attack: crate::attack::AttackBundle::default(),
137
138                        _local_entity: azalea_entity::LocalEntity,
139                    });
140            }
141            ClientboundConfigPacket::KeepAlive(p) => {
142                debug!("Got keep alive packet (in configuration) {p:?} for {player_entity:?}");
143
144                let mut system_state: SystemState<(
145                    Query<&RawConnection>,
146                    EventWriter<KeepAliveEvent>,
147                )> = SystemState::new(ecs);
148                let (query, mut keepalive_events) = system_state.get_mut(ecs);
149                let raw_conn = query.get(player_entity).unwrap();
150
151                keepalive_events.send(KeepAliveEvent {
152                    entity: player_entity,
153                    id: p.id,
154                });
155                raw_conn
156                    .write_packet(ServerboundKeepAlive { id: p.id })
157                    .unwrap();
158            }
159            ClientboundConfigPacket::Ping(p) => {
160                debug!("Got ping packet {p:?}");
161
162                let mut system_state: SystemState<Query<&RawConnection>> = SystemState::new(ecs);
163                let mut query = system_state.get_mut(ecs);
164                let raw_conn = query.get_mut(player_entity).unwrap();
165
166                raw_conn
167                    .write_packet(config::s_pong::ServerboundPong { id: p.id })
168                    .unwrap();
169            }
170            ClientboundConfigPacket::ResourcePackPush(p) => {
171                debug!("Got resource pack packet {p:?}");
172
173                let mut system_state: SystemState<Query<&RawConnection>> = SystemState::new(ecs);
174                let mut query = system_state.get_mut(ecs);
175                let raw_conn = query.get_mut(player_entity).unwrap();
176
177                // always accept resource pack
178                raw_conn
179                    .write_packet(ServerboundResourcePack {
180                        id: p.id,
181                        action: config::s_resource_pack::Action::Accepted,
182                    })
183                    .unwrap();
184            }
185            ClientboundConfigPacket::ResourcePackPop(_) => {
186                // we can ignore this
187            }
188            ClientboundConfigPacket::UpdateEnabledFeatures(p) => {
189                debug!("Got update enabled features packet {p:?}");
190            }
191            ClientboundConfigPacket::UpdateTags(_p) => {
192                debug!("Got update tags packet");
193            }
194            ClientboundConfigPacket::CookieRequest(p) => {
195                debug!("Got cookie request packet {p:?}");
196
197                let mut system_state: SystemState<Query<&RawConnection>> = SystemState::new(ecs);
198                let mut query = system_state.get_mut(ecs);
199                let raw_conn = query.get_mut(player_entity).unwrap();
200
201                raw_conn
202                    .write_packet(ServerboundCookieResponse {
203                        key: p.key,
204                        // cookies aren't implemented
205                        payload: None,
206                    })
207                    .unwrap();
208            }
209            ClientboundConfigPacket::ResetChat(p) => {
210                debug!("Got reset chat packet {p:?}");
211            }
212            ClientboundConfigPacket::StoreCookie(p) => {
213                debug!("Got store cookie packet {p:?}");
214            }
215            ClientboundConfigPacket::Transfer(p) => {
216                debug!("Got transfer packet {p:?}");
217            }
218            ClientboundConfigPacket::SelectKnownPacks(p) => {
219                debug!("Got select known packs packet {p:?}");
220
221                let mut system_state: SystemState<Query<&RawConnection>> = SystemState::new(ecs);
222                let mut query = system_state.get_mut(ecs);
223                let raw_conn = query.get_mut(player_entity).unwrap();
224
225                // resource pack management isn't implemented
226                raw_conn
227                    .write_packet(ServerboundSelectKnownPacks {
228                        known_packs: vec![],
229                    })
230                    .unwrap();
231            }
232            ClientboundConfigPacket::ServerLinks(_) => {}
233            ClientboundConfigPacket::CustomReportDetails(_) => {}
234        }
235    }
236}
237
238/// An event for sending a packet to the server while we're in the
239/// `configuration` state.
240#[derive(Event)]
241pub struct SendConfigurationEvent {
242    pub sent_by: Entity,
243    pub packet: ServerboundConfigPacket,
244}
245impl SendConfigurationEvent {
246    pub fn new(sent_by: Entity, packet: impl Packet<ServerboundConfigPacket>) -> Self {
247        let packet = packet.into_variant();
248        Self { sent_by, packet }
249    }
250}
251
252pub fn handle_send_packet_event(
253    mut send_packet_events: EventReader<SendConfigurationEvent>,
254    mut query: Query<(&mut RawConnection, Option<&InConfigState>)>,
255) {
256    for event in send_packet_events.read() {
257        if let Ok((raw_conn, in_configuration_state)) = query.get_mut(event.sent_by) {
258            if in_configuration_state.is_none() {
259                error!(
260                    "Tried to send a configuration packet {:?} while not in configuration state",
261                    event.packet
262                );
263                continue;
264            }
265            debug!("Sending packet: {:?}", event.packet);
266            if let Err(e) = raw_conn.write_packet(event.packet.clone()) {
267                error!("Failed to send packet: {e}");
268            }
269        }
270    }
271}