aboutsummaryrefslogtreecommitdiff
path: root/azalea-protocol
diff options
context:
space:
mode:
Diffstat (limited to 'azalea-protocol')
-rwxr-xr-xazalea-protocol/packet-macros/src/lib.rs203
-rwxr-xr-xazalea-protocol/src/mc_buf/read.rs57
-rwxr-xr-xazalea-protocol/src/mc_buf/write.rs37
-rw-r--r--azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs22
-rw-r--r--azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs6
-rw-r--r--azalea-protocol/src/packets/game/clientbound_set_entity_data_packet.rs419
-rwxr-xr-xazalea-protocol/src/packets/game/mod.rs4
-rwxr-xr-xazalea-protocol/src/packets/status/serverbound_status_request_packet.rs1
8 files changed, 632 insertions, 117 deletions
diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs
index 0c98f4a3..6d1789a8 100755
--- a/azalea-protocol/packet-macros/src/lib.rs
+++ b/azalea-protocol/packet-macros/src/lib.rs
@@ -7,105 +7,146 @@ use syn::{
};
fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
- let fields = match data {
- syn::Data::Struct(syn::DataStruct { fields, .. }) => fields,
- _ => panic!("#[derive(*Packet)] can only be used on structs"),
- };
- let FieldsNamed { named, .. } = match fields {
- syn::Fields::Named(f) => f,
- _ => panic!("#[derive(*Packet)] can only be used on structs with named fields"),
- };
+ match data {
+ syn::Data::Struct(syn::DataStruct { fields, .. }) => {
+ let FieldsNamed { named, .. } = match fields {
+ syn::Fields::Named(f) => f,
+ _ => panic!("#[derive(*Packet)] can only be used on structs with named fields"),
+ };
- let read_fields = named
- .iter()
- .map(|f| {
- let field_name = &f.ident;
- let field_type = &f.ty;
- // do a different buf.write_* for each field depending on the type
- // if it's a string, use buf.write_string
- match field_type {
- syn::Type::Path(_) => {
- if f.attrs.iter().any(|a| a.path.is_ident("varint")) {
- quote! {
- let #field_name = crate::mc_buf::McBufVarintReadable::varint_read_into(buf).await?;
- }
- } else {
- quote! {
- let #field_name = crate::mc_buf::McBufReadable::read_into(buf).await?;
+ let read_fields = named
+ .iter()
+ .map(|f| {
+ let field_name = &f.ident;
+ let field_type = &f.ty;
+ // do a different buf.write_* for each field depending on the type
+ // if it's a string, use buf.write_string
+ match field_type {
+ syn::Type::Path(_) => {
+ if f.attrs.iter().any(|a| a.path.is_ident("varint")) {
+ quote! {
+ let #field_name = crate::mc_buf::McBufVarintReadable::varint_read_into(buf).await?;
+ }
+ } else {
+ quote! {
+ let #field_name = crate::mc_buf::McBufReadable::read_into(buf).await?;
+ }
}
+ }
+ _ => panic!(
+ "Error reading field {}: {}",
+ field_name.clone().unwrap(),
+ field_type.to_token_stream()
+ ),
}
+ })
+ .collect::<Vec<_>>();
+ let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>();
+
+ quote! {
+ #[async_trait::async_trait]
+ impl crate::mc_buf::McBufReadable for #ident {
+ async fn read_into<R>(buf: &mut R) -> Result<Self, String>
+ where
+ R: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send,
+ {
+ #(#read_fields)*
+ Ok(#ident {
+ #(#read_field_names: #read_field_names),*
+ })
}
- _ => panic!(
- "Error reading field {}: {}",
- field_name.clone().unwrap(),
- field_type.to_token_stream()
- ),
}
- })
- .collect::<Vec<_>>();
- let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>();
+ }
+ }
+ syn::Data::Enum(syn::DataEnum { variants, .. }) => {
+ let mut match_contents = quote!();
+ for variant in variants {
+ let variant_name = &variant.ident;
+ let variant_discrim = &variant
+ .discriminant
+ .as_ref()
+ .expect("enum variant must have a discriminant")
+ .1;
+ match_contents.extend(quote! {
+ #variant_discrim => Ok(Self::#variant_name),
+ });
+ }
- quote! {
- #[async_trait::async_trait]
- impl crate::mc_buf::McBufReadable for #ident {
- async fn read_into<R>(buf: &mut R) -> Result<Self, String>
- where
- R: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send,
- {
- #(#read_fields)*
- Ok(#ident {
- #(#read_field_names: #read_field_names),*
- })
+ quote! {
+ #[async_trait::async_trait]
+ impl crate::mc_buf::McBufReadable for #ident {
+ async fn read_into<R>(buf: &mut R) -> Result<Self, String>
+ where
+ R: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send,
+ {
+ let id = buf.read_varint().await?;
+ match id {
+ #match_contents
+ _ => Err(format!("Unknown enum variant {}", id)),
+ }
+ }
+ }
+ }
}
- }
+ _ => panic!("#[derive(*Packet)] can only be used on structs"),
}
}
fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
- let fields = match data {
- syn::Data::Struct(syn::DataStruct { fields, .. }) => fields,
- _ => panic!("#[derive(*Packet)] can only be used on structs"),
- };
- let FieldsNamed { named, .. } = match fields {
- syn::Fields::Named(f) => f,
- _ => panic!("#[derive(*Packet)] can only be used on structs with named fields"),
- };
+ match data {
+ syn::Data::Struct(syn::DataStruct { fields, .. }) => {
+ let FieldsNamed { named, .. } = match fields {
+ syn::Fields::Named(f) => f,
+ _ => panic!("#[derive(*Packet)] can only be used on structs with named fields"),
+ };
- let write_fields = named
- .iter()
- .map(|f| {
- let field_name = &f.ident;
- let field_type = &f.ty;
- // do a different buf.write_* for each field depending on the type
- // if it's a string, use buf.write_string
- match field_type {
- syn::Type::Path(_) => {
- if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) {
- quote! {
- crate::mc_buf::McBufVarintWritable::varint_write_into(&self.#field_name, buf)?;
- }
- } else {
- quote! {
- crate::mc_buf::McBufWritable::write_into(&self.#field_name, buf)?;
+ let write_fields = named
+ .iter()
+ .map(|f| {
+ let field_name = &f.ident;
+ let field_type = &f.ty;
+ // do a different buf.write_* for each field depending on the type
+ // if it's a string, use buf.write_string
+ match field_type {
+ syn::Type::Path(_) => {
+ if f.attrs.iter().any(|attr| attr.path.is_ident("varint")) {
+ quote! {
+ crate::mc_buf::McBufVarintWritable::varint_write_into(&self.#field_name, buf)?;
+ }
+ } else {
+ quote! {
+ crate::mc_buf::McBufWritable::write_into(&self.#field_name, buf)?;
+ }
}
}
+ _ => panic!(
+ "Error writing field {}: {}",
+ field_name.clone().unwrap(),
+ field_type.to_token_stream()
+ ),
}
- _ => panic!(
- "Error writing field {}: {}",
- field_name.clone().unwrap(),
- field_type.to_token_stream()
- ),
- }
- })
- .collect::<Vec<_>>();
+ })
+ .collect::<Vec<_>>();
- quote! {
- impl crate::mc_buf::McBufWritable for #ident {
- fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
- #(#write_fields)*
- Ok(())
+ quote! {
+ impl crate::mc_buf::McBufWritable for #ident {
+ fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
+ #(#write_fields)*
+ Ok(())
+ }
+ }
+ }
+ }
+ syn::Data::Enum(syn::DataEnum { .. }) => {
+ quote! {
+ impl crate::mc_buf::McBufWritable for #ident {
+ fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
+ crate::mc_buf::Writable::write_varint(buf, *self as i32)
+ }
+ }
}
}
+ _ => panic!("#[derive(*Packet)] can only be used on structs"),
}
}
diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs
index b345aac9..53c137b3 100755
--- a/azalea-protocol/src/mc_buf/read.rs
+++ b/azalea-protocol/src/mc_buf/read.rs
@@ -2,7 +2,7 @@ use async_trait::async_trait;
use azalea_chat::component::Component;
use azalea_core::{
difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation,
- serializable_uuid::SerializableUuid, Slot, SlotData,
+ serializable_uuid::SerializableUuid, BlockPos, Direction, Slot, SlotData,
};
use serde::Deserialize;
use tokio::io::{AsyncRead, AsyncReadExt};
@@ -473,29 +473,14 @@ impl McBufReadable for Option<GameType> {
// Option<String>
#[async_trait]
-impl McBufReadable for Option<String> {
- async fn read_into<R>(buf: &mut R) -> Result<Self, String>
- where
- R: AsyncRead + std::marker::Unpin + std::marker::Send,
- {
- let present = buf.read_boolean().await?;
- Ok(if present {
- Some(buf.read_utf().await?)
- } else {
- None
- })
- }
-}
-// Option<Component>
-#[async_trait]
-impl McBufReadable for Option<Component> {
- async fn read_into<R>(buf: &mut R) -> Result<Self, String>
+impl<T: McBufReadable> McBufReadable for Option<T> {
+ default async fn read_into<R>(buf: &mut R) -> Result<Self, String>
where
R: AsyncRead + std::marker::Unpin + std::marker::Send,
{
let present = buf.read_boolean().await?;
Ok(if present {
- Some(Component::read_into(buf).await?)
+ Some(T::read_into(buf).await?)
} else {
None
})
@@ -567,3 +552,37 @@ impl McBufReadable for Uuid {
buf.read_uuid().await
}
}
+
+// BlockPos
+#[async_trait]
+impl McBufReadable for BlockPos {
+ async fn read_into<R>(buf: &mut R) -> Result<Self, String>
+ where
+ R: AsyncRead + std::marker::Unpin + std::marker::Send,
+ {
+ let val = u64::read_into(buf).await?;
+ let x = (val >> 38) as i32;
+ let y = (val & 0xFFF) as i32;
+ let z = ((val >> 12) & 0x3FFFFFF) as i32;
+ Ok(BlockPos { x, y, z })
+ }
+}
+
+// Direction
+#[async_trait]
+impl McBufReadable for Direction {
+ async fn read_into<R>(buf: &mut R) -> Result<Self, String>
+ where
+ R: AsyncRead + std::marker::Unpin + std::marker::Send,
+ {
+ match buf.read_varint().await? {
+ 0 => Ok(Self::Down),
+ 1 => Ok(Self::Up),
+ 2 => Ok(Self::North),
+ 3 => Ok(Self::South),
+ 4 => Ok(Self::West),
+ 5 => Ok(Self::East),
+ _ => Err("Invalid direction".to_string()),
+ }
+ }
+}
diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs
index 4c7ac60c..e5437ae8 100755
--- a/azalea-protocol/src/mc_buf/write.rs
+++ b/azalea-protocol/src/mc_buf/write.rs
@@ -3,7 +3,7 @@ use async_trait::async_trait;
use azalea_chat::component::Component;
use azalea_core::{
difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation,
- serializable_uuid::SerializableUuid, Slot,
+ serializable_uuid::SerializableUuid, BlockPos, Direction, Slot,
};
use byteorder::{BigEndian, WriteBytesExt};
use std::io::Write;
@@ -336,21 +336,8 @@ impl McBufWritable for Option<GameType> {
}
// Option<String>
-impl McBufWritable for Option<String> {
- fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
- if let Some(s) = self {
- buf.write_boolean(true)?;
- buf.write_utf(s)?;
- } else {
- buf.write_boolean(false)?;
- };
- Ok(())
- }
-}
-
-// Option<Component>
-impl McBufWritable for Option<Component> {
- fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
+impl<T: McBufWritable> McBufWritable for Option<T> {
+ default fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
if let Some(s) = self {
buf.write_boolean(true)?;
s.write_into(buf)?;
@@ -418,3 +405,21 @@ impl McBufWritable for Uuid {
Ok(())
}
}
+
+// BlockPos
+impl McBufWritable for BlockPos {
+ fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
+ buf.write_long(
+ (((self.x & 0x3FFFFFF) as i64) << 38)
+ | (((self.z & 0x3FFFFFF) as i64) << 12)
+ | ((self.y & 0xFFF) as i64),
+ )
+ }
+}
+
+// Direction
+impl McBufWritable for Direction {
+ fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
+ buf.write_varint(*self as i32)
+ }
+}
diff --git a/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs b/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs
new file mode 100644
index 00000000..f45e71c9
--- /dev/null
+++ b/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs
@@ -0,0 +1,22 @@
+use packet_macros::GamePacket;
+use uuid::Uuid;
+
+#[derive(Clone, Debug, GamePacket)]
+pub struct ClientboundAddEntityPacket {
+ #[varint]
+ pub id: i32,
+ pub uuid: Uuid,
+ // TODO: have an entity type struct
+ #[varint]
+ pub entity_type: i32,
+ pub x: f64,
+ pub y: f64,
+ pub z: f64,
+ pub x_rot: i8,
+ pub y_rot: i8,
+ // pub y_head_rot: i8,
+ pub data: i32,
+ pub x_vel: u16,
+ pub y_vel: u16,
+ pub z_vel: u16,
+}
diff --git a/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs b/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs
index b916cb8e..1d017c2a 100644
--- a/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs
@@ -26,3 +26,9 @@ pub struct BlockEntity {
type_: i32,
data: azalea_nbt::Tag,
}
+
+pub struct ChunkSection {}
+
+impl ClientboundLevelChunkPacketData {
+ pub fn read(world_height: u32) {}
+}
diff --git a/azalea-protocol/src/packets/game/clientbound_set_entity_data_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_entity_data_packet.rs
new file mode 100644
index 00000000..4cc456f3
--- /dev/null
+++ b/azalea-protocol/src/packets/game/clientbound_set_entity_data_packet.rs
@@ -0,0 +1,419 @@
+use crate::{
+ mc_buf::{Readable, Writable},
+ packets::{McBufReadable, McBufWritable},
+};
+use async_trait::async_trait;
+use azalea_chat::component::Component;
+use azalea_core::{BlockPos, Direction, Slot};
+use packet_macros::{GamePacket, McBufReadable, McBufWritable};
+use tokio::io::AsyncRead;
+use uuid::Uuid;
+
+#[derive(Clone, Debug, GamePacket)]
+pub struct ClientboundSetEntityDataPacket {
+ #[varint]
+ pub id: i32,
+ pub metadata: Vec<EntityDataItem>,
+}
+
+#[derive(Clone, Debug)]
+pub struct EntityDataItem {
+ // we can't identify what the index is for here because we don't know the
+ // entity type
+ pub index: u8,
+ pub value: EntityDataValue,
+}
+
+#[async_trait]
+impl McBufReadable for Vec<EntityDataItem> {
+ async fn read_into<R>(buf: &mut R) -> Result<Self, String>
+ where
+ R: AsyncRead + std::marker::Unpin + std::marker::Send,
+ {
+ let mut metadata = Vec::new();
+ loop {
+ let index = buf.read_byte().await?;
+ if index == 0xff {
+ break;
+ }
+ let value = EntityDataValue::read_into(buf).await?;
+ metadata.push(EntityDataItem { index, value });
+ }
+ Ok(metadata)
+ }
+}
+
+impl McBufWritable for Vec<EntityDataItem> {
+ fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
+ for item in self {
+ buf.write_byte(item.index)?;
+ item.value.write_into(buf)?;
+ }
+ buf.write_byte(0xff)?;
+ Ok(())
+ }
+}
+
+#[derive(Clone, Debug)]
+pub enum EntityDataValue {
+ Byte(u8),
+ // varint
+ Int(i32),
+ Float(f32),
+ String(String),
+ Component(Component),
+ OptionalComponent(Option<Component>),
+ ItemStack(Slot),
+ Boolean(bool),
+ Rotations { x: f32, y: f32, z: f32 },
+ BlockPos(BlockPos),
+ OptionalBlockPos(Option<BlockPos>),
+ Direction(Direction),
+ OptionalUuid(Option<Uuid>),
+ // 0 for absent (implies air); otherwise, a block state ID as per the global palette
+ // this is a varint
+ OptionalBlockState(Option<i32>),
+ CompoundTag(azalea_nbt::Tag),
+ Particle(Particle),
+ VillagerData(VillagerData),
+ // 0 for absent; 1 + actual value otherwise. Used for entity IDs.
+ OptionalUnsignedInt(Option<u32>),
+ Pose(Pose),
+}
+
+#[async_trait]
+impl McBufReadable for EntityDataValue {
+ async fn read_into<R>(buf: &mut R) -> Result<Self, String>
+ where
+ R: AsyncRead + std::marker::Unpin + std::marker::Send,
+ {
+ let type_ = buf.read_varint().await?;
+ Ok(match type_ {
+ 0 => EntityDataValue::Byte(buf.read_byte().await?),
+ 1 => EntityDataValue::Int(buf.read_varint().await?),
+ 2 => EntityDataValue::Float(buf.read_float().await?),
+ 3 => EntityDataValue::String(buf.read_utf().await?),
+ 4 => EntityDataValue::Component(Component::read_into(buf).await?),
+ 5 => EntityDataValue::OptionalComponent(Option::<Component>::read_into(buf).await?),
+ 6 => EntityDataValue::ItemStack(Slot::read_into(buf).await?),
+ 7 => EntityDataValue::Boolean(buf.read_boolean().await?),
+ 8 => EntityDataValue::Rotations {
+ x: buf.read_float().await?,
+ y: buf.read_float().await?,
+ z: buf.read_float().await?,
+ },
+ 9 => EntityDataValue::BlockPos(BlockPos::read_into(buf).await?),
+ 10 => EntityDataValue::OptionalBlockPos(Option::<BlockPos>::read_into(buf).await?),
+ 11 => EntityDataValue::Direction(Direction::read_into(buf).await?),
+ 12 => EntityDataValue::OptionalUuid(Option::<Uuid>::read_into(buf).await?),
+ 13 => EntityDataValue::OptionalBlockState({
+ let val = i32::read_into(buf).await?;
+ if val == 0 {
+ None
+ } else {
+ Some(val)
+ }
+ }),
+ 14 => EntityDataValue::CompoundTag(azalea_nbt::Tag::read_into(buf).await?),
+ 15 => EntityDataValue::Particle(Particle::read_into(buf).await?),
+ 16 => EntityDataValue::VillagerData(VillagerData::read_into(buf).await?),
+ 17 => EntityDataValue::OptionalUnsignedInt({
+ let val = buf.read_varint().await?;
+ if val == 0 {
+ None
+ } else {
+ Some((val - 1) as u32)
+ }
+ }),
+ 18 => EntityDataValue::Pose(Pose::read_into(buf).await?),
+ _ => return Err(format!("Unknown entity data type: {}", type_)),
+ })
+ }
+}
+
+impl McBufWritable for EntityDataValue {
+ fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
+ todo!();
+ }
+}
+
+#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)]
+pub enum Pose {
+ Standing = 0,
+ FallFlying = 1,
+ Sleeping = 2,
+ Swimming = 3,
+ SpinAttack = 4,
+ Sneaking = 5,
+ LongJumping = 6,
+ Dying = 7,
+}
+
+#[derive(Debug, Clone, McBufReadable, McBufWritable)]
+pub struct VillagerData {
+ #[varint]
+ type_: u32,
+ #[varint]
+ profession: u32,
+ #[varint]
+ level: u32,
+}
+
+#[derive(Debug, Clone, McBufReadable, McBufWritable)]
+pub struct Particle {
+ #[varint]
+ pub id: i32,
+ pub data: ParticleData,
+}
+
+#[derive(Clone, Debug)]
+pub enum ParticleData {
+ AmbientEntityEffect,
+ AngryVillager,
+ Block(BlockParticle),
+ BlockMarker(BlockParticle),
+ Bubble,
+ Cloud,
+ Crit,
+ DamageIndicator,
+ DragonBreath,
+ DrippingLava,
+ FallingLava,
+ LandingLava,
+ DrippingWater,
+ FallingWater,
+ Dust(DustParticle),
+ DustColorTransition(DustColorTransitionParticle),
+ Effect,
+ ElderGuardian,
+ EnchantedHit,
+ Enchant,
+ EndRod,
+ EntityEffect,
+ ExplosionEmitter,
+ Explosion,
+ FallingDust(BlockParticle),
+ Firework,
+ Fishing,
+ Flame,
+ SoulFireFlame,
+ Soul,
+ Flash,
+ HappyVillager,
+ Composter,
+ Heart,
+ InstantEffect,
+ Item(ItemParticle),
+ Vibration(VibrationParticle),
+ ItemSlime,
+ ItemSnowball,
+ LargeSmoke,
+ Lava,
+ Mycelium,
+ Note,
+ Poof,
+ Portal,
+ Rain,
+ Smoke,
+ Sneeze,
+ Spit,
+ SquidInk,
+ SweepAttack,
+ TotemOfUndying,
+ Underwater,
+ Splash,
+ Witch,
+ BubblePop,
+ CurrentDown,
+ BubbleColumnUp,
+ Nautilus,
+ Dolphin,
+ CampfireCozySmoke,
+ CampfireSignalSmoke,
+ DrippingHoney,
+ FallingHoney,
+ LandingHoney,
+ FallingNectar,
+ FallingSporeBlossom,
+ Ash,
+ CrimsonSpore,
+ WarpedSpore,
+ SporeBlossomAir,
+ DrippingObsidianTear,
+ FallingObsidianTear,
+ LandingObsidianTear,
+ ReversePortal,
+ WhiteAsh,
+ SmallFlame,
+ Snowflake,
+ DrippingDripstoneLava,
+ FallingDripstoneLava,
+ DrippingDripstoneWater,
+ FallingDripstoneWater,
+ GlowSquidInk,
+ Glow,
+ WaxOn,
+ WaxOff,
+ ElectricSpark,
+ Scrape,
+}
+
+#[derive(Debug, Clone, McBufReadable, McBufWritable)]
+pub struct BlockParticle {
+ #[varint]
+ pub block_state: i32,
+}
+#[derive(Debug, Clone, McBufReadable, McBufWritable)]
+pub struct DustParticle {
+ /// Red value, 0-1
+ pub red: f32,
+ /// Green value, 0-1
+ pub green: f32,
+ /// Blue value, 0-1
+ pub blue: f32,
+ /// The scale, will be clamped between 0.01 and 4.
+ pub scale: f32,
+}
+
+#[derive(Debug, Clone, McBufReadable, McBufWritable)]
+pub struct DustColorTransitionParticle {
+ /// Red value, 0-1
+ pub from_red: f32,
+ /// Green value, 0-1
+ pub from_green: f32,
+ /// Blue value, 0-1
+ pub from_blue: f32,
+ /// The scale, will be clamped between 0.01 and 4.
+ pub scale: f32,
+ /// Red value, 0-1
+ pub to_red: f32,
+ /// Green value, 0-1
+ pub to_green: f32,
+ /// Blue value, 0-1
+ pub to_blue: f32,
+}
+
+#[derive(Debug, Clone, McBufReadable, McBufWritable)]
+pub struct ItemParticle {
+ pub item: Slot,
+}
+
+#[derive(Debug, Clone, McBufReadable, McBufWritable)]
+pub struct VibrationParticle {
+ pub origin: BlockPos,
+ pub position_type: String,
+ pub block_position: BlockPos,
+ #[varint]
+ pub entity_id: u32,
+ #[varint]
+ pub ticks: u32,
+}
+
+#[async_trait]
+impl McBufReadable for ParticleData {
+ async fn read_into<R>(buf: &mut R) -> Result<Self, String>
+ where
+ R: AsyncRead + std::marker::Unpin + std::marker::Send,
+ {
+ let id = buf.read_varint().await?;
+ Ok(match id {
+ 0 => ParticleData::AmbientEntityEffect,
+ 1 => ParticleData::AngryVillager,
+ 2 => ParticleData::Block(BlockParticle::read_into(buf).await?),
+ 3 => ParticleData::BlockMarker(BlockParticle::read_into(buf).await?),
+ 4 => ParticleData::Bubble,
+ 5 => ParticleData::Cloud,
+ 6 => ParticleData::Crit,
+ 7 => ParticleData::DamageIndicator,
+ 8 => ParticleData::DragonBreath,
+ 9 => ParticleData::DrippingLava,
+ 10 => ParticleData::FallingLava,
+ 11 => ParticleData::LandingLava,
+ 12 => ParticleData::DrippingWater,
+ 13 => ParticleData::FallingWater,
+ 14 => ParticleData::Dust(DustParticle::read_into(buf).await?),
+ 15 => ParticleData::DustColorTransition(
+ DustColorTransitionParticle::read_into(buf).await?,
+ ),
+ 16 => ParticleData::Effect,
+ 17 => ParticleData::ElderGuardian,
+ 18 => ParticleData::EnchantedHit,
+ 19 => ParticleData::Enchant,
+ 20 => ParticleData::EndRod,
+ 21 => ParticleData::EntityEffect,
+ 22 => ParticleData::ExplosionEmitter,
+ 23 => ParticleData::Explosion,
+ 24 => ParticleData::FallingDust(BlockParticle::read_into(buf).await?),
+ 25 => ParticleData::Firework,
+ 26 => ParticleData::Fishing,
+ 27 => ParticleData::Flame,
+ 28 => ParticleData::SoulFireFlame,
+ 29 => ParticleData::Soul,
+ 30 => ParticleData::Flash,
+ 31 => ParticleData::HappyVillager,
+ 32 => ParticleData::Composter,
+ 33 => ParticleData::Heart,
+ 34 => ParticleData::InstantEffect,
+ 35 => ParticleData::Item(ItemParticle::read_into(buf).await?),
+ 36 => ParticleData::Vibration(VibrationParticle::read_into(buf).await?),
+ 37 => ParticleData::ItemSlime,
+ 38 => ParticleData::ItemSnowball,
+ 39 => ParticleData::LargeSmoke,
+ 40 => ParticleData::Lava,
+ 41 => ParticleData::Mycelium,
+ 42 => ParticleData::Note,
+ 43 => ParticleData::Poof,
+ 44 => ParticleData::Portal,
+ 45 => ParticleData::Rain,
+ 46 => ParticleData::Smoke,
+ 47 => ParticleData::Sneeze,
+ 48 => ParticleData::Spit,
+ 49 => ParticleData::SquidInk,
+ 50 => ParticleData::SweepAttack,
+ 51 => ParticleData::TotemOfUndying,
+ 52 => ParticleData::Underwater,
+ 53 => ParticleData::Splash,
+ 54 => ParticleData::Witch,
+ 55 => ParticleData::BubblePop,
+ 56 => ParticleData::CurrentDown,
+ 57 => ParticleData::BubbleColumnUp,
+ 58 => ParticleData::Nautilus,
+ 59 => ParticleData::Dolphin,
+ 60 => ParticleData::CampfireCozySmoke,
+ 61 => ParticleData::CampfireSignalSmoke,
+ 62 => ParticleData::DrippingHoney,
+ 63 => ParticleData::FallingHoney,
+ 64 => ParticleData::LandingHoney,
+ 65 => ParticleData::FallingNectar,
+ 66 => ParticleData::FallingSporeBlossom,
+ 67 => ParticleData::Ash,
+ 68 => ParticleData::CrimsonSpore,
+ 69 => ParticleData::WarpedSpore,
+ 70 => ParticleData::SporeBlossomAir,
+ 71 => ParticleData::DrippingObsidianTear,
+ 72 => ParticleData::FallingObsidianTear,
+ 73 => ParticleData::LandingObsidianTear,
+ 74 => ParticleData::ReversePortal,
+ 75 => ParticleData::WhiteAsh,
+ 76 => ParticleData::SmallFlame,
+ 77 => ParticleData::Snowflake,
+ 78 => ParticleData::DrippingDripstoneLava,
+ 79 => ParticleData::FallingDripstoneLava,
+ 80 => ParticleData::DrippingDripstoneWater,
+ 81 => ParticleData::FallingDripstoneWater,
+ 82 => ParticleData::GlowSquidInk,
+ 83 => ParticleData::Glow,
+ 84 => ParticleData::WaxOn,
+ 85 => ParticleData::WaxOff,
+ 86 => ParticleData::ElectricSpark,
+ 87 => ParticleData::Scrape,
+ _ => return Err(format!("Unknown particle id: {}", id)),
+ })
+ }
+}
+
+impl McBufWritable for ParticleData {
+ fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
+ todo!()
+ }
+}
diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs
index aac1a9a0..e27255ac 100755
--- a/azalea-protocol/src/packets/game/mod.rs
+++ b/azalea-protocol/src/packets/game/mod.rs
@@ -1,3 +1,4 @@
+pub mod clientbound_add_entity_packet;
pub mod clientbound_add_mob_packet;
pub mod clientbound_change_difficulty_packet;
pub mod clientbound_custom_payload_packet;
@@ -13,6 +14,7 @@ pub mod clientbound_player_position_packet;
pub mod clientbound_recipe_packet;
pub mod clientbound_set_carried_item_packet;
pub mod clientbound_set_chunk_cache_center;
+pub mod clientbound_set_entity_data_packet;
pub mod clientbound_update_recipes_packet;
pub mod clientbound_update_tags_packet;
pub mod clientbound_update_view_distance_packet;
@@ -26,6 +28,7 @@ declare_state_packets!(
0x0a: serverbound_custom_payload_packet::ServerboundCustomPayloadPacket,
},
Clientbound => {
+ 0x00: clientbound_add_entity_packet::ClientboundAddEntityPacket,
0x02: clientbound_add_mob_packet::ClientboundAddMobPacket,
0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket,
0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket,
@@ -42,6 +45,7 @@ declare_state_packets!(
0x48: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket,
0x49: clientbound_set_chunk_cache_center::ClientboundSetChunkCacheCenterPacket,
0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket,
+ 0x4d: clientbound_set_entity_data_packet::ClientboundSetEntityDataPacket,
0x66: clientbound_update_recipes_packet::ClientboundUpdateRecipesPacket,
0x67: clientbound_update_tags_packet::ClientboundUpdateTagsPacket
}
diff --git a/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs b/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs
index e77687ec..c15673d9 100755
--- a/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs
+++ b/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs
@@ -1,5 +1,4 @@
use packet_macros::StatusPacket;
-use std::hash::Hash;
#[derive(Clone, Debug, StatusPacket)]
pub struct ServerboundStatusRequestPacket {}