Skip to main content

azalea_client/plugins/
connection.rs

1#![allow(clippy::redundant_field_names, reason = "Triggered by `Error` derive")]
2
3use std::{
4    fmt::Debug,
5    io::Cursor,
6    mem,
7    sync::{
8        Arc,
9        atomic::{self, AtomicBool},
10    },
11};
12
13use azalea_crypto::Aes128CfbEnc;
14use azalea_protocol::{
15    connect::{RawReadConnection, RawWriteConnection},
16    packets::{
17        ConnectionProtocol, Packet, ProtocolPacket, config::ClientboundConfigPacket,
18        game::ClientboundGamePacket, login::ClientboundLoginPacket,
19    },
20    read::{ReadPacketError, deserialize_packet},
21    write::serialize_packet,
22};
23use bevy_app::prelude::*;
24use bevy_ecs::prelude::*;
25use bevy_tasks::{IoTaskPool, futures_lite::future};
26use thiserror::Error;
27use tokio::{
28    io::AsyncWriteExt,
29    net::tcp::OwnedWriteHalf,
30    sync::mpsc::{self},
31};
32use tracing::{debug, error, info, trace};
33
34use super::packet::{
35    config::ReceiveConfigPacketEvent, game::ReceiveGamePacketEvent, login::ReceiveLoginPacketEvent,
36};
37use crate::packet::{config, game, login};
38
39pub struct ConnectionPlugin;
40impl Plugin for ConnectionPlugin {
41    fn build(&self, app: &mut App) {
42        app.add_systems(PreUpdate, (read_packets, poll_all_writer_tasks).chain());
43    }
44}
45
46pub fn read_packets(ecs: &mut World) {
47    let mut entity_and_conn_query = ecs.query::<(Entity, &mut RawConnection)>();
48    let mut conn_query = ecs.query::<&mut RawConnection>();
49
50    let mut entities_handling_packets = Vec::new();
51    let mut entities_with_injected_packets = Vec::new();
52    for (entity, mut raw_conn) in entity_and_conn_query.iter_mut(ecs) {
53        if !raw_conn.injected_clientbound_packets.is_empty() {
54            entities_with_injected_packets.push((
55                entity,
56                mem::take(&mut raw_conn.injected_clientbound_packets),
57            ));
58        }
59
60        if raw_conn.network.is_none() {
61            // no network connection, don't bother with the normal packet handling
62            continue;
63        }
64
65        entities_handling_packets.push(entity);
66    }
67
68    let mut queued_packet_events = QueuedPacketEvents::default();
69
70    // handle injected packets, see the comment on
71    // RawConnection::injected_clientbound_packets for more info
72    for (entity, raw_packets) in entities_with_injected_packets {
73        for raw_packet in raw_packets {
74            let conn = conn_query.get(ecs, entity).unwrap();
75            let state = conn.state;
76
77            trace!("Received injected packet with bytes: {raw_packet:?}");
78            if let Err(e) =
79                handle_raw_packet(ecs, &raw_packet, entity, state, &mut queued_packet_events)
80            {
81                error!("Error reading injected packet: {e}");
82            }
83        }
84    }
85
86    for entity in entities_handling_packets {
87        loop {
88            let mut conn = conn_query.get_mut(ecs, entity).unwrap();
89            let net_conn = conn.net_conn().unwrap();
90            let read_res = net_conn.reader.try_read();
91            let state = conn.state;
92            match read_res {
93                Ok(Some(raw_packet)) => {
94                    let raw_packet = Arc::<[u8]>::from(raw_packet);
95                    if let Err(e) = handle_raw_packet(
96                        ecs,
97                        &raw_packet,
98                        entity,
99                        state,
100                        &mut queued_packet_events,
101                    ) {
102                        error!("Error reading packet: {e}");
103                    }
104                }
105                Ok(None) => {
106                    // no packets available
107                    break;
108                }
109                Err(err) => {
110                    log_for_error(&err);
111
112                    if matches!(
113                        &*err,
114                        ReadPacketError::IoError { .. } | ReadPacketError::ConnectionClosed
115                    ) {
116                        info!("Server closed connection");
117                        // ungraceful disconnect :(
118                        conn.network = None;
119                        // setting this will make us send a DisconnectEvent
120                        conn.is_alive = false;
121                    }
122
123                    break;
124                }
125            }
126        }
127    }
128
129    queued_packet_events.write_messages(ecs);
130}
131
132fn poll_all_writer_tasks(mut conn_query: Query<&mut RawConnection>) {
133    for mut conn in conn_query.iter_mut() {
134        if let Some(net_conn) = &mut conn.network {
135            // this needs to be done at some point every update to make sure packets are
136            // actually sent to the network
137
138            if net_conn.poll_writer().is_some() {
139                // means the writer task ended
140                conn.network = None;
141                conn.is_alive = false;
142            }
143        }
144    }
145}
146
147#[derive(Default)]
148pub struct QueuedPacketEvents {
149    login: Vec<ReceiveLoginPacketEvent>,
150    config: Vec<ReceiveConfigPacketEvent>,
151    game: Vec<ReceiveGamePacketEvent>,
152}
153impl QueuedPacketEvents {
154    fn write_messages(&mut self, ecs: &mut World) {
155        ecs.write_message_batch(self.login.drain(..));
156        ecs.write_message_batch(self.config.drain(..));
157        ecs.write_message_batch(self.game.drain(..));
158    }
159}
160
161fn log_for_error(error: &ReadPacketError) {
162    if !matches!(*error, ReadPacketError::ConnectionClosed) {
163        error!("Error reading packet from Client: {error:?}");
164    }
165}
166
167/// The client's connection to the server.
168#[derive(Component)]
169pub struct RawConnection {
170    /// The network connection to the server.
171    ///
172    /// This isn't guaranteed to be present, for example during the main packet
173    /// handlers or at all times during tests.
174    ///
175    /// You shouldn't rely on this. Instead, use the events for sending packets
176    /// like [`SendGamePacketEvent`](crate::packet::game::SendGamePacketEvent) /
177    /// [`SendConfigPacketEvent`](crate::packet::config::SendConfigPacketEvent)
178    /// / [`SendLoginPacketEvent`](crate::packet::login::SendLoginPacketEvent).
179    ///
180    /// To check if we haven't disconnected from the server, use
181    /// [`Self::is_alive`].
182    pub(crate) network: Option<NetworkConnection>,
183    pub state: ConnectionProtocol,
184    pub(crate) is_alive: bool,
185
186    /// This exists for internal testing purposes and probably shouldn't be used
187    /// for normal bots.
188    ///
189    /// It's basically a way to make our client think it received a packet from
190    /// the server without needing to interact with the network.
191    pub injected_clientbound_packets: Vec<Box<[u8]>>,
192}
193impl RawConnection {
194    pub fn new(
195        reader: RawReadConnection,
196        writer: RawWriteConnection,
197        state: ConnectionProtocol,
198    ) -> Self {
199        let task_pool = IoTaskPool::get();
200
201        let (network_packet_writer_tx, network_packet_writer_rx) =
202            mpsc::unbounded_channel::<Box<[u8]>>();
203
204        let writer_task =
205            task_pool.spawn(write_task(network_packet_writer_rx, writer.write_stream));
206
207        let mut conn = Self::new_networkless(state);
208        conn.network = Some(NetworkConnection {
209            reader,
210            enc_cipher: writer.enc_cipher,
211            network_packet_writer_tx,
212            writer_task,
213        });
214
215        conn
216    }
217
218    pub fn new_networkless(state: ConnectionProtocol) -> Self {
219        Self {
220            network: None,
221            state,
222            is_alive: true,
223            injected_clientbound_packets: Vec::new(),
224        }
225    }
226
227    pub fn is_alive(&self) -> bool {
228        self.is_alive
229    }
230
231    /// Write a packet to the server without emitting any events.
232    ///
233    /// This is called by the handlers for [`SendGamePacketEvent`],
234    /// [`SendConfigPacketEvent`], and [`SendLoginPacketEvent`].
235    ///
236    /// [`SendGamePacketEvent`]: crate::packet::game::SendGamePacketEvent
237    /// [`SendConfigPacketEvent`]: crate::packet::config::SendConfigPacketEvent
238    /// [`SendLoginPacketEvent`]: crate::packet::login::SendLoginPacketEvent
239    pub fn write<P: ProtocolPacket + Debug>(
240        &mut self,
241        packet: impl Packet<P>,
242    ) -> Result<(), WritePacketError> {
243        if let Some(network) = &mut self.network {
244            network.write(packet)?;
245        } else {
246            static WARNED: AtomicBool = AtomicBool::new(false);
247            if !WARNED.swap(true, atomic::Ordering::Relaxed) {
248                debug!(
249                    "tried to write packet to the network but there is no NetworkConnection. if you're trying to send a packet from the handler function, use self.write instead"
250                );
251            }
252        }
253        Ok(())
254    }
255
256    pub fn net_conn(&mut self) -> Option<&mut NetworkConnection> {
257        self.network.as_mut()
258    }
259}
260
261pub fn handle_raw_packet(
262    ecs: &mut World,
263    raw_packet: &[u8],
264    entity: Entity,
265    state: ConnectionProtocol,
266    queued_packet_events: &mut QueuedPacketEvents,
267) -> Result<(), Box<ReadPacketError>> {
268    let stream = &mut Cursor::new(raw_packet);
269    match state {
270        ConnectionProtocol::Handshake => {
271            unreachable!()
272        }
273        ConnectionProtocol::Game => {
274            let packet = Arc::new(deserialize_packet::<ClientboundGamePacket>(stream)?);
275            trace!("Packet: {packet:?}");
276            game::process_packet(ecs, entity, packet.as_ref());
277            queued_packet_events
278                .game
279                .push(ReceiveGamePacketEvent { entity, packet });
280        }
281        ConnectionProtocol::Status => {
282            unreachable!()
283        }
284        ConnectionProtocol::Login => {
285            let packet = Arc::new(deserialize_packet::<ClientboundLoginPacket>(stream)?);
286            trace!("Packet: {packet:?}");
287            login::process_packet(ecs, entity, &packet);
288            queued_packet_events
289                .login
290                .push(ReceiveLoginPacketEvent { entity, packet });
291        }
292        ConnectionProtocol::Configuration => {
293            let packet = Arc::new(deserialize_packet::<ClientboundConfigPacket>(stream)?);
294            trace!("Packet: {packet:?}");
295            config::process_packet(ecs, entity, &packet);
296            queued_packet_events
297                .config
298                .push(ReceiveConfigPacketEvent { entity, packet });
299        }
300    };
301
302    Ok(())
303}
304
305pub struct NetworkConnection {
306    reader: RawReadConnection,
307    // compression threshold is in the RawReadConnection
308    pub enc_cipher: Option<Aes128CfbEnc>,
309
310    pub writer_task: bevy_tasks::Task<()>,
311    /// A queue of raw TCP packets to send.
312    ///
313    /// These will not be modified further, they should already be serialized
314    /// and compressed and encrypted before being added here.
315    network_packet_writer_tx: mpsc::UnboundedSender<Box<[u8]>>,
316}
317impl NetworkConnection {
318    pub fn write<P: ProtocolPacket + Debug>(
319        &mut self,
320        packet: impl Packet<P>,
321    ) -> Result<(), WritePacketError> {
322        let packet = packet.into_variant();
323        let raw_packet = serialize_packet(&packet)?;
324        self.write_raw(&raw_packet)?;
325
326        Ok(())
327    }
328
329    pub fn write_raw(&mut self, raw_packet: &[u8]) -> Result<(), WritePacketError> {
330        let network_packet = azalea_protocol::write::encode_to_network_packet(
331            raw_packet,
332            self.reader.compression_threshold,
333            &mut self.enc_cipher,
334        );
335        self.network_packet_writer_tx
336            .send(network_packet.into_boxed_slice())?;
337        Ok(())
338    }
339
340    /// Makes sure packets get sent and returns Some(()) if the connection has
341    /// closed.
342    pub fn poll_writer(&mut self) -> Option<()> {
343        let poll_once_res = future::poll_once(&mut self.writer_task);
344        future::block_on(poll_once_res)
345    }
346
347    pub fn set_compression_threshold(&mut self, threshold: Option<u32>) {
348        trace!("Set compression threshold to {threshold:?}");
349        self.reader.compression_threshold = threshold;
350    }
351    /// Set the encryption key that is used to encrypt and decrypt packets.
352    ///
353    /// The same key is used for both reading and writing.
354    pub fn set_encryption_key(&mut self, key: [u8; 16]) {
355        trace!("Enabled protocol encryption");
356        let (enc_cipher, dec_cipher) = azalea_crypto::create_cipher(&key);
357        self.reader.dec_cipher = Some(dec_cipher);
358        self.enc_cipher = Some(enc_cipher);
359    }
360}
361
362async fn write_task(
363    mut network_packet_writer_rx: mpsc::UnboundedReceiver<Box<[u8]>>,
364    mut write_half: OwnedWriteHalf,
365) {
366    while let Some(network_packet) = network_packet_writer_rx.recv().await {
367        if let Err(e) = write_half.write_all(&network_packet).await {
368            debug!("Error writing packet to server: {e}");
369            break;
370        };
371    }
372
373    trace!("write task is done");
374}
375
376#[derive(Debug, Error)]
377pub enum WritePacketError {
378    #[error("Wrong protocol state: expected {expected:?}, got {got:?}")]
379    WrongState {
380        expected: ConnectionProtocol,
381        got: ConnectionProtocol,
382    },
383    #[error(transparent)]
384    Encoding(#[from] azalea_protocol::write::PacketEncodeError),
385    #[error(transparent)]
386    SendError {
387        #[from]
388        #[backtrace]
389        source: mpsc::error::SendError<Box<[u8]>>,
390    },
391}