From 7b3e2e4bf793466a351510c7fbbd08234e93bb0e Mon Sep 17 00:00:00 2001 From: mat <27899617+mat-1@users.noreply.github.com> Date: Thu, 21 Sep 2023 11:16:29 -0500 Subject: 1.20.2 (#99) * add configuration state * start updating to 23w31a * implement a bit more of 23w31a * chunk batching * start adding configuration state * ioasfhjgsd * almost works * configuration state mostly implemented * handle other packets in configuration state and fix keepalive * cleanup, fix warnings * 23w32a * fix some doctests * 23w33a * 23w35a * 1.20.2-pre2 * fix system conflicts * 1.20.2-pre4 * make tests compile * tests pass * 1.20.2-rc2 * 1.20.2 * Revert "1.20.2" This reverts commit dd152fd265332ead333c919e585ded6d609d7468. * didn't mean to commit that code --------- Co-authored-by: mat --- azalea-protocol/azalea-protocol-macros/src/lib.rs | 18 +- azalea-protocol/examples/handshake_proxy.rs | 8 +- azalea-protocol/src/connect.rs | 200 +++++--- azalea-protocol/src/lib.rs | 8 +- azalea-protocol/src/packets/common.rs | 16 + .../clientbound_custom_payload_packet.rs | 10 + .../configuration/clientbound_disconnect_packet.rs | 8 + .../clientbound_finish_configuration_packet.rs | 5 + .../configuration/clientbound_keep_alive_packet.rs | 7 + .../configuration/clientbound_ping_packet.rs | 7 + .../clientbound_registry_data_packet.rs | 408 +++++++++++++++++ .../clientbound_resource_pack_packet.rs | 11 + .../clientbound_update_enabled_features_packet.rs | 8 + .../clientbound_update_tags_packet.rs | 73 +++ azalea-protocol/src/packets/configuration/mod.rs | 39 ++ .../serverbound_client_information_packet.rs | 181 ++++++++ .../serverbound_custom_payload_packet.rs | 10 + .../serverbound_finish_configuration_packet.rs | 5 + .../configuration/serverbound_keep_alive_packet.rs | 7 + .../configuration/serverbound_pong_packet.rs | 7 + .../serverbound_resource_pack_packet.rs | 15 + .../clientbound_chunk_batch_finished_packet.rs | 8 + .../game/clientbound_chunk_batch_start_packet.rs | 5 + .../game/clientbound_forget_level_chunk_packet.rs | 4 +- .../src/packets/game/clientbound_login_packet.rs | 509 +-------------------- .../src/packets/game/clientbound_ping_packet.rs | 1 + .../game/clientbound_pong_response_packet.rs | 7 + .../src/packets/game/clientbound_respawn_packet.rs | 14 +- .../clientbound_set_display_objective_packet.rs | 25 +- .../game/clientbound_start_configuration_packet.rs | 5 + .../game/clientbound_update_advancements_packet.rs | 46 +- azalea-protocol/src/packets/game/mod.rs | 318 ++++++------- .../serverbound_chunk_batch_received_packet.rs | 7 + .../game/serverbound_client_information_packet.rs | 176 +------ ...erverbound_configuration_acknowledged_packet.rs | 5 + .../game/serverbound_ping_request_packet.rs | 7 + .../packets/handshake/client_intention_packet.rs | 13 - azalea-protocol/src/packets/handshake/mod.rs | 11 - .../packets/handshaking/client_intention_packet.rs | 13 + azalea-protocol/src/packets/handshaking/mod.rs | 11 + azalea-protocol/src/packets/login/mod.rs | 6 +- .../serverbound_custom_query_answer_packet.rs | 10 + .../src/packets/login/serverbound_hello_packet.rs | 4 +- .../login/serverbound_login_acknowledged_packet.rs | 5 + azalea-protocol/src/packets/mod.rs | 8 +- azalea-protocol/src/read.rs | 136 ++++-- azalea-protocol/src/write.rs | 26 +- 47 files changed, 1414 insertions(+), 1017 deletions(-) create mode 100644 azalea-protocol/src/packets/common.rs create mode 100644 azalea-protocol/src/packets/configuration/clientbound_custom_payload_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/clientbound_disconnect_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/clientbound_finish_configuration_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/clientbound_keep_alive_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/clientbound_ping_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/clientbound_registry_data_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/clientbound_resource_pack_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/clientbound_update_enabled_features_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/clientbound_update_tags_packet.rs create mode 100755 azalea-protocol/src/packets/configuration/mod.rs create mode 100644 azalea-protocol/src/packets/configuration/serverbound_client_information_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/serverbound_custom_payload_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/serverbound_finish_configuration_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/serverbound_keep_alive_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/serverbound_pong_packet.rs create mode 100644 azalea-protocol/src/packets/configuration/serverbound_resource_pack_packet.rs create mode 100644 azalea-protocol/src/packets/game/clientbound_chunk_batch_finished_packet.rs create mode 100644 azalea-protocol/src/packets/game/clientbound_chunk_batch_start_packet.rs create mode 100755 azalea-protocol/src/packets/game/clientbound_pong_response_packet.rs create mode 100644 azalea-protocol/src/packets/game/clientbound_start_configuration_packet.rs create mode 100644 azalea-protocol/src/packets/game/serverbound_chunk_batch_received_packet.rs create mode 100644 azalea-protocol/src/packets/game/serverbound_configuration_acknowledged_packet.rs create mode 100755 azalea-protocol/src/packets/game/serverbound_ping_request_packet.rs delete mode 100755 azalea-protocol/src/packets/handshake/client_intention_packet.rs delete mode 100755 azalea-protocol/src/packets/handshake/mod.rs create mode 100755 azalea-protocol/src/packets/handshaking/client_intention_packet.rs create mode 100755 azalea-protocol/src/packets/handshaking/mod.rs create mode 100644 azalea-protocol/src/packets/login/serverbound_custom_query_answer_packet.rs create mode 100644 azalea-protocol/src/packets/login/serverbound_login_acknowledged_packet.rs (limited to 'azalea-protocol') diff --git a/azalea-protocol/azalea-protocol-macros/src/lib.rs b/azalea-protocol/azalea-protocol-macros/src/lib.rs index e04a2dbc..ffecc13a 100755 --- a/azalea-protocol/azalea-protocol-macros/src/lib.rs +++ b/azalea-protocol/azalea-protocol-macros/src/lib.rs @@ -47,7 +47,7 @@ pub fn derive_serverbound_game_packet(input: TokenStream) -> TokenStream { pub fn derive_serverbound_handshake_packet(input: TokenStream) -> TokenStream { as_packet_derive( input, - quote! {crate::packets::handshake::ServerboundHandshakePacket}, + quote! {crate::packets::handshaking::ServerboundHandshakePacket}, ) } #[proc_macro_derive(ServerboundLoginPacket, attributes(var))] @@ -64,6 +64,13 @@ pub fn derive_serverbound_status_packet(input: TokenStream) -> TokenStream { quote! {crate::packets::status::ServerboundStatusPacket}, ) } +#[proc_macro_derive(ServerboundConfigurationPacket, attributes(var))] +pub fn derive_serverbound_configuration_packet(input: TokenStream) -> TokenStream { + as_packet_derive( + input, + quote! {crate::packets::configuration::ServerboundConfigurationPacket}, + ) +} #[proc_macro_derive(ClientboundGamePacket, attributes(var))] pub fn derive_clientbound_game_packet(input: TokenStream) -> TokenStream { @@ -73,7 +80,7 @@ pub fn derive_clientbound_game_packet(input: TokenStream) -> TokenStream { pub fn derive_clientbound_handshake_packet(input: TokenStream) -> TokenStream { as_packet_derive( input, - quote! {crate::packets::handshake::ClientboundHandshakePacket}, + quote! {crate::packets::handshaking::ClientboundHandshakePacket}, ) } #[proc_macro_derive(ClientboundLoginPacket, attributes(var))] @@ -90,6 +97,13 @@ pub fn derive_clientbound_status_packet(input: TokenStream) -> TokenStream { quote! {crate::packets::status::ClientboundStatusPacket}, ) } +#[proc_macro_derive(ClientboundConfigurationPacket, attributes(var))] +pub fn derive_clientbound_configuration_packet(input: TokenStream) -> TokenStream { + as_packet_derive( + input, + quote! {crate::packets::configuration::ClientboundConfigurationPacket}, + ) +} #[derive(Debug)] struct PacketIdPair { diff --git a/azalea-protocol/examples/handshake_proxy.rs b/azalea-protocol/examples/handshake_proxy.rs index 34e9553f..f7fb0f5c 100644 --- a/azalea-protocol/examples/handshake_proxy.rs +++ b/azalea-protocol/examples/handshake_proxy.rs @@ -4,7 +4,7 @@ use azalea_protocol::{ connect::Connection, packets::{ - handshake::{ + handshaking::{ client_intention_packet::ClientIntentionPacket, ClientboundHandshakePacket, ServerboundHandshakePacket, }, @@ -145,11 +145,7 @@ async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> { "Player \'{0}\' from {1} logging in with uuid: {2}", hello.name, ip.ip(), - if let Some(id) = hello.profile_id { - id.to_string() - } else { - String::new() - } + hello.profile_id.to_string() ); tokio::spawn(transfer(conn.unwrap()?, intent, hello).map(|r| { diff --git a/azalea-protocol/src/connect.rs b/azalea-protocol/src/connect.rs index 5df1d874..9c573506 100755 --- a/azalea-protocol/src/connect.rs +++ b/azalea-protocol/src/connect.rs @@ -1,19 +1,23 @@ //! Connect to remote servers/clients. +use crate::packets::configuration::{ + ClientboundConfigurationPacket, ServerboundConfigurationPacket, +}; use crate::packets::game::{ClientboundGamePacket, ServerboundGamePacket}; -use crate::packets::handshake::{ClientboundHandshakePacket, ServerboundHandshakePacket}; +use crate::packets::handshaking::{ClientboundHandshakePacket, ServerboundHandshakePacket}; use crate::packets::login::clientbound_hello_packet::ClientboundHelloPacket; use crate::packets::login::{ClientboundLoginPacket, ServerboundLoginPacket}; use crate::packets::status::{ClientboundStatusPacket, ServerboundStatusPacket}; use crate::packets::ProtocolPacket; -use crate::read::{read_packet, try_read_packet, ReadPacketError}; -use crate::write::write_packet; +use crate::read::{deserialize_packet, read_raw_packet, try_read_raw_packet, ReadPacketError}; +use crate::write::{serialize_packet, write_raw_packet}; use azalea_auth::game_profile::GameProfile; use azalea_auth::sessionserver::{ClientSessionServerError, ServerSessionServerError}; use azalea_crypto::{Aes128CfbDec, Aes128CfbEnc}; use bytes::BytesMut; use log::{error, info}; use std::fmt::Debug; +use std::io::Cursor; use std::marker::PhantomData; use std::net::SocketAddr; use thiserror::Error; @@ -22,20 +26,28 @@ use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf, ReuniteError}; use tokio::net::TcpStream; use uuid::Uuid; -/// The read half of a connection. -pub struct ReadConnection { +pub struct RawReadConnection { pub read_stream: OwnedReadHalf, pub buffer: BytesMut, pub compression_threshold: Option, pub dec_cipher: Option, - _reading: PhantomData, } -/// The write half of a connection. -pub struct WriteConnection { +pub struct RawWriteConnection { pub write_stream: OwnedWriteHalf, pub compression_threshold: Option, pub enc_cipher: Option, +} + +/// The read half of a connection. +pub struct ReadConnection { + pub raw: RawReadConnection, + _reading: PhantomData, +} + +/// The write half of a connection. +pub struct WriteConnection { + pub raw: RawWriteConnection, _writing: PhantomData, } @@ -55,7 +67,7 @@ pub struct WriteConnection { /// serverbound_hello_packet::ServerboundHelloPacket, /// serverbound_key_packet::ServerboundKeyPacket /// }, -/// handshake::client_intention_packet::ClientIntentionPacket +/// handshaking::client_intention_packet::ClientIntentionPacket /// } /// }; /// @@ -82,7 +94,7 @@ pub struct WriteConnection { /// conn.write( /// ServerboundHelloPacket { /// name: "bot".to_string(), -/// profile_id: None, +/// profile_id: uuid::Uuid::nil(), /// } /// .get(), /// ) @@ -108,7 +120,7 @@ pub struct WriteConnection { /// conn.set_compression_threshold(p.compression_threshold); /// } /// ClientboundLoginPacket::GameProfile(p) => { -/// break (conn.game(), p.game_profile); +/// break (conn.configuration(), p.game_profile); /// } /// ClientboundLoginPacket::LoginDisconnect(p) => { /// eprintln!("login disconnect: {}", p.reason); @@ -126,13 +138,9 @@ pub struct Connection { pub writer: WriteConnection, } -impl ReadConnection -where - R: ProtocolPacket + Debug, -{ - /// Read a packet from the stream. - pub async fn read(&mut self) -> Result> { - read_packet::( +impl RawReadConnection { + pub async fn read(&mut self) -> Result, Box> { + read_raw_packet::<_>( &mut self.read_stream, &mut self.buffer, self.compression_threshold, @@ -141,10 +149,8 @@ where .await } - /// Try to read a packet from the stream, or return Ok(None) if there's no - /// packet. - pub fn try_read(&mut self) -> Result, Box> { - try_read_packet::( + pub fn try_read(&mut self) -> Result>, Box> { + try_read_raw_packet::<_>( &mut self.read_stream, &mut self.buffer, self.compression_threshold, @@ -152,14 +158,11 @@ where ) } } -impl WriteConnection -where - W: ProtocolPacket + Debug, -{ - /// Write a packet to the server. - pub async fn write(&mut self, packet: W) -> std::io::Result<()> { - if let Err(e) = write_packet( - &packet, + +impl RawWriteConnection { + pub async fn write(&mut self, packet: &[u8]) -> std::io::Result<()> { + if let Err(e) = write_raw_packet( + packet, &mut self.write_stream, self.compression_threshold, &mut self.enc_cipher, @@ -184,6 +187,42 @@ where } } +impl ReadConnection +where + R: ProtocolPacket + Debug, +{ + /// Read a packet from the stream. + pub async fn read(&mut self) -> Result> { + let raw_packet = self.raw.read().await?; + deserialize_packet(&mut Cursor::new(raw_packet.as_slice())) + } + + /// Try to read a packet from the stream, or return Ok(None) if there's no + /// packet. + pub fn try_read(&mut self) -> Result, Box> { + let Some(raw_packet) = self.raw.try_read()? else { + return Ok(None); + }; + Ok(Some(deserialize_packet(&mut Cursor::new( + raw_packet.as_slice(), + ))?)) + } +} +impl WriteConnection +where + W: ProtocolPacket + Debug, +{ + /// Write a packet to the server. + pub async fn write(&mut self, packet: W) -> std::io::Result<()> { + self.raw.write(&serialize_packet(&packet).unwrap()).await + } + + /// End the connection. + pub async fn shutdown(&mut self) -> std::io::Result<()> { + self.raw.shutdown().await + } +} + impl Connection where R: ProtocolPacket + Debug, @@ -230,16 +269,20 @@ impl Connection { Ok(Connection { reader: ReadConnection { - read_stream, - buffer: BytesMut::new(), - compression_threshold: None, - dec_cipher: None, + raw: RawReadConnection { + read_stream, + buffer: BytesMut::new(), + compression_threshold: None, + dec_cipher: None, + }, _reading: PhantomData, }, writer: WriteConnection { - write_stream, - compression_threshold: None, - enc_cipher: None, + raw: RawWriteConnection { + write_stream, + compression_threshold: None, + enc_cipher: None, + }, _writing: PhantomData, }, }) @@ -267,11 +310,11 @@ impl Connection { pub fn set_compression_threshold(&mut self, threshold: i32) { // if you pass a threshold of less than 0, compression is disabled if threshold >= 0 { - self.reader.compression_threshold = Some(threshold as u32); - self.writer.compression_threshold = Some(threshold as u32); + self.reader.raw.compression_threshold = Some(threshold as u32); + self.writer.raw.compression_threshold = Some(threshold as u32); } else { - self.reader.compression_threshold = None; - self.writer.compression_threshold = None; + self.reader.raw.compression_threshold = None; + self.writer.raw.compression_threshold = None; } } @@ -279,14 +322,16 @@ impl Connection { /// the same for both reading and writing. pub fn set_encryption_key(&mut self, key: [u8; 16]) { let (enc_cipher, dec_cipher) = azalea_crypto::create_cipher(&key); - self.reader.dec_cipher = Some(dec_cipher); - self.writer.enc_cipher = Some(enc_cipher); + self.reader.raw.dec_cipher = Some(dec_cipher); + self.writer.raw.enc_cipher = Some(enc_cipher); } - /// Change our state from login to game. This is the state that's used when - /// you're actually in the game. + /// Change our state from login to configuration. This is the state where + /// the server sends us the registries and resource pack and stuff. #[must_use] - pub fn game(self) -> Connection { + pub fn configuration( + self, + ) -> Connection { Connection::from(self) } @@ -384,11 +429,11 @@ impl Connection { pub fn set_compression_threshold(&mut self, threshold: i32) { // if you pass a threshold of less than 0, compression is disabled if threshold >= 0 { - self.reader.compression_threshold = Some(threshold as u32); - self.writer.compression_threshold = Some(threshold as u32); + self.reader.raw.compression_threshold = Some(threshold as u32); + self.writer.raw.compression_threshold = Some(threshold as u32); } else { - self.reader.compression_threshold = None; - self.writer.compression_threshold = None; + self.reader.raw.compression_threshold = None; + self.writer.raw.compression_threshold = None; } } @@ -396,8 +441,8 @@ impl Connection { /// the same for both reading and writing. pub fn set_encryption_key(&mut self, key: [u8; 16]) { let (enc_cipher, dec_cipher) = azalea_crypto::create_cipher(&key); - self.reader.dec_cipher = Some(dec_cipher); - self.writer.enc_cipher = Some(enc_cipher); + self.reader.raw.dec_cipher = Some(dec_cipher); + self.writer.raw.enc_cipher = Some(enc_cipher); } /// Change our state from login to game. This is the state that's used when @@ -421,6 +466,25 @@ impl Connection { } } +impl Connection { + /// Change our state from configuration to game. This is the state that's + /// used when the client is actually in the world. + #[must_use] + pub fn game(self) -> Connection { + Connection::from(self) + } +} + +impl Connection { + /// Change our state back to configuration. + #[must_use] + pub fn configuration( + self, + ) -> Connection { + Connection::from(self) + } +} + // rust doesn't let us implement From because allegedly it conflicts with // `core`'s "impl From for T" so we do this instead impl Connection @@ -438,16 +502,11 @@ where { Connection { reader: ReadConnection { - read_stream: connection.reader.read_stream, - buffer: connection.reader.buffer, - compression_threshold: connection.reader.compression_threshold, - dec_cipher: connection.reader.dec_cipher, + raw: connection.reader.raw, _reading: PhantomData, }, writer: WriteConnection { - compression_threshold: connection.writer.compression_threshold, - write_stream: connection.writer.write_stream, - enc_cipher: connection.writer.enc_cipher, + raw: connection.writer.raw, _writing: PhantomData, }, } @@ -459,16 +518,20 @@ where Connection { reader: ReadConnection { - read_stream, - buffer: BytesMut::new(), - compression_threshold: None, - dec_cipher: None, + raw: RawReadConnection { + read_stream, + buffer: BytesMut::new(), + compression_threshold: None, + dec_cipher: None, + }, _reading: PhantomData, }, writer: WriteConnection { - write_stream, - compression_threshold: None, - enc_cipher: None, + raw: RawWriteConnection { + write_stream, + compression_threshold: None, + enc_cipher: None, + }, _writing: PhantomData, }, } @@ -476,6 +539,9 @@ where /// Convert from a `Connection` into a `TcpStream`. Useful for servers. pub fn unwrap(self) -> Result { - self.reader.read_stream.reunite(self.writer.write_stream) + self.reader + .raw + .read_stream + .reunite(self.writer.raw.write_stream) } } diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs index 34f7cd4c..aac449c9 100644 --- a/azalea-protocol/src/lib.rs +++ b/azalea-protocol/src/lib.rs @@ -86,7 +86,7 @@ mod tests { login::{serverbound_hello_packet::ServerboundHelloPacket, ServerboundLoginPacket}, }, read::{compression_decoder, read_packet}, - write::{compression_encoder, packet_encoder, write_packet}, + write::{compression_encoder, serialize_packet, write_packet}, }; use bytes::BytesMut; use uuid::Uuid; @@ -95,7 +95,7 @@ mod tests { async fn test_hello_packet() { let packet = ServerboundHelloPacket { name: "test".to_string(), - profile_id: Some(Uuid::nil()), + profile_id: Uuid::nil(), } .get(); let mut stream = Vec::new(); @@ -119,7 +119,7 @@ mod tests { async fn test_double_hello_packet() { let packet = ServerboundHelloPacket { name: "test".to_string(), - profile_id: Some(Uuid::nil()), + profile_id: Uuid::nil(), } .get(); let mut stream = Vec::new(); @@ -145,7 +145,7 @@ mod tests { async fn test_read_long_compressed_chat() { let compression_threshold = 256; - let buf = packet_encoder( + let buf = serialize_packet( &ServerboundChatPacket { message: "a".repeat(256), timestamp: 0, diff --git a/azalea-protocol/src/packets/common.rs b/azalea-protocol/src/packets/common.rs new file mode 100644 index 00000000..0fa7cb1f --- /dev/null +++ b/azalea-protocol/src/packets/common.rs @@ -0,0 +1,16 @@ +use azalea_buf::McBuf; +use azalea_core::{GameMode, GlobalPos, OptionalGameType, ResourceLocation}; + +#[derive(Clone, Debug, McBuf)] +pub struct CommonPlayerSpawnInfo { + pub dimension_type: ResourceLocation, + pub dimension: ResourceLocation, + pub seed: i64, + pub game_type: GameMode, + pub previous_game_type: OptionalGameType, + pub is_debug: bool, + pub is_flat: bool, + pub last_death_location: Option, + #[var] + pub portal_cooldown: u32, +} diff --git a/azalea-protocol/src/packets/configuration/clientbound_custom_payload_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_custom_payload_packet.rs new file mode 100644 index 00000000..0b0ea902 --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_custom_payload_packet.rs @@ -0,0 +1,10 @@ +use azalea_buf::McBuf; +use azalea_buf::UnsizedByteArray; +use azalea_core::ResourceLocation; +use azalea_protocol_macros::ClientboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundCustomPayloadPacket { + pub identifier: ResourceLocation, + pub data: UnsizedByteArray, +} diff --git a/azalea-protocol/src/packets/configuration/clientbound_disconnect_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_disconnect_packet.rs new file mode 100644 index 00000000..cd0ed42a --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_disconnect_packet.rs @@ -0,0 +1,8 @@ +use azalea_buf::McBuf; +use azalea_chat::FormattedText; +use azalea_protocol_macros::ClientboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundDisconnectPacket { + pub reason: FormattedText, +} diff --git a/azalea-protocol/src/packets/configuration/clientbound_finish_configuration_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_finish_configuration_packet.rs new file mode 100644 index 00000000..81251108 --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_finish_configuration_packet.rs @@ -0,0 +1,5 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundFinishConfigurationPacket {} diff --git a/azalea-protocol/src/packets/configuration/clientbound_keep_alive_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_keep_alive_packet.rs new file mode 100644 index 00000000..83c25610 --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_keep_alive_packet.rs @@ -0,0 +1,7 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundKeepAlivePacket { + pub id: u64, +} diff --git a/azalea-protocol/src/packets/configuration/clientbound_ping_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_ping_packet.rs new file mode 100644 index 00000000..968dcaed --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_ping_packet.rs @@ -0,0 +1,7 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundPingPacket { + pub id: u32, +} diff --git a/azalea-protocol/src/packets/configuration/clientbound_registry_data_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_registry_data_packet.rs new file mode 100644 index 00000000..85345a1e --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_registry_data_packet.rs @@ -0,0 +1,408 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundConfigurationPacket; + +use self::registry::RegistryHolder; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundRegistryDataPacket { + pub registry_holder: RegistryHolder, +} + +pub mod registry { + //! [ClientboundRegistryDataPacket](super::ClientboundRegistryDataPacket) + //! Registry Structures + //! + //! This module contains the structures used to represent the registry + //! sent to the client upon login. This contains a lot of information about + //! the game, including the types of chat messages, dimensions, and + //! biomes. + + use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; + use azalea_core::ResourceLocation; + use azalea_nbt::Nbt; + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + use std::{collections::HashMap, io::Cursor}; + + /// The base of the registry. + /// + /// This is the registry that is sent to the client upon login. + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct RegistryHolder { + pub registries: HashMap, + } + + impl TryFrom for RegistryHolder { + type Error = serde_json::Error; + + fn try_from(value: Nbt) -> Result { + Ok(RegistryHolder { + registries: serde_json::from_value(serde_json::to_value(value)?)?, + }) + } + } + + impl TryInto for RegistryHolder { + type Error = serde_json::Error; + + fn try_into(self) -> Result { + serde_json::from_value(serde_json::to_value(self.registries)?) + } + } + + impl McBufReadable for RegistryHolder { + fn read_from(buf: &mut Cursor<&[u8]>) -> Result { + RegistryHolder::try_from(Nbt::read_from(buf)?) + .map_err(|e| BufReadError::Deserialization { source: e }) + } + } + + impl McBufWritable for RegistryHolder { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + TryInto::::try_into(self.clone())?.write_into(buf) + } + } + + /// A collection of values for a certain type of registry data. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct RegistryType { + #[serde(rename = "type")] + pub kind: ResourceLocation, + pub value: Vec>, + } + + /// A value for a certain type of registry data. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct TypeValue { + pub id: u32, + pub name: ResourceLocation, + pub element: T, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct TrimMaterialElement { + pub asset_name: String, + pub ingredient: ResourceLocation, + pub item_model_index: f32, + pub override_armor_materials: HashMap, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + } + + /// Data about a kind of chat message + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct ChatTypeElement { + pub chat: ChatTypeData, + pub narration: ChatTypeData, + } + + /// Data about a chat message. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct ChatTypeData { + pub translation_key: String, + pub parameters: Vec, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub style: Option, + } + + /// The style of a chat message. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct ChatTypeStyle { + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub color: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "Convert")] + pub bold: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "Convert")] + pub italic: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "Convert")] + pub underlined: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "Convert")] + pub strikethrough: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "Convert")] + pub obfuscated: Option, + } + + /// Dimension attributes. + #[cfg(feature = "strict_registry")] + #[derive(Debug, Clone, Serialize, Deserialize)] + #[serde(deny_unknown_fields)] + pub struct DimensionTypeElement { + pub ambient_light: f32, + #[serde(with = "Convert")] + pub bed_works: bool, + pub coordinate_scale: f32, + pub effects: ResourceLocation, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub fixed_time: Option, + #[serde(with = "Convert")] + pub has_ceiling: bool, + #[serde(with = "Convert")] + pub has_raids: bool, + #[serde(with = "Convert")] + pub has_skylight: bool, + pub height: u32, + pub infiniburn: ResourceLocation, + pub logical_height: u32, + pub min_y: i32, + pub monster_spawn_block_light_limit: u32, + pub monster_spawn_light_level: MonsterSpawnLightLevel, + #[serde(with = "Convert")] + pub natural: bool, + #[serde(with = "Convert")] + pub piglin_safe: bool, + #[serde(with = "Convert")] + pub respawn_anchor_works: bool, + #[serde(with = "Convert")] + pub ultrawarm: bool, + } + + /// Dimension attributes. + #[cfg(not(feature = "strict_registry"))] + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct DimensionTypeElement { + pub height: u32, + pub min_y: i32, + #[serde(flatten)] + pub _extra: HashMap, + } + + /// The light level at which monsters can spawn. + /// + /// This can be either a single minimum value, or a formula with a min and + /// max. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[serde(untagged)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub enum MonsterSpawnLightLevel { + /// A simple minimum value. + Simple(u32), + /// A complex value with a type, minimum, and maximum. + /// Vanilla minecraft only uses one type, "minecraft:uniform". + Complex { + #[serde(rename = "type")] + kind: ResourceLocation, + value: MonsterSpawnLightLevelValues, + }, + } + + /// The min and max light levels at which monsters can spawn. + /// + /// Values are inclusive. + #[derive(Debug, Copy, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct MonsterSpawnLightLevelValues { + #[serde(rename = "min_inclusive")] + pub min: u32, + #[serde(rename = "max_inclusive")] + pub max: u32, + } + + /// Biome attributes. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct WorldTypeElement { + #[serde(with = "Convert")] + pub has_precipitation: bool, + pub temperature: f32, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub temperature_modifier: Option, + pub downfall: f32, + pub effects: BiomeEffects, + } + + /// The precipitation of a biome. + #[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub enum BiomePrecipitation { + #[serde(rename = "none")] + None, + #[serde(rename = "rain")] + Rain, + #[serde(rename = "snow")] + Snow, + } + + /// The effects of a biome. + /// + /// This includes the sky, fog, water, and grass color, + /// as well as music and other sound effects. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct BiomeEffects { + pub sky_color: u32, + pub fog_color: u32, + pub water_color: u32, + pub water_fog_color: u32, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub foliage_color: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub grass_color: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub grass_color_modifier: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub music: Option, + pub mood_sound: BiomeMoodSound, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub additions_sound: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub ambient_sound: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub particle: Option, + } + + /// The music of the biome. + /// + /// Some biomes have unique music that only play when inside them. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct BiomeMusic { + #[serde(with = "Convert")] + pub replace_current_music: bool, + pub max_delay: u32, + pub min_delay: u32, + pub sound: azalea_registry::SoundEvent, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct BiomeMoodSound { + pub tick_delay: u32, + pub block_search_extent: u32, + pub offset: f32, + pub sound: azalea_registry::SoundEvent, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct AdditionsSound { + pub tick_chance: f32, + pub sound: azalea_registry::SoundEvent, + } + + /// Biome particles. + /// + /// Some biomes have particles that spawn in the air. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct BiomeParticle { + pub probability: f32, + pub options: HashMap, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct TrimPatternElement { + #[serde(flatten)] + pub pattern: HashMap, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] + pub struct DamageTypeElement { + pub message_id: String, + pub scaling: String, + pub exhaustion: f32, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub effects: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub death_message_type: Option, + } + + // Using a trait because you can't implement methods for + // types you don't own, in this case Option and bool. + trait Convert: Sized { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer; + + fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>; + } + + // Convert between bool and u8 + impl Convert for bool { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u8(if *self { 1 } else { 0 }) + } + + fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + convert::(u8::deserialize(deserializer)?) + } + } + + // Convert between Option and u8 + impl Convert for Option { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if let Some(value) = self { + Convert::serialize(value, serializer) + } else { + serializer.serialize_none() + } + } + + fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + if let Some(value) = Option::::deserialize(deserializer)? { + Ok(Some(convert::(value)?)) + } else { + Ok(None) + } + } + } + + // Deserializing logic here to deduplicate code + fn convert<'de, D>(value: u8) -> Result + where + D: Deserializer<'de>, + { + match value { + 0 => Ok(false), + 1 => Ok(true), + other => Err(de::Error::invalid_value( + de::Unexpected::Unsigned(other as u64), + &"zero or one", + )), + } + } +} diff --git a/azalea-protocol/src/packets/configuration/clientbound_resource_pack_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_resource_pack_packet.rs new file mode 100644 index 00000000..b05210b5 --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_resource_pack_packet.rs @@ -0,0 +1,11 @@ +use azalea_buf::McBuf; +use azalea_chat::FormattedText; +use azalea_protocol_macros::ClientboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundResourcePackPacket { + pub url: String, + pub hash: String, + pub required: bool, + pub prompt: Option, +} diff --git a/azalea-protocol/src/packets/configuration/clientbound_update_enabled_features_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_update_enabled_features_packet.rs new file mode 100644 index 00000000..5eedabc1 --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_update_enabled_features_packet.rs @@ -0,0 +1,8 @@ +use azalea_buf::McBuf; +use azalea_core::ResourceLocation; +use azalea_protocol_macros::ClientboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundUpdateEnabledFeaturesPacket { + pub features: Vec, +} diff --git a/azalea-protocol/src/packets/configuration/clientbound_update_tags_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_update_tags_packet.rs new file mode 100644 index 00000000..215c9439 --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_update_tags_packet.rs @@ -0,0 +1,73 @@ +use azalea_buf::{BufReadError, McBuf, McBufVarReadable, McBufVarWritable}; +use azalea_buf::{McBufReadable, McBufWritable}; +use azalea_core::ResourceLocation; +use azalea_protocol_macros::ClientboundConfigurationPacket; +use std::io::Cursor; +use std::ops::Deref; +use std::{collections::HashMap, io::Write}; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundUpdateTagsPacket { + pub tags: TagMap, +} + +#[derive(Clone, Debug)] +pub struct Tags { + pub name: ResourceLocation, + pub elements: Vec, +} + +#[derive(Clone, Debug)] +pub struct TagMap(pub HashMap>); + +impl McBufReadable for TagMap { + fn read_from(buf: &mut Cursor<&[u8]>) -> Result { + let length = u32::var_read_from(buf)? as usize; + let mut data = HashMap::with_capacity(length); + for _ in 0..length { + let tag_type = ResourceLocation::read_from(buf)?; + let tags_count = i32::var_read_from(buf)? as usize; + let mut tags_vec = Vec::with_capacity(tags_count); + for _ in 0..tags_count { + let tags = Tags::read_from(buf)?; + tags_vec.push(tags); + } + data.insert(tag_type, tags_vec); + } + Ok(TagMap(data)) + } +} + +impl McBufWritable for TagMap { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + (self.len() as u32).var_write_into(buf)?; + for (k, v) in &self.0 { + k.write_into(buf)?; + v.write_into(buf)?; + } + Ok(()) + } +} +impl McBufReadable for Tags { + fn read_from(buf: &mut Cursor<&[u8]>) -> Result { + let name = ResourceLocation::read_from(buf)?; + let elements = Vec::::var_read_from(buf)?; + Ok(Tags { name, elements }) + } +} + +impl McBufWritable for Tags { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + self.name.write_into(buf)?; + self.elements.var_write_into(buf)?; + Ok(()) + } +} + +impl Deref for TagMap { + type Target = HashMap>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/azalea-protocol/src/packets/configuration/mod.rs b/azalea-protocol/src/packets/configuration/mod.rs new file mode 100755 index 00000000..8244e90a --- /dev/null +++ b/azalea-protocol/src/packets/configuration/mod.rs @@ -0,0 +1,39 @@ +pub mod clientbound_custom_payload_packet; +pub mod clientbound_disconnect_packet; +pub mod clientbound_finish_configuration_packet; +pub mod clientbound_keep_alive_packet; +pub mod clientbound_ping_packet; +pub mod clientbound_registry_data_packet; +pub mod clientbound_resource_pack_packet; +pub mod clientbound_update_enabled_features_packet; +pub mod clientbound_update_tags_packet; +pub mod serverbound_client_information_packet; +pub mod serverbound_custom_payload_packet; +pub mod serverbound_finish_configuration_packet; +pub mod serverbound_keep_alive_packet; +pub mod serverbound_pong_packet; +pub mod serverbound_resource_pack_packet; +use azalea_protocol_macros::declare_state_packets; + +declare_state_packets!( + ConfigurationPacket, + Serverbound => { + 0x00: serverbound_client_information_packet::ServerboundClientInformationPacket, + 0x01: serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, + 0x02: serverbound_finish_configuration_packet::ServerboundFinishConfigurationPacket, + 0x03: serverbound_keep_alive_packet::ServerboundKeepAlivePacket, + 0x04: serverbound_pong_packet::ServerboundPongPacket, + 0x05: serverbound_resource_pack_packet::ServerboundResourcePackPacket, + }, + Clientbound => { + 0x00: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, + 0x01: clientbound_disconnect_packet::ClientboundDisconnectPacket, + 0x02: clientbound_finish_configuration_packet::ClientboundFinishConfigurationPacket, + 0x03: clientbound_keep_alive_packet::ClientboundKeepAlivePacket, + 0x04: clientbound_ping_packet::ClientboundPingPacket, + 0x05: clientbound_registry_data_packet::ClientboundRegistryDataPacket, + 0x06: clientbound_resource_pack_packet::ClientboundResourcePackPacket, + 0x07: clientbound_update_enabled_features_packet::ClientboundUpdateEnabledFeaturesPacket, + 0x08: clientbound_update_tags_packet::ClientboundUpdateTagsPacket, + } +); diff --git a/azalea-protocol/src/packets/configuration/serverbound_client_information_packet.rs b/azalea-protocol/src/packets/configuration/serverbound_client_information_packet.rs new file mode 100644 index 00000000..2af70b83 --- /dev/null +++ b/azalea-protocol/src/packets/configuration/serverbound_client_information_packet.rs @@ -0,0 +1,181 @@ +use azalea_buf::{McBuf, McBufReadable, McBufWritable}; +use azalea_core::FixedBitSet; +use azalea_protocol_macros::ServerboundConfigurationPacket; +use bevy_ecs::component::Component; + +#[derive(Clone, Debug, McBuf, ServerboundConfigurationPacket, PartialEq, Eq)] +pub struct ServerboundClientInformationPacket { + pub information: ClientInformation, +} + +/// A component that contains some of the "settings" for this client that are +/// sent to the server, such as render distance. This is only present on local +/// players. +#[derive(Clone, Debug, McBuf, PartialEq, Eq, Component)] +pub struct ClientInformation { + /// The locale of the client. + pub language: String, + /// The view distance of the client in chunks, same as the render distance + /// in-game. + pub view_distance: u8, + /// The types of chat messages the client wants to receive. Note that many + /// servers ignore this. + pub chat_visibility: ChatVisibility, + /// Whether the messages sent from the server should have colors. Note that + /// many servers ignore this and always send colored messages. + pub chat_colors: bool, + pub model_customization: ModelCustomization, + pub main_hand: HumanoidArm, + pub text_filtering_enabled: bool, + /// Whether the client should show up as "Anonymous Player" in the server + /// list. + pub allows_listing: bool, +} + +impl Default for ClientInformation { + fn default() -> Self { + Self { + language: "en_us".to_string(), + view_distance: 8, + chat_visibility: ChatVisibility::default(), + chat_colors: true, + model_customization: ModelCustomization::default(), + main_hand: HumanoidArm::Right, + text_filtering_enabled: false, + allows_listing: false, + } + } +} + +#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum ChatVisibility { + /// All chat messages should be sent to the client. + #[default] + Full = 0, + /// Chat messages from other players should be not sent to the client, only + /// messages from the server like "Player joined the game" should be sent. + System = 1, + /// No chat messages should be sent to the client. + Hidden = 2, +} + +#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum HumanoidArm { + Left = 0, + #[default] + Right = 1, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ModelCustomization { + pub cape: bool, + pub jacket: bool, + pub left_sleeve: bool, + pub right_sleeve: bool, + pub left_pants: bool, + pub right_pants: bool, + pub hat: bool, +} + +impl Default for ModelCustomization { + fn default() -> Self { + Self { + cape: true, + jacket: true, + left_sleeve: true, + right_sleeve: true, + left_pants: true, + right_pants: true, + hat: true, + } + } +} + +impl McBufReadable for ModelCustomization { + fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result { + let set = FixedBitSet::<7>::read_from(buf)?; + Ok(Self { + cape: set.index(0), + jacket: set.index(1), + left_sleeve: set.index(2), + right_sleeve: set.index(3), + left_pants: set.index(4), + right_pants: set.index(5), + hat: set.index(6), + }) + } +} + +impl McBufWritable for ModelCustomization { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + let mut set = FixedBitSet::<7>::new(); + if self.cape { + set.set(0); + } + if self.jacket { + set.set(1); + } + if self.left_sleeve { + set.set(2); + } + if self.right_sleeve { + set.set(3); + } + if self.left_pants { + set.set(4); + } + if self.right_pants { + set.set(5); + } + if self.hat { + set.set(6); + } + set.write_into(buf) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Cursor; + + #[test] + fn test_client_information_packet() { + { + let data = ClientInformation::default(); + let mut buf = Vec::new(); + data.write_into(&mut buf).unwrap(); + let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); + + let read_data = ClientInformation::read_from(&mut data_cursor).unwrap(); + assert_eq!(read_data, data); + } + + { + let data = ClientInformation { + language: "en_gb".to_string(), + view_distance: 24, + chat_visibility: ChatVisibility::Hidden, + chat_colors: false, + model_customization: ModelCustomization { + cape: false, + jacket: false, + left_sleeve: true, + right_sleeve: false, + left_pants: true, + right_pants: false, + hat: true, + }, + main_hand: HumanoidArm::Left, + text_filtering_enabled: true, + allows_listing: true, + }; + let mut buf = Vec::new(); + data.write_into(&mut buf).unwrap(); + let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); + + let read_data = ClientInformation::read_from(&mut data_cursor).unwrap(); + assert_eq!(read_data, data); + } + } +} diff --git a/azalea-protocol/src/packets/configuration/serverbound_custom_payload_packet.rs b/azalea-protocol/src/packets/configuration/serverbound_custom_payload_packet.rs new file mode 100644 index 00000000..589256bd --- /dev/null +++ b/azalea-protocol/src/packets/configuration/serverbound_custom_payload_packet.rs @@ -0,0 +1,10 @@ +use azalea_buf::McBuf; +use azalea_buf::UnsizedByteArray; +use azalea_core::ResourceLocation; +use azalea_protocol_macros::ServerboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ServerboundConfigurationPacket)] +pub struct ServerboundCustomPayloadPacket { + pub identifier: ResourceLocation, + pub data: UnsizedByteArray, +} diff --git a/azalea-protocol/src/packets/configuration/serverbound_finish_configuration_packet.rs b/azalea-protocol/src/packets/configuration/serverbound_finish_configuration_packet.rs new file mode 100644 index 00000000..53e04182 --- /dev/null +++ b/azalea-protocol/src/packets/configuration/serverbound_finish_configuration_packet.rs @@ -0,0 +1,5 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ServerboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ServerboundConfigurationPacket)] +pub struct ServerboundFinishConfigurationPacket {} diff --git a/azalea-protocol/src/packets/configuration/serverbound_keep_alive_packet.rs b/azalea-protocol/src/packets/configuration/serverbound_keep_alive_packet.rs new file mode 100644 index 00000000..4604df46 --- /dev/null +++ b/azalea-protocol/src/packets/configuration/serverbound_keep_alive_packet.rs @@ -0,0 +1,7 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ServerboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ServerboundConfigurationPacket)] +pub struct ServerboundKeepAlivePacket { + pub id: u64, +} diff --git a/azalea-protocol/src/packets/configuration/serverbound_pong_packet.rs b/azalea-protocol/src/packets/configuration/serverbound_pong_packet.rs new file mode 100644 index 00000000..153e3fea --- /dev/null +++ b/azalea-protocol/src/packets/configuration/serverbound_pong_packet.rs @@ -0,0 +1,7 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ServerboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ServerboundConfigurationPacket)] +pub struct ServerboundPongPacket { + pub id: u32, +} diff --git a/azalea-protocol/src/packets/configuration/serverbound_resource_pack_packet.rs b/azalea-protocol/src/packets/configuration/serverbound_resource_pack_packet.rs new file mode 100644 index 00000000..11149115 --- /dev/null +++ b/azalea-protocol/src/packets/configuration/serverbound_resource_pack_packet.rs @@ -0,0 +1,15 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ServerboundConfigurationPacket; + +#[derive(Clone, Debug, McBuf, ServerboundConfigurationPacket)] +pub struct ServerboundResourcePackPacket { + pub action: Action, +} + +#[derive(McBuf, Clone, Copy, Debug)] +pub enum Action { + SuccessfullyLoaded = 0, + Declined = 1, + FailedDownload = 2, + Accepted = 3, +} diff --git a/azalea-protocol/src/packets/game/clientbound_chunk_batch_finished_packet.rs b/azalea-protocol/src/packets/game/clientbound_chunk_batch_finished_packet.rs new file mode 100644 index 00000000..27bc2f25 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_chunk_batch_finished_packet.rs @@ -0,0 +1,8 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundGamePacket; + +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] +pub struct ClientboundChunkBatchFinishedPacket { + #[var] + pub batch_size: u32, +} diff --git a/azalea-protocol/src/packets/game/clientbound_chunk_batch_start_packet.rs b/azalea-protocol/src/packets/game/clientbound_chunk_batch_start_packet.rs new file mode 100644 index 00000000..308ba8c9 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_chunk_batch_start_packet.rs @@ -0,0 +1,5 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundGamePacket; + +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] +pub struct ClientboundChunkBatchStartPacket {} diff --git a/azalea-protocol/src/packets/game/clientbound_forget_level_chunk_packet.rs b/azalea-protocol/src/packets/game/clientbound_forget_level_chunk_packet.rs index f25ba3a4..524afd08 100755 --- a/azalea-protocol/src/packets/game/clientbound_forget_level_chunk_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_forget_level_chunk_packet.rs @@ -1,8 +1,8 @@ use azalea_buf::McBuf; +use azalea_core::ChunkPos; use azalea_protocol_macros::ClientboundGamePacket; #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundForgetLevelChunkPacket { - pub x: i32, - pub z: i32, + pub pos: ChunkPos, } diff --git a/azalea-protocol/src/packets/game/clientbound_login_packet.rs b/azalea-protocol/src/packets/game/clientbound_login_packet.rs index 3b8ae03d..b6054a75 100755 --- a/azalea-protocol/src/packets/game/clientbound_login_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_login_packet.rs @@ -1,6 +1,7 @@ -use self::registry::RegistryHolder; +use crate::packets::common::CommonPlayerSpawnInfo; + use azalea_buf::McBuf; -use azalea_core::{GameMode, GlobalPos, OptionalGameType, ResourceLocation}; +use azalea_core::ResourceLocation; use azalea_protocol_macros::ClientboundGamePacket; /// The first packet sent by the server to the client after login. @@ -11,13 +12,7 @@ use azalea_protocol_macros::ClientboundGamePacket; pub struct ClientboundLoginPacket { pub player_id: u32, pub hardcore: bool, - pub game_type: GameMode, - pub previous_game_type: OptionalGameType, pub levels: Vec, - pub registry_holder: RegistryHolder, - pub dimension_type: ResourceLocation, - pub dimension: ResourceLocation, - pub seed: i64, #[var] pub max_players: i32, #[var] @@ -26,500 +21,6 @@ pub struct ClientboundLoginPacket { pub simulation_distance: u32, pub reduced_debug_info: bool, pub show_death_screen: bool, - pub is_debug: bool, - pub is_flat: bool, - pub last_death_location: Option, - #[var] - pub portal_cooldown: u32, -} - -pub mod registry { - //! [ClientboundLoginPacket](super::ClientboundLoginPacket) Registry - //! Structures - //! - //! This module contains the structures used to represent the registry - //! sent to the client upon login. This contains a lot of information about - //! the game, including the types of chat messages, dimensions, and - //! biomes. - - use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; - use azalea_core::ResourceLocation; - use azalea_nbt::Nbt; - use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; - use std::{collections::HashMap, io::Cursor}; - - /// The base of the registry. - /// - /// This is the registry that is sent to the client upon login. - /// - /// As a tag, it is a compound tag that only contains a single compound tag. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct RegistryHolder { - #[serde(rename = "")] - pub root: RegistryRoot, - } - - impl TryFrom for RegistryHolder { - type Error = serde_json::Error; - - fn try_from(value: Nbt) -> Result { - serde_json::from_value(serde_json::to_value(value)?) - } - } - - impl TryInto for RegistryHolder { - type Error = serde_json::Error; - - fn try_into(self) -> Result { - serde_json::from_value(serde_json::to_value(self)?) - } - } - - impl McBufReadable for RegistryHolder { - fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - RegistryHolder::try_from(Nbt::read_from(buf)?) - .map_err(|e| BufReadError::Deserialization { source: e }) - } - } - - impl McBufWritable for RegistryHolder { - fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - TryInto::::try_into(self.clone())?.write_into(buf) - } - } - - /// The main part of the registry. - /// - /// The only field of [`RegistryHolder`]. - /// Contains information from the server about chat, dimensions, - /// and world generation. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct RegistryRoot { - #[cfg(feature = "strict_registry")] - #[serde(rename = "minecraft:trim_material")] - pub trim_material: RegistryType, - #[cfg(not(feature = "strict_registry"))] - #[serde(default, rename = "minecraft:trim_material")] - pub trim_material: Nbt, - - #[cfg(feature = "strict_registry")] - #[serde(rename = "minecraft:chat_type")] - pub chat_type: RegistryType, - #[cfg(not(feature = "strict_registry"))] - #[serde(default, rename = "minecraft:chat_type")] - pub chat_type: Nbt, - - #[serde(rename = "minecraft:dimension_type")] - pub dimension_type: RegistryType, - - #[cfg(feature = "strict_registry")] - #[serde(rename = "minecraft:worldgen/biome")] - pub world_type: RegistryType, - #[cfg(not(feature = "strict_registry"))] - #[serde(default, rename = "minecraft:worldgen/biome")] - pub world_type: Nbt, - - #[cfg(feature = "strict_registry")] - #[serde(rename = "minecraft:trim_pattern")] - pub trim_pattern: RegistryType, - #[cfg(not(feature = "strict_registry"))] - #[serde(default, rename = "minecraft:trim_pattern")] - pub trim_pattern: Nbt, - - #[cfg(feature = "strict_registry")] - #[serde(rename = "minecraft:damage_type")] - pub damage_type: RegistryType, - #[cfg(not(feature = "strict_registry"))] - #[serde(default, rename = "minecraft:damage_type")] - pub damage_type: Nbt, - } - - /// A collection of values for a certain type of registry data. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct RegistryType { - #[serde(rename = "type")] - pub kind: ResourceLocation, - pub value: Vec>, - } - - /// A value for a certain type of registry data. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct TypeValue { - pub id: u32, - pub name: ResourceLocation, - pub element: T, - } - - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct TrimMaterialElement { - pub asset_name: String, - pub ingredient: ResourceLocation, - pub item_model_index: f32, - pub override_armor_materials: HashMap, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, - } - - /// Data about a kind of chat message - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct ChatTypeElement { - pub chat: ChatTypeData, - pub narration: ChatTypeData, - } - - /// Data about a chat message. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct ChatTypeData { - pub translation_key: String, - pub parameters: Vec, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub style: Option, - } - - /// The style of a chat message. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct ChatTypeStyle { - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub color: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "Convert")] - pub bold: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "Convert")] - pub italic: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "Convert")] - pub underlined: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "Convert")] - pub strikethrough: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "Convert")] - pub obfuscated: Option, - } - - /// Dimension attributes. - #[cfg(feature = "strict_registry")] - #[derive(Debug, Clone, Serialize, Deserialize)] - #[serde(deny_unknown_fields)] - pub struct DimensionTypeElement { - pub ambient_light: f32, - #[serde(with = "Convert")] - pub bed_works: bool, - pub coordinate_scale: f32, - pub effects: ResourceLocation, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub fixed_time: Option, - #[serde(with = "Convert")] - pub has_ceiling: bool, - #[serde(with = "Convert")] - pub has_raids: bool, - #[serde(with = "Convert")] - pub has_skylight: bool, - pub height: u32, - pub infiniburn: ResourceLocation, - pub logical_height: u32, - pub min_y: i32, - pub monster_spawn_block_light_limit: u32, - pub monster_spawn_light_level: MonsterSpawnLightLevel, - #[serde(with = "Convert")] - pub natural: bool, - #[serde(with = "Convert")] - pub piglin_safe: bool, - #[serde(with = "Convert")] - pub respawn_anchor_works: bool, - #[serde(with = "Convert")] - pub ultrawarm: bool, - } - - /// Dimension attributes. - #[cfg(not(feature = "strict_registry"))] - #[derive(Debug, Clone, Serialize, Deserialize)] - pub struct DimensionTypeElement { - pub height: u32, - pub min_y: i32, - #[serde(flatten)] - pub _extra: HashMap, - } - - /// The light level at which monsters can spawn. - /// - /// This can be either a single minimum value, or a formula with a min and - /// max. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[serde(untagged)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub enum MonsterSpawnLightLevel { - /// A simple minimum value. - Simple(u32), - /// A complex value with a type, minimum, and maximum. - /// Vanilla minecraft only uses one type, "minecraft:uniform". - Complex { - #[serde(rename = "type")] - kind: ResourceLocation, - value: MonsterSpawnLightLevelValues, - }, - } - - /// The min and max light levels at which monsters can spawn. - /// - /// Values are inclusive. - #[derive(Debug, Copy, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct MonsterSpawnLightLevelValues { - #[serde(rename = "min_inclusive")] - pub min: u32, - #[serde(rename = "max_inclusive")] - pub max: u32, - } - - /// Biome attributes. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct WorldTypeElement { - #[serde(with = "Convert")] - pub has_precipitation: bool, - pub temperature: f32, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub temperature_modifier: Option, - pub downfall: f32, - pub effects: BiomeEffects, - } - - /// The precipitation of a biome. - #[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub enum BiomePrecipitation { - #[serde(rename = "none")] - None, - #[serde(rename = "rain")] - Rain, - #[serde(rename = "snow")] - Snow, - } - - /// The effects of a biome. - /// - /// This includes the sky, fog, water, and grass color, - /// as well as music and other sound effects. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct BiomeEffects { - pub sky_color: u32, - pub fog_color: u32, - pub water_color: u32, - pub water_fog_color: u32, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub foliage_color: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub grass_color: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub grass_color_modifier: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub music: Option, - pub mood_sound: BiomeMoodSound, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub additions_sound: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub ambient_sound: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub particle: Option, - } - - /// The music of the biome. - /// - /// Some biomes have unique music that only play when inside them. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct BiomeMusic { - #[serde(with = "Convert")] - pub replace_current_music: bool, - pub max_delay: u32, - pub min_delay: u32, - pub sound: azalea_registry::SoundEvent, - } - - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct BiomeMoodSound { - pub tick_delay: u32, - pub block_search_extent: u32, - pub offset: f32, - pub sound: azalea_registry::SoundEvent, - } - - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct AdditionsSound { - pub tick_chance: f32, - pub sound: azalea_registry::SoundEvent, - } - - /// Biome particles. - /// - /// Some biomes have particles that spawn in the air. - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct BiomeParticle { - pub probability: f32, - pub options: HashMap, - } - - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct TrimPatternElement { - #[serde(flatten)] - pub pattern: HashMap, - } - - #[derive(Debug, Clone, Serialize, Deserialize)] - #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))] - pub struct DamageTypeElement { - pub message_id: String, - pub scaling: String, - pub exhaustion: f32, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub effects: Option, - #[serde(default)] - #[serde(skip_serializing_if = "Option::is_none")] - pub death_message_type: Option, - } - - // Using a trait because you can't implement methods for - // types you don't own, in this case Option and bool. - trait Convert: Sized { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer; - - fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>; - } - - // Convert between bool and u8 - impl Convert for bool { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_u8(if *self { 1 } else { 0 }) - } - - fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - convert::(u8::deserialize(deserializer)?) - } - } - - // Convert between Option and u8 - impl Convert for Option { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - if let Some(value) = self { - Convert::serialize(value, serializer) - } else { - serializer.serialize_none() - } - } - - fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - if let Some(value) = Option::::deserialize(deserializer)? { - Ok(Some(convert::(value)?)) - } else { - Ok(None) - } - } - } - - // Deserializing logic here to deduplicate code - fn convert<'de, D>(value: u8) -> Result - where - D: Deserializer<'de>, - { - match value { - 0 => Ok(false), - 1 => Ok(true), - other => Err(de::Error::invalid_value( - de::Unexpected::Unsigned(other as u64), - &"zero or one", - )), - } - } -} - -#[cfg(test)] -mod tests { - use super::registry::{DimensionTypeElement, RegistryHolder, RegistryRoot, RegistryType}; - use azalea_core::ResourceLocation; - use azalea_nbt::Nbt; - - #[test] - fn test_convert() { - // Do NOT use Nbt::End, they should be Nbt::Compound. - // This is just for testing. - let registry = RegistryHolder { - root: RegistryRoot { - trim_material: Nbt::End, - chat_type: Nbt::End, - dimension_type: RegistryType:: { - kind: ResourceLocation::new("minecraft:dimension_type"), - value: Vec::new(), - }, - world_type: Nbt::End, - trim_pattern: Nbt::End, - damage_type: Nbt::End, - }, - }; - - let tag: Nbt = registry.try_into().unwrap(); - let root = tag - .as_compound() - .unwrap() - .get("") - .unwrap() - .as_compound() - .unwrap(); - - let dimension = root - .get("minecraft:dimension_type") - .unwrap() - .as_compound() - .unwrap(); - let dimension_type = dimension.get("type").unwrap().as_string().unwrap().as_str(); - assert!(dimension_type == "minecraft:dimension_type"); - } + pub do_limited_crafting: bool, + pub common: CommonPlayerSpawnInfo, } diff --git a/azalea-protocol/src/packets/game/clientbound_ping_packet.rs b/azalea-protocol/src/packets/game/clientbound_ping_packet.rs index 0bd2c8c3..82de4fab 100755 --- a/azalea-protocol/src/packets/game/clientbound_ping_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_ping_packet.rs @@ -3,5 +3,6 @@ use azalea_protocol_macros::ClientboundGamePacket; #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundPingPacket { + #[var] pub id: u32, } diff --git a/azalea-protocol/src/packets/game/clientbound_pong_response_packet.rs b/azalea-protocol/src/packets/game/clientbound_pong_response_packet.rs new file mode 100755 index 00000000..0b48198e --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_pong_response_packet.rs @@ -0,0 +1,7 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundGamePacket; + +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] +pub struct ClientboundPongResponsePacket { + pub time: u64, +} diff --git a/azalea-protocol/src/packets/game/clientbound_respawn_packet.rs b/azalea-protocol/src/packets/game/clientbound_respawn_packet.rs index 71ccfd18..7e20a843 100755 --- a/azalea-protocol/src/packets/game/clientbound_respawn_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_respawn_packet.rs @@ -1,18 +1,10 @@ use azalea_buf::McBuf; -use azalea_core::{GameMode, GlobalPos, OptionalGameType, ResourceLocation}; use azalea_protocol_macros::ClientboundGamePacket; +use crate::packets::common::CommonPlayerSpawnInfo; + #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundRespawnPacket { - pub dimension_type: ResourceLocation, - pub dimension: ResourceLocation, - pub seed: u64, - pub game_type: GameMode, - pub previous_game_type: OptionalGameType, - pub is_debug: bool, - pub is_flat: bool, + pub common: CommonPlayerSpawnInfo, pub data_to_keep: u8, - pub last_death_location: Option, - #[var] - pub portal_cooldown: u32, } diff --git a/azalea-protocol/src/packets/game/clientbound_set_display_objective_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_display_objective_packet.rs index bf63d7da..7e5ed317 100755 --- a/azalea-protocol/src/packets/game/clientbound_set_display_objective_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_display_objective_packet.rs @@ -3,6 +3,29 @@ use azalea_protocol_macros::ClientboundGamePacket; #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundSetDisplayObjectivePacket { - pub slot: u8, + pub slot: DisplaySlot, pub objective_name: String, } + +#[derive(Clone, Debug, Copy, McBuf)] +pub enum DisplaySlot { + List = 0, + Sidebar, + BelowName, + TeamBlack, + TeamDarkBlue, + TeamDarkGreen, + TeamDarkAqua, + TeamDarkRed, + TeamDarkPurple, + TeamGold, + TeamGray, + TeamDarkGray, + TeamBlue, + TeamGreen, + TeamAqua, + TeamRed, + TeamLightPurple, + TeamYellow, + TeamWhite, +} diff --git a/azalea-protocol/src/packets/game/clientbound_start_configuration_packet.rs b/azalea-protocol/src/packets/game/clientbound_start_configuration_packet.rs new file mode 100644 index 00000000..b6ad9615 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_start_configuration_packet.rs @@ -0,0 +1,5 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundGamePacket; + +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] +pub struct ClientboundStartConfigurationPacket {} diff --git a/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs index 81704b37..5a792849 100755 --- a/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs @@ -9,7 +9,7 @@ use std::io::Cursor; #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundUpdateAdvancementsPacket { pub reset: bool, - pub added: HashMap, + pub added: Vec, pub removed: Vec, pub progress: HashMap, } @@ -18,7 +18,6 @@ pub struct ClientboundUpdateAdvancementsPacket { pub struct Advancement { pub parent_id: Option, pub display: Option, - pub criteria: HashMap, pub requirements: Vec>, pub sends_telemetry_event: bool, } @@ -103,15 +102,17 @@ pub enum FrameType { Goal = 2, } -// nothing is written here -#[derive(Clone, Debug, McBuf)] -pub struct Criterion {} - -pub type AdvancementProgress = HashMap; +pub type AdvancementProgress = HashMap; #[derive(Clone, Debug, McBuf)] pub struct CriterionProgress { - date: Option, + pub date: Option, +} + +#[derive(Clone, Debug, McBuf)] +pub struct AdvancementHolder { + pub id: ResourceLocation, + pub value: Advancement, } #[cfg(test)] @@ -125,9 +126,9 @@ mod tests { fn test() { let packet = ClientboundUpdateAdvancementsPacket { reset: true, - added: [( - ResourceLocation::new("minecraft:test"), - Advancement { + added: [AdvancementHolder { + id: ResourceLocation::new("minecraft:test"), + value: Advancement { parent_id: None, display: Some(DisplayInfo { title: FormattedText::from("title".to_string()), @@ -140,18 +141,17 @@ mod tests { x: 0.0, y: 0.0, }), - criteria: HashMap::new(), requirements: Vec::new(), sends_telemetry_event: false, }, - )] + }] .into_iter() .collect(), removed: vec![ResourceLocation::new("minecraft:test2")], progress: [( ResourceLocation::new("minecraft:test3"), [( - ResourceLocation::new("minecraft:test4"), + "minecraft:test4".to_string(), CriterionProgress { date: Some(123456789), }, @@ -173,12 +173,26 @@ mod tests { let advancement = packet .added - .get(&ResourceLocation::new("minecraft:test")) + .into_iter() + .find_map(|a| { + if a.id == ResourceLocation::new("minecraft:test") { + Some(a.value) + } else { + None + } + }) .unwrap() .clone(); let read_advancement = read_packet .added - .get(&ResourceLocation::new("minecraft:test")) + .into_iter() + .find_map(|a| { + if a.id == ResourceLocation::new("minecraft:test") { + Some(a.value) + } else { + None + } + }) .unwrap() .clone(); assert_eq!(advancement.parent_id, read_advancement.parent_id); diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 0b7c0d52..c806f21d 100755 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -1,6 +1,5 @@ pub mod clientbound_add_entity_packet; pub mod clientbound_add_experience_orb_packet; -pub mod clientbound_add_player_packet; pub mod clientbound_animate_packet; pub mod clientbound_award_stats_packet; pub mod clientbound_block_changed_ack_packet; @@ -11,6 +10,8 @@ pub mod clientbound_block_update_packet; pub mod clientbound_boss_event_packet; pub mod clientbound_bundle_packet; pub mod clientbound_change_difficulty_packet; +pub mod clientbound_chunk_batch_finished_packet; +pub mod clientbound_chunk_batch_start_packet; pub mod clientbound_chunks_biomes_packet; pub mod clientbound_clear_titles_packet; pub mod clientbound_command_suggestions_packet; @@ -59,6 +60,7 @@ pub mod clientbound_player_info_remove_packet; pub mod clientbound_player_info_update_packet; pub mod clientbound_player_look_at_packet; pub mod clientbound_player_position_packet; +pub mod clientbound_pong_response_packet; pub mod clientbound_recipe_packet; pub mod clientbound_remove_entities_packet; pub mod clientbound_remove_mob_effect_packet; @@ -97,6 +99,7 @@ pub mod clientbound_set_title_text_packet; pub mod clientbound_set_titles_animation_packet; pub mod clientbound_sound_entity_packet; pub mod clientbound_sound_packet; +pub mod clientbound_start_configuration_packet; pub mod clientbound_stop_sound_packet; pub mod clientbound_system_chat_packet; pub mod clientbound_tab_list_packet; @@ -105,7 +108,6 @@ pub mod clientbound_take_item_entity_packet; pub mod clientbound_teleport_entity_packet; pub mod clientbound_update_advancements_packet; pub mod clientbound_update_attributes_packet; -pub mod clientbound_update_enabled_features_packet; pub mod clientbound_update_mob_effect_packet; pub mod clientbound_update_recipes_packet; pub mod clientbound_update_tags_packet; @@ -116,9 +118,11 @@ pub mod serverbound_chat_ack_packet; pub mod serverbound_chat_command_packet; pub mod serverbound_chat_packet; pub mod serverbound_chat_session_update_packet; +pub mod serverbound_chunk_batch_received_packet; pub mod serverbound_client_command_packet; pub mod serverbound_client_information_packet; pub mod serverbound_command_suggestion_packet; +pub mod serverbound_configuration_acknowledged_packet; pub mod serverbound_container_button_click_packet; pub mod serverbound_container_click_packet; pub mod serverbound_container_close_packet; @@ -136,6 +140,7 @@ pub mod serverbound_move_player_status_only_packet; pub mod serverbound_move_vehicle_packet; pub mod serverbound_paddle_boat_packet; pub mod serverbound_pick_item_packet; +pub mod serverbound_ping_request_packet; pub mod serverbound_place_recipe_packet; pub mod serverbound_player_abilities_packet; pub mod serverbound_player_action_packet; @@ -173,162 +178,167 @@ declare_state_packets!( 0x04: serverbound_chat_command_packet::ServerboundChatCommandPacket, 0x05: serverbound_chat_packet::ServerboundChatPacket, 0x06: serverbound_chat_session_update_packet::ServerboundChatSessionUpdatePacket, - 0x07: serverbound_client_command_packet::ServerboundClientCommandPacket, - 0x08: serverbound_client_information_packet::ServerboundClientInformationPacket, - 0x09: serverbound_command_suggestion_packet::ServerboundCommandSuggestionPacket, - 0x0a: serverbound_container_button_click_packet::ServerboundContainerButtonClickPacket, - 0x0b: serverbound_container_click_packet::ServerboundContainerClickPacket, - 0x0c: serverbound_container_close_packet::ServerboundContainerClosePacket, - 0x0d: serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, - 0x0e: serverbound_edit_book_packet::ServerboundEditBookPacket, - 0x0f: serverbound_entity_tag_query::ServerboundEntityTagQuery, - 0x10: serverbound_interact_packet::ServerboundInteractPacket, - 0x11: serverbound_jigsaw_generate_packet::ServerboundJigsawGeneratePacket, - 0x12: serverbound_keep_alive_packet::ServerboundKeepAlivePacket, - 0x13: serverbound_lock_difficulty_packet::ServerboundLockDifficultyPacket, - 0x14: serverbound_move_player_pos_packet::ServerboundMovePlayerPosPacket, - 0x15: serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket, - 0x16: serverbound_move_player_rot_packet::ServerboundMovePlayerRotPacket, - 0x17: serverbound_move_player_status_only_packet::ServerboundMovePlayerStatusOnlyPacket, - 0x18: serverbound_move_vehicle_packet::ServerboundMoveVehiclePacket, - 0x19: serverbound_paddle_boat_packet::ServerboundPaddleBoatPacket, - 0x1a: serverbound_pick_item_packet::ServerboundPickItemPacket, - 0x1b: serverbound_place_recipe_packet::ServerboundPlaceRecipePacket, - 0x1c: serverbound_player_abilities_packet::ServerboundPlayerAbilitiesPacket, - 0x1d: serverbound_player_action_packet::ServerboundPlayerActionPacket, - 0x1e: serverbound_player_command_packet::ServerboundPlayerCommandPacket, - 0x1f: serverbound_player_input_packet::ServerboundPlayerInputPacket, - 0x20: serverbound_pong_packet::ServerboundPongPacket, - 0x21: serverbound_recipe_book_change_settings_packet::ServerboundRecipeBookChangeSettingsPacket, - 0x22: serverbound_recipe_book_seen_recipe_packet::ServerboundRecipeBookSeenRecipePacket, - 0x23: serverbound_rename_item_packet::ServerboundRenameItemPacket, - 0x24: serverbound_resource_pack_packet::ServerboundResourcePackPacket, - 0x25: serverbound_seen_advancements_packet::ServerboundSeenAdvancementsPacket, - 0x26: serverbound_select_trade_packet::ServerboundSelectTradePacket, - 0x27: serverbound_set_beacon_packet::ServerboundSetBeaconPacket, - 0x28: serverbound_set_carried_item_packet::ServerboundSetCarriedItemPacket, - 0x29: serverbound_set_command_block_packet::ServerboundSetCommandBlockPacket, - 0x2a: serverbound_set_command_minecart_packet::ServerboundSetCommandMinecartPacket, - 0x2b: serverbound_set_creative_mode_slot_packet::ServerboundSetCreativeModeSlotPacket, - 0x2c: serverbound_set_jigsaw_block_packet::ServerboundSetJigsawBlockPacket, - 0x2d: serverbound_set_structure_block_packet::ServerboundSetStructureBlockPacket, - 0x2e: serverbound_sign_update_packet::ServerboundSignUpdatePacket, - 0x2f: serverbound_swing_packet::ServerboundSwingPacket, - 0x30: serverbound_teleport_to_entity_packet::ServerboundTeleportToEntityPacket, - 0x31: serverbound_use_item_on_packet::ServerboundUseItemOnPacket, - 0x32: serverbound_use_item_packet::ServerboundUseItemPacket, + 0x07: serverbound_chunk_batch_received_packet::ServerboundChunkBatchReceivedPacket, + 0x08: serverbound_client_command_packet::ServerboundClientCommandPacket, + 0x09: serverbound_client_information_packet::ServerboundClientInformationPacket, + 0x0a: serverbound_command_suggestion_packet::ServerboundCommandSuggestionPacket, + 0x0b: serverbound_configuration_acknowledged_packet::ServerboundConfigurationAcknowledgedPacket, + 0x0c: serverbound_container_button_click_packet::ServerboundContainerButtonClickPacket, + 0x0d: serverbound_container_click_packet::ServerboundContainerClickPacket, + 0x0e: serverbound_container_close_packet::ServerboundContainerClosePacket, + 0x0f: serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, + 0x10: serverbound_edit_book_packet::ServerboundEditBookPacket, + 0x11: serverbound_entity_tag_query::ServerboundEntityTagQuery, + 0x12: serverbound_interact_packet::ServerboundInteractPacket, + 0x13: serverbound_jigsaw_generate_packet::ServerboundJigsawGeneratePacket, + 0x14: serverbound_keep_alive_packet::ServerboundKeepAlivePacket, + 0x15: serverbound_lock_difficulty_packet::ServerboundLockDifficultyPacket, + 0x16: serverbound_move_player_pos_packet::ServerboundMovePlayerPosPacket, + 0x17: serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket, + 0x18: serverbound_move_player_rot_packet::ServerboundMovePlayerRotPacket, + 0x19: serverbound_move_player_status_only_packet::ServerboundMovePlayerStatusOnlyPacket, + 0x1a: serverbound_move_vehicle_packet::ServerboundMoveVehiclePacket, + 0x1b: serverbound_paddle_boat_packet::ServerboundPaddleBoatPacket, + 0x1c: serverbound_pick_item_packet::ServerboundPickItemPacket, + 0x1d: serverbound_ping_request_packet::ServerboundPingRequestPacket, + 0x1e: serverbound_place_recipe_packet::ServerboundPlaceRecipePacket, + 0x1f: serverbound_player_abilities_packet::ServerboundPlayerAbilitiesPacket, + 0x20: serverbound_player_action_packet::ServerboundPlayerActionPacket, + 0x21: serverbound_player_command_packet::ServerboundPlayerCommandPacket, + 0x22: serverbound_player_input_packet::ServerboundPlayerInputPacket, + 0x23: serverbound_pong_packet::ServerboundPongPacket, + 0x24: serverbound_recipe_book_change_settings_packet::ServerboundRecipeBookChangeSettingsPacket, + 0x25: serverbound_recipe_book_seen_recipe_packet::ServerboundRecipeBookSeenRecipePacket, + 0x26: serverbound_rename_item_packet::ServerboundRenameItemPacket, + 0x27: serverbound_resource_pack_packet::ServerboundResourcePackPacket, + 0x28: serverbound_seen_advancements_packet::ServerboundSeenAdvancementsPacket, + 0x29: serverbound_select_trade_packet::ServerboundSelectTradePacket, + 0x2a: serverbound_set_beacon_packet::ServerboundSetBeaconPacket, + 0x2b: serverbound_set_carried_item_packet::ServerboundSetCarriedItemPacket, + 0x2c: serverbound_set_command_block_packet::ServerboundSetCommandBlockPacket, + 0x2d: serverbound_set_command_minecart_packet::ServerboundSetCommandMinecartPacket, + 0x2e: serverbound_set_creative_mode_slot_packet::ServerboundSetCreativeModeSlotPacket, + 0x2f: serverbound_set_jigsaw_block_packet::ServerboundSetJigsawBlockPacket, + 0x30: serverbound_set_structure_block_packet::ServerboundSetStructureBlockPacket, + 0x31: serverbound_sign_update_packet::ServerboundSignUpdatePacket, + 0x32: serverbound_swing_packet::ServerboundSwingPacket, + 0x33: serverbound_teleport_to_entity_packet::ServerboundTeleportToEntityPacket, + 0x34: serverbound_use_item_on_packet::ServerboundUseItemOnPacket, + 0x35: serverbound_use_item_packet::ServerboundUseItemPacket, }, Clientbound => { 0x00: clientbound_bundle_packet::ClientboundBundlePacket, 0x01: clientbound_add_entity_packet::ClientboundAddEntityPacket, 0x02: clientbound_add_experience_orb_packet::ClientboundAddExperienceOrbPacket, - 0x03: clientbound_add_player_packet::ClientboundAddPlayerPacket, - 0x04: clientbound_animate_packet::ClientboundAnimatePacket, - 0x05: clientbound_award_stats_packet::ClientboundAwardStatsPacket, - 0x06: clientbound_block_changed_ack_packet::ClientboundBlockChangedAckPacket, - 0x07: clientbound_block_destruction_packet::ClientboundBlockDestructionPacket, - 0x08: clientbound_block_entity_data_packet::ClientboundBlockEntityDataPacket, - 0x09: clientbound_block_event_packet::ClientboundBlockEventPacket, - 0x0a: clientbound_block_update_packet::ClientboundBlockUpdatePacket, - 0x0b: clientbound_boss_event_packet::ClientboundBossEventPacket, - 0x0c: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, - 0x0d: clientbound_chunks_biomes_packet::ClientboundChunksBiomesPacket, - 0x0e: clientbound_clear_titles_packet::ClientboundClearTitlesPacket, - 0x0f: clientbound_command_suggestions_packet::ClientboundCommandSuggestionsPacket, - 0x10: clientbound_commands_packet::ClientboundCommandsPacket, - 0x11: clientbound_container_close_packet::ClientboundContainerClosePacket, - 0x12: clientbound_container_set_content_packet::ClientboundContainerSetContentPacket, - 0x13: clientbound_container_set_data_packet::ClientboundContainerSetDataPacket, - 0x14: clientbound_container_set_slot_packet::ClientboundContainerSetSlotPacket, - 0x15: clientbound_cooldown_packet::ClientboundCooldownPacket, - 0x16: clientbound_custom_chat_completions_packet::ClientboundCustomChatCompletionsPacket, - 0x17: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, - 0x18: clientbound_damage_event_packet::ClientboundDamageEventPacket, - 0x19: clientbound_delete_chat_packet::ClientboundDeleteChatPacket, - 0x1a: clientbound_disconnect_packet::ClientboundDisconnectPacket, - 0x1b: clientbound_disguised_chat_packet::ClientboundDisguisedChatPacket, - 0x1c: clientbound_entity_event_packet::ClientboundEntityEventPacket, - 0x1d: clientbound_explode_packet::ClientboundExplodePacket, - 0x1e: clientbound_forget_level_chunk_packet::ClientboundForgetLevelChunkPacket, - 0x1f: clientbound_game_event_packet::ClientboundGameEventPacket, - 0x20: clientbound_horse_screen_open_packet::ClientboundHorseScreenOpenPacket, - 0x21: clientbound_hurt_animation_packet::ClientboundHurtAnimationPacket, - 0x22: clientbound_initialize_border_packet::ClientboundInitializeBorderPacket, - 0x23: clientbound_keep_alive_packet::ClientboundKeepAlivePacket, - 0x24: clientbound_level_chunk_with_light_packet::ClientboundLevelChunkWithLightPacket, - 0x25: clientbound_level_event_packet::ClientboundLevelEventPacket, - 0x26: clientbound_level_particles_packet::ClientboundLevelParticlesPacket, - 0x27: clientbound_light_update_packet::ClientboundLightUpdatePacket, - 0x28: clientbound_login_packet::ClientboundLoginPacket, - 0x29: clientbound_map_item_data_packet::ClientboundMapItemDataPacket, - 0x2a: clientbound_merchant_offers_packet::ClientboundMerchantOffersPacket, - 0x2b: clientbound_move_entity_pos_packet::ClientboundMoveEntityPosPacket, - 0x2c: clientbound_move_entity_pos_rot_packet::ClientboundMoveEntityPosRotPacket, - 0x2d: clientbound_move_entity_rot_packet::ClientboundMoveEntityRotPacket, - 0x2e: clientbound_move_vehicle_packet::ClientboundMoveVehiclePacket, - 0x2f: clientbound_open_book_packet::ClientboundOpenBookPacket, - 0x30: clientbound_open_screen_packet::ClientboundOpenScreenPacket, - 0x31: clientbound_open_sign_editor_packet::ClientboundOpenSignEditorPacket, - 0x32: clientbound_ping_packet::ClientboundPingPacket, - 0x33: clientbound_place_ghost_recipe_packet::ClientboundPlaceGhostRecipePacket, - 0x34: clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket, - 0x35: clientbound_player_chat_packet::ClientboundPlayerChatPacket, - 0x36: clientbound_player_combat_end_packet::ClientboundPlayerCombatEndPacket, - 0x37: clientbound_player_combat_enter_packet::ClientboundPlayerCombatEnterPacket, - 0x38: clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket, - 0x39: clientbound_player_info_remove_packet::ClientboundPlayerInfoRemovePacket, - 0x3a: clientbound_player_info_update_packet::ClientboundPlayerInfoUpdatePacket, - 0x3b: clientbound_player_look_at_packet::ClientboundPlayerLookAtPacket, - 0x3c: clientbound_player_position_packet::ClientboundPlayerPositionPacket, - 0x3d: clientbound_recipe_packet::ClientboundRecipePacket, - 0x3e: clientbound_remove_entities_packet::ClientboundRemoveEntitiesPacket, - 0x3f: clientbound_remove_mob_effect_packet::ClientboundRemoveMobEffectPacket, - 0x40: clientbound_resource_pack_packet::ClientboundResourcePackPacket, - 0x41: clientbound_respawn_packet::ClientboundRespawnPacket, - 0x42: clientbound_rotate_head_packet::ClientboundRotateHeadPacket, - 0x43: clientbound_section_blocks_update_packet::ClientboundSectionBlocksUpdatePacket, - 0x44: clientbound_select_advancements_tab_packet::ClientboundSelectAdvancementsTabPacket, - 0x45: clientbound_server_data_packet::ClientboundServerDataPacket, - 0x46: clientbound_set_action_bar_text_packet::ClientboundSetActionBarTextPacket, - 0x47: clientbound_set_border_center_packet::ClientboundSetBorderCenterPacket, - 0x48: clientbound_set_border_lerp_size_packet::ClientboundSetBorderLerpSizePacket, - 0x49: clientbound_set_border_size_packet::ClientboundSetBorderSizePacket, - 0x4a: clientbound_set_border_warning_delay_packet::ClientboundSetBorderWarningDelayPacket, - 0x4b: clientbound_set_border_warning_distance_packet::ClientboundSetBorderWarningDistancePacket, - 0x4c: clientbound_set_camera_packet::ClientboundSetCameraPacket, - 0x4d: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket, - 0x4e: clientbound_set_chunk_cache_center_packet::ClientboundSetChunkCacheCenterPacket, - 0x4f: clientbound_set_chunk_cache_radius_packet::ClientboundSetChunkCacheRadiusPacket, - 0x50: clientbound_set_default_spawn_position_packet::ClientboundSetDefaultSpawnPositionPacket, - 0x51: clientbound_set_display_objective_packet::ClientboundSetDisplayObjectivePacket, - 0x52: clientbound_set_entity_data_packet::ClientboundSetEntityDataPacket, - 0x53: clientbound_set_entity_link_packet::ClientboundSetEntityLinkPacket, - 0x54: clientbound_set_entity_motion_packet::ClientboundSetEntityMotionPacket, - 0x55: clientbound_set_equipment_packet::ClientboundSetEquipmentPacket, - 0x56: clientbound_set_experience_packet::ClientboundSetExperiencePacket, - 0x57: clientbound_set_health_packet::ClientboundSetHealthPacket, - 0x58: clientbound_set_objective_packet::ClientboundSetObjectivePacket, - 0x59: clientbound_set_passengers_packet::ClientboundSetPassengersPacket, - 0x5a: clientbound_set_player_team_packet::ClientboundSetPlayerTeamPacket, - 0x5b: clientbound_set_score_packet::ClientboundSetScorePacket, - 0x5c: clientbound_set_simulation_distance_packet::ClientboundSetSimulationDistancePacket, - 0x5d: clientbound_set_subtitle_text_packet::ClientboundSetSubtitleTextPacket, - 0x5e: clientbound_set_time_packet::ClientboundSetTimePacket, - 0x5f: clientbound_set_title_text_packet::ClientboundSetTitleTextPacket, - 0x60: clientbound_set_titles_animation_packet::ClientboundSetTitlesAnimationPacket, - 0x61: clientbound_sound_entity_packet::ClientboundSoundEntityPacket, - 0x62: clientbound_sound_packet::ClientboundSoundPacket, - 0x63: clientbound_stop_sound_packet::ClientboundStopSoundPacket, - 0x64: clientbound_system_chat_packet::ClientboundSystemChatPacket, - 0x65: clientbound_tab_list_packet::ClientboundTabListPacket, - 0x66: clientbound_tag_query_packet::ClientboundTagQueryPacket, - 0x67: clientbound_take_item_entity_packet::ClientboundTakeItemEntityPacket, - 0x68: clientbound_teleport_entity_packet::ClientboundTeleportEntityPacket, - 0x69: clientbound_update_advancements_packet::ClientboundUpdateAdvancementsPacket, - 0x6a: clientbound_update_attributes_packet::ClientboundUpdateAttributesPacket, - 0x6b: clientbound_update_enabled_features_packet::ClientboundUpdateEnabledFeaturesPacket, - 0x6c: clientbound_update_mob_effect_packet::ClientboundUpdateMobEffectPacket, - 0x6d: clientbound_update_recipes_packet::ClientboundUpdateRecipesPacket, - 0x6e: clientbound_update_tags_packet::ClientboundUpdateTagsPacket, + 0x03: clientbound_animate_packet::ClientboundAnimatePacket, + 0x04: clientbound_award_stats_packet::ClientboundAwardStatsPacket, + 0x05: clientbound_block_changed_ack_packet::ClientboundBlockChangedAckPacket, + 0x06: clientbound_block_destruction_packet::ClientboundBlockDestructionPacket, + 0x07: clientbound_block_entity_data_packet::ClientboundBlockEntityDataPacket, + 0x08: clientbound_block_event_packet::ClientboundBlockEventPacket, + 0x09: clientbound_block_update_packet::ClientboundBlockUpdatePacket, + 0x0a: clientbound_boss_event_packet::ClientboundBossEventPacket, + 0x0b: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, + 0x0c: clientbound_chunk_batch_finished_packet::ClientboundChunkBatchFinishedPacket, + 0x0d: clientbound_chunk_batch_start_packet::ClientboundChunkBatchStartPacket, + 0x0e: clientbound_chunks_biomes_packet::ClientboundChunksBiomesPacket, + 0x0f: clientbound_clear_titles_packet::ClientboundClearTitlesPacket, + 0x10: clientbound_command_suggestions_packet::ClientboundCommandSuggestionsPacket, + 0x11: clientbound_commands_packet::ClientboundCommandsPacket, + 0x12: clientbound_container_close_packet::ClientboundContainerClosePacket, + 0x13: clientbound_container_set_content_packet::ClientboundContainerSetContentPacket, + 0x14: clientbound_container_set_data_packet::ClientboundContainerSetDataPacket, + 0x15: clientbound_container_set_slot_packet::ClientboundContainerSetSlotPacket, + 0x16: clientbound_cooldown_packet::ClientboundCooldownPacket, + 0x17: clientbound_custom_chat_completions_packet::ClientboundCustomChatCompletionsPacket, + 0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, + 0x19: clientbound_damage_event_packet::ClientboundDamageEventPacket, + 0x1a: clientbound_delete_chat_packet::ClientboundDeleteChatPacket, + 0x1b: clientbound_disconnect_packet::ClientboundDisconnectPacket, + 0x1c: clientbound_disguised_chat_packet::ClientboundDisguisedChatPacket, + 0x1d: clientbound_entity_event_packet::ClientboundEntityEventPacket, + 0x1e: clientbound_explode_packet::ClientboundExplodePacket, + 0x1f: clientbound_forget_level_chunk_packet::ClientboundForgetLevelChunkPacket, + 0x20: clientbound_game_event_packet::ClientboundGameEventPacket, + 0x21: clientbound_horse_screen_open_packet::ClientboundHorseScreenOpenPacket, + 0x22: clientbound_hurt_animation_packet::ClientboundHurtAnimationPacket, + 0x23: clientbound_initialize_border_packet::ClientboundInitializeBorderPacket, + 0x24: clientbound_keep_alive_packet::ClientboundKeepAlivePacket, + 0x25: clientbound_level_chunk_with_light_packet::ClientboundLevelChunkWithLightPacket, + 0x26: clientbound_level_event_packet::ClientboundLevelEventPacket, + 0x27: clientbound_level_particles_packet::ClientboundLevelParticlesPacket, + 0x28: clientbound_light_update_packet::ClientboundLightUpdatePacket, + 0x29: clientbound_login_packet::ClientboundLoginPacket, + 0x2a: clientbound_map_item_data_packet::ClientboundMapItemDataPacket, + 0x2b: clientbound_merchant_offers_packet::ClientboundMerchantOffersPacket, + 0x2c: clientbound_move_entity_pos_packet::ClientboundMoveEntityPosPacket, + 0x2d: clientbound_move_entity_pos_rot_packet::ClientboundMoveEntityPosRotPacket, + 0x2e: clientbound_move_entity_rot_packet::ClientboundMoveEntityRotPacket, + 0x2f: clientbound_move_vehicle_packet::ClientboundMoveVehiclePacket, + 0x30: clientbound_open_book_packet::ClientboundOpenBookPacket, + 0x31: clientbound_open_screen_packet::ClientboundOpenScreenPacket, + 0x32: clientbound_open_sign_editor_packet::ClientboundOpenSignEditorPacket, + 0x33: clientbound_ping_packet::ClientboundPingPacket, + 0x34: clientbound_pong_response_packet::ClientboundPongResponsePacket, + 0x35: clientbound_place_ghost_recipe_packet::ClientboundPlaceGhostRecipePacket, + 0x36: clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket, + 0x37: clientbound_player_chat_packet::ClientboundPlayerChatPacket, + 0x38: clientbound_player_combat_end_packet::ClientboundPlayerCombatEndPacket, + 0x39: clientbound_player_combat_enter_packet::ClientboundPlayerCombatEnterPacket, + 0x3a: clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket, + 0x3b: clientbound_player_info_remove_packet::ClientboundPlayerInfoRemovePacket, + 0x3c: clientbound_player_info_update_packet::ClientboundPlayerInfoUpdatePacket, + 0x3d: clientbound_player_look_at_packet::ClientboundPlayerLookAtPacket, + 0x3e: clientbound_player_position_packet::ClientboundPlayerPositionPacket, + 0x3f: clientbound_recipe_packet::ClientboundRecipePacket, + 0x40: clientbound_remove_entities_packet::ClientboundRemoveEntitiesPacket, + 0x41: clientbound_remove_mob_effect_packet::ClientboundRemoveMobEffectPacket, + 0x42: clientbound_resource_pack_packet::ClientboundResourcePackPacket, + 0x43: clientbound_respawn_packet::ClientboundRespawnPacket, + 0x44: clientbound_rotate_head_packet::ClientboundRotateHeadPacket, + 0x45: clientbound_section_blocks_update_packet::ClientboundSectionBlocksUpdatePacket, + 0x46: clientbound_select_advancements_tab_packet::ClientboundSelectAdvancementsTabPacket, + 0x47: clientbound_server_data_packet::ClientboundServerDataPacket, + 0x48: clientbound_set_action_bar_text_packet::ClientboundSetActionBarTextPacket, + 0x49: clientbound_set_border_center_packet::ClientboundSetBorderCenterPacket, + 0x4a: clientbound_set_border_lerp_size_packet::ClientboundSetBorderLerpSizePacket, + 0x4b: clientbound_set_border_size_packet::ClientboundSetBorderSizePacket, + 0x4c: clientbound_set_border_warning_delay_packet::ClientboundSetBorderWarningDelayPacket, + 0x4d: clientbound_set_border_warning_distance_packet::ClientboundSetBorderWarningDistancePacket, + 0x4e: clientbound_set_camera_packet::ClientboundSetCameraPacket, + 0x4f: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket, + 0x50: clientbound_set_chunk_cache_center_packet::ClientboundSetChunkCacheCenterPacket, + 0x51: clientbound_set_chunk_cache_radius_packet::ClientboundSetChunkCacheRadiusPacket, + 0x52: clientbound_set_default_spawn_position_packet::ClientboundSetDefaultSpawnPositionPacket, + 0x53: clientbound_set_display_objective_packet::ClientboundSetDisplayObjectivePacket, + 0x54: clientbound_set_entity_data_packet::ClientboundSetEntityDataPacket, + 0x55: clientbound_set_entity_link_packet::ClientboundSetEntityLinkPacket, + 0x56: clientbound_set_entity_motion_packet::ClientboundSetEntityMotionPacket, + 0x57: clientbound_set_equipment_packet::ClientboundSetEquipmentPacket, + 0x58: clientbound_set_experience_packet::ClientboundSetExperiencePacket, + 0x59: clientbound_set_health_packet::ClientboundSetHealthPacket, + 0x5a: clientbound_set_objective_packet::ClientboundSetObjectivePacket, + 0x5b: clientbound_set_passengers_packet::ClientboundSetPassengersPacket, + 0x5c: clientbound_set_player_team_packet::ClientboundSetPlayerTeamPacket, + 0x5d: clientbound_set_score_packet::ClientboundSetScorePacket, + 0x5e: clientbound_set_simulation_distance_packet::ClientboundSetSimulationDistancePacket, + 0x5f: clientbound_set_subtitle_text_packet::ClientboundSetSubtitleTextPacket, + 0x60: clientbound_set_time_packet::ClientboundSetTimePacket, + 0x61: clientbound_set_title_text_packet::ClientboundSetTitleTextPacket, + 0x62: clientbound_set_titles_animation_packet::ClientboundSetTitlesAnimationPacket, + 0x63: clientbound_sound_entity_packet::ClientboundSoundEntityPacket, + 0x64: clientbound_sound_packet::ClientboundSoundPacket, + 0x65: clientbound_start_configuration_packet::ClientboundStartConfigurationPacket, + 0x66: clientbound_stop_sound_packet::ClientboundStopSoundPacket, + 0x67: clientbound_system_chat_packet::ClientboundSystemChatPacket, + 0x68: clientbound_tab_list_packet::ClientboundTabListPacket, + 0x69: clientbound_tag_query_packet::ClientboundTagQueryPacket, + 0x6a: clientbound_take_item_entity_packet::ClientboundTakeItemEntityPacket, + 0x6b: clientbound_teleport_entity_packet::ClientboundTeleportEntityPacket, + 0x6c: clientbound_update_advancements_packet::ClientboundUpdateAdvancementsPacket, + 0x6d: clientbound_update_attributes_packet::ClientboundUpdateAttributesPacket, + 0x6e: clientbound_update_mob_effect_packet::ClientboundUpdateMobEffectPacket, + 0x6f: clientbound_update_recipes_packet::ClientboundUpdateRecipesPacket, + 0x70: clientbound_update_tags_packet::ClientboundUpdateTagsPacket, } ); diff --git a/azalea-protocol/src/packets/game/serverbound_chunk_batch_received_packet.rs b/azalea-protocol/src/packets/game/serverbound_chunk_batch_received_packet.rs new file mode 100644 index 00000000..9f18f967 --- /dev/null +++ b/azalea-protocol/src/packets/game/serverbound_chunk_batch_received_packet.rs @@ -0,0 +1,7 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ServerboundGamePacket; + +#[derive(Clone, Debug, McBuf, ServerboundGamePacket)] +pub struct ServerboundChunkBatchReceivedPacket { + pub desired_chunks_per_tick: f32, +} diff --git a/azalea-protocol/src/packets/game/serverbound_client_information_packet.rs b/azalea-protocol/src/packets/game/serverbound_client_information_packet.rs index 9cace991..37a101f5 100755 --- a/azalea-protocol/src/packets/game/serverbound_client_information_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_client_information_packet.rs @@ -1,175 +1,9 @@ -use azalea_buf::{McBuf, McBufReadable, McBufWritable}; -use azalea_core::FixedBitSet; +use azalea_buf::McBuf; use azalea_protocol_macros::ServerboundGamePacket; -use bevy_ecs::component::Component; -#[derive(Clone, Debug, McBuf, ServerboundGamePacket, PartialEq, Eq, Component)] -pub struct ServerboundClientInformationPacket { - /// The locale of the client. - pub language: String, - /// The view distance of the client in chunks, same as the render distance - /// in-game. - pub view_distance: u8, - /// The types of chat messages the client wants to receive. Note that many - /// servers ignore this. - pub chat_visibility: ChatVisibility, - /// Whether the messages sent from the server should have colors. Note that - /// many servers ignore this and always send colored messages. - pub chat_colors: bool, - pub model_customization: ModelCustomization, - pub main_hand: HumanoidArm, - pub text_filtering_enabled: bool, - /// Whether the client should show up as "Anonymous Player" in the server - /// list. - pub allows_listing: bool, -} - -impl Default for ServerboundClientInformationPacket { - fn default() -> Self { - Self { - language: "en_us".to_string(), - view_distance: 8, - chat_visibility: ChatVisibility::default(), - chat_colors: true, - model_customization: ModelCustomization::default(), - main_hand: HumanoidArm::Right, - text_filtering_enabled: false, - allows_listing: false, - } - } -} - -#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)] -pub enum ChatVisibility { - /// All chat messages should be sent to the client. - #[default] - Full = 0, - /// Chat messages from other players should be not sent to the client, only - /// messages from the server like "Player joined the game" should be sent. - System = 1, - /// No chat messages should be sent to the client. - Hidden = 2, -} +use crate::packets::configuration::serverbound_client_information_packet::ClientInformation; -#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)] -pub enum HumanoidArm { - Left = 0, - #[default] - Right = 1, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct ModelCustomization { - pub cape: bool, - pub jacket: bool, - pub left_sleeve: bool, - pub right_sleeve: bool, - pub left_pants: bool, - pub right_pants: bool, - pub hat: bool, -} - -impl Default for ModelCustomization { - fn default() -> Self { - Self { - cape: true, - jacket: true, - left_sleeve: true, - right_sleeve: true, - left_pants: true, - right_pants: true, - hat: true, - } - } -} - -impl McBufReadable for ModelCustomization { - fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result { - let set = FixedBitSet::<7>::read_from(buf)?; - Ok(Self { - cape: set.index(0), - jacket: set.index(1), - left_sleeve: set.index(2), - right_sleeve: set.index(3), - left_pants: set.index(4), - right_pants: set.index(5), - hat: set.index(6), - }) - } -} - -impl McBufWritable for ModelCustomization { - fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - let mut set = FixedBitSet::<7>::new(); - if self.cape { - set.set(0); - } - if self.jacket { - set.set(1); - } - if self.left_sleeve { - set.set(2); - } - if self.right_sleeve { - set.set(3); - } - if self.left_pants { - set.set(4); - } - if self.right_pants { - set.set(5); - } - if self.hat { - set.set(6); - } - set.write_into(buf) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::io::Cursor; - - #[test] - fn test_client_information_packet() { - { - let data = ServerboundClientInformationPacket::default(); - let mut buf = Vec::new(); - data.write_into(&mut buf).unwrap(); - let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); - - let read_data = - ServerboundClientInformationPacket::read_from(&mut data_cursor).unwrap(); - assert_eq!(read_data, data); - } - - { - let data = ServerboundClientInformationPacket { - language: "en_gb".to_string(), - view_distance: 24, - chat_visibility: ChatVisibility::Hidden, - chat_colors: false, - model_customization: ModelCustomization { - cape: false, - jacket: false, - left_sleeve: true, - right_sleeve: false, - left_pants: true, - right_pants: false, - hat: true, - }, - main_hand: HumanoidArm::Left, - text_filtering_enabled: true, - allows_listing: true, - }; - let mut buf = Vec::new(); - data.write_into(&mut buf).unwrap(); - let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf); - - let read_data = - ServerboundClientInformationPacket::read_from(&mut data_cursor).unwrap(); - assert_eq!(read_data, data); - } - } +#[derive(Clone, Debug, McBuf, ServerboundGamePacket)] +pub struct ServerboundClientInformationPacket { + pub information: ClientInformation, } diff --git a/azalea-protocol/src/packets/game/serverbound_configuration_acknowledged_packet.rs b/azalea-protocol/src/packets/game/serverbound_configuration_acknowledged_packet.rs new file mode 100644 index 00000000..c790972a --- /dev/null +++ b/azalea-protocol/src/packets/game/serverbound_configuration_acknowledged_packet.rs @@ -0,0 +1,5 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ServerboundGamePacket; + +#[derive(Clone, Debug, McBuf, ServerboundGamePacket)] +pub struct ServerboundConfigurationAcknowledgedPacket {} diff --git a/azalea-protocol/src/packets/game/serverbound_ping_request_packet.rs b/azalea-protocol/src/packets/game/serverbound_ping_request_packet.rs new file mode 100755 index 00000000..0966e941 --- /dev/null +++ b/azalea-protocol/src/packets/game/serverbound_ping_request_packet.rs @@ -0,0 +1,7 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ServerboundGamePacket; + +#[derive(Clone, Debug, McBuf, ServerboundGamePacket)] +pub struct ServerboundPingRequestPacket { + pub time: u64, +} diff --git a/azalea-protocol/src/packets/handshake/client_intention_packet.rs b/azalea-protocol/src/packets/handshake/client_intention_packet.rs deleted file mode 100755 index 1900d476..00000000 --- a/azalea-protocol/src/packets/handshake/client_intention_packet.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::packets::ConnectionProtocol; -use azalea_buf::McBuf; -use azalea_protocol_macros::ServerboundHandshakePacket; -use std::hash::Hash; - -#[derive(Hash, Clone, Debug, McBuf, ServerboundHandshakePacket)] -pub struct ClientIntentionPacket { - #[var] - pub protocol_version: u32, - pub hostname: String, - pub port: u16, - pub intention: ConnectionProtocol, -} diff --git a/azalea-protocol/src/packets/handshake/mod.rs b/azalea-protocol/src/packets/handshake/mod.rs deleted file mode 100755 index f2e8810e..00000000 --- a/azalea-protocol/src/packets/handshake/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod client_intention_packet; - -use azalea_protocol_macros::declare_state_packets; - -declare_state_packets!( - HandshakePacket, - Serverbound => { - 0x00: client_intention_packet::ClientIntentionPacket, - }, - Clientbound => {} -); diff --git a/azalea-protocol/src/packets/handshaking/client_intention_packet.rs b/azalea-protocol/src/packets/handshaking/client_intention_packet.rs new file mode 100755 index 00000000..1900d476 --- /dev/null +++ b/azalea-protocol/src/packets/handshaking/client_intention_packet.rs @@ -0,0 +1,13 @@ +use crate::packets::ConnectionProtocol; +use azalea_buf::McBuf; +use azalea_protocol_macros::ServerboundHandshakePacket; +use std::hash::Hash; + +#[derive(Hash, Clone, Debug, McBuf, ServerboundHandshakePacket)] +pub struct ClientIntentionPacket { + #[var] + pub protocol_version: u32, + pub hostname: String, + pub port: u16, + pub intention: ConnectionProtocol, +} diff --git a/azalea-protocol/src/packets/handshaking/mod.rs b/azalea-protocol/src/packets/handshaking/mod.rs new file mode 100755 index 00000000..f2e8810e --- /dev/null +++ b/azalea-protocol/src/packets/handshaking/mod.rs @@ -0,0 +1,11 @@ +pub mod client_intention_packet; + +use azalea_protocol_macros::declare_state_packets; + +declare_state_packets!( + HandshakePacket, + Serverbound => { + 0x00: client_intention_packet::ClientIntentionPacket, + }, + Clientbound => {} +); diff --git a/azalea-protocol/src/packets/login/mod.rs b/azalea-protocol/src/packets/login/mod.rs index 1186304a..f9bf8dad 100755 --- a/azalea-protocol/src/packets/login/mod.rs +++ b/azalea-protocol/src/packets/login/mod.rs @@ -3,9 +3,10 @@ pub mod clientbound_game_profile_packet; pub mod clientbound_hello_packet; pub mod clientbound_login_compression_packet; pub mod clientbound_login_disconnect_packet; -pub mod serverbound_custom_query_packet; +pub mod serverbound_custom_query_answer_packet; pub mod serverbound_hello_packet; pub mod serverbound_key_packet; +pub mod serverbound_login_acknowledged_packet; use azalea_protocol_macros::declare_state_packets; @@ -14,7 +15,8 @@ declare_state_packets!( Serverbound => { 0x00: serverbound_hello_packet::ServerboundHelloPacket, 0x01: serverbound_key_packet::ServerboundKeyPacket, - 0x02: serverbound_custom_query_packet::ServerboundCustomQueryPacket, + 0x02: serverbound_custom_query_answer_packet::ServerboundCustomQueryAnswerPacket, + 0x03: serverbound_login_acknowledged_packet::ServerboundLoginAcknowledgedPacket, }, Clientbound => { 0x00: clientbound_login_disconnect_packet::ClientboundLoginDisconnectPacket, diff --git a/azalea-protocol/src/packets/login/serverbound_custom_query_answer_packet.rs b/azalea-protocol/src/packets/login/serverbound_custom_query_answer_packet.rs new file mode 100644 index 00000000..7eb8b28f --- /dev/null +++ b/azalea-protocol/src/packets/login/serverbound_custom_query_answer_packet.rs @@ -0,0 +1,10 @@ +use azalea_buf::{McBuf, UnsizedByteArray}; +use azalea_protocol_macros::ServerboundLoginPacket; +use std::hash::Hash; + +#[derive(Hash, Clone, Debug, McBuf, ServerboundLoginPacket)] +pub struct ServerboundCustomQueryAnswerPacket { + #[var] + pub transaction_id: u32, + pub data: Option, +} diff --git a/azalea-protocol/src/packets/login/serverbound_hello_packet.rs b/azalea-protocol/src/packets/login/serverbound_hello_packet.rs index c0fc7b1e..d46fe308 100755 --- a/azalea-protocol/src/packets/login/serverbound_hello_packet.rs +++ b/azalea-protocol/src/packets/login/serverbound_hello_packet.rs @@ -5,7 +5,7 @@ use uuid::Uuid; #[derive(Clone, Debug, PartialEq, Eq, McBuf, ServerboundLoginPacket)] pub struct ServerboundHelloPacket { pub name: String, - pub profile_id: Option, + pub profile_id: Uuid, } #[cfg(test)] @@ -19,7 +19,7 @@ mod tests { fn test_read_write() { let packet = ServerboundHelloPacket { name: "test".to_string(), - profile_id: Some(Uuid::nil()), + profile_id: Uuid::nil(), }; let mut buf: Vec = Vec::new(); packet.write_into(&mut buf).unwrap(); diff --git a/azalea-protocol/src/packets/login/serverbound_login_acknowledged_packet.rs b/azalea-protocol/src/packets/login/serverbound_login_acknowledged_packet.rs new file mode 100644 index 00000000..c242a494 --- /dev/null +++ b/azalea-protocol/src/packets/login/serverbound_login_acknowledged_packet.rs @@ -0,0 +1,5 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ServerboundLoginPacket; + +#[derive(Clone, Debug, McBuf, ServerboundLoginPacket)] +pub struct ServerboundLoginAcknowledgedPacket {} diff --git a/azalea-protocol/src/packets/mod.rs b/azalea-protocol/src/packets/mod.rs index a9e2948c..eb902da2 100755 --- a/azalea-protocol/src/packets/mod.rs +++ b/azalea-protocol/src/packets/mod.rs @@ -1,5 +1,7 @@ +pub mod common; +pub mod configuration; pub mod game; -pub mod handshake; +pub mod handshaking; pub mod login; pub mod status; @@ -10,7 +12,7 @@ use std::io::{Cursor, Write}; // TODO: rename the packet files to just like clientbound_add_entity instead of // clientbound_add_entity_packet -pub const PROTOCOL_VERSION: u32 = 763; +pub const PROTOCOL_VERSION: u32 = 764; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ConnectionProtocol { @@ -18,6 +20,7 @@ pub enum ConnectionProtocol { Game = 0, Status = 1, Login = 2, + Configuration = 3, } impl ConnectionProtocol { @@ -28,6 +31,7 @@ impl ConnectionProtocol { 0 => Some(ConnectionProtocol::Game), 1 => Some(ConnectionProtocol::Status), 2 => Some(ConnectionProtocol::Login), + 3 => Some(ConnectionProtocol::Configuration), _ => None, } } diff --git a/azalea-protocol/src/read.rs b/azalea-protocol/src/read.rs index bffb22bd..4002f4cb 100755 --- a/azalea-protocol/src/read.rs +++ b/azalea-protocol/src/read.rs @@ -127,7 +127,7 @@ fn frame_splitter(buffer: &mut BytesMut) -> Result>, FrameSplitte Ok(None) } -fn packet_decoder( +pub fn deserialize_packet( stream: &mut Cursor<&[u8]>, ) -> Result> { // Packet ID @@ -216,71 +216,123 @@ pub async fn read_packet<'a, P: ProtocolPacket + Debug, R>( where R: AsyncRead + std::marker::Unpin + std::marker::Send + std::marker::Sync, { - let mut framed = FramedRead::new(stream, BytesCodec::new()); + let raw_packet = read_raw_packet(stream, buffer, compression_threshold, cipher).await?; + let packet = deserialize_packet(&mut Cursor::new(raw_packet.as_slice()))?; + Ok(packet) +} + +/// Try to read a single packet from a stream. Returns None if we haven't +/// received a full packet yet. +pub fn try_read_packet( + stream: &mut R, + buffer: &mut BytesMut, + compression_threshold: Option, + cipher: &mut Option, +) -> Result, Box> +where + R: AsyncRead + Unpin + Send + Sync, +{ + let Some(raw_packet) = try_read_raw_packet(stream, buffer, compression_threshold, cipher)? + else { + return Ok(None); + }; + let packet = deserialize_packet(&mut Cursor::new(raw_packet.as_slice()))?; + Ok(Some(packet)) +} + +pub async fn read_raw_packet<'a, R>( + stream: &'a mut R, + buffer: &mut BytesMut, + compression_threshold: Option, + cipher: &mut Option, +) -> Result, Box> +where + R: AsyncRead + std::marker::Unpin + std::marker::Send + std::marker::Sync, +{ loop { - if let Some(buf) = try_process_buffer::(buffer, compression_threshold)? { + if let Some(buf) = read_raw_packet_from_buffer::(buffer, compression_threshold)? { // we got a full packet!! return Ok(buf); }; - // if we were given a cipher, decrypt the packet - if let Some(message) = framed.next().await { - let mut bytes = message.map_err(ReadPacketError::from)?; - - if let Some(cipher) = cipher { - azalea_crypto::decrypt_packet(cipher, &mut bytes); - } - - buffer.extend_from_slice(&bytes); - } else { - return Err(Box::new(ReadPacketError::ConnectionClosed)); - }; + let bytes = read_and_decrypt_frame(stream, cipher).await?; + buffer.extend_from_slice(&bytes); } } - -/// Try to read a single packet from a stream. Returns None if we haven't -/// received a full packet yet. -pub fn try_read_packet( +pub fn try_read_raw_packet( stream: &mut R, buffer: &mut BytesMut, compression_threshold: Option, cipher: &mut Option, -) -> Result, Box> +) -> Result>, Box> where R: AsyncRead + std::marker::Unpin + std::marker::Send + std::marker::Sync, { - let mut framed = FramedRead::new(stream, BytesCodec::new()); loop { - if let Some(buf) = try_process_buffer::(buffer, compression_threshold)? { + if let Some(buf) = read_raw_packet_from_buffer::(buffer, compression_threshold)? { // we got a full packet!! return Ok(Some(buf)); }; + let Some(bytes) = try_read_and_decrypt_frame(stream, cipher)? else { + // no data received + return Ok(None); + }; + // we got some data, so add it to the buffer and try again + buffer.extend_from_slice(&bytes); + } +} - // if we were given a cipher, decrypt the packet - if let Some(message) = future::block_on(future::poll_once(framed.next())) { - if let Some(message) = message { - let mut bytes = message.map_err(ReadPacketError::from)?; +async fn read_and_decrypt_frame( + stream: &mut R, + cipher: &mut Option, +) -> Result> +where + R: AsyncRead + Unpin + Send + Sync, +{ + let mut framed = FramedRead::new(stream, BytesCodec::new()); - if let Some(cipher) = cipher { - azalea_crypto::decrypt_packet(cipher, &mut bytes); - } + let Some(message) = framed.next().await else { + return Err(Box::new(ReadPacketError::ConnectionClosed)); + }; + let mut bytes = message.map_err(ReadPacketError::from)?; - buffer.extend_from_slice(&bytes); - } else { - return Err(Box::new(ReadPacketError::ConnectionClosed)); - } - } else { - return Ok(None); - }; + // decrypt if necessary + if let Some(cipher) = cipher { + azalea_crypto::decrypt_packet(cipher, &mut bytes); } + + Ok(bytes) +} +fn try_read_and_decrypt_frame( + stream: &mut R, + cipher: &mut Option, +) -> Result, Box> +where + R: AsyncRead + Unpin + Send + Sync, +{ + let mut framed = FramedRead::new(stream, BytesCodec::new()); + + let Some(message) = future::block_on(future::poll_once(framed.next())) else { + // nothing yet + return Ok(None); + }; + let Some(message) = message else { + return Err(Box::new(ReadPacketError::ConnectionClosed)); + }; + let mut bytes = message.map_err(ReadPacketError::from)?; + + // decrypt if necessary + if let Some(cipher) = cipher { + azalea_crypto::decrypt_packet(cipher, &mut bytes); + } + + Ok(Some(bytes)) } -/// Try to get a Minecraft packet from a buffer. Returns None if the packet -/// isn't complete yet. -pub fn try_process_buffer( +pub fn read_raw_packet_from_buffer( buffer: &mut BytesMut, compression_threshold: Option, -) -> Result, Box> +) -> Result>, Box> where R: AsyncRead + std::marker::Unpin + std::marker::Send + std::marker::Sync, { @@ -306,7 +358,5 @@ where trace!("Reading packet with bytes: {buf_string}"); } - let packet = packet_decoder(&mut Cursor::new(&buf[..]))?; - - Ok(Some(packet)) + Ok(Some(buf)) } diff --git a/azalea-protocol/src/write.rs b/azalea-protocol/src/write.rs index 9e2e042b..0c3131a0 100755 --- a/azalea-protocol/src/write.rs +++ b/azalea-protocol/src/write.rs @@ -29,7 +29,7 @@ pub enum PacketEncodeError { }, } -pub fn packet_encoder( +pub fn serialize_packet( packet: &P, ) -> Result, PacketEncodeError> { let mut buf = Vec::new(); @@ -89,14 +89,28 @@ where W: AsyncWrite + Unpin + Send, { trace!("Sending packet: {packet:?}"); - let mut buf = packet_encoder(packet).unwrap(); + let raw_packet = serialize_packet(packet).unwrap(); + write_raw_packet(&raw_packet, stream, compression_threshold, cipher).await +} + +pub async fn write_raw_packet( + raw_packet: &[u8], + stream: &mut W, + compression_threshold: Option, + cipher: &mut Option, +) -> std::io::Result<()> +where + W: AsyncWrite + Unpin + Send, +{ + trace!("Writing raw packet: {raw_packet:?}"); + let mut raw_packet = raw_packet.to_vec(); if let Some(threshold) = compression_threshold { - buf = compression_encoder(&buf, threshold).await.unwrap(); + raw_packet = compression_encoder(&raw_packet, threshold).await.unwrap(); } - buf = frame_prepender(buf).unwrap(); + raw_packet = frame_prepender(raw_packet).unwrap(); // if we were given a cipher, encrypt the packet if let Some(cipher) = cipher { - azalea_crypto::encrypt_packet(cipher, &mut buf); + azalea_crypto::encrypt_packet(cipher, &mut raw_packet); } - stream.write_all(&buf).await + stream.write_all(&raw_packet).await } -- cgit v1.2.3