aboutsummaryrefslogtreecommitdiff
path: root/azalea-protocol
diff options
context:
space:
mode:
Diffstat (limited to 'azalea-protocol')
-rwxr-xr-xazalea-protocol/azalea-protocol-macros/src/lib.rs18
-rw-r--r--azalea-protocol/examples/handshake_proxy.rs8
-rwxr-xr-xazalea-protocol/src/connect.rs200
-rw-r--r--azalea-protocol/src/lib.rs8
-rw-r--r--azalea-protocol/src/packets/common.rs16
-rw-r--r--azalea-protocol/src/packets/configuration/clientbound_custom_payload_packet.rs10
-rw-r--r--azalea-protocol/src/packets/configuration/clientbound_disconnect_packet.rs8
-rw-r--r--azalea-protocol/src/packets/configuration/clientbound_finish_configuration_packet.rs5
-rw-r--r--azalea-protocol/src/packets/configuration/clientbound_keep_alive_packet.rs7
-rw-r--r--azalea-protocol/src/packets/configuration/clientbound_ping_packet.rs7
-rw-r--r--azalea-protocol/src/packets/configuration/clientbound_registry_data_packet.rs408
-rw-r--r--azalea-protocol/src/packets/configuration/clientbound_resource_pack_packet.rs11
-rw-r--r--azalea-protocol/src/packets/configuration/clientbound_update_enabled_features_packet.rs8
-rw-r--r--azalea-protocol/src/packets/configuration/clientbound_update_tags_packet.rs73
-rwxr-xr-xazalea-protocol/src/packets/configuration/mod.rs39
-rw-r--r--azalea-protocol/src/packets/configuration/serverbound_client_information_packet.rs181
-rw-r--r--azalea-protocol/src/packets/configuration/serverbound_custom_payload_packet.rs10
-rw-r--r--azalea-protocol/src/packets/configuration/serverbound_finish_configuration_packet.rs5
-rw-r--r--azalea-protocol/src/packets/configuration/serverbound_keep_alive_packet.rs7
-rw-r--r--azalea-protocol/src/packets/configuration/serverbound_pong_packet.rs7
-rw-r--r--azalea-protocol/src/packets/configuration/serverbound_resource_pack_packet.rs15
-rw-r--r--azalea-protocol/src/packets/game/clientbound_chunk_batch_finished_packet.rs8
-rw-r--r--azalea-protocol/src/packets/game/clientbound_chunk_batch_start_packet.rs5
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_forget_level_chunk_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_login_packet.rs509
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_ping_packet.rs1
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_pong_response_packet.rs7
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_respawn_packet.rs14
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_set_display_objective_packet.rs25
-rw-r--r--azalea-protocol/src/packets/game/clientbound_start_configuration_packet.rs5
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs46
-rwxr-xr-xazalea-protocol/src/packets/game/mod.rs318
-rw-r--r--azalea-protocol/src/packets/game/serverbound_chunk_batch_received_packet.rs7
-rwxr-xr-xazalea-protocol/src/packets/game/serverbound_client_information_packet.rs176
-rw-r--r--azalea-protocol/src/packets/game/serverbound_configuration_acknowledged_packet.rs5
-rwxr-xr-xazalea-protocol/src/packets/game/serverbound_ping_request_packet.rs7
-rwxr-xr-xazalea-protocol/src/packets/handshaking/client_intention_packet.rs (renamed from azalea-protocol/src/packets/handshake/client_intention_packet.rs)0
-rwxr-xr-xazalea-protocol/src/packets/handshaking/mod.rs (renamed from azalea-protocol/src/packets/handshake/mod.rs)0
-rwxr-xr-xazalea-protocol/src/packets/login/mod.rs6
-rw-r--r--azalea-protocol/src/packets/login/serverbound_custom_query_answer_packet.rs10
-rwxr-xr-xazalea-protocol/src/packets/login/serverbound_hello_packet.rs4
-rw-r--r--azalea-protocol/src/packets/login/serverbound_login_acknowledged_packet.rs5
-rwxr-xr-xazalea-protocol/src/packets/mod.rs8
-rwxr-xr-xazalea-protocol/src/read.rs136
-rwxr-xr-xazalea-protocol/src/write.rs26
45 files changed, 1390 insertions, 993 deletions
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<R: ProtocolPacket> {
+pub struct RawReadConnection {
pub read_stream: OwnedReadHalf,
pub buffer: BytesMut,
pub compression_threshold: Option<u32>,
pub dec_cipher: Option<Aes128CfbDec>,
- _reading: PhantomData<R>,
}
-/// The write half of a connection.
-pub struct WriteConnection<W: ProtocolPacket> {
+pub struct RawWriteConnection {
pub write_stream: OwnedWriteHalf,
pub compression_threshold: Option<u32>,
pub enc_cipher: Option<Aes128CfbEnc>,
+}
+
+/// The read half of a connection.
+pub struct ReadConnection<R: ProtocolPacket> {
+ pub raw: RawReadConnection,
+ _reading: PhantomData<R>,
+}
+
+/// The write half of a connection.
+pub struct WriteConnection<W: ProtocolPacket> {
+ pub raw: RawWriteConnection,
_writing: PhantomData<W>,
}
@@ -55,7 +67,7 @@ pub struct WriteConnection<W: ProtocolPacket> {
/// 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<W: ProtocolPacket> {
/// conn.write(
/// ServerboundHelloPacket {
/// name: "bot".to_string(),
-/// profile_id: None,
+/// profile_id: uuid::Uuid::nil(),
/// }
/// .get(),
/// )
@@ -108,7 +120,7 @@ pub struct WriteConnection<W: ProtocolPacket> {
/// 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<R: ProtocolPacket, W: ProtocolPacket> {
pub writer: WriteConnection<W>,
}
-impl<R> ReadConnection<R>
-where
- R: ProtocolPacket + Debug,
-{
- /// Read a packet from the stream.
- pub async fn read(&mut self) -> Result<R, Box<ReadPacketError>> {
- read_packet::<R, _>(
+impl RawReadConnection {
+ pub async fn read(&mut self) -> Result<Vec<u8>, Box<ReadPacketError>> {
+ 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<Option<R>, Box<ReadPacketError>> {
- try_read_packet::<R, _>(
+ pub fn try_read(&mut self) -> Result<Option<Vec<u8>>, Box<ReadPacketError>> {
+ try_read_raw_packet::<_>(
&mut self.read_stream,
&mut self.buffer,
self.compression_threshold,
@@ -152,14 +158,11 @@ where
)
}
}
-impl<W> WriteConnection<W>
-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<R> ReadConnection<R>
+where
+ R: ProtocolPacket + Debug,
+{
+ /// Read a packet from the stream.
+ pub async fn read(&mut self) -> Result<R, Box<ReadPacketError>> {
+ 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<Option<R>, Box<ReadPacketError>> {
+ let Some(raw_packet) = self.raw.try_read()? else {
+ return Ok(None);
+ };
+ Ok(Some(deserialize_packet(&mut Cursor::new(
+ raw_packet.as_slice(),
+ ))?))
+ }
+}
+impl<W> WriteConnection<W>
+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<R, W> Connection<R, W>
where
R: ProtocolPacket + Debug,
@@ -230,16 +269,20 @@ impl Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> {
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<ClientboundLoginPacket, ServerboundLoginPacket> {
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<ClientboundLoginPacket, ServerboundLoginPacket> {
/// 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<ClientboundGamePacket, ServerboundGamePacket> {
+ pub fn configuration(
+ self,
+ ) -> Connection<ClientboundConfigurationPacket, ServerboundConfigurationPacket> {
Connection::from(self)
}
@@ -384,11 +429,11 @@ impl Connection<ServerboundLoginPacket, ClientboundLoginPacket> {
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<ServerboundLoginPacket, ClientboundLoginPacket> {
/// 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<ServerboundLoginPacket, ClientboundLoginPacket> {
}
}
+impl Connection<ClientboundConfigurationPacket, ServerboundConfigurationPacket> {
+ /// 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<ClientboundGamePacket, ServerboundGamePacket> {
+ Connection::from(self)
+ }
+}
+
+impl Connection<ClientboundGamePacket, ServerboundGamePacket> {
+ /// Change our state back to configuration.
+ #[must_use]
+ pub fn configuration(
+ self,
+ ) -> Connection<ClientboundConfigurationPacket, ServerboundConfigurationPacket> {
+ Connection::from(self)
+ }
+}
+
// rust doesn't let us implement From because allegedly it conflicts with
// `core`'s "impl<T> From<T> for T" so we do this instead
impl<R1, W1> Connection<R1, W1>
@@ -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<TcpStream, ReuniteError> {
- 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<GlobalPos>,
+ #[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<ResourceLocation, Nbt>,
+ }
+
+ impl TryFrom<Nbt> for RegistryHolder {
+ type Error = serde_json::Error;
+
+ fn try_from(value: Nbt) -> Result<Self, Self::Error> {
+ Ok(RegistryHolder {
+ registries: serde_json::from_value(serde_json::to_value(value)?)?,
+ })
+ }
+ }
+
+ impl TryInto<Nbt> for RegistryHolder {
+ type Error = serde_json::Error;
+
+ fn try_into(self) -> Result<Nbt, Self::Error> {
+ serde_json::from_value(serde_json::to_value(self.registries)?)
+ }
+ }
+
+ impl McBufReadable for RegistryHolder {
+ fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ 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::<Nbt>::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<T> {
+ #[serde(rename = "type")]
+ pub kind: ResourceLocation,
+ pub value: Vec<TypeValue<T>>,
+ }
+
+ /// 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<T> {
+ 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<String, String>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub description: Option<String>,
+ }
+
+ /// 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<String>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub style: Option<ChatTypeStyle>,
+ }
+
+ /// 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<String>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(with = "Convert")]
+ pub bold: Option<bool>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(with = "Convert")]
+ pub italic: Option<bool>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(with = "Convert")]
+ pub underlined: Option<bool>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(with = "Convert")]
+ pub strikethrough: Option<bool>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(with = "Convert")]
+ pub obfuscated: Option<bool>,
+ }
+
+ /// 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<u32>,
+ #[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<String, Nbt>,
+ }
+
+ /// 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<String>,
+ 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<u32>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub grass_color: Option<u32>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub grass_color_modifier: Option<String>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub music: Option<BiomeMusic>,
+ pub mood_sound: BiomeMoodSound,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub additions_sound: Option<AdditionsSound>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub ambient_sound: Option<ResourceLocation>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub particle: Option<BiomeParticle>,
+ }
+
+ /// 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<String, String>,
+ }
+
+ #[derive(Debug, Clone, Serialize, Deserialize)]
+ #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))]
+ pub struct TrimPatternElement {
+ #[serde(flatten)]
+ pub pattern: HashMap<String, String>,
+ }
+
+ #[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<String>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub death_message_type: Option<String>,
+ }
+
+ // Using a trait because you can't implement methods for
+ // types you don't own, in this case Option<bool> and bool.
+ trait Convert: Sized {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer;
+
+ fn deserialize<'de, D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>;
+ }
+
+ // Convert between bool and u8
+ impl Convert for bool {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_u8(if *self { 1 } else { 0 })
+ }
+
+ fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ convert::<D>(u8::deserialize(deserializer)?)
+ }
+ }
+
+ // Convert between Option<bool> and u8
+ impl Convert for Option<bool> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ if let Some(value) = self {
+ Convert::serialize(value, serializer)
+ } else {
+ serializer.serialize_none()
+ }
+ }
+
+ fn deserialize<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ if let Some(value) = Option::<u8>::deserialize(deserializer)? {
+ Ok(Some(convert::<D>(value)?))
+ } else {
+ Ok(None)
+ }
+ }
+ }
+
+ // Deserializing logic here to deduplicate code
+ fn convert<'de, D>(value: u8) -> Result<bool, D::Error>
+ 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<FormattedText>,
+}
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<ResourceLocation>,
+}
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<i32>,
+}
+
+#[derive(Clone, Debug)]
+pub struct TagMap(pub HashMap<ResourceLocation, Vec<Tags>>);
+
+impl McBufReadable for TagMap {
+ fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ 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<Self, BufReadError> {
+ let name = ResourceLocation::read_from(buf)?;
+ let elements = Vec::<i32>::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<ResourceLocation, Vec<Tags>>;
+
+ 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<Self, azalea_buf::BufReadError> {
+ 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<ResourceLocation>,
- 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<GlobalPos>,
- #[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<Nbt> for RegistryHolder {
- type Error = serde_json::Error;
-
- fn try_from(value: Nbt) -> Result<Self, Self::Error> {
- serde_json::from_value(serde_json::to_value(value)?)
- }
- }
-
- impl TryInto<Nbt> for RegistryHolder {
- type Error = serde_json::Error;
-
- fn try_into(self) -> Result<Nbt, Self::Error> {
- serde_json::from_value(serde_json::to_value(self)?)
- }
- }
-
- impl McBufReadable for RegistryHolder {
- fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- 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::<Nbt>::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<TrimMaterialElement>,
- #[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<ChatTypeElement>,
- #[cfg(not(feature = "strict_registry"))]
- #[serde(default, rename = "minecraft:chat_type")]
- pub chat_type: Nbt,
-
- #[serde(rename = "minecraft:dimension_type")]
- pub dimension_type: RegistryType<DimensionTypeElement>,
-
- #[cfg(feature = "strict_registry")]
- #[serde(rename = "minecraft:worldgen/biome")]
- pub world_type: RegistryType<WorldTypeElement>,
- #[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<TrimPatternElement>,
- #[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<DamageTypeElement>,
- #[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<T> {
- #[serde(rename = "type")]
- pub kind: ResourceLocation,
- pub value: Vec<TypeValue<T>>,
- }
-
- /// 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<T> {
- 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<String, String>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- pub description: Option<String>,
- }
-
- /// 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<String>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- pub style: Option<ChatTypeStyle>,
- }
-
- /// 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<String>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- #[serde(with = "Convert")]
- pub bold: Option<bool>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- #[serde(with = "Convert")]
- pub italic: Option<bool>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- #[serde(with = "Convert")]
- pub underlined: Option<bool>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- #[serde(with = "Convert")]
- pub strikethrough: Option<bool>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- #[serde(with = "Convert")]
- pub obfuscated: Option<bool>,
- }
-
- /// 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<u32>,
- #[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<String, Nbt>,
- }
-
- /// 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<String>,
- 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<u32>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- pub grass_color: Option<u32>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- pub grass_color_modifier: Option<String>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- pub music: Option<BiomeMusic>,
- pub mood_sound: BiomeMoodSound,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- pub additions_sound: Option<AdditionsSound>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- pub ambient_sound: Option<ResourceLocation>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- pub particle: Option<BiomeParticle>,
- }
-
- /// 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<String, String>,
- }
-
- #[derive(Debug, Clone, Serialize, Deserialize)]
- #[cfg_attr(feature = "strict_registry", serde(deny_unknown_fields))]
- pub struct TrimPatternElement {
- #[serde(flatten)]
- pub pattern: HashMap<String, String>,
- }
-
- #[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<String>,
- #[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- pub death_message_type: Option<String>,
- }
-
- // Using a trait because you can't implement methods for
- // types you don't own, in this case Option<bool> and bool.
- trait Convert: Sized {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer;
-
- fn deserialize<'de, D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>;
- }
-
- // Convert between bool and u8
- impl Convert for bool {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- serializer.serialize_u8(if *self { 1 } else { 0 })
- }
-
- fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
- where
- D: Deserializer<'de>,
- {
- convert::<D>(u8::deserialize(deserializer)?)
- }
- }
-
- // Convert between Option<bool> and u8
- impl Convert for Option<bool> {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- if let Some(value) = self {
- Convert::serialize(value, serializer)
- } else {
- serializer.serialize_none()
- }
- }
-
- fn deserialize<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
- where
- D: Deserializer<'de>,
- {
- if let Some(value) = Option::<u8>::deserialize(deserializer)? {
- Ok(Some(convert::<D>(value)?))
- } else {
- Ok(None)
- }
- }
- }
-
- // Deserializing logic here to deduplicate code
- fn convert<'de, D>(value: u8) -> Result<bool, D::Error>
- 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::<DimensionTypeElement> {
- 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<GlobalPos>,
- #[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<ResourceLocation, Advancement>,
+ pub added: Vec<AdvancementHolder>,
pub removed: Vec<ResourceLocation>,
pub progress: HashMap<ResourceLocation, AdvancementProgress>,
}
@@ -18,7 +18,6 @@ pub struct ClientboundUpdateAdvancementsPacket {
pub struct Advancement {
pub parent_id: Option<ResourceLocation>,
pub display: Option<DisplayInfo>,
- pub criteria: HashMap<ResourceLocation, Criterion>,
pub requirements: Vec<Vec<String>>,
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<ResourceLocation, CriterionProgress>;
+pub type AdvancementProgress = HashMap<String, CriterionProgress>;
#[derive(Clone, Debug, McBuf)]
pub struct CriterionProgress {
- date: Option<u64>,
+ pub date: Option<u64>,
+}
+
+#[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<Self, azalea_buf::BufReadError> {
- 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/handshaking/client_intention_packet.rs
index 1900d476..1900d476 100755
--- a/azalea-protocol/src/packets/handshake/client_intention_packet.rs
+++ b/azalea-protocol/src/packets/handshaking/client_intention_packet.rs
diff --git a/azalea-protocol/src/packets/handshake/mod.rs b/azalea-protocol/src/packets/handshaking/mod.rs
index f2e8810e..f2e8810e 100755
--- a/azalea-protocol/src/packets/handshake/mod.rs
+++ b/azalea-protocol/src/packets/handshaking/mod.rs
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<UnsizedByteArray>,
+}
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<Uuid>,
+ 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<u8> = 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<Option<Vec<u8>>, FrameSplitte
Ok(None)
}
-fn packet_decoder<P: ProtocolPacket + Debug>(
+pub fn deserialize_packet<P: ProtocolPacket + Debug>(
stream: &mut Cursor<&[u8]>,
) -> Result<P, Box<ReadPacketError>> {
// 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<P: ProtocolPacket + Debug, R>(
+ stream: &mut R,
+ buffer: &mut BytesMut,
+ compression_threshold: Option<u32>,
+ cipher: &mut Option<Aes128CfbDec>,
+) -> Result<Option<P>, Box<ReadPacketError>>
+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<u32>,
+ cipher: &mut Option<Aes128CfbDec>,
+) -> Result<Vec<u8>, Box<ReadPacketError>>
+where
+ R: AsyncRead + std::marker::Unpin + std::marker::Send + std::marker::Sync,
+{
loop {
- if let Some(buf) = try_process_buffer::<P, R>(buffer, compression_threshold)? {
+ if let Some(buf) = read_raw_packet_from_buffer::<R>(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<P: ProtocolPacket + Debug, R>(
+pub fn try_read_raw_packet<R>(
stream: &mut R,
buffer: &mut BytesMut,
compression_threshold: Option<u32>,
cipher: &mut Option<Aes128CfbDec>,
-) -> Result<Option<P>, Box<ReadPacketError>>
+) -> Result<Option<Vec<u8>>, Box<ReadPacketError>>
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::<P, R>(buffer, compression_threshold)? {
+ if let Some(buf) = read_raw_packet_from_buffer::<R>(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<R>(
+ stream: &mut R,
+ cipher: &mut Option<Aes128CfbDec>,
+) -> Result<BytesMut, Box<ReadPacketError>>
+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<R>(
+ stream: &mut R,
+ cipher: &mut Option<Aes128CfbDec>,
+) -> Result<Option<BytesMut>, Box<ReadPacketError>>
+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<P: ProtocolPacket + Debug, R>(
+pub fn read_raw_packet_from_buffer<R>(
buffer: &mut BytesMut,
compression_threshold: Option<u32>,
-) -> Result<Option<P>, Box<ReadPacketError>>
+) -> Result<Option<Vec<u8>>, Box<ReadPacketError>>
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<P: ProtocolPacket + std::fmt::Debug>(
+pub fn serialize_packet<P: ProtocolPacket + Debug>(
packet: &P,
) -> Result<Vec<u8>, 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<W>(
+ raw_packet: &[u8],
+ stream: &mut W,
+ compression_threshold: Option<u32>,
+ cipher: &mut Option<Aes128CfbEnc>,
+) -> 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
}