Implement clientbound packets (#190)

Co-authored-by: Ryan Johnson <ryanj00a@gmail.com>
This commit is contained in:
Guac 2023-01-19 22:00:35 -08:00 committed by GitHub
parent 34c01d0f24
commit ad89e4457c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 374 additions and 14 deletions

View file

@ -13,6 +13,14 @@ pub fn from_binary_slice(slice: &mut &[u8]) -> Result<(Compound, String)> {
let mut state = DecodeState { slice, depth: 0 };
let root_tag = state.read_tag()?;
// For cases such as Block Entity Data in the
// ChunkUpdateAndUpdateLight Packet
// https://wiki.vg/Protocol#Chunk_Data_and_Update_Light
if root_tag == Tag::End {
return Ok((Compound::new(), String::new()));
}
if root_tag != Tag::Compound {
return Err(Error::new_owned(format!(
"expected root tag for compound (got {root_tag})",

View file

@ -16,7 +16,7 @@ serde = { version = "1.0.147", features = ["derive"] }
serde_json = "1.0.87"
thiserror = "1.0.37"
tracing = "0.1.37"
uuid = "1.2.1"
uuid = { version = "1.2.1", features = ["serde"] }
valence_derive = { version = "0.1.0", path = "../valence_derive" }
valence_nbt = { version = "0.5.0", path = "../valence_nbt" }

View file

@ -143,9 +143,12 @@ pub mod play {
pub message: &'a str,
pub timestamp: u64,
pub salt: u64,
pub signature: &'a [u8],
pub signed_preview: bool,
pub acknowledgement: MessageAcknowledgment<'a>,
pub signature: Option<&'a [u8; 256]>,
pub message_count: VarInt,
// This is a bitset of 20; each bit represents one
// of the last 20 messages received and whether or not
// the message was acknowledged by the client
pub acknowledgement: &'a [u8; 3],
}
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]

View file

@ -10,8 +10,8 @@ use crate::raw_bytes::RawBytes;
use crate::text::Text;
use crate::types::{
AttributeProperty, BossBarAction, ChunkDataBlockEntity, Difficulty, GameEventKind, GameMode,
GlobalPos, PlayerAbilitiesFlags, SignedProperty, SoundCategory, SyncPlayerPosLookFlags,
TagGroup,
GlobalPos, PlayerAbilitiesFlags, SignedProperty, SoundCategory, Statistic,
SyncPlayerPosLookFlags, TagGroup,
};
use crate::username::Username;
use crate::var_int::VarInt;
@ -21,7 +21,10 @@ use crate::LengthPrefixedArray;
pub mod commands;
pub mod declare_recipes;
pub mod particle;
pub mod player_chat_message;
pub mod player_info_update;
pub mod set_equipment;
pub mod update_advancements;
pub mod update_recipe_book;
pub mod status {
@ -102,7 +105,10 @@ pub mod login {
pub mod play {
use commands::Node;
pub use particle::ParticleS2c;
pub use player_chat_message::PlayerChatMessage;
pub use player_info_update::PlayerInfoUpdate;
pub use set_equipment::SetEquipment;
pub use update_advancements::UpdateAdvancements;
pub use update_recipe_book::UpdateRecipeBook;
use super::*;
@ -148,6 +154,12 @@ pub mod play {
pub animation: u8, // TODO: use Animation enum.
}
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x04]
pub struct AwardStatistics {
pub statistics: Vec<Statistic>,
}
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x05]
pub struct AcknowledgeBlockChange {
@ -350,6 +362,29 @@ pub mod play {
pub block_light_arrays: &'a [LengthPrefixedArray<u8, 2048>],
}
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x21]
pub struct WorldEvent {
pub event: i32,
pub location: BlockPos,
pub data: i32,
pub disable_relative_volume: bool,
}
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x23]
pub struct UpdateLight {
pub chunk_x: VarInt,
pub chunk_z: VarInt,
pub trust_edges: bool,
pub sky_light_mask: Vec<u64>,
pub block_light_mask: Vec<u64>,
pub empty_sky_light_mask: Vec<u64>,
pub empty_block_light_mask: Vec<u64>,
pub sky_light_arrays: Vec<LengthPrefixedArray<u8, 2048>>,
pub block_light_arrays: Vec<LengthPrefixedArray<u8, 2048>>,
}
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x24]
pub struct LoginPlay<'a> {
@ -439,13 +474,6 @@ pub mod play {
pub fov_modifier: f32,
}
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x31]
pub struct PlayerChatMessage<'a> {
// TODO: A bunch of crap.
pub data: RawBytes<'a>,
}
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x34]
pub struct CombatDeath {
@ -609,6 +637,14 @@ pub mod play {
pub food_saturation: f32,
}
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x55]
pub struct SetPassengers {
/// Vehicle's entity id
pub entity_id: VarInt,
pub passengers: Vec<VarInt>,
}
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x59]
pub struct SetSubtitleText(pub Text);
@ -675,6 +711,14 @@ pub mod play {
pub footer: Text,
}
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x63]
pub struct PickupItem {
pub collected_entity_id: VarInt,
pub collector_entity_id: VarInt,
pub pickup_item_count: VarInt,
}
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x64]
pub struct TeleportEntity {
@ -715,6 +759,7 @@ pub mod play {
SpawnExperienceOrb,
SpawnPlayer,
EntityAnimationS2c,
AwardStatistics,
AcknowledgeBlockChange,
SetBlockDestroyStage,
BlockEntityData,
@ -736,6 +781,8 @@ pub mod play {
WorldBorderInitialize,
KeepAliveS2c,
ChunkDataAndUpdateLight<'a>,
WorldEvent,
UpdateLight,
ParticleS2c,
LoginPlay<'a>,
UpdateEntityPosition,
@ -762,8 +809,10 @@ pub mod play {
SetDefaultSpawnPosition,
SetEntityMetadata<'a>,
SetEntityVelocity,
SetEquipment,
SetExperience,
SetHealth,
SetPassengers,
SetSubtitleText,
UpdateTime,
SetTitleText,
@ -772,7 +821,9 @@ pub mod play {
SoundEffect,
SystemChatMessage,
SetTabListHeaderAndFooter,
PickupItem,
TeleportEntity,
UpdateAdvancements<'a>,
UpdateAttributes<'a>,
FeatureFlags<'a>,
DeclareRecipes<'a>,

View file

@ -0,0 +1,131 @@
use std::io::Write;
use crate::{Decode, DecodePacket, Encode, EncodePacket, Text, Uuid, VarInt};
#[derive(Clone, PartialEq, Debug, EncodePacket, DecodePacket)]
#[packet_id = 0x31]
pub struct PlayerChatMessage<'a> {
pub sender: Uuid,
pub index: VarInt,
pub message_signature: Option<&'a [u8; 256]>,
pub message: String,
pub time_stamp: u64,
pub salt: u64,
pub previous_messages: Vec<PreviousMessage<'a>>,
pub unsigned_content: Option<Text>,
pub filter_type: MessageFilterType,
pub filter_type_bits: Option<u8>,
pub chat_type: VarInt,
pub network_name: Text,
pub network_target_name: Option<Text>,
}
#[derive(Clone, PartialEq, Debug)]
pub struct PreviousMessage<'a> {
pub message_id: i32,
pub signature: Option<&'a [u8; 256]>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)]
pub enum MessageFilterType {
PassThrough,
FullyFiltered,
PartiallyFiltered,
}
impl<'a> Encode for PlayerChatMessage<'a> {
fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
self.sender.encode(&mut w)?;
self.index.encode(&mut w)?;
self.message_signature.encode(&mut w)?;
self.message.encode(&mut w)?;
self.time_stamp.encode(&mut w)?;
self.salt.encode(&mut w)?;
self.previous_messages.encode(&mut w)?;
self.unsigned_content.encode(&mut w)?;
self.filter_type.encode(&mut w)?;
if self.filter_type == MessageFilterType::PartiallyFiltered {
match self.filter_type_bits {
// Filler data
None => 0u8.encode(&mut w)?,
Some(bits) => bits.encode(&mut w)?,
}
}
self.chat_type.encode(&mut w)?;
self.network_name.encode(&mut w)?;
self.network_target_name.encode(&mut w)?;
Ok(())
}
}
impl<'a> Decode<'a> for PlayerChatMessage<'a> {
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
let sender = Uuid::decode(r)?;
let index = VarInt::decode(r)?;
let message_signature = Option::<&'a [u8; 256]>::decode(r)?;
let message = String::decode(r)?;
let time_stamp = u64::decode(r)?;
let salt = u64::decode(r)?;
let previous_messages = Vec::<PreviousMessage>::decode(r)?;
let unsigned_content = Option::<Text>::decode(r)?;
let filter_type = MessageFilterType::decode(r)?;
let filter_type_bits = match filter_type {
MessageFilterType::PartiallyFiltered => Some(u8::decode(r)?),
_ => None,
};
let chat_type = VarInt::decode(r)?;
let network_name = Text::decode(r)?;
let network_target_name = Option::<Text>::decode(r)?;
Ok(Self {
sender,
index,
message_signature,
message,
time_stamp,
salt,
previous_messages,
unsigned_content,
filter_type,
filter_type_bits,
chat_type,
network_name,
network_target_name,
})
}
}
impl<'a> Encode for PreviousMessage<'a> {
fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
VarInt(self.message_id + 1).encode(&mut w)?;
match self.signature {
None => {}
Some(signature) => signature.encode(&mut w)?,
}
Ok(())
}
}
impl<'a> Decode<'a> for PreviousMessage<'a> {
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
let message_id = VarInt::decode(r)?.0 - 1;
let signature = if message_id == -1 {
Some(<&[u8; 256]>::decode(r)?)
} else {
None
};
Ok(Self {
message_id,
signature,
})
}
}

View file

@ -100,6 +100,7 @@ impl<'a> Decode<'a> for PlayerInfoUpdate<'a> {
if actions.add_player() {
entry.username = Decode::decode(r)?;
entry.properties = Decode::decode(r)?;
}
if actions.initialize_chat() {
@ -110,6 +111,10 @@ impl<'a> Decode<'a> for PlayerInfoUpdate<'a> {
entry.game_mode = Decode::decode(r)?;
}
if actions.update_listed() {
entry.listed = Decode::decode(r)?;
}
if actions.update_latency() {
entry.ping = VarInt::decode(r)?.0;
}

View file

@ -0,0 +1,59 @@
use std::io::Write;
use crate::{Decode, DecodePacket, Encode, EncodePacket, ItemStack, VarInt};
#[derive(Clone, PartialEq, Debug, EncodePacket, DecodePacket)]
#[packet_id = 0x51]
pub struct SetEquipment {
pub entity_id: VarInt,
pub equipment: Vec<EquipmentEntry>,
}
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
pub struct EquipmentEntry {
pub slot: i8,
pub item: Option<ItemStack>,
}
impl Encode for SetEquipment {
fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
self.entity_id.encode(&mut w)?;
for i in 0..self.equipment.len() {
let slot = self.equipment[i].slot;
if i != self.equipment.len() - 1 {
(slot | -128).encode(&mut w)?;
} else {
slot.encode(&mut w)?;
}
self.equipment[i].item.encode(&mut w)?;
}
Ok(())
}
}
impl<'a> Decode<'a> for SetEquipment {
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
let entity_id = VarInt::decode(r)?;
let mut equipment = vec![];
loop {
let slot = i8::decode(r)?;
let item = Option::<ItemStack>::decode(r)?;
equipment.push(EquipmentEntry {
slot: slot & 127,
item,
});
if slot & -128 == 0 {
break;
}
}
Ok(Self {
entity_id,
equipment,
})
}
}

View file

@ -0,0 +1,95 @@
use std::io::Write;
use crate::{Decode, DecodePacket, Encode, EncodePacket, Ident, ItemStack, Text, VarInt};
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x65]
pub struct UpdateAdvancements<'a> {
pub reset: bool,
pub advancement_mapping: Vec<(Ident<&'a str>, Advancement<'a>)>,
pub identifiers: Vec<Ident<&'a str>>,
pub progress_mapping: Vec<(Ident<&'a str>, Vec<AdvancementCriteria<'a>>)>,
}
#[derive(Clone, PartialEq, Debug, Encode, Decode)]
pub struct Advancement<'a> {
pub parent_id: Option<Ident<&'a str>>,
pub display_data: Option<AdvancementDisplay<'a>>,
pub criteria: Vec<(Ident<&'a str>, ())>,
pub requirements: Vec<AdvancementRequirements<'a>>,
}
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
pub struct AdvancementRequirements<'a> {
pub requirement: Vec<&'a str>,
}
#[derive(Clone, PartialEq, Debug)]
pub struct AdvancementDisplay<'a> {
pub title: Text,
pub description: Text,
pub icon: Option<ItemStack>,
pub frame_type: VarInt,
pub flags: i32,
pub background_texture: Option<Ident<&'a str>>,
pub x_coord: f64,
pub y_coord: f64,
}
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
pub struct AdvancementCriteria<'a> {
pub criterion_identifier: Ident<&'a str>,
/// If present, the criteria has been achieved at the
/// time wrapped; time represented as millis since epoch
pub criterion_progress: Option<i64>,
}
impl Encode for AdvancementDisplay<'_> {
fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
self.title.encode(&mut w)?;
self.description.encode(&mut w)?;
self.icon.encode(&mut w)?;
self.frame_type.encode(&mut w)?;
self.flags.encode(&mut w)?;
match self.background_texture {
None => {}
Some(texture) => texture.encode(&mut w)?,
}
self.x_coord.encode(&mut w)?;
self.y_coord.encode(&mut w)?;
Ok(())
}
}
impl<'a> Decode<'a> for AdvancementDisplay<'a> {
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
let title = Text::decode(r)?;
let description = Text::decode(r)?;
let icon = Option::<ItemStack>::decode(r)?;
let frame_type = VarInt::decode(r)?;
let flags = i32::decode(r)?;
let background_texture = if flags & 1 == 1 {
Some(Ident::<&'a str>::decode(r)?)
} else {
None
};
let x_coord = f64::decode(r)?;
let y_coord = f64::decode(r)?;
Ok(Self {
title,
description,
icon,
frame_type,
flags,
background_texture,
x_coord,
y_coord,
})
}
}

View file

@ -7,6 +7,7 @@ use std::{fmt, ops};
use anyhow::Context;
use serde::de::Visitor;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use uuid::Uuid;
use crate::{Decode, Encode, Ident, Result};
@ -209,7 +210,7 @@ enum HoverEvent {
name: Text,
#[serde(rename = "type")]
kind: Ident<String>,
// TODO: id (hyphenated entity UUID as a string)
id: Uuid,
},
}

View file

@ -370,3 +370,10 @@ pub struct Tag<'a> {
pub name: Ident<&'a str>,
pub entries: Vec<VarInt>,
}
#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode)]
pub struct Statistic {
pub category_id: VarInt,
pub statistic_id: VarInt,
pub value: VarInt,
}