Add recipe and tags packets (#182)

Also improve generated `Encode`, `Decode` error messages and fix
unnecessary build script reruns.
This commit is contained in:
Ryan Johnson 2022-12-30 06:18:24 -08:00 committed by GitHub
parent 623e88908d
commit 8d9c0a7553
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 588 additions and 35 deletions

View file

@ -9,7 +9,7 @@ mod entity;
mod entity_event; mod entity_event;
pub fn main() -> anyhow::Result<()> { pub fn main() -> anyhow::Result<()> {
println!("cargo:rerun-if-changed=extracted/"); println!("cargo:rerun-if-changed=../../extracted/");
let generators = [ let generators = [
(entity::build as fn() -> _, "entity.rs"), (entity::build as fn() -> _, "entity.rs"),

View file

@ -24,7 +24,7 @@ use valence_protocol::packets::s2c::play::{
}; };
use valence_protocol::particle::{Particle, ParticleS2c}; use valence_protocol::particle::{Particle, ParticleS2c};
use valence_protocol::types::{ use valence_protocol::types::{
AttributeProperty, DisplayedSkinParts, GameMode, GameStateChangeReason, SyncPlayerPosLookFlags, AttributeProperty, DisplayedSkinParts, GameEventKind, GameMode, SyncPlayerPosLookFlags,
}; };
use valence_protocol::{ use valence_protocol::{
BlockPos, EncodePacket, Ident, ItemStack, RawBytes, Text, Username, VarInt, BlockPos, EncodePacket, Ident, ItemStack, RawBytes, Text, Username, VarInt,
@ -531,7 +531,7 @@ impl<C: Config> Client<C> {
if !self.created_this_tick() { if !self.created_this_tick() {
self.queue_packet(&GameEvent { self.queue_packet(&GameEvent {
reason: GameStateChangeReason::ChangeGameMode, kind: GameEventKind::ChangeGameMode,
value: game_mode as i32 as f32, value: game_mode as i32 as f32,
}); });
} }
@ -541,10 +541,10 @@ impl<C: Config> Client<C> {
/// Sets whether or not the client sees rain. /// Sets whether or not the client sees rain.
pub fn set_raining(&mut self, raining: bool) { pub fn set_raining(&mut self, raining: bool) {
self.queue_packet(&GameEvent { self.queue_packet(&GameEvent {
reason: if raining { kind: if raining {
GameStateChangeReason::BeginRaining GameEventKind::BeginRaining
} else { } else {
GameStateChangeReason::EndRaining GameEventKind::EndRaining
}, },
value: 0.0, value: 0.0,
}); });
@ -556,7 +556,7 @@ impl<C: Config> Client<C> {
/// The rain level is clamped between `0.0.` and `1.0`. /// The rain level is clamped between `0.0.` and `1.0`.
pub fn set_rain_level(&mut self, rain_level: f32) { pub fn set_rain_level(&mut self, rain_level: f32) {
self.queue_packet(&GameEvent { self.queue_packet(&GameEvent {
reason: GameStateChangeReason::RainLevelChange, kind: GameEventKind::RainLevelChange,
value: rain_level.clamp(0.0, 1.0), value: rain_level.clamp(0.0, 1.0),
}); });
} }
@ -571,7 +571,7 @@ impl<C: Config> Client<C> {
/// The thunder level is clamped between `0.0` and `1.0`. /// The thunder level is clamped between `0.0` and `1.0`.
pub fn set_thunder_level(&mut self, thunder_level: f32) { pub fn set_thunder_level(&mut self, thunder_level: f32) {
self.queue_packet(&GameEvent { self.queue_packet(&GameEvent {
reason: GameStateChangeReason::ThunderLevelChange, kind: GameEventKind::ThunderLevelChange,
value: thunder_level.clamp(0.0, 1.0), value: thunder_level.clamp(0.0, 1.0),
}); });
} }
@ -699,7 +699,7 @@ impl<C: Config> Client<C> {
/// Respawns client. Optionally can roll the credits before respawning. /// Respawns client. Optionally can roll the credits before respawning.
pub fn win_game(&mut self, show_credits: bool) { pub fn win_game(&mut self, show_credits: bool) {
self.queue_packet(&GameEvent { self.queue_packet(&GameEvent {
reason: GameStateChangeReason::WinGame, kind: GameEventKind::WinGame,
value: if show_credits { 1.0 } else { 0.0 }, value: if show_credits { 1.0 } else { 0.0 },
}); });
} }
@ -715,7 +715,7 @@ impl<C: Config> Client<C> {
if !self.created_this_tick() { if !self.created_this_tick() {
self.queue_packet(&GameEvent { self.queue_packet(&GameEvent {
reason: GameStateChangeReason::EnableRespawnScreen, kind: GameEventKind::EnableRespawnScreen,
value: if enable { 0.0 } else { 1.0 }, value: if enable { 0.0 } else { 1.0 },
}); });
} }

View file

@ -10,7 +10,7 @@ use crate::{
pub fn derive_decode(item: TokenStream) -> Result<TokenStream> { pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
let mut input = parse2::<DeriveInput>(item)?; let mut input = parse2::<DeriveInput>(item)?;
let name = input.ident; let input_name = input.ident;
if input.generics.lifetimes().count() > 1 { if input.generics.lifetimes().count() > 1 {
return Err(Error::new( return Err(Error::new(
@ -34,7 +34,7 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
Fields::Named(fields) => { Fields::Named(fields) => {
let init = fields.named.iter().map(|f| { let init = fields.named.iter().map(|f| {
let name = f.ident.as_ref().unwrap(); let name = f.ident.as_ref().unwrap();
let ctx = format!("failed to decode field `{name}`"); let ctx = format!("failed to decode field `{name}` in `{input_name}`");
quote! { quote! {
#name: Decode::decode(_r).context(#ctx)?, #name: Decode::decode(_r).context(#ctx)?,
} }
@ -49,7 +49,7 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
Fields::Unnamed(fields) => { Fields::Unnamed(fields) => {
let init = (0..fields.unnamed.len()) let init = (0..fields.unnamed.len())
.map(|i| { .map(|i| {
let ctx = format!("failed to decode field `{i}`"); let ctx = format!("failed to decode field `{i}` in `{input_name}`");
quote! { quote! {
Decode::decode(_r).context(#ctx)?, Decode::decode(_r).context(#ctx)?,
} }
@ -73,7 +73,7 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
Ok(quote! { Ok(quote! {
#[allow(unused_imports)] #[allow(unused_imports)]
impl #impl_generics ::valence_protocol::__private::Decode<#lifetime> for #name #ty_generics impl #impl_generics ::valence_protocol::__private::Decode<#lifetime> for #input_name #ty_generics
#where_clause #where_clause
{ {
fn decode(_r: &mut &#lifetime [u8]) -> ::valence_protocol::__private::Result<Self> { fn decode(_r: &mut &#lifetime [u8]) -> ::valence_protocol::__private::Result<Self> {
@ -100,7 +100,8 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
.map(|f| { .map(|f| {
let field = f.ident.as_ref().unwrap(); let field = f.ident.as_ref().unwrap();
let ctx = format!( let ctx = format!(
"failed to decode field `{field}` in variant `{name}`", "failed to decode field `{field}` in variant `{name}` in \
`{input_name}`",
); );
quote! { quote! {
#field: Decode::decode(_r).context(#ctx)?, #field: Decode::decode(_r).context(#ctx)?,
@ -116,7 +117,8 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
let init = (0..fields.unnamed.len()) let init = (0..fields.unnamed.len())
.map(|i| { .map(|i| {
let ctx = format!( let ctx = format!(
"failed to decode field `{i}` in variant `{name}`", "failed to decode field `{i}` in variant `{name}` in \
`{input_name}`",
); );
quote! { quote! {
Decode::decode(_r).context(#ctx)?, Decode::decode(_r).context(#ctx)?,
@ -143,16 +145,17 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
Ok(quote! { Ok(quote! {
#[allow(unused_imports)] #[allow(unused_imports)]
impl #impl_generics ::valence_protocol::__private::Decode<#lifetime> for #name #ty_generics impl #impl_generics ::valence_protocol::__private::Decode<#lifetime> for #input_name #ty_generics
#where_clause #where_clause
{ {
fn decode(_r: &mut &#lifetime [u8]) -> ::valence_protocol::__private::Result<Self> { fn decode(_r: &mut &#lifetime [u8]) -> ::valence_protocol::__private::Result<Self> {
use ::valence_protocol::__private::{Decode, Context, VarInt, bail}; use ::valence_protocol::__private::{Decode, Context, VarInt, bail};
let disc = VarInt::decode(_r).context("failed to decode enum discriminant")?.0; let ctx = concat!("failed to decode enum discriminant in `", stringify!(#input_name), "`");
let disc = VarInt::decode(_r).context(ctx)?.0;
match disc { match disc {
#decode_arms #decode_arms
n => bail!("unexpected enum discriminant {}", disc), n => bail!("unexpected enum discriminant {} in `{}`", disc, stringify!(#input_name)),
} }
} }
} }

View file

@ -8,7 +8,7 @@ use crate::{add_trait_bounds, find_packet_id_attr, pair_variants_with_discrimina
pub fn derive_encode(item: TokenStream) -> Result<TokenStream> { pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
let mut input = parse2::<DeriveInput>(item)?; let mut input = parse2::<DeriveInput>(item)?;
let name = input.ident; let input_name = input.ident;
add_trait_bounds( add_trait_bounds(
&mut input.generics, &mut input.generics,
@ -25,7 +25,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
.iter() .iter()
.map(|f| { .map(|f| {
let name = &f.ident.as_ref().unwrap(); let name = &f.ident.as_ref().unwrap();
let ctx = format!("failed to encode field `{name}`"); let ctx = format!("failed to encode field `{name}` in `{input_name}`");
quote! { quote! {
self.#name.encode(&mut _w).context(#ctx)?; self.#name.encode(&mut _w).context(#ctx)?;
} }
@ -34,7 +34,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
Fields::Unnamed(fields) => (0..fields.unnamed.len()) Fields::Unnamed(fields) => (0..fields.unnamed.len())
.map(|i| { .map(|i| {
let lit = LitInt::new(&i.to_string(), Span::call_site()); let lit = LitInt::new(&i.to_string(), Span::call_site());
let ctx = format!("failed to encode field `{lit}`"); let ctx = format!("failed to encode field `{lit}` in `{input_name}`");
quote! { quote! {
self.#lit.encode(&mut _w).context(#ctx)?; self.#lit.encode(&mut _w).context(#ctx)?;
} }
@ -45,7 +45,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
Ok(quote! { Ok(quote! {
#[allow(unused_imports)] #[allow(unused_imports)]
impl #impl_generics ::valence_protocol::__private::Encode for #name #ty_generics impl #impl_generics ::valence_protocol::__private::Encode for #input_name #ty_generics
#where_clause #where_clause
{ {
fn encode(&self, mut _w: impl ::std::io::Write) -> ::valence_protocol::__private::Result<()> { fn encode(&self, mut _w: impl ::std::io::Write) -> ::valence_protocol::__private::Result<()> {
@ -67,7 +67,8 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
let variant_name = &variant.ident; let variant_name = &variant.ident;
let disc_ctx = format!( let disc_ctx = format!(
"failed to encode enum discriminant {disc} for variant `{variant_name}`", "failed to encode enum discriminant {disc} for variant `{variant_name}` \
in `{input_name}`",
); );
match &variant.fields { match &variant.fields {
@ -83,7 +84,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
.map(|name| { .map(|name| {
let ctx = format!( let ctx = format!(
"failed to encode field `{name}` in variant \ "failed to encode field `{name}` in variant \
`{variant_name}`", `{variant_name}` in `{input_name}`",
); );
quote! { quote! {
@ -111,7 +112,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
.map(|name| { .map(|name| {
let ctx = format!( let ctx = format!(
"failed to encode field `{name}` in variant \ "failed to encode field `{name}` in variant \
`{variant_name}`" `{variant_name}` in `{input_name}`"
); );
quote! { quote! {
@ -142,7 +143,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
Ok(quote! { Ok(quote! {
#[allow(unused_imports, unreachable_code)] #[allow(unused_imports, unreachable_code)]
impl #impl_generics ::valence_protocol::Encode for #name #ty_generics impl #impl_generics ::valence_protocol::Encode for #input_name #ty_generics
#where_clause #where_clause
{ {
fn encode(&self, mut _w: impl ::std::io::Write) -> ::valence_protocol::__private::Result<()> { fn encode(&self, mut _w: impl ::std::io::Write) -> ::valence_protocol::__private::Result<()> {

View file

@ -15,7 +15,6 @@ struct TopLevel {
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
struct Block { struct Block {
#[allow(unused)]
id: u16, id: u16,
item_id: u16, item_id: u16,
translation_key: String, translation_key: String,
@ -362,6 +361,18 @@ pub fn build() -> anyhow::Result<TokenStream> {
}) })
.collect::<TokenStream>(); .collect::<TokenStream>();
let block_kind_from_raw_arms = blocks
.iter()
.map(|block| {
let name = ident(block.name.to_pascal_case());
let id = block.id;
quote! {
#id => Some(BlockKind::#name),
}
})
.collect::<TokenStream>();
let block_kind_count = blocks.len(); let block_kind_count = blocks.len();
let prop_names = blocks let prop_names = blocks
@ -642,6 +653,23 @@ pub fn build() -> anyhow::Result<TokenStream> {
} }
} }
/// Constructs a block kind from a raw block kind ID.
///
/// If the given ID is invalid, `None` is returned.
pub const fn from_raw(id: u16) -> Option<Self> {
match id {
#block_kind_from_raw_arms
_ => None,
}
}
/// Converts this block kind to its underlying raw block state ID.
///
/// The original block kind can be recovered with [`BlockKind::from_raw`].
pub const fn to_raw(self) -> u16 {
self as u16
}
/// An array of all block kinds. /// An array of all block kinds.
pub const ALL: [Self; #block_kind_count] = [#(Self::#block_kind_variants,)*]; pub const ALL: [Self; #block_kind_count] = [#(Self::#block_kind_variants,)*];
} }

View file

@ -11,7 +11,7 @@ mod item;
mod translation_key; mod translation_key;
pub fn main() -> anyhow::Result<()> { pub fn main() -> anyhow::Result<()> {
println!("cargo:rerun-if-changed=../extracted/"); println!("cargo:rerun-if-changed=../../extracted/");
let generators = [ let generators = [
(block::build as fn() -> _, "block.rs"), (block::build as fn() -> _, "block.rs"),

View file

@ -51,7 +51,7 @@ fn fmt_block_state(bs: BlockState, f: &mut fmt::Formatter) -> fmt::Result {
impl Encode for BlockState { impl Encode for BlockState {
fn encode(&self, w: impl Write) -> Result<()> { fn encode(&self, w: impl Write) -> Result<()> {
VarInt(self.0 as i32).encode(w) VarInt(self.to_raw() as i32).encode(w)
} }
} }
@ -64,6 +64,21 @@ impl Decode<'_> for BlockState {
} }
} }
impl Encode for BlockKind {
fn encode(&self, w: impl Write) -> Result<()> {
VarInt(self.to_raw() as i32).encode(w)
}
}
impl Decode<'_> for BlockKind {
fn decode(r: &mut &[u8]) -> Result<Self> {
let id = VarInt::decode(r)?.0;
let errmsg = "invalid block kind ID";
BlockKind::from_raw(id.try_into().context(errmsg)?).context(errmsg)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)]
pub enum BlockFace { pub enum BlockFace {
/// -Y /// -Y

View file

@ -1,4 +1,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{BTreeSet, HashSet};
use std::hash::{BuildHasher, Hash};
use std::io::Write; use std::io::Write;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::rc::Rc; use std::rc::Rc;
@ -429,6 +431,92 @@ impl<'a, T: Decode<'a>> Decode<'a> for Box<[T]> {
} }
} }
impl<T: Encode, S> Encode for HashSet<T, S> {
fn encode(&self, mut w: impl Write) -> Result<()> {
let len = self.len();
ensure!(
len <= i32::MAX as usize,
"length of hash set ({len}) exceeds i32::MAX"
);
VarInt(len as i32).encode(&mut w)?;
for val in self {
val.encode(&mut w)?;
}
Ok(())
}
}
impl<'a, T, S> Decode<'a> for HashSet<T, S>
where
T: Eq + Hash + Decode<'a>,
S: BuildHasher + Default,
{
fn decode(r: &mut &'a [u8]) -> Result<Self> {
let len = VarInt::decode(r)?.0;
ensure!(len >= 0, "attempt to decode hash set with negative length");
let len = len as usize;
// Don't allocate more memory than what would roughly fit in a single packet in
// case we get a malicious array length.
let cap = (MAX_PACKET_SIZE as usize / mem::size_of::<T>().max(1)).min(len);
let mut set = HashSet::with_capacity_and_hasher(cap, S::default());
for _ in 0..len {
ensure!(
set.insert(T::decode(r)?),
"encountered duplicate item while decoding hash set"
);
}
Ok(set)
}
}
impl<T: Encode> Encode for BTreeSet<T> {
fn encode(&self, mut w: impl Write) -> Result<()> {
let len = self.len();
ensure!(
len <= i32::MAX as usize,
"length of b-tree set ({len}) exceeds i32::MAX"
);
VarInt(len as i32).encode(&mut w)?;
for val in self {
val.encode(&mut w)?;
}
Ok(())
}
}
impl<'a, T: Ord + Decode<'a>> Decode<'a> for BTreeSet<T> {
fn decode(r: &mut &'a [u8]) -> Result<Self> {
let len = VarInt::decode(r)?.0;
ensure!(
len >= 0,
"attempt to decode b-tree set with negative length"
);
let len = len as usize;
let mut set = BTreeSet::new();
for _ in 0..len {
ensure!(
set.insert(T::decode(r)?),
"encountered duplicate item while decoding b-tree set"
);
}
Ok(set)
}
}
// ==== String ==== // // ==== String ==== //
impl Encode for str { impl Encode for str {

View file

@ -112,6 +112,7 @@ pub mod packets;
pub mod particle; pub mod particle;
pub mod player_list; 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;

View file

@ -7,11 +7,12 @@ 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, GameMode, AttributeProperty, BossBarAction, ChunkDataBlockEntity, Difficulty, GameEventKind, GameMode,
GameStateChangeReason, GlobalPos, PlayerAbilitiesFlags, SignedProperty, SoundCategory, GlobalPos, PlayerAbilitiesFlags, SignedProperty, SoundCategory, SyncPlayerPosLookFlags,
SyncPlayerPosLookFlags, TagGroup,
}; };
use crate::username::Username; use crate::username::Username;
use crate::var_int::VarInt; use crate::var_int::VarInt;
@ -276,7 +277,7 @@ pub mod play {
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)] #[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
#[packet_id = 0x1c] #[packet_id = 0x1c]
pub struct GameEvent { pub struct GameEvent {
pub reason: GameStateChangeReason, pub kind: GameEventKind,
pub value: f32, pub value: f32,
} }
@ -673,6 +674,16 @@ pub mod play {
pub features: Vec<Ident<&'a str>>, pub features: Vec<Ident<&'a str>>,
} }
#[derive(Clone, Debug, Encode, Decode, EncodePacket, DecodePacket)]
#[packet_id = 0x69]
pub struct DeclareRecipes<'a> {
pub recipes: Vec<DeclaredRecipe<'a>>,
}
#[derive(Clone, Debug, Encode, Decode, EncodePacket, DecodePacket)]
#[packet_id = 0x6a]
pub struct UpdateTags<'a>(pub Vec<TagGroup<'a>>);
packet_enum! { packet_enum! {
#[derive(Clone)] #[derive(Clone)]
S2cPlayPacket<'a> { S2cPlayPacket<'a> {
@ -737,6 +748,8 @@ pub mod play {
TeleportEntity, TeleportEntity,
UpdateAttributes<'a>, UpdateAttributes<'a>,
FeatureFlags<'a>, FeatureFlags<'a>,
DeclareRecipes<'a>,
UpdateTags<'a>,
} }
} }
} }

View file

@ -0,0 +1,392 @@
use std::io::Write;
use anyhow::{bail, ensure};
use crate::{Decode, Encode, Ident, ItemStack, VarInt};
#[derive(Clone, PartialEq, Debug)]
pub enum DeclaredRecipe<'a> {
CraftingShapeless {
recipe_id: Ident<&'a str>,
group: &'a str,
category: CraftingCategory,
ingredients: Vec<Ingredient>,
result: Option<ItemStack>,
},
CraftingShaped {
recipe_id: Ident<&'a str>,
width: VarInt,
height: VarInt,
group: &'a str,
category: CraftingCategory,
ingredients: Vec<Ingredient>,
result: Option<ItemStack>,
},
CraftingSpecial {
kind: SpecialCraftingKind,
recipe_id: Ident<&'a str>,
category: CraftingCategory,
},
Smelting {
recipe_id: Ident<&'a str>,
group: &'a str,
category: SmeltCategory,
ingredient: Ingredient,
result: Option<ItemStack>,
experience: f32,
cooking_time: VarInt,
},
Blasting {
recipe_id: Ident<&'a str>,
group: &'a str,
category: SmeltCategory,
ingredient: Ingredient,
result: Option<ItemStack>,
experience: f32,
cooking_time: VarInt,
},
Smoking {
recipe_id: Ident<&'a str>,
group: &'a str,
category: SmeltCategory,
ingredient: Ingredient,
result: Option<ItemStack>,
experience: f32,
cooking_time: VarInt,
},
CampfireCooking {
recipe_id: Ident<&'a str>,
group: &'a str,
category: SmeltCategory,
ingredient: Ingredient,
result: Option<ItemStack>,
experience: f32,
cooking_time: VarInt,
},
Stonecutting {
recipe_id: Ident<&'a str>,
group: &'a str,
ingredient: Ingredient,
result: Option<ItemStack>,
},
Smithing {
recipe_id: Ident<&'a str>,
base: Ingredient,
addition: Ingredient,
result: Option<ItemStack>,
},
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum SpecialCraftingKind {
ArmorDye,
BookCloning,
MapCloning,
MapExtending,
FireworkRocket,
FireworkStar,
FireworkStarFade,
RepairItem,
TippedArrow,
BannerDuplicate,
BannerAddPattern,
ShieldDecoration,
ShulkerBoxColoring,
SuspiciousStew,
}
/// Any item in the Vec may be used for the recipe.
pub type Ingredient = Vec<Option<ItemStack>>;
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)]
pub enum CraftingCategory {
Building,
Redstone,
Equipment,
Misc,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)]
pub enum SmeltCategory {
Food,
Blocks,
Misc,
}
impl<'a> Encode for DeclaredRecipe<'a> {
fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
match self {
DeclaredRecipe::CraftingShapeless {
recipe_id,
group,
category,
ingredients,
result,
} => {
"crafting_shapeless".encode(&mut w)?;
recipe_id.encode(&mut w)?;
group.encode(&mut w)?;
category.encode(&mut w)?;
ingredients.encode(&mut w)?;
result.encode(w)
}
DeclaredRecipe::CraftingShaped {
recipe_id,
width,
height,
group,
category,
ingredients,
result,
} => {
"crafting_shaped".encode(&mut w)?;
recipe_id.encode(&mut w)?;
width.encode(&mut w)?;
height.encode(&mut w)?;
group.encode(&mut w)?;
category.encode(&mut w)?;
ensure!(
(width.0 as usize).saturating_mul(height.0 as usize) == ingredients.len(),
"width * height must be equal to the number of ingredients"
);
for ing in ingredients {
ing.encode(&mut w)?;
}
result.encode(w)
}
DeclaredRecipe::CraftingSpecial {
kind,
recipe_id,
category,
} => {
match kind {
SpecialCraftingKind::ArmorDye => "crafting_special_armordye",
SpecialCraftingKind::BookCloning => "crafting_special_bookcloning",
SpecialCraftingKind::MapCloning => "crafting_special_mapcloning",
SpecialCraftingKind::MapExtending => "crafting_special_mapextending",
SpecialCraftingKind::FireworkRocket => "crafting_special_firework_rocket",
SpecialCraftingKind::FireworkStar => "crafting_special_firework_star",
SpecialCraftingKind::FireworkStarFade => "crafting_special_firework_star_fade",
SpecialCraftingKind::RepairItem => "crafting_special_repairitem",
SpecialCraftingKind::TippedArrow => "crafting_special_tippedarrow",
SpecialCraftingKind::BannerDuplicate => "crafting_special_bannerduplicate",
SpecialCraftingKind::BannerAddPattern => "crafting_special_banneraddpattern",
SpecialCraftingKind::ShieldDecoration => "crafting_special_shielddecoration",
SpecialCraftingKind::ShulkerBoxColoring => {
"crafting_special_shulkerboxcoloring"
}
SpecialCraftingKind::SuspiciousStew => "crafting_special_suspiciousstew",
}
.encode(&mut w)?;
recipe_id.encode(&mut w)?;
category.encode(w)
}
DeclaredRecipe::Smelting {
recipe_id,
group,
category,
ingredient,
result,
experience,
cooking_time,
} => {
"smelting".encode(&mut w)?;
recipe_id.encode(&mut w)?;
group.encode(&mut w)?;
category.encode(&mut w)?;
ingredient.encode(&mut w)?;
result.encode(&mut w)?;
experience.encode(&mut w)?;
cooking_time.encode(w)
}
DeclaredRecipe::Blasting {
recipe_id,
group,
category,
ingredient,
result,
experience,
cooking_time,
} => {
"blasting".encode(&mut w)?;
recipe_id.encode(&mut w)?;
group.encode(&mut w)?;
category.encode(&mut w)?;
ingredient.encode(&mut w)?;
result.encode(&mut w)?;
experience.encode(&mut w)?;
cooking_time.encode(w)
}
DeclaredRecipe::Smoking {
recipe_id,
group,
category,
ingredient,
result,
experience,
cooking_time,
} => {
"smoking".encode(&mut w)?;
recipe_id.encode(&mut w)?;
group.encode(&mut w)?;
category.encode(&mut w)?;
ingredient.encode(&mut w)?;
result.encode(&mut w)?;
experience.encode(&mut w)?;
cooking_time.encode(w)
}
DeclaredRecipe::CampfireCooking {
recipe_id,
group,
category,
ingredient,
result,
experience,
cooking_time,
} => {
"campfire_cooking".encode(&mut w)?;
recipe_id.encode(&mut w)?;
group.encode(&mut w)?;
category.encode(&mut w)?;
ingredient.encode(&mut w)?;
result.encode(&mut w)?;
experience.encode(&mut w)?;
cooking_time.encode(w)
}
DeclaredRecipe::Stonecutting {
recipe_id,
group,
ingredient,
result,
} => {
"stonecutting".encode(&mut w)?;
recipe_id.encode(&mut w)?;
group.encode(&mut w)?;
ingredient.encode(&mut w)?;
result.encode(w)
}
DeclaredRecipe::Smithing {
recipe_id,
base,
addition,
result,
} => {
"smithing".encode(&mut w)?;
recipe_id.encode(&mut w)?;
base.encode(&mut w)?;
addition.encode(&mut w)?;
result.encode(w)
}
}
}
}
impl<'a> Decode<'a> for DeclaredRecipe<'a> {
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
Ok(match Ident::<&str>::decode(r)?.path() {
"crafting_shapeless" => Self::CraftingShapeless {
recipe_id: Decode::decode(r)?,
group: Decode::decode(r)?,
category: Decode::decode(r)?,
ingredients: Decode::decode(r)?,
result: Decode::decode(r)?,
},
"crafting_shaped" => {
let recipe_id = Ident::<&str>::decode(r)?;
let width = VarInt::decode(r)?.0;
let height = VarInt::decode(r)?.0;
let group = <&str>::decode(r)?;
let category = CraftingCategory::decode(r)?;
let mut ingredients = Vec::new();
for _ in 0..width.saturating_mul(height) {
ingredients.push(Ingredient::decode(r)?);
}
Self::CraftingShaped {
recipe_id,
width: VarInt(width),
height: VarInt(height),
group,
category,
ingredients,
result: Decode::decode(r)?,
}
}
"smelting" => Self::Smelting {
recipe_id: Decode::decode(r)?,
group: Decode::decode(r)?,
category: Decode::decode(r)?,
ingredient: Decode::decode(r)?,
result: Decode::decode(r)?,
experience: Decode::decode(r)?,
cooking_time: Decode::decode(r)?,
},
"blasting" => Self::Blasting {
recipe_id: Decode::decode(r)?,
group: Decode::decode(r)?,
category: Decode::decode(r)?,
ingredient: Decode::decode(r)?,
result: Decode::decode(r)?,
experience: Decode::decode(r)?,
cooking_time: Decode::decode(r)?,
},
"smoking" => Self::Smoking {
recipe_id: Decode::decode(r)?,
group: Decode::decode(r)?,
category: Decode::decode(r)?,
ingredient: Decode::decode(r)?,
result: Decode::decode(r)?,
experience: Decode::decode(r)?,
cooking_time: Decode::decode(r)?,
},
"campfire_cooking" => Self::CampfireCooking {
recipe_id: Decode::decode(r)?,
group: Decode::decode(r)?,
category: Decode::decode(r)?,
ingredient: Decode::decode(r)?,
result: Decode::decode(r)?,
experience: Decode::decode(r)?,
cooking_time: Decode::decode(r)?,
},
"stonecutting" => Self::Stonecutting {
recipe_id: Decode::decode(r)?,
group: Decode::decode(r)?,
ingredient: Decode::decode(r)?,
result: Decode::decode(r)?,
},
"smithing" => Self::Smithing {
recipe_id: Decode::decode(r)?,
base: Decode::decode(r)?,
addition: Decode::decode(r)?,
result: Decode::decode(r)?,
},
other => Self::CraftingSpecial {
kind: match other {
"crafting_special_armordye" => SpecialCraftingKind::ArmorDye,
"crafting_special_bookcloning" => SpecialCraftingKind::BookCloning,
"crafting_special_mapcloning" => SpecialCraftingKind::MapCloning,
"crafting_special_mapextending" => SpecialCraftingKind::MapExtending,
"crafting_special_firework_rocket" => SpecialCraftingKind::FireworkRocket,
"crafting_special_firework_star" => SpecialCraftingKind::FireworkStar,
"crafting_special_firework_star_fade" => SpecialCraftingKind::FireworkStarFade,
"crafting_special_repairitem" => SpecialCraftingKind::RepairItem,
"crafting_special_tippedarrow" => SpecialCraftingKind::TippedArrow,
"crafting_special_bannerduplicate" => SpecialCraftingKind::BannerDuplicate,
"crafting_special_banneraddpattern" => SpecialCraftingKind::BannerAddPattern,
"crafting_special_shielddecoration" => SpecialCraftingKind::ShieldDecoration,
"crafting_special_shulkerboxcoloring" => {
SpecialCraftingKind::ShulkerBoxColoring
}
"crafting_special_suspiciousstew" => SpecialCraftingKind::SuspiciousStew,
_ => bail!("unknown recipe type \"{other}\""),
},
recipe_id: Decode::decode(r)?,
category: CraftingCategory::decode(r)?,
},
})
}
}

View file

@ -241,7 +241,7 @@ pub enum SoundCategory {
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)] #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)]
pub enum GameStateChangeReason { pub enum GameEventKind {
NoRespawnBlockAvailable, NoRespawnBlockAvailable,
EndRaining, EndRaining,
BeginRaining, BeginRaining,
@ -358,3 +358,15 @@ pub struct PlayerAbilitiesFlags {
#[bits(4)] #[bits(4)]
_pad: u8, _pad: u8,
} }
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
pub struct TagGroup<'a> {
pub kind: Ident<&'a str>,
pub tags: Vec<Tag<'a>>,
}
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
pub struct Tag<'a> {
pub name: Ident<&'a str>,
pub entries: Vec<VarInt>,
}