mirror of
https://github.com/italicsjenga/valence.git
synced 2025-01-11 07:11:30 +11:00
Implement clientbound packets (#190)
Co-authored-by: Ryan Johnson <ryanj00a@gmail.com>
This commit is contained in:
parent
34c01d0f24
commit
ad89e4457c
|
@ -13,6 +13,14 @@ pub fn from_binary_slice(slice: &mut &[u8]) -> Result<(Compound, String)> {
|
||||||
let mut state = DecodeState { slice, depth: 0 };
|
let mut state = DecodeState { slice, depth: 0 };
|
||||||
|
|
||||||
let root_tag = state.read_tag()?;
|
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 {
|
if root_tag != Tag::Compound {
|
||||||
return Err(Error::new_owned(format!(
|
return Err(Error::new_owned(format!(
|
||||||
"expected root tag for compound (got {root_tag})",
|
"expected root tag for compound (got {root_tag})",
|
||||||
|
|
|
@ -16,7 +16,7 @@ serde = { version = "1.0.147", features = ["derive"] }
|
||||||
serde_json = "1.0.87"
|
serde_json = "1.0.87"
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
tracing = "0.1.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_derive = { version = "0.1.0", path = "../valence_derive" }
|
||||||
valence_nbt = { version = "0.5.0", path = "../valence_nbt" }
|
valence_nbt = { version = "0.5.0", path = "../valence_nbt" }
|
||||||
|
|
||||||
|
|
|
@ -143,9 +143,12 @@ pub mod play {
|
||||||
pub message: &'a str,
|
pub message: &'a str,
|
||||||
pub timestamp: u64,
|
pub timestamp: u64,
|
||||||
pub salt: u64,
|
pub salt: u64,
|
||||||
pub signature: &'a [u8],
|
pub signature: Option<&'a [u8; 256]>,
|
||||||
pub signed_preview: bool,
|
pub message_count: VarInt,
|
||||||
pub acknowledgement: MessageAcknowledgment<'a>,
|
// 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)]
|
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
|
|
|
@ -10,8 +10,8 @@ use crate::raw_bytes::RawBytes;
|
||||||
use crate::text::Text;
|
use crate::text::Text;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
AttributeProperty, BossBarAction, ChunkDataBlockEntity, Difficulty, GameEventKind, GameMode,
|
AttributeProperty, BossBarAction, ChunkDataBlockEntity, Difficulty, GameEventKind, GameMode,
|
||||||
GlobalPos, PlayerAbilitiesFlags, SignedProperty, SoundCategory, SyncPlayerPosLookFlags,
|
GlobalPos, PlayerAbilitiesFlags, SignedProperty, SoundCategory, Statistic,
|
||||||
TagGroup,
|
SyncPlayerPosLookFlags, TagGroup,
|
||||||
};
|
};
|
||||||
use crate::username::Username;
|
use crate::username::Username;
|
||||||
use crate::var_int::VarInt;
|
use crate::var_int::VarInt;
|
||||||
|
@ -21,7 +21,10 @@ use crate::LengthPrefixedArray;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod declare_recipes;
|
pub mod declare_recipes;
|
||||||
pub mod particle;
|
pub mod particle;
|
||||||
|
pub mod player_chat_message;
|
||||||
pub mod player_info_update;
|
pub mod player_info_update;
|
||||||
|
pub mod set_equipment;
|
||||||
|
pub mod update_advancements;
|
||||||
pub mod update_recipe_book;
|
pub mod update_recipe_book;
|
||||||
|
|
||||||
pub mod status {
|
pub mod status {
|
||||||
|
@ -102,7 +105,10 @@ pub mod login {
|
||||||
pub mod play {
|
pub mod play {
|
||||||
use commands::Node;
|
use commands::Node;
|
||||||
pub use particle::ParticleS2c;
|
pub use particle::ParticleS2c;
|
||||||
|
pub use player_chat_message::PlayerChatMessage;
|
||||||
pub use player_info_update::PlayerInfoUpdate;
|
pub use player_info_update::PlayerInfoUpdate;
|
||||||
|
pub use set_equipment::SetEquipment;
|
||||||
|
pub use update_advancements::UpdateAdvancements;
|
||||||
pub use update_recipe_book::UpdateRecipeBook;
|
pub use update_recipe_book::UpdateRecipeBook;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -148,6 +154,12 @@ pub mod play {
|
||||||
pub animation: u8, // TODO: use Animation enum.
|
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)]
|
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
#[packet_id = 0x05]
|
#[packet_id = 0x05]
|
||||||
pub struct AcknowledgeBlockChange {
|
pub struct AcknowledgeBlockChange {
|
||||||
|
@ -350,6 +362,29 @@ pub mod play {
|
||||||
pub block_light_arrays: &'a [LengthPrefixedArray<u8, 2048>],
|
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)]
|
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
#[packet_id = 0x24]
|
#[packet_id = 0x24]
|
||||||
pub struct LoginPlay<'a> {
|
pub struct LoginPlay<'a> {
|
||||||
|
@ -439,13 +474,6 @@ pub mod play {
|
||||||
pub fov_modifier: f32,
|
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)]
|
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
#[packet_id = 0x34]
|
#[packet_id = 0x34]
|
||||||
pub struct CombatDeath {
|
pub struct CombatDeath {
|
||||||
|
@ -609,6 +637,14 @@ pub mod play {
|
||||||
pub food_saturation: f32,
|
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)]
|
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
#[packet_id = 0x59]
|
#[packet_id = 0x59]
|
||||||
pub struct SetSubtitleText(pub Text);
|
pub struct SetSubtitleText(pub Text);
|
||||||
|
@ -675,6 +711,14 @@ pub mod play {
|
||||||
pub footer: Text,
|
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)]
|
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
#[packet_id = 0x64]
|
#[packet_id = 0x64]
|
||||||
pub struct TeleportEntity {
|
pub struct TeleportEntity {
|
||||||
|
@ -715,6 +759,7 @@ pub mod play {
|
||||||
SpawnExperienceOrb,
|
SpawnExperienceOrb,
|
||||||
SpawnPlayer,
|
SpawnPlayer,
|
||||||
EntityAnimationS2c,
|
EntityAnimationS2c,
|
||||||
|
AwardStatistics,
|
||||||
AcknowledgeBlockChange,
|
AcknowledgeBlockChange,
|
||||||
SetBlockDestroyStage,
|
SetBlockDestroyStage,
|
||||||
BlockEntityData,
|
BlockEntityData,
|
||||||
|
@ -736,6 +781,8 @@ pub mod play {
|
||||||
WorldBorderInitialize,
|
WorldBorderInitialize,
|
||||||
KeepAliveS2c,
|
KeepAliveS2c,
|
||||||
ChunkDataAndUpdateLight<'a>,
|
ChunkDataAndUpdateLight<'a>,
|
||||||
|
WorldEvent,
|
||||||
|
UpdateLight,
|
||||||
ParticleS2c,
|
ParticleS2c,
|
||||||
LoginPlay<'a>,
|
LoginPlay<'a>,
|
||||||
UpdateEntityPosition,
|
UpdateEntityPosition,
|
||||||
|
@ -762,8 +809,10 @@ pub mod play {
|
||||||
SetDefaultSpawnPosition,
|
SetDefaultSpawnPosition,
|
||||||
SetEntityMetadata<'a>,
|
SetEntityMetadata<'a>,
|
||||||
SetEntityVelocity,
|
SetEntityVelocity,
|
||||||
|
SetEquipment,
|
||||||
SetExperience,
|
SetExperience,
|
||||||
SetHealth,
|
SetHealth,
|
||||||
|
SetPassengers,
|
||||||
SetSubtitleText,
|
SetSubtitleText,
|
||||||
UpdateTime,
|
UpdateTime,
|
||||||
SetTitleText,
|
SetTitleText,
|
||||||
|
@ -772,7 +821,9 @@ pub mod play {
|
||||||
SoundEffect,
|
SoundEffect,
|
||||||
SystemChatMessage,
|
SystemChatMessage,
|
||||||
SetTabListHeaderAndFooter,
|
SetTabListHeaderAndFooter,
|
||||||
|
PickupItem,
|
||||||
TeleportEntity,
|
TeleportEntity,
|
||||||
|
UpdateAdvancements<'a>,
|
||||||
UpdateAttributes<'a>,
|
UpdateAttributes<'a>,
|
||||||
FeatureFlags<'a>,
|
FeatureFlags<'a>,
|
||||||
DeclareRecipes<'a>,
|
DeclareRecipes<'a>,
|
||||||
|
|
131
crates/valence_protocol/src/packets/s2c/player_chat_message.rs
Normal file
131
crates/valence_protocol/src/packets/s2c/player_chat_message.rs
Normal 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -100,6 +100,7 @@ impl<'a> Decode<'a> for PlayerInfoUpdate<'a> {
|
||||||
|
|
||||||
if actions.add_player() {
|
if actions.add_player() {
|
||||||
entry.username = Decode::decode(r)?;
|
entry.username = Decode::decode(r)?;
|
||||||
|
entry.properties = Decode::decode(r)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if actions.initialize_chat() {
|
if actions.initialize_chat() {
|
||||||
|
@ -110,6 +111,10 @@ impl<'a> Decode<'a> for PlayerInfoUpdate<'a> {
|
||||||
entry.game_mode = Decode::decode(r)?;
|
entry.game_mode = Decode::decode(r)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if actions.update_listed() {
|
||||||
|
entry.listed = Decode::decode(r)?;
|
||||||
|
}
|
||||||
|
|
||||||
if actions.update_latency() {
|
if actions.update_latency() {
|
||||||
entry.ping = VarInt::decode(r)?.0;
|
entry.ping = VarInt::decode(r)?.0;
|
||||||
}
|
}
|
||||||
|
|
59
crates/valence_protocol/src/packets/s2c/set_equipment.rs
Normal file
59
crates/valence_protocol/src/packets/s2c/set_equipment.rs
Normal 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ use std::{fmt, ops};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use serde::de::Visitor;
|
use serde::de::Visitor;
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{Decode, Encode, Ident, Result};
|
use crate::{Decode, Encode, Ident, Result};
|
||||||
|
|
||||||
|
@ -209,7 +210,7 @@ enum HoverEvent {
|
||||||
name: Text,
|
name: Text,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: Ident<String>,
|
kind: Ident<String>,
|
||||||
// TODO: id (hyphenated entity UUID as a string)
|
id: Uuid,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -370,3 +370,10 @@ pub struct Tag<'a> {
|
||||||
pub name: Ident<&'a str>,
|
pub name: Ident<&'a str>,
|
||||||
pub entries: Vec<VarInt>,
|
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,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue