From 9f5e5c092be9167e4d5222fdee4a1d2c419e5052 Mon Sep 17 00:00:00 2001 From: EightFactorial Date: Tue, 6 Dec 2022 18:48:48 -0800 Subject: Complete ClientboundCommand{Suggestion}sPacket, Serde support for NBT Tags (#49) * Serializing ClientboundStatusResponsePacket Enable serialization of ClientboundStatusResponsePacket * Update clientbound_status_response_packet.rs Add options previewsChat and enforcesSecureChat * Serialize Style and TextColor * Serialize BaseComponent * Serialize TextComponent * Fix Style * Serialize Component * Fix multiple formats per message, fix reset tag * Fix Style, again * Use FlatMapSerializer * Forgot italics * Count struct fields, reorganize logic * Serialize TranslatableComponent * Rewrite TextComponent Serializer * Fix using TextColor::Parse * Code Cleanup * Add default attribute, just in case * Clippy * use serde derive feature + preferred formatting choices * McBufWritable for BrigadierNodeStub * Thanks Clippy... * Implement suggestions in azalea-brigadier * Serde support for NBT Tags * Serde options * Forgot Options * Oops, that's McBufWritable for BrigadierParser * Fix McBufWritable for SlotData * Complete ClientboundUpdateRecipesPacket * fix some issues * better impl McBufReadable for Suggestions Co-authored-by: BuildTools Co-authored-by: mat --- .../game/clientbound_command_suggestions_packet.rs | 48 ++-- .../packets/game/clientbound_commands_packet.rs | 259 ++++++++++++++++++++- .../game/clientbound_update_recipes_packet.rs | 146 +++++++++++- 3 files changed, 422 insertions(+), 31 deletions(-) (limited to 'azalea-protocol/src/packets') diff --git a/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs b/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs index c6f426a9..652ce78a 100755 --- a/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs @@ -1,32 +1,36 @@ -// use azalea_brigadier::context::StringRange; -use azalea_buf::{ - // BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable, - BufReadError, - McBufReadable, - McBufWritable, -}; +use azalea_brigadier::suggestion::Suggestions; +use azalea_buf::McBuf; +use azalea_chat::Component; use azalea_protocol_macros::ClientboundGamePacket; -use std::io::{Cursor, Write}; -#[derive(Clone, Debug, ClientboundGamePacket)] +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundCommandSuggestionsPacket { #[var] pub id: u32, - // pub suggestions: Suggestions, + pub suggestions: Suggestions, } -impl McBufReadable for ClientboundCommandSuggestionsPacket { - fn read_from(_buf: &mut Cursor<&[u8]>) -> Result { - // let id = u32::var_read_from(buf)?; - // let start = u32::var_read_from(buf)? as usize; - // let length = u32::var_read_from(buf)? as usize; - // let stringrange = StringRange::between(start, start + length); - todo!("Suggestions aren't implemented in azalea-brigadier yet") - } -} +#[cfg(test)] +mod tests { + use super::*; + use azalea_brigadier::{context::StringRange, suggestion::Suggestion}; + use azalea_buf::{McBufReadable, McBufWritable}; + use std::io::Cursor; -impl McBufWritable for ClientboundCommandSuggestionsPacket { - fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> { - todo!() + #[test] + fn test_suggestions() { + let suggestions = Suggestions { + range: StringRange::new(0, 5), + suggestions: vec![Suggestion { + text: "foo".to_string(), + range: StringRange::new(1, 4), + tooltip: Some(Component::from("bar".to_string())), + }], + }; + let mut buf = Vec::new(); + suggestions.write_into(&mut buf).unwrap(); + let mut cursor = Cursor::new(&buf[..]); + let suggestions = Suggestions::read_from(&mut cursor).unwrap(); + assert_eq!(suggestions, suggestions); } } diff --git a/azalea-protocol/src/packets/game/clientbound_commands_packet.rs b/azalea-protocol/src/packets/game/clientbound_commands_packet.rs index 2505d2d9..dcaf8d66 100755 --- a/azalea-protocol/src/packets/game/clientbound_commands_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_commands_packet.rs @@ -1,7 +1,7 @@ use azalea_buf::BufReadError; use azalea_buf::McBuf; use azalea_buf::McBufVarReadable; -use azalea_buf::{McBufReadable, McBufWritable}; +use azalea_buf::{McBufReadable, McBufVarWritable, McBufWritable}; use azalea_core::ResourceLocation; use azalea_protocol_macros::ClientboundGamePacket; use log::warn; @@ -25,8 +25,13 @@ pub struct BrigadierNodeStub { #[derive(Debug, Clone)] pub struct BrigadierNumber { - min: Option, - max: Option, + pub min: Option, + pub max: Option, +} +impl BrigadierNumber { + pub fn new(min: Option, max: Option) -> BrigadierNumber { + BrigadierNumber { min, max } + } } impl McBufReadable for BrigadierNumber { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { @@ -119,7 +124,6 @@ pub enum BrigadierParser { Dimension, Uuid, NbtTag, - NbtCompoundTag, Time, ResourceOrTag { registry_key: ResourceLocation }, Resource { registry_key: ResourceLocation }, @@ -157,7 +161,7 @@ impl McBufReadable for BrigadierParser { 16 => Ok(BrigadierParser::Color), 17 => Ok(BrigadierParser::Component), 18 => Ok(BrigadierParser::Message), - 19 => Ok(BrigadierParser::NbtCompoundTag), + 19 => Ok(BrigadierParser::Nbt), 20 => Ok(BrigadierParser::NbtTag), 21 => Ok(BrigadierParser::NbtPath), 22 => Ok(BrigadierParser::Objective), @@ -202,6 +206,181 @@ impl McBufReadable for BrigadierParser { } } +impl McBufWritable for BrigadierParser { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + match &self { + BrigadierParser::Bool => { + u32::var_write_into(&0, buf)?; + } + BrigadierParser::Float(f) => { + u32::var_write_into(&1, buf)?; + f.write_into(buf)?; + } + BrigadierParser::Double(d) => { + u32::var_write_into(&2, buf)?; + d.write_into(buf)?; + } + BrigadierParser::Integer(i) => { + u32::var_write_into(&3, buf)?; + i.write_into(buf)?; + } + BrigadierParser::Long(l) => { + u32::var_write_into(&4, buf)?; + l.write_into(buf)?; + } + BrigadierParser::String(s) => { + u32::var_write_into(&5, buf)?; + s.write_into(buf)?; + } + BrigadierParser::Entity { + single, + players_only, + } => { + u32::var_write_into(&6, buf)?; + let mut bitmask: u8 = 0x00; + if *single { + bitmask |= 0x01; + } + if *players_only { + bitmask |= 0x02; + } + bitmask.write_into(buf)?; + } + BrigadierParser::GameProfile => { + u32::var_write_into(&7, buf)?; + } + BrigadierParser::BlockPos => { + u32::var_write_into(&8, buf)?; + } + BrigadierParser::ColumnPos => { + u32::var_write_into(&9, buf)?; + } + BrigadierParser::Vec3 => { + u32::var_write_into(&10, buf)?; + } + BrigadierParser::Vec2 => { + u32::var_write_into(&11, buf)?; + } + BrigadierParser::BlockState => { + u32::var_write_into(&12, buf)?; + } + BrigadierParser::BlockPredicate => { + u32::var_write_into(&13, buf)?; + } + BrigadierParser::ItemStack => { + u32::var_write_into(&14, buf)?; + } + BrigadierParser::ItemPredicate => { + u32::var_write_into(&15, buf)?; + } + BrigadierParser::Color => { + u32::var_write_into(&16, buf)?; + } + BrigadierParser::Component => { + u32::var_write_into(&17, buf)?; + } + BrigadierParser::Message => { + u32::var_write_into(&18, buf)?; + } + BrigadierParser::Nbt => { + u32::var_write_into(&19, buf)?; + } + BrigadierParser::NbtTag => { + u32::var_write_into(&20, buf)?; + } + BrigadierParser::NbtPath => { + u32::var_write_into(&21, buf)?; + } + BrigadierParser::Objective => { + u32::var_write_into(&22, buf)?; + } + BrigadierParser::ObjectiveCriteira => { + u32::var_write_into(&23, buf)?; + } + BrigadierParser::Operation => { + u32::var_write_into(&24, buf)?; + } + BrigadierParser::Particle => { + u32::var_write_into(&25, buf)?; + } + BrigadierParser::Angle => { + u32::var_write_into(&26, buf)?; + } + BrigadierParser::Rotation => { + u32::var_write_into(&27, buf)?; + } + BrigadierParser::ScoreboardSlot => { + u32::var_write_into(&28, buf)?; + } + BrigadierParser::ScoreHolder { allows_multiple } => { + u32::var_write_into(&29, buf)?; + if *allows_multiple { + buf.write_all(&[0x01])?; + } else { + buf.write_all(&[0x00])?; + } + } + BrigadierParser::Swizzle => { + u32::var_write_into(&30, buf)?; + } + BrigadierParser::Team => { + u32::var_write_into(&31, buf)?; + } + BrigadierParser::ItemSlot => { + u32::var_write_into(&32, buf)?; + } + BrigadierParser::ResourceLocation => { + u32::var_write_into(&33, buf)?; + } + BrigadierParser::MobEffect => { + u32::var_write_into(&34, buf)?; + } + BrigadierParser::Function => { + u32::var_write_into(&35, buf)?; + } + BrigadierParser::EntityAnchor => { + u32::var_write_into(&36, buf)?; + } + BrigadierParser::IntRange => { + u32::var_write_into(&37, buf)?; + } + BrigadierParser::FloatRange => { + u32::var_write_into(&38, buf)?; + } + BrigadierParser::ItemEnchantment => { + u32::var_write_into(&39, buf)?; + } + BrigadierParser::EntitySummon => { + u32::var_write_into(&40, buf)?; + } + BrigadierParser::Dimension => { + u32::var_write_into(&41, buf)?; + } + BrigadierParser::Time => { + u32::var_write_into(&42, buf)?; + } + BrigadierParser::ResourceOrTag { registry_key } => { + u32::var_write_into(&43, buf)?; + registry_key.write_into(buf)?; + } + BrigadierParser::Resource { registry_key } => { + u32::var_write_into(&44, buf)?; + registry_key.write_into(buf)?; + } + BrigadierParser::TemplateMirror => { + u32::var_write_into(&45, buf)?; + } + BrigadierParser::TemplateRotation => { + u32::var_write_into(&46, buf)?; + } + BrigadierParser::Uuid => { + u32::var_write_into(&47, buf)?; + } + } + Ok(()) + } +} + // TODO: BrigadierNodeStub should have more stuff impl McBufReadable for BrigadierNodeStub { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { @@ -264,8 +443,74 @@ impl McBufReadable for BrigadierNodeStub { } impl McBufWritable for BrigadierNodeStub { - fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> { - todo!() + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + match &self.node_type { + NodeType::Root => { + let mut flags = 0x00; + if self.is_executable { + flags |= 0x04; + } + if self.redirect_node.is_some() { + flags |= 0x08; + } + flags.var_write_into(buf)?; + + self.children.var_write_into(buf)?; + + if let Some(redirect) = self.redirect_node { + redirect.var_write_into(buf)?; + } + } + NodeType::Literal { name } => { + let mut flags = 0x01; + if self.is_executable { + flags |= 0x04; + } + if self.redirect_node.is_some() { + flags |= 0x08; + } + flags.var_write_into(buf)?; + + self.children.var_write_into(buf)?; + + if let Some(redirect) = self.redirect_node { + redirect.var_write_into(buf)?; + } + + name.write_into(buf)?; + } + NodeType::Argument { + name, + parser, + suggestions_type, + } => { + let mut flags = 0x02; + if self.is_executable { + flags |= 0x04; + } + if self.redirect_node.is_some() { + flags |= 0x08; + } + if suggestions_type.is_some() { + flags |= 0x10; + } + flags.var_write_into(buf)?; + + self.children.var_write_into(buf)?; + + if let Some(redirect) = self.redirect_node { + redirect.var_write_into(buf)?; + } + + name.write_into(buf)?; + parser.write_into(buf)?; + + if let Some(suggestion) = suggestions_type { + suggestion.write_into(buf)?; + } + } + } + Ok(()) } } diff --git a/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs index db31ef78..9c325d29 100755 --- a/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs @@ -121,8 +121,150 @@ pub struct Ingredient { } impl McBufWritable for Recipe { - fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> { - todo!() + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + match &self.data { + RecipeData::CraftingShapeless(recipe) => { + ResourceLocation::new("minecraft:crafting_shapeless") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + recipe.write_into(buf)?; + } + RecipeData::CraftingShaped(recipe) => { + ResourceLocation::new("minecraft:crafting_shaped") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + recipe.write_into(buf)?; + } + RecipeData::CraftingSpecialArmorDye => { + ResourceLocation::new("minecraft:crafting_special_armordye") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialBookCloning => { + ResourceLocation::new("minecraft:crafting_special_bookcloning") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialMapCloning => { + ResourceLocation::new("minecraft:crafting_special_mapcloning") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialMapExtending => { + ResourceLocation::new("minecraft:crafting_special_mapextending") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialFireworkRocket => { + ResourceLocation::new("minecraft:crafting_special_firework_rocket") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialFireworkStar => { + ResourceLocation::new("minecraft:crafting_special_firework_star") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialFireworkStarFade => { + ResourceLocation::new("minecraft:crafting_special_firework_star_fade") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialRepairItem => { + ResourceLocation::new("minecraft:crafting_special_repairitem") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialTippedArrow => { + ResourceLocation::new("minecraft:crafting_special_tippedarrow") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialBannerDuplicate => { + ResourceLocation::new("minecraft:crafting_special_bannerduplicate") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialBannerAddPattern => { + ResourceLocation::new("minecraft:crafting_special_banneraddpattern") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialShieldDecoration => { + ResourceLocation::new("minecraft:crafting_special_shielddecoration") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialShulkerBoxColoring => { + ResourceLocation::new("minecraft:crafting_special_shulkerboxcoloring") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::CraftingSpecialSuspiciousStew => { + ResourceLocation::new("minecraft:crafting_special_suspiciousstew") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + } + RecipeData::Smelting(recipe) => { + ResourceLocation::new("minecraft:smelting") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + recipe.write_into(buf)?; + } + RecipeData::Blasting(recipe) => { + ResourceLocation::new("minecraft:blasting") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + recipe.write_into(buf)?; + } + RecipeData::Smoking(recipe) => { + ResourceLocation::new("minecraft:smoking") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + recipe.write_into(buf)?; + } + RecipeData::CampfireCooking(recipe) => { + ResourceLocation::new("minecraft:campfire_cooking") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + recipe.write_into(buf)?; + } + RecipeData::Stonecutting(recipe) => { + ResourceLocation::new("minecraft:stonecutting") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + recipe.write_into(buf)?; + } + RecipeData::Smithing(recipe) => { + ResourceLocation::new("minecraft:smithing") + .unwrap() + .write_into(buf)?; + self.identifier.write_into(buf)?; + recipe.write_into(buf)?; + } + }; + Ok(()) } } -- cgit v1.2.3