use azalea_buf::BufReadError; use azalea_buf::McBuf; use azalea_buf::McBufVarReadable; use azalea_buf::{McBufReadable, McBufWritable}; use azalea_core::ResourceLocation; use azalea_protocol_macros::ClientboundGamePacket; use log::warn; use std::io::Cursor; use std::io::Write; #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundCommandsPacket { pub entries: Vec, #[var] pub root_index: u32, } #[derive(Debug, Clone)] pub struct BrigadierNodeStub { pub is_executable: bool, pub children: Vec, pub redirect_node: Option, pub node_type: NodeType, } #[derive(Debug, Clone)] pub struct BrigadierNumber { min: Option, max: Option, } impl McBufReadable for BrigadierNumber { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { let flags = u8::read_from(buf)?; let min = if flags & 0x01 != 0 { Some(T::read_from(buf)?) } else { None }; let max = if flags & 0x02 != 0 { Some(T::read_from(buf)?) } else { None }; Ok(BrigadierNumber { min, max }) } } impl McBufWritable for BrigadierNumber { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { let mut flags: u8 = 0; if self.min.is_some() { flags |= 0x01; } if self.max.is_some() { flags |= 0x02; } flags.write_into(buf)?; if let Some(min) = &self.min { min.write_into(buf)?; } if let Some(max) = &self.max { max.write_into(buf)?; } Ok(()) } } #[derive(Debug, Clone, Copy, McBuf)] pub enum BrigadierString { /// Reads a single word SingleWord = 0, // If it starts with a ", keeps reading until another " (allowing escaping with \). Otherwise behaves the same as SINGLE_WORD QuotablePhrase = 1, // Reads the rest of the content after the cursor. Quotes will not be removed. GreedyPhrase = 2, } #[derive(Debug, Clone)] pub enum BrigadierParser { Bool, Double(BrigadierNumber), Float(BrigadierNumber), Integer(BrigadierNumber), Long(BrigadierNumber), String(BrigadierString), Entity { single: bool, players_only: bool }, GameProfile, BlockPos, ColumnPos, Vec3, Vec2, BlockState, BlockPredicate, ItemStack, ItemPredicate, Color, Component, Message, Nbt, NbtPath, Objective, ObjectiveCriteira, Operation, Particle, Rotation, Angle, ScoreboardSlot, ScoreHolder { allows_multiple: bool }, Swizzle, Team, ItemSlot, ResourceLocation, MobEffect, Function, EntityAnchor, IntRange, FloatRange, ItemEnchantment, EntitySummon, Dimension, Uuid, NbtTag, NbtCompoundTag, Time, ResourceOrTag { registry_key: ResourceLocation }, Resource { registry_key: ResourceLocation }, TemplateMirror, TemplateRotation, } impl McBufReadable for BrigadierParser { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { let parser_type = u32::var_read_from(buf)?; match parser_type { 0 => Ok(BrigadierParser::Bool), 1 => Ok(BrigadierParser::Float(BrigadierNumber::read_from(buf)?)), 2 => Ok(BrigadierParser::Double(BrigadierNumber::read_from(buf)?)), 3 => Ok(BrigadierParser::Integer(BrigadierNumber::read_from(buf)?)), 4 => Ok(BrigadierParser::Long(BrigadierNumber::read_from(buf)?)), 5 => Ok(BrigadierParser::String(BrigadierString::read_from(buf)?)), 6 => { let flags = u8::read_from(buf)?; Ok(BrigadierParser::Entity { single: flags & 0x01 != 0, players_only: flags & 0x02 != 0, }) } 7 => Ok(BrigadierParser::GameProfile), 8 => Ok(BrigadierParser::BlockPos), 9 => Ok(BrigadierParser::ColumnPos), 10 => Ok(BrigadierParser::Vec3), 11 => Ok(BrigadierParser::Vec2), 12 => Ok(BrigadierParser::BlockState), 13 => Ok(BrigadierParser::BlockPredicate), 14 => Ok(BrigadierParser::ItemStack), 15 => Ok(BrigadierParser::ItemPredicate), 16 => Ok(BrigadierParser::Color), 17 => Ok(BrigadierParser::Component), 18 => Ok(BrigadierParser::Message), 19 => Ok(BrigadierParser::NbtCompoundTag), 20 => Ok(BrigadierParser::NbtTag), 21 => Ok(BrigadierParser::NbtPath), 22 => Ok(BrigadierParser::Objective), 23 => Ok(BrigadierParser::ObjectiveCriteira), 24 => Ok(BrigadierParser::Operation), 25 => Ok(BrigadierParser::Particle), 26 => Ok(BrigadierParser::Angle), 27 => Ok(BrigadierParser::Rotation), 28 => Ok(BrigadierParser::ScoreboardSlot), 29 => { let flags = u8::read_from(buf)?; Ok(BrigadierParser::ScoreHolder { allows_multiple: flags & 0x01 != 0, }) } 30 => Ok(BrigadierParser::Swizzle), 31 => Ok(BrigadierParser::Team), 32 => Ok(BrigadierParser::ItemSlot), 33 => Ok(BrigadierParser::ResourceLocation), 34 => Ok(BrigadierParser::MobEffect), 35 => Ok(BrigadierParser::Function), 36 => Ok(BrigadierParser::EntityAnchor), 37 => Ok(BrigadierParser::IntRange), 38 => Ok(BrigadierParser::FloatRange), 39 => Ok(BrigadierParser::ItemEnchantment), 40 => Ok(BrigadierParser::EntitySummon), 41 => Ok(BrigadierParser::Dimension), 42 => Ok(BrigadierParser::Time), 43 => Ok(BrigadierParser::ResourceOrTag { registry_key: ResourceLocation::read_from(buf)?, }), 44 => Ok(BrigadierParser::Resource { registry_key: ResourceLocation::read_from(buf)?, }), 45 => Ok(BrigadierParser::TemplateMirror), 46 => Ok(BrigadierParser::TemplateRotation), 47 => Ok(BrigadierParser::Uuid), _ => Err(BufReadError::UnexpectedEnumVariant { id: parser_type as i32, }), } } } // TODO: BrigadierNodeStub should have more stuff impl McBufReadable for BrigadierNodeStub { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { let flags = u8::read_from(buf)?; if flags > 31 { warn!( "Warning: The flags from a Brigadier node are over 31 ({flags}; {flags:#b}). This is probably a bug.", ); } let node_type = flags & 0x03; let is_executable = flags & 0x04 != 0; let has_redirect = flags & 0x08 != 0; let has_suggestions_type = flags & 0x10 != 0; let children = Vec::::var_read_from(buf)?; let redirect_node = if has_redirect { Some(u32::var_read_from(buf)?) } else { None }; // argument node if node_type == 2 { let name = String::read_from(buf)?; let parser = BrigadierParser::read_from(buf)?; let suggestions_type = if has_suggestions_type { Some(ResourceLocation::read_from(buf)?) } else { None }; return Ok(BrigadierNodeStub { is_executable, children, redirect_node, node_type: NodeType::Argument { name, parser, suggestions_type, }, }); } // literal node if node_type == 1 { let name = String::read_from(buf)?; return Ok(BrigadierNodeStub { is_executable, children, redirect_node, node_type: NodeType::Literal { name }, }); } Ok(BrigadierNodeStub { is_executable, children, redirect_node, node_type: NodeType::Root, }) } } impl McBufWritable for BrigadierNodeStub { fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> { todo!() } } #[derive(Debug, Clone)] pub enum NodeType { Root, Literal { name: String, }, Argument { name: String, parser: BrigadierParser, suggestions_type: Option, }, } impl BrigadierNodeStub { pub fn name(&self) -> Option<&str> { match &self.node_type { NodeType::Root => None, NodeType::Literal { name } => Some(name), NodeType::Argument { name, .. } => Some(name), } } }