1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

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 <unconfigured@null.spigotmc.org>
Co-authored-by: mat <github@matdoes.dev>
This commit is contained in:
EightFactorial 2022-12-06 18:48:48 -08:00 committed by GitHub
parent e99a822995
commit 9f5e5c092b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 563 additions and 96 deletions

6
Cargo.lock generated
View file

@ -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]]

View file

@ -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"]

View file

@ -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())
}
}

View file

@ -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<String>,
cursor: Option<usize>,
}
@ -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
}

View file

@ -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;

View file

@ -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<String> for Message {
fn from(s: String) -> Self {
Self(s)
}
}

View file

@ -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<M = String> {
pub text: String,
pub tooltip: Option<Message>,
pub range: StringRange,
pub tooltip: Option<M>,
}
impl Suggestion {
impl<M: Clone> Suggestion<M> {
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<M> {
if range == &self.range {
return self.clone();
}
@ -46,3 +56,12 @@ impl Suggestion {
}
}
}
#[cfg(feature = "azalea-buf")]
impl McBufWritable for Suggestion<Component> {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
self.text.write_into(buf)?;
self.tooltip.write_into(buf)?;
Ok(())
}
}

View file

@ -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<M = String> {
pub range: StringRange,
pub suggestions: Vec<Suggestion>,
pub suggestions: Vec<Suggestion<M>>,
}
impl Suggestions {
pub fn merge(command: &str, input: &[Suggestions]) -> Self {
impl<M: Clone + Eq + Hash> Suggestions<M> {
pub fn merge(command: &str, input: &[Suggestions<M>]) -> 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<Suggestion>) -> Self {
pub fn create(command: &str, suggestions: &HashSet<Suggestion<M>>) -> 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<Suggestion> = texts.into_iter().collect();
let mut sorted = texts.into_iter().collect::<Vec<_>>();
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<M> Default for Suggestions<M> {
fn default() -> Self {
Self {
range: StringRange::default(),
suggestions: Vec::new(),
}
}
}
#[cfg(feature = "azalea-buf")]
impl McBufReadable for Suggestions<Component> {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
#[derive(McBuf)]
struct StandaloneSuggestion {
pub text: String,
pub tooltip: Option<Component>,
}
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::<StandaloneSuggestion>::read_from(buf)?
.into_iter()
.map(|s| Suggestion {
text: s.text,
tooltip: s.tooltip,
range: range.clone(),
})
.collect::<Vec<_>>();
suggestions.sort_by(|a, b| a.text.cmp(&b.text));
Ok(Suggestions { range, suggestions })
}
}
#[cfg(feature = "azalea-buf")]
impl McBufWritable for Suggestions<Component> {
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(())
}
}

View file

@ -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<Self, BufReadError> {
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::<SlotData>::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(())
}
}

View file

@ -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"]}

View file

@ -1,5 +1,3 @@
# Azalea NBT
A fast NBT serializer and deserializer.
TODO: serde support for fast registry_holder parsing in azalea-client

View file

@ -193,6 +193,10 @@ impl Tag {
write_compound(writer, value, false)?;
Ok(())
}
Tag::End => {
0u8.write_into(writer)?;
Ok(())
}
_ => Err(Error::InvalidTag),
}
}

View file

@ -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

View file

@ -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"

View file

@ -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<Component>,
}
impl McBufReadable for ClientboundCommandSuggestionsPacket {
fn read_from(_buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
// 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);
}
}

View file

@ -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<T> {
min: Option<T>,
max: Option<T>,
pub min: Option<T>,
pub max: Option<T>,
}
impl<T> BrigadierNumber<T> {
pub fn new(min: Option<T>, max: Option<T>) -> BrigadierNumber<T> {
BrigadierNumber { min, max }
}
}
impl<T: McBufReadable> McBufReadable for BrigadierNumber<T> {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
@ -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<Self, BufReadError> {
@ -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(())
}
}

View file

@ -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(())
}
}