mirror of
https://github.com/italicsjenga/valence.git
synced 2025-01-11 07:11:30 +11:00
Commands and recipe book packets (#183)
Implement the commands and recipe book packets. Also reorganizes some modules in valence_protocol.
This commit is contained in:
parent
8d9c0a7553
commit
7574fa33c5
|
@ -13,6 +13,7 @@ use rayon::iter::ParallelIterator;
|
||||||
use tokio::sync::OwnedSemaphorePermit;
|
use tokio::sync::OwnedSemaphorePermit;
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use valence_protocol::packets::s2c::particle::{Particle, ParticleS2c};
|
||||||
use valence_protocol::packets::s2c::play::{
|
use valence_protocol::packets::s2c::play::{
|
||||||
AcknowledgeBlockChange, ClearTitles, CloseContainerS2c, CombatDeath, DisconnectPlay,
|
AcknowledgeBlockChange, ClearTitles, CloseContainerS2c, CombatDeath, DisconnectPlay,
|
||||||
EntityAnimationS2c, EntityEvent, GameEvent, KeepAliveS2c, LoginPlayOwned, OpenScreen,
|
EntityAnimationS2c, EntityEvent, GameEvent, KeepAliveS2c, LoginPlayOwned, OpenScreen,
|
||||||
|
@ -22,7 +23,6 @@ use valence_protocol::packets::s2c::play::{
|
||||||
SetSubtitleText, SetTitleAnimationTimes, SetTitleText, SynchronizePlayerPosition,
|
SetSubtitleText, SetTitleAnimationTimes, SetTitleText, SynchronizePlayerPosition,
|
||||||
SystemChatMessage, UnloadChunk, UpdateAttributes, UpdateTime,
|
SystemChatMessage, UnloadChunk, UpdateAttributes, UpdateTime,
|
||||||
};
|
};
|
||||||
use valence_protocol::particle::{Particle, ParticleS2c};
|
|
||||||
use valence_protocol::types::{
|
use valence_protocol::types::{
|
||||||
AttributeProperty, DisplayedSkinParts, GameEventKind, GameMode, SyncPlayerPosLookFlags,
|
AttributeProperty, DisplayedSkinParts, GameEventKind, GameMode, SyncPlayerPosLookFlags,
|
||||||
};
|
};
|
||||||
|
|
|
@ -135,8 +135,8 @@ pub mod prelude {
|
||||||
pub use valence_protocol::block::{PropName, PropValue};
|
pub use valence_protocol::block::{PropName, PropValue};
|
||||||
pub use valence_protocol::entity_meta::Pose;
|
pub use valence_protocol::entity_meta::Pose;
|
||||||
pub use valence_protocol::ident::IdentError;
|
pub use valence_protocol::ident::IdentError;
|
||||||
|
pub use valence_protocol::packets::s2c::particle::Particle;
|
||||||
pub use valence_protocol::packets::s2c::play::SetTitleAnimationTimes;
|
pub use valence_protocol::packets::s2c::play::SetTitleAnimationTimes;
|
||||||
pub use valence_protocol::particle::Particle;
|
|
||||||
pub use valence_protocol::text::Color;
|
pub use valence_protocol::text::Color;
|
||||||
pub use valence_protocol::types::{GameMode, Hand, SoundCategory};
|
pub use valence_protocol::types::{GameMode, Hand, SoundCategory};
|
||||||
pub use valence_protocol::{
|
pub use valence_protocol::{
|
||||||
|
|
|
@ -6,7 +6,9 @@ use std::ops::{Deref, DerefMut, Index, IndexMut};
|
||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use valence_protocol::packets::s2c::play::{PlayerInfoRemove, SetTabListHeaderAndFooter};
|
use valence_protocol::packets::s2c::play::{PlayerInfoRemove, SetTabListHeaderAndFooter};
|
||||||
use valence_protocol::player_list::{Actions, Entry as PacketEntry, PlayerInfoUpdate};
|
use valence_protocol::packets::s2c::player_info_update::{
|
||||||
|
Actions, Entry as PacketEntry, PlayerInfoUpdate,
|
||||||
|
};
|
||||||
use valence_protocol::types::{GameMode, SignedProperty};
|
use valence_protocol::types::{GameMode, SignedProperty};
|
||||||
use valence_protocol::Text;
|
use valence_protocol::Text;
|
||||||
|
|
||||||
|
|
|
@ -109,10 +109,7 @@ mod impls;
|
||||||
mod inventory;
|
mod inventory;
|
||||||
mod item;
|
mod item;
|
||||||
pub mod packets;
|
pub mod packets;
|
||||||
pub mod particle;
|
|
||||||
pub mod player_list;
|
|
||||||
mod raw_bytes;
|
mod raw_bytes;
|
||||||
pub mod recipe;
|
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod translation_key;
|
pub mod translation_key;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
@ -7,7 +7,6 @@ use crate::byte_angle::ByteAngle;
|
||||||
use crate::ident::Ident;
|
use crate::ident::Ident;
|
||||||
use crate::item::ItemStack;
|
use crate::item::ItemStack;
|
||||||
use crate::raw_bytes::RawBytes;
|
use crate::raw_bytes::RawBytes;
|
||||||
use crate::recipe::DeclaredRecipe;
|
|
||||||
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,
|
||||||
|
@ -19,6 +18,12 @@ use crate::var_int::VarInt;
|
||||||
use crate::var_long::VarLong;
|
use crate::var_long::VarLong;
|
||||||
use crate::LengthPrefixedArray;
|
use crate::LengthPrefixedArray;
|
||||||
|
|
||||||
|
pub mod commands;
|
||||||
|
pub mod declare_recipes;
|
||||||
|
pub mod particle;
|
||||||
|
pub mod player_info_update;
|
||||||
|
pub mod update_recipe_book;
|
||||||
|
|
||||||
pub mod status {
|
pub mod status {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -95,9 +100,13 @@ pub mod login {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod play {
|
pub mod play {
|
||||||
|
use commands::Node;
|
||||||
|
pub use particle::ParticleS2c;
|
||||||
|
pub use player_info_update::PlayerInfoUpdate;
|
||||||
|
pub use update_recipe_book::UpdateRecipeBook;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
pub use crate::particle::ParticleS2c;
|
use crate::packets::s2c::declare_recipes::DeclaredRecipe;
|
||||||
pub use crate::player_list::PlayerInfoUpdate;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
#[packet_id = 0x00]
|
#[packet_id = 0x00]
|
||||||
|
@ -189,6 +198,13 @@ pub mod play {
|
||||||
pub reset: bool,
|
pub reset: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
|
#[packet_id = 0x0e]
|
||||||
|
pub struct Commands<'a> {
|
||||||
|
pub commands: Vec<Node<'a>>,
|
||||||
|
pub root_index: VarInt,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
#[packet_id = 0x0f]
|
#[packet_id = 0x0f]
|
||||||
pub struct CloseContainerS2c {
|
pub struct CloseContainerS2c {
|
||||||
|
@ -527,6 +543,14 @@ pub mod play {
|
||||||
pub blocks: &'a [VarLong],
|
pub blocks: &'a [VarLong],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
|
#[packet_id = 0x41]
|
||||||
|
pub struct ServerData<'a> {
|
||||||
|
pub motd: Option<Text>,
|
||||||
|
pub icon: Option<&'a str>,
|
||||||
|
pub enforce_secure_chat: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
#[derive(Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||||
#[packet_id = 0x42]
|
#[packet_id = 0x42]
|
||||||
pub struct SetActionBarText(pub Text);
|
pub struct SetActionBarText(pub Text);
|
||||||
|
@ -698,6 +722,7 @@ pub mod play {
|
||||||
BossBar,
|
BossBar,
|
||||||
SetDifficulty,
|
SetDifficulty,
|
||||||
ClearTitles,
|
ClearTitles,
|
||||||
|
Commands<'a>,
|
||||||
CloseContainerS2c,
|
CloseContainerS2c,
|
||||||
SetContainerContent,
|
SetContainerContent,
|
||||||
SetContainerProperty,
|
SetContainerProperty,
|
||||||
|
@ -723,11 +748,13 @@ pub mod play {
|
||||||
PlayerInfoRemove,
|
PlayerInfoRemove,
|
||||||
PlayerInfoUpdate<'a>,
|
PlayerInfoUpdate<'a>,
|
||||||
SynchronizePlayerPosition,
|
SynchronizePlayerPosition,
|
||||||
|
UpdateRecipeBook<'a>,
|
||||||
RemoveEntities,
|
RemoveEntities,
|
||||||
ResourcePackS2c<'a>,
|
ResourcePackS2c<'a>,
|
||||||
Respawn<'a>,
|
Respawn<'a>,
|
||||||
SetHeadRotation,
|
SetHeadRotation,
|
||||||
UpdateSectionBlocks,
|
UpdateSectionBlocks,
|
||||||
|
ServerData<'a>,
|
||||||
SetActionBarText,
|
SetActionBarText,
|
||||||
SetHeldItemS2c,
|
SetHeldItemS2c,
|
||||||
SetCenterChunk,
|
SetCenterChunk,
|
||||||
|
|
432
crates/valence_protocol/src/packets/s2c/commands.rs
Normal file
432
crates/valence_protocol/src/packets/s2c/commands.rs
Normal file
|
@ -0,0 +1,432 @@
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
use byteorder::WriteBytesExt;
|
||||||
|
|
||||||
|
use crate::{Decode, Encode, Ident, VarInt};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Node<'a> {
|
||||||
|
pub children: Vec<VarInt>,
|
||||||
|
pub data: NodeData<'a>,
|
||||||
|
pub executable: bool,
|
||||||
|
pub redirect_node: Option<VarInt>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum NodeData<'a> {
|
||||||
|
Root,
|
||||||
|
Literal {
|
||||||
|
name: &'a str,
|
||||||
|
},
|
||||||
|
Argument {
|
||||||
|
name: &'a str,
|
||||||
|
parser: Parser<'a>,
|
||||||
|
suggestion: Option<Suggestion>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub enum Suggestion {
|
||||||
|
AskServer,
|
||||||
|
AllRecipes,
|
||||||
|
AvailableSounds,
|
||||||
|
AvailableBiomes,
|
||||||
|
SummonableEntities,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Parser<'a> {
|
||||||
|
Bool,
|
||||||
|
Float { min: Option<f32>, max: Option<f32> },
|
||||||
|
Double { min: Option<f64>, max: Option<f64> },
|
||||||
|
Integer { min: Option<i32>, max: Option<i32> },
|
||||||
|
Long { min: Option<i64>, max: Option<i64> },
|
||||||
|
String(StringArg),
|
||||||
|
Entity { single: bool, only_players: bool },
|
||||||
|
GameProfile,
|
||||||
|
BlockPos,
|
||||||
|
ColumnPos,
|
||||||
|
Vec3,
|
||||||
|
Vec2,
|
||||||
|
BlockState,
|
||||||
|
BlockPredicate,
|
||||||
|
ItemStack,
|
||||||
|
ItemPredicate,
|
||||||
|
Color,
|
||||||
|
Component,
|
||||||
|
Message,
|
||||||
|
NbtCompoundTag,
|
||||||
|
NbtTag,
|
||||||
|
NbtPath,
|
||||||
|
Objective,
|
||||||
|
ObjectiveCriteria,
|
||||||
|
Operation,
|
||||||
|
Particle,
|
||||||
|
Angle,
|
||||||
|
Rotation,
|
||||||
|
ScoreboardSlot,
|
||||||
|
ScoreHolder { allow_multiple: bool },
|
||||||
|
Swizzle,
|
||||||
|
Team,
|
||||||
|
ItemSlot,
|
||||||
|
ResourceLocation,
|
||||||
|
Function,
|
||||||
|
EntityAnchor,
|
||||||
|
IntRange,
|
||||||
|
FloatRange,
|
||||||
|
Dimension,
|
||||||
|
GameMode,
|
||||||
|
Time,
|
||||||
|
ResourceOrTag { registry: Ident<&'a str> },
|
||||||
|
ResourceOrTagKey { registry: Ident<&'a str> },
|
||||||
|
Resource { registry: Ident<&'a str> },
|
||||||
|
ResourceKey { registry: Ident<&'a str> },
|
||||||
|
TemplateMirror,
|
||||||
|
TemplateRotation,
|
||||||
|
Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
|
pub enum StringArg {
|
||||||
|
SingleWord,
|
||||||
|
QuotablePhrase,
|
||||||
|
GreedyPhrase,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for Node<'_> {
|
||||||
|
fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
|
||||||
|
let node_type = match &self.data {
|
||||||
|
NodeData::Root => 0,
|
||||||
|
NodeData::Literal { .. } => 1,
|
||||||
|
NodeData::Argument { .. } => 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
let has_suggestion = matches!(
|
||||||
|
&self.data,
|
||||||
|
NodeData::Argument {
|
||||||
|
suggestion: Some(_),
|
||||||
|
..
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let flags: u8 = node_type
|
||||||
|
| (self.executable as u8 * 0x04)
|
||||||
|
| (self.redirect_node.is_some() as u8 * 0x08)
|
||||||
|
| (has_suggestion as u8 * 0x10);
|
||||||
|
|
||||||
|
w.write_u8(flags)?;
|
||||||
|
|
||||||
|
self.children.encode(&mut w)?;
|
||||||
|
|
||||||
|
if let Some(redirect_node) = self.redirect_node {
|
||||||
|
redirect_node.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &self.data {
|
||||||
|
NodeData::Root => {}
|
||||||
|
NodeData::Literal { name } => {
|
||||||
|
name.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
NodeData::Argument {
|
||||||
|
name,
|
||||||
|
parser,
|
||||||
|
suggestion,
|
||||||
|
} => {
|
||||||
|
name.encode(&mut w)?;
|
||||||
|
parser.encode(&mut w)?;
|
||||||
|
|
||||||
|
if let Some(suggestion) = suggestion {
|
||||||
|
match suggestion {
|
||||||
|
Suggestion::AskServer => "ask_server",
|
||||||
|
Suggestion::AllRecipes => "all_recipes",
|
||||||
|
Suggestion::AvailableSounds => "available_sounds",
|
||||||
|
Suggestion::AvailableBiomes => "available_biomes",
|
||||||
|
Suggestion::SummonableEntities => "summonable_entities",
|
||||||
|
}
|
||||||
|
.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Decode<'a> for Node<'a> {
|
||||||
|
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
|
||||||
|
let flags = u8::decode(r)?;
|
||||||
|
|
||||||
|
let children = Vec::decode(r)?;
|
||||||
|
|
||||||
|
let redirect_node = if flags & 0x08 != 0 {
|
||||||
|
Some(VarInt::decode(r)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let node_data = match flags & 0x3 {
|
||||||
|
0 => NodeData::Root,
|
||||||
|
1 => NodeData::Literal {
|
||||||
|
name: <&str>::decode(r)?,
|
||||||
|
},
|
||||||
|
2 => NodeData::Argument {
|
||||||
|
name: <&str>::decode(r)?,
|
||||||
|
parser: Parser::decode(r)?,
|
||||||
|
suggestion: if flags & 0x10 != 0 {
|
||||||
|
Some(match Ident::<&str>::decode(r)?.path() {
|
||||||
|
"ask_server" => Suggestion::AskServer,
|
||||||
|
"all_recipes" => Suggestion::AllRecipes,
|
||||||
|
"available_sounds" => Suggestion::AvailableSounds,
|
||||||
|
"available_biomes" => Suggestion::AvailableBiomes,
|
||||||
|
"summonable_entities" => Suggestion::SummonableEntities,
|
||||||
|
other => bail!("unknown command suggestion type of \"{other}\""),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
},
|
||||||
|
n => bail!("invalid node type of {n}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
children,
|
||||||
|
data: node_data,
|
||||||
|
executable: flags & 0x04 != 0,
|
||||||
|
redirect_node,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for Parser<'_> {
|
||||||
|
fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
|
||||||
|
match self {
|
||||||
|
Parser::Bool => 0u8.encode(&mut w)?,
|
||||||
|
Parser::Float { min, max } => {
|
||||||
|
1u8.encode(&mut w)?;
|
||||||
|
|
||||||
|
(min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?;
|
||||||
|
|
||||||
|
if let Some(min) = min {
|
||||||
|
min.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(max) = max {
|
||||||
|
max.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Parser::Double { min, max } => {
|
||||||
|
2u8.encode(&mut w)?;
|
||||||
|
|
||||||
|
(min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?;
|
||||||
|
|
||||||
|
if let Some(min) = min {
|
||||||
|
min.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(max) = max {
|
||||||
|
max.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Parser::Integer { min, max } => {
|
||||||
|
3u8.encode(&mut w)?;
|
||||||
|
|
||||||
|
(min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?;
|
||||||
|
|
||||||
|
if let Some(min) = min {
|
||||||
|
min.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(max) = max {
|
||||||
|
max.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Parser::Long { min, max } => {
|
||||||
|
4u8.encode(&mut w)?;
|
||||||
|
|
||||||
|
(min.is_some() as u8 | (max.is_some() as u8 * 0x2)).encode(&mut w)?;
|
||||||
|
|
||||||
|
if let Some(min) = min {
|
||||||
|
min.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(max) = max {
|
||||||
|
max.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Parser::String(arg) => {
|
||||||
|
5u8.encode(&mut w)?;
|
||||||
|
arg.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
Parser::Entity {
|
||||||
|
single,
|
||||||
|
only_players,
|
||||||
|
} => {
|
||||||
|
6u8.encode(&mut w)?;
|
||||||
|
(*single as u8 | (*only_players as u8 * 0x2)).encode(&mut w)?;
|
||||||
|
}
|
||||||
|
Parser::GameProfile => 7u8.encode(&mut w)?,
|
||||||
|
Parser::BlockPos => 8u8.encode(&mut w)?,
|
||||||
|
Parser::ColumnPos => 9u8.encode(&mut w)?,
|
||||||
|
Parser::Vec3 => 10u8.encode(&mut w)?,
|
||||||
|
Parser::Vec2 => 11u8.encode(&mut w)?,
|
||||||
|
Parser::BlockState => 12u8.encode(&mut w)?,
|
||||||
|
Parser::BlockPredicate => 13u8.encode(&mut w)?,
|
||||||
|
Parser::ItemStack => 14u8.encode(&mut w)?,
|
||||||
|
Parser::ItemPredicate => 15u8.encode(&mut w)?,
|
||||||
|
Parser::Color => 16u8.encode(&mut w)?,
|
||||||
|
Parser::Component => 17u8.encode(&mut w)?,
|
||||||
|
Parser::Message => 18u8.encode(&mut w)?,
|
||||||
|
Parser::NbtCompoundTag => 19u8.encode(&mut w)?,
|
||||||
|
Parser::NbtTag => 20u8.encode(&mut w)?,
|
||||||
|
Parser::NbtPath => 21u8.encode(&mut w)?,
|
||||||
|
Parser::Objective => 22u8.encode(&mut w)?,
|
||||||
|
Parser::ObjectiveCriteria => 23u8.encode(&mut w)?,
|
||||||
|
Parser::Operation => 24u8.encode(&mut w)?,
|
||||||
|
Parser::Particle => 25u8.encode(&mut w)?,
|
||||||
|
Parser::Angle => 26u8.encode(&mut w)?,
|
||||||
|
Parser::Rotation => 27u8.encode(&mut w)?,
|
||||||
|
Parser::ScoreboardSlot => 28u8.encode(&mut w)?,
|
||||||
|
Parser::ScoreHolder { allow_multiple } => {
|
||||||
|
29u8.encode(&mut w)?;
|
||||||
|
allow_multiple.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
Parser::Swizzle => 30u8.encode(&mut w)?,
|
||||||
|
Parser::Team => 31u8.encode(&mut w)?,
|
||||||
|
Parser::ItemSlot => 32u8.encode(&mut w)?,
|
||||||
|
Parser::ResourceLocation => 33u8.encode(&mut w)?,
|
||||||
|
Parser::Function => 34u8.encode(&mut w)?,
|
||||||
|
Parser::EntityAnchor => 35u8.encode(&mut w)?,
|
||||||
|
Parser::IntRange => 36u8.encode(&mut w)?,
|
||||||
|
Parser::FloatRange => 37u8.encode(&mut w)?,
|
||||||
|
Parser::Dimension => 38u8.encode(&mut w)?,
|
||||||
|
Parser::GameMode => 39u8.encode(&mut w)?,
|
||||||
|
Parser::Time => 40u8.encode(&mut w)?,
|
||||||
|
Parser::ResourceOrTag { registry } => {
|
||||||
|
41u8.encode(&mut w)?;
|
||||||
|
registry.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
Parser::ResourceOrTagKey { registry } => {
|
||||||
|
42u8.encode(&mut w)?;
|
||||||
|
registry.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
Parser::Resource { registry } => {
|
||||||
|
43u8.encode(&mut w)?;
|
||||||
|
registry.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
Parser::ResourceKey { registry } => {
|
||||||
|
44u8.encode(&mut w)?;
|
||||||
|
registry.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
Parser::TemplateMirror => 45u8.encode(&mut w)?,
|
||||||
|
Parser::TemplateRotation => 46u8.encode(&mut w)?,
|
||||||
|
Parser::Uuid => 47u8.encode(&mut w)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Decode<'a> for Parser<'a> {
|
||||||
|
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
|
||||||
|
fn decode_min_max<'a, T: Decode<'a>>(
|
||||||
|
r: &mut &'a [u8],
|
||||||
|
) -> anyhow::Result<(Option<T>, Option<T>)> {
|
||||||
|
let flags = u8::decode(r)?;
|
||||||
|
|
||||||
|
let min = if flags & 0x1 != 0 {
|
||||||
|
Some(T::decode(r)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let max = if flags & 0x2 != 0 {
|
||||||
|
Some(T::decode(r)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((min, max))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(match u8::decode(r)? {
|
||||||
|
0 => Self::Bool,
|
||||||
|
1 => {
|
||||||
|
let (min, max) = decode_min_max(r)?;
|
||||||
|
Self::Float { min, max }
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
let (min, max) = decode_min_max(r)?;
|
||||||
|
Self::Double { min, max }
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
let (min, max) = decode_min_max(r)?;
|
||||||
|
Self::Integer { min, max }
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
let (min, max) = decode_min_max(r)?;
|
||||||
|
Self::Long { min, max }
|
||||||
|
}
|
||||||
|
5 => Self::String(StringArg::decode(r)?),
|
||||||
|
6 => {
|
||||||
|
let flags = u8::decode(r)?;
|
||||||
|
Self::Entity {
|
||||||
|
single: flags & 0x1 != 0,
|
||||||
|
only_players: flags & 0x2 != 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
7 => Self::GameProfile,
|
||||||
|
8 => Self::BlockPos,
|
||||||
|
9 => Self::ColumnPos,
|
||||||
|
10 => Self::Vec3,
|
||||||
|
11 => Self::Vec2,
|
||||||
|
12 => Self::BlockState,
|
||||||
|
13 => Self::BlockPredicate,
|
||||||
|
14 => Self::ItemStack,
|
||||||
|
15 => Self::ItemPredicate,
|
||||||
|
16 => Self::Color,
|
||||||
|
17 => Self::Component,
|
||||||
|
18 => Self::Message,
|
||||||
|
19 => Self::NbtCompoundTag,
|
||||||
|
20 => Self::NbtTag,
|
||||||
|
21 => Self::NbtPath,
|
||||||
|
22 => Self::Objective,
|
||||||
|
23 => Self::ObjectiveCriteria,
|
||||||
|
24 => Self::Operation,
|
||||||
|
25 => Self::Particle,
|
||||||
|
26 => Self::Angle,
|
||||||
|
27 => Self::Rotation,
|
||||||
|
28 => Self::ScoreboardSlot,
|
||||||
|
29 => Self::ScoreHolder {
|
||||||
|
allow_multiple: bool::decode(r)?,
|
||||||
|
},
|
||||||
|
30 => Self::Swizzle,
|
||||||
|
31 => Self::Team,
|
||||||
|
32 => Self::ItemSlot,
|
||||||
|
33 => Self::ResourceLocation,
|
||||||
|
34 => Self::Function,
|
||||||
|
35 => Self::EntityAnchor,
|
||||||
|
36 => Self::IntRange,
|
||||||
|
37 => Self::FloatRange,
|
||||||
|
38 => Self::Dimension,
|
||||||
|
39 => Self::GameMode,
|
||||||
|
40 => Self::Time,
|
||||||
|
41 => Self::ResourceOrTag {
|
||||||
|
registry: Ident::decode(r)?,
|
||||||
|
},
|
||||||
|
42 => Self::ResourceOrTagKey {
|
||||||
|
registry: Ident::decode(r)?,
|
||||||
|
},
|
||||||
|
43 => Self::Resource {
|
||||||
|
registry: Ident::decode(r)?,
|
||||||
|
},
|
||||||
|
44 => Self::ResourceKey {
|
||||||
|
registry: Ident::decode(r)?,
|
||||||
|
},
|
||||||
|
45 => Self::TemplateMirror,
|
||||||
|
46 => Self::TemplateRotation,
|
||||||
|
47 => Self::Uuid,
|
||||||
|
n => bail!("unknown command parser ID of {n}"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
|
||||||
|
use crate::{Decode, DecodePacket, Encode, EncodePacket, Ident, VarInt};
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, EncodePacket, DecodePacket)]
|
||||||
|
#[packet_id = 0x39]
|
||||||
|
pub struct UpdateRecipeBook<'a> {
|
||||||
|
pub action: UpdateRecipeBookAction<'a>,
|
||||||
|
pub crafting_recipe_book_open: bool,
|
||||||
|
pub crafting_recipe_book_filter_active: bool,
|
||||||
|
pub smelting_recipe_book_open: bool,
|
||||||
|
pub smelting_recipe_book_filter_active: bool,
|
||||||
|
pub blast_furnace_recipe_book_open: bool,
|
||||||
|
pub blast_furnace_recipe_book_filter_active: bool,
|
||||||
|
pub smoker_recipe_book_open: bool,
|
||||||
|
pub smoker_recipe_book_filter_active: bool,
|
||||||
|
pub recipe_ids: Vec<Ident<&'a str>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub enum UpdateRecipeBookAction<'a> {
|
||||||
|
Init { recipe_ids: Vec<Ident<&'a str>> },
|
||||||
|
Add,
|
||||||
|
Remove,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for UpdateRecipeBook<'_> {
|
||||||
|
fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
|
||||||
|
VarInt(match &self.action {
|
||||||
|
UpdateRecipeBookAction::Init { .. } => 0,
|
||||||
|
UpdateRecipeBookAction::Add => 1,
|
||||||
|
UpdateRecipeBookAction::Remove => 2,
|
||||||
|
})
|
||||||
|
.encode(&mut w)?;
|
||||||
|
|
||||||
|
self.crafting_recipe_book_open.encode(&mut w)?;
|
||||||
|
self.crafting_recipe_book_filter_active.encode(&mut w)?;
|
||||||
|
self.smelting_recipe_book_open.encode(&mut w)?;
|
||||||
|
self.smelting_recipe_book_filter_active.encode(&mut w)?;
|
||||||
|
self.blast_furnace_recipe_book_open.encode(&mut w)?;
|
||||||
|
self.blast_furnace_recipe_book_filter_active
|
||||||
|
.encode(&mut w)?;
|
||||||
|
self.smoker_recipe_book_open.encode(&mut w)?;
|
||||||
|
self.smoker_recipe_book_filter_active.encode(&mut w)?;
|
||||||
|
self.recipe_ids.encode(&mut w)?;
|
||||||
|
if let UpdateRecipeBookAction::Init { recipe_ids } = &self.action {
|
||||||
|
recipe_ids.encode(&mut w)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Decode<'a> for UpdateRecipeBook<'a> {
|
||||||
|
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
|
||||||
|
let action_id = VarInt::decode(r)?.0;
|
||||||
|
|
||||||
|
let crafting_recipe_book_open = bool::decode(r)?;
|
||||||
|
let crafting_recipe_book_filter_active = bool::decode(r)?;
|
||||||
|
let smelting_recipe_book_open = bool::decode(r)?;
|
||||||
|
let smelting_recipe_book_filter_active = bool::decode(r)?;
|
||||||
|
let blast_furnace_recipe_book_open = bool::decode(r)?;
|
||||||
|
let blast_furnace_recipe_book_filter_active = bool::decode(r)?;
|
||||||
|
let smoker_recipe_book_open = bool::decode(r)?;
|
||||||
|
let smoker_recipe_book_filter_active = bool::decode(r)?;
|
||||||
|
let recipe_ids = Vec::decode(r)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
action: match action_id {
|
||||||
|
0 => UpdateRecipeBookAction::Init {
|
||||||
|
recipe_ids: Vec::decode(r)?,
|
||||||
|
},
|
||||||
|
1 => UpdateRecipeBookAction::Add,
|
||||||
|
2 => UpdateRecipeBookAction::Remove,
|
||||||
|
n => bail!("unknown recipe book action of {n}"),
|
||||||
|
},
|
||||||
|
crafting_recipe_book_open,
|
||||||
|
crafting_recipe_book_filter_active,
|
||||||
|
smelting_recipe_book_open,
|
||||||
|
smelting_recipe_book_filter_active,
|
||||||
|
blast_furnace_recipe_book_open,
|
||||||
|
blast_furnace_recipe_book_filter_active,
|
||||||
|
smoker_recipe_book_open,
|
||||||
|
smoker_recipe_book_filter_active,
|
||||||
|
recipe_ids,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ use std::borrow::Cow;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::{fmt, ops};
|
use std::{fmt, ops};
|
||||||
|
|
||||||
|
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};
|
||||||
|
|
||||||
|
@ -740,7 +741,11 @@ impl Encode for Text {
|
||||||
impl Decode<'_> for Text {
|
impl Decode<'_> for Text {
|
||||||
fn decode(r: &mut &[u8]) -> Result<Self> {
|
fn decode(r: &mut &[u8]) -> Result<Self> {
|
||||||
let string = <&str>::decode(r)?;
|
let string = <&str>::decode(r)?;
|
||||||
Ok(serde_json::from_str(string)?)
|
if string.is_empty() {
|
||||||
|
Ok(Self::default())
|
||||||
|
} else {
|
||||||
|
serde_json::from_str(string).context("decoding text JSON")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue