diff --git a/Cargo.lock b/Cargo.lock index f8a5fb7b..747a262b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,7 @@ dependencies = [ "cfg-if", "getrandom", "once_cell", + "serde", "version_check", ] @@ -170,6 +171,10 @@ dependencies = [ [[package]] name = "azalea-brigadier" version = "0.4.0" +dependencies = [ + "azalea-buf", + "azalea-chat", +] [[package]] name = "azalea-buf" @@ -274,6 +279,7 @@ dependencies = [ "log", "num-derive", "num-traits", + "serde", ] [[package]] diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml index 0a5d56c5..43e71b3c 100755 --- a/azalea-brigadier/Cargo.toml +++ b/azalea-brigadier/Cargo.toml @@ -9,3 +9,8 @@ version = "0.4.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +azalea-buf = {path = "../azalea-buf", version = "^0.4.0", optional = true} +azalea-chat = {path = "../azalea-chat", version = "^0.4.0", optional = true} + +[features] +azalea-buf = ["dep:azalea-buf", "dep:azalea-chat"] diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs index d95ee237..c3c3c900 100755 --- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs +++ b/azalea-brigadier/src/exceptions/builtin_exceptions.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::{message::Message, string_reader::StringReader}; +use crate::string_reader::StringReader; use super::command_syntax_exception::CommandSyntaxException; @@ -148,12 +148,12 @@ impl fmt::Debug for BuiltInExceptions { impl BuiltInExceptions { pub fn create(self) -> CommandSyntaxException { - let message = Message::from(format!("{self:?}")); + let message = format!("{self:?}"); CommandSyntaxException::create(self, message) } pub fn create_with_context(self, reader: &StringReader) -> CommandSyntaxException { - let message = Message::from(format!("{self:?}")); + let message = format!("{self:?}"); CommandSyntaxException::new(self, message, reader.string(), reader.cursor()) } } diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs index 14376a87..0254820d 100755 --- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs +++ b/azalea-brigadier/src/exceptions/command_syntax_exception.rs @@ -1,5 +1,4 @@ use super::builtin_exceptions::BuiltInExceptions; -use crate::message::Message; use std::{ cmp, fmt::{self, Write}, @@ -8,7 +7,7 @@ use std::{ #[derive(Clone, PartialEq)] pub struct CommandSyntaxException { pub type_: BuiltInExceptions, - message: Message, + message: String, input: Option, cursor: Option, } @@ -16,7 +15,7 @@ pub struct CommandSyntaxException { const CONTEXT_AMOUNT: usize = 10; impl CommandSyntaxException { - pub fn new(type_: BuiltInExceptions, message: Message, input: &str, cursor: usize) -> Self { + pub fn new(type_: BuiltInExceptions, message: String, input: &str, cursor: usize) -> Self { Self { type_, message, @@ -25,7 +24,7 @@ impl CommandSyntaxException { } } - pub fn create(type_: BuiltInExceptions, message: Message) -> Self { + pub fn create(type_: BuiltInExceptions, message: String) -> Self { Self { type_, message, @@ -35,7 +34,7 @@ impl CommandSyntaxException { } pub fn message(&self) -> String { - let mut message = self.message.string(); + let mut message = self.message.clone(); let context = self.context(); if let Some(context) = context { write!( @@ -49,7 +48,7 @@ impl CommandSyntaxException { message } - pub fn raw_message(&self) -> &Message { + pub fn raw_message(&self) -> &String { &self.message } diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index 3c37b7f3..cf2ce571 100755 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -3,7 +3,6 @@ pub mod builder; pub mod command_dispatcher; pub mod context; pub mod exceptions; -pub mod message; pub mod modifier; pub mod parse_results; pub mod string_reader; diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs deleted file mode 100755 index 75e07d4e..00000000 --- a/azalea-brigadier/src/message.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct Message(String); - -impl Message { - pub fn string(&self) -> String { - self.0.to_string() - } -} - -impl From for Message { - fn from(s: String) -> Self { - Self(s) - } -} diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs index 4c9a9547..114a4c47 100755 --- a/azalea-brigadier/src/suggestion/mod.rs +++ b/azalea-brigadier/src/suggestion/mod.rs @@ -1,16 +1,26 @@ mod suggestions; -use crate::{context::StringRange, message::Message}; +use crate::context::StringRange; +#[cfg(feature = "azalea-buf")] +use azalea_buf::McBufWritable; +#[cfg(feature = "azalea-buf")] +use azalea_chat::Component; +#[cfg(feature = "azalea-buf")] +use std::io::Write; pub use suggestions::*; +/// A suggestion given to the user for what they might want to type next. +/// +/// The `M` generic is the type of the tooltip, so for example a `String` or +/// just `()` if you don't care about it. #[derive(Debug, Clone, Hash, Eq, PartialEq)] -pub struct Suggestion { - pub range: StringRange, +pub struct Suggestion { pub text: String, - pub tooltip: Option, + pub range: StringRange, + pub tooltip: Option, } -impl Suggestion { +impl Suggestion { pub fn apply(&self, input: &str) -> String { if self.range.start() == 0 && self.range.end() == input.len() { return input.to_string(); @@ -27,7 +37,7 @@ impl Suggestion { result } - pub fn expand(&self, command: &str, range: &StringRange) -> Suggestion { + pub fn expand(&self, command: &str, range: &StringRange) -> Suggestion { if range == &self.range { return self.clone(); } @@ -46,3 +56,12 @@ impl Suggestion { } } } + +#[cfg(feature = "azalea-buf")] +impl McBufWritable for Suggestion { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + self.text.write_into(buf)?; + self.tooltip.write_into(buf)?; + Ok(()) + } +} diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs index 1fe361f1..06ef9661 100755 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ b/azalea-brigadier/src/suggestion/suggestions.rs @@ -1,15 +1,23 @@ use super::Suggestion; use crate::context::StringRange; -use std::collections::HashSet; +#[cfg(feature = "azalea-buf")] +use azalea_buf::{ + BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable, +}; +#[cfg(feature = "azalea-buf")] +use azalea_chat::Component; +#[cfg(feature = "azalea-buf")] +use std::io::{Cursor, Write}; +use std::{collections::HashSet, hash::Hash}; -#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] -pub struct Suggestions { +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct Suggestions { pub range: StringRange, - pub suggestions: Vec, + pub suggestions: Vec>, } -impl Suggestions { - pub fn merge(command: &str, input: &[Suggestions]) -> Self { +impl Suggestions { + pub fn merge(command: &str, input: &[Suggestions]) -> Self { if input.is_empty() { return Suggestions::default(); } else if input.len() == 1 { @@ -24,7 +32,7 @@ impl Suggestions { Suggestions::create(command, &texts) } - pub fn create(command: &str, suggestions: &HashSet) -> Self { + pub fn create(command: &str, suggestions: &HashSet>) -> Self { if suggestions.is_empty() { return Suggestions::default(); }; @@ -39,7 +47,7 @@ impl Suggestions { for suggestion in suggestions { texts.insert(suggestion.expand(command, &range)); } - let mut sorted: Vec = texts.into_iter().collect(); + let mut sorted = texts.into_iter().collect::>(); sorted.sort_by(|a, b| a.text.cmp(&b.text)); Suggestions { range, @@ -47,3 +55,53 @@ impl Suggestions { } } } + +// this can't be derived because that'd require the generic to have `Default` +// too even if it's not actually necessary +impl Default for Suggestions { + fn default() -> Self { + Self { + range: StringRange::default(), + suggestions: Vec::new(), + } + } +} + +#[cfg(feature = "azalea-buf")] +impl McBufReadable for Suggestions { + fn read_from(buf: &mut Cursor<&[u8]>) -> Result { + #[derive(McBuf)] + struct StandaloneSuggestion { + pub text: String, + pub tooltip: Option, + } + + let start = u32::var_read_from(buf)? as usize; + let length = u32::var_read_from(buf)? as usize; + let range = StringRange::between(start, start + length); + + // the range of a Suggestion depends on the Suggestions containing it, + // so we can't just `impl McBufReadable for Suggestion` + let mut suggestions = Vec::::read_from(buf)? + .into_iter() + .map(|s| Suggestion { + text: s.text, + tooltip: s.tooltip, + range: range.clone(), + }) + .collect::>(); + suggestions.sort_by(|a, b| a.text.cmp(&b.text)); + + Ok(Suggestions { range, suggestions }) + } +} + +#[cfg(feature = "azalea-buf")] +impl McBufWritable for Suggestions { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + (self.range.start() as u32).var_write_into(buf)?; + (self.range.length() as u32).var_write_into(buf)?; + self.suggestions.write_into(buf)?; + Ok(()) + } +} diff --git a/azalea-core/src/slot.rs b/azalea-core/src/slot.rs index f1cd4f0b..4406e08d 100755 --- a/azalea-core/src/slot.rs +++ b/azalea-core/src/slot.rs @@ -1,6 +1,7 @@ // TODO: have an azalea-inventory or azalea-container crate and put this there use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable}; +use azalea_nbt::Tag; use std::io::{Cursor, Write}; #[derive(Debug, Clone, Default)] @@ -13,29 +14,27 @@ pub enum Slot { #[derive(Debug, Clone, McBuf)] pub struct SlotData { #[var] - pub id: i32, + pub id: u32, pub count: u8, - pub nbt: azalea_nbt::Tag, + pub nbt: Tag, } impl McBufReadable for Slot { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let present = bool::read_from(buf)?; - if !present { - return Ok(Slot::Empty); - } - let slot = SlotData::read_from(buf)?; - Ok(Slot::Present(slot)) + let slot = Option::::read_from(buf)?; + Ok(slot.map_or(Slot::Empty, Slot::Present)) } } impl McBufWritable for Slot { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { match self { - Slot::Empty => 0u8.write_into(buf)?, - Slot::Present(i) => i.write_into(buf)?, - } - + Slot::Empty => false.write_into(buf)?, + Slot::Present(i) => { + true.write_into(buf)?; + i.write_into(buf)?; + } + }; Ok(()) } } diff --git a/azalea-nbt/Cargo.toml b/azalea-nbt/Cargo.toml index 11cad33d..1ff52cdd 100755 --- a/azalea-nbt/Cargo.toml +++ b/azalea-nbt/Cargo.toml @@ -9,13 +9,14 @@ repository = "https://github.com/mat-1/azalea/tree/main/azalea-nbt" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ahash = "^0.8.0" +ahash = { version = "^0.8.0", features = ["serde"]} azalea-buf = {path = "../azalea-buf", version = "^0.4.0" } byteorder = "^1.4.3" flate2 = "^1.0.23" log = "0.4.17" num-derive = "^0.3.3" num-traits = "^0.2.14" +serde = {version = "^1.0.148", features = ["derive"]} [dev-dependencies] criterion = {version = "^0.3.5", features = ["html_reports"]} diff --git a/azalea-nbt/README.md b/azalea-nbt/README.md index 4d5cecc4..19498cf3 100755 --- a/azalea-nbt/README.md +++ b/azalea-nbt/README.md @@ -1,5 +1,3 @@ # Azalea NBT A fast NBT serializer and deserializer. - -TODO: serde support for fast registry_holder parsing in azalea-client diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index 49c31192..87464fd9 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -193,6 +193,10 @@ impl Tag { write_compound(writer, value, false)?; Ok(()) } + Tag::End => { + 0u8.write_into(writer)?; + Ok(()) + } _ => Err(Error::InvalidTag), } } diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs index de57b0a2..2bebe156 100755 --- a/azalea-nbt/src/tag.rs +++ b/azalea-nbt/src/tag.rs @@ -1,6 +1,8 @@ use ahash::AHashMap; +use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] pub enum Tag { End, // 0 Byte(i8), // 1 diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index f9cf7d86..32ec5b90 100755 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -11,17 +11,17 @@ version = "0.4.0" [dependencies] async-compression = {version = "^0.3.8", features = ["tokio", "zlib"], optional = true} async-recursion = "1.0.0" -azalea-auth = {path = "../azalea-auth", version = "^0.4.0" } -azalea-block = {path = "../azalea-block", default-features = false, version = "^0.4.0" } -azalea-brigadier = {path = "../azalea-brigadier", version = "^0.4.0" } -azalea-buf = {path = "../azalea-buf", version = "^0.4.0" } -azalea-chat = {path = "../azalea-chat", version = "^0.4.0" } -azalea-core = {path = "../azalea-core", optional = true, version = "^0.4.0" } -azalea-crypto = {path = "../azalea-crypto", version = "^0.4.0" } -azalea-nbt = {path = "../azalea-nbt", version = "^0.4.0" } -azalea-protocol-macros = {path = "./azalea-protocol-macros", version = "^0.4.0" } -azalea-registry = {path = "../azalea-registry", version = "^0.4.0" } -azalea-world = {path = "../azalea-world", version = "^0.4.0" } +azalea-auth = {path = "../azalea-auth", version = "^0.4.0"} +azalea-block = {path = "../azalea-block", default-features = false, version = "^0.4.0"} +azalea-brigadier = {path = "../azalea-brigadier", version = "^0.4.0", features = ["azalea-buf"]} +azalea-buf = {path = "../azalea-buf", version = "^0.4.0"} +azalea-chat = {path = "../azalea-chat", version = "^0.4.0"} +azalea-core = {path = "../azalea-core", optional = true, version = "^0.4.0"} +azalea-crypto = {path = "../azalea-crypto", version = "^0.4.0"} +azalea-nbt = {path = "../azalea-nbt", version = "^0.4.0"} +azalea-protocol-macros = {path = "./azalea-protocol-macros", version = "^0.4.0"} +azalea-registry = {path = "../azalea-registry", version = "^0.4.0"} +azalea-world = {path = "../azalea-world", version = "^0.4.0"} byteorder = "^1.4.3" bytes = "^1.1.0" flate2 = "1.0.23" 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(()) } }