aboutsummaryrefslogtreecommitdiff
path: root/azalea-protocol/src/packets/configuration
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2023-09-21 11:16:29 -0500
committerGitHub <noreply@github.com>2023-09-21 11:16:29 -0500
commit7b3e2e4bf793466a351510c7fbbd08234e93bb0e (patch)
tree7177a919de9982d9e3c7f36a76d2025696f465b6 /azalea-protocol/src/packets/configuration
parent83cce236145cdab1872a472a70943b669a880965 (diff)
downloadazalea-drasl-7b3e2e4bf793466a351510c7fbbd08234e93bb0e.tar.xz
1.20.2 (#99)
* add configuration state * start updating to 23w31a * implement a bit more of 23w31a * chunk batching * start adding configuration state * ioasfhjgsd * almost works * configuration state mostly implemented * handle other packets in configuration state and fix keepalive * cleanup, fix warnings * 23w32a * fix some doctests * 23w33a * 23w35a * 1.20.2-pre2 * fix system conflicts * 1.20.2-pre4 * make tests compile * tests pass * 1.20.2-rc2 * 1.20.2 * Revert "1.20.2" This reverts commit dd152fd265332ead333c919e585ded6d609d7468. * didn't mean to commit that code --------- Co-authored-by: mat <git@matdoes.dev>
Diffstat (limited to 'azalea-protocol/src/packets/configuration')
-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
16 files changed, 801 insertions, 0 deletions
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,
+}