mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
Add recipe and tags packets (#182)
Also improve generated `Encode`, `Decode` error messages and fix unnecessary build script reruns.
This commit is contained in:
parent
623e88908d
commit
8d9c0a7553
|
@ -9,7 +9,7 @@ mod entity;
|
|||
mod entity_event;
|
||||
|
||||
pub fn main() -> anyhow::Result<()> {
|
||||
println!("cargo:rerun-if-changed=extracted/");
|
||||
println!("cargo:rerun-if-changed=../../extracted/");
|
||||
|
||||
let generators = [
|
||||
(entity::build as fn() -> _, "entity.rs"),
|
||||
|
|
|
@ -24,7 +24,7 @@ use valence_protocol::packets::s2c::play::{
|
|||
};
|
||||
use valence_protocol::particle::{Particle, ParticleS2c};
|
||||
use valence_protocol::types::{
|
||||
AttributeProperty, DisplayedSkinParts, GameMode, GameStateChangeReason, SyncPlayerPosLookFlags,
|
||||
AttributeProperty, DisplayedSkinParts, GameEventKind, GameMode, SyncPlayerPosLookFlags,
|
||||
};
|
||||
use valence_protocol::{
|
||||
BlockPos, EncodePacket, Ident, ItemStack, RawBytes, Text, Username, VarInt,
|
||||
|
@ -531,7 +531,7 @@ impl<C: Config> Client<C> {
|
|||
|
||||
if !self.created_this_tick() {
|
||||
self.queue_packet(&GameEvent {
|
||||
reason: GameStateChangeReason::ChangeGameMode,
|
||||
kind: GameEventKind::ChangeGameMode,
|
||||
value: game_mode as i32 as f32,
|
||||
});
|
||||
}
|
||||
|
@ -541,10 +541,10 @@ impl<C: Config> Client<C> {
|
|||
/// Sets whether or not the client sees rain.
|
||||
pub fn set_raining(&mut self, raining: bool) {
|
||||
self.queue_packet(&GameEvent {
|
||||
reason: if raining {
|
||||
GameStateChangeReason::BeginRaining
|
||||
kind: if raining {
|
||||
GameEventKind::BeginRaining
|
||||
} else {
|
||||
GameStateChangeReason::EndRaining
|
||||
GameEventKind::EndRaining
|
||||
},
|
||||
value: 0.0,
|
||||
});
|
||||
|
@ -556,7 +556,7 @@ impl<C: Config> Client<C> {
|
|||
/// The rain level is clamped between `0.0.` and `1.0`.
|
||||
pub fn set_rain_level(&mut self, rain_level: f32) {
|
||||
self.queue_packet(&GameEvent {
|
||||
reason: GameStateChangeReason::RainLevelChange,
|
||||
kind: GameEventKind::RainLevelChange,
|
||||
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`.
|
||||
pub fn set_thunder_level(&mut self, thunder_level: f32) {
|
||||
self.queue_packet(&GameEvent {
|
||||
reason: GameStateChangeReason::ThunderLevelChange,
|
||||
kind: GameEventKind::ThunderLevelChange,
|
||||
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.
|
||||
pub fn win_game(&mut self, show_credits: bool) {
|
||||
self.queue_packet(&GameEvent {
|
||||
reason: GameStateChangeReason::WinGame,
|
||||
kind: GameEventKind::WinGame,
|
||||
value: if show_credits { 1.0 } else { 0.0 },
|
||||
});
|
||||
}
|
||||
|
@ -715,7 +715,7 @@ impl<C: Config> Client<C> {
|
|||
|
||||
if !self.created_this_tick() {
|
||||
self.queue_packet(&GameEvent {
|
||||
reason: GameStateChangeReason::EnableRespawnScreen,
|
||||
kind: GameEventKind::EnableRespawnScreen,
|
||||
value: if enable { 0.0 } else { 1.0 },
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
|
||||
let mut input = parse2::<DeriveInput>(item)?;
|
||||
|
||||
let name = input.ident;
|
||||
let input_name = input.ident;
|
||||
|
||||
if input.generics.lifetimes().count() > 1 {
|
||||
return Err(Error::new(
|
||||
|
@ -34,7 +34,7 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
|
|||
Fields::Named(fields) => {
|
||||
let init = fields.named.iter().map(|f| {
|
||||
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! {
|
||||
#name: Decode::decode(_r).context(#ctx)?,
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
|
|||
Fields::Unnamed(fields) => {
|
||||
let init = (0..fields.unnamed.len())
|
||||
.map(|i| {
|
||||
let ctx = format!("failed to decode field `{i}`");
|
||||
let ctx = format!("failed to decode field `{i}` in `{input_name}`");
|
||||
quote! {
|
||||
Decode::decode(_r).context(#ctx)?,
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
|
|||
|
||||
Ok(quote! {
|
||||
#[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
|
||||
{
|
||||
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| {
|
||||
let field = f.ident.as_ref().unwrap();
|
||||
let ctx = format!(
|
||||
"failed to decode field `{field}` in variant `{name}`",
|
||||
"failed to decode field `{field}` in variant `{name}` in \
|
||||
`{input_name}`",
|
||||
);
|
||||
quote! {
|
||||
#field: Decode::decode(_r).context(#ctx)?,
|
||||
|
@ -116,7 +117,8 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
|
|||
let init = (0..fields.unnamed.len())
|
||||
.map(|i| {
|
||||
let ctx = format!(
|
||||
"failed to decode field `{i}` in variant `{name}`",
|
||||
"failed to decode field `{i}` in variant `{name}` in \
|
||||
`{input_name}`",
|
||||
);
|
||||
quote! {
|
||||
Decode::decode(_r).context(#ctx)?,
|
||||
|
@ -143,16 +145,17 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
|
|||
|
||||
Ok(quote! {
|
||||
#[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
|
||||
{
|
||||
fn decode(_r: &mut &#lifetime [u8]) -> ::valence_protocol::__private::Result<Self> {
|
||||
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 {
|
||||
#decode_arms
|
||||
n => bail!("unexpected enum discriminant {}", disc),
|
||||
n => bail!("unexpected enum discriminant {} in `{}`", disc, stringify!(#input_name)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
let mut input = parse2::<DeriveInput>(item)?;
|
||||
|
||||
let name = input.ident;
|
||||
let input_name = input.ident;
|
||||
|
||||
add_trait_bounds(
|
||||
&mut input.generics,
|
||||
|
@ -25,7 +25,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
|
|||
.iter()
|
||||
.map(|f| {
|
||||
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! {
|
||||
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())
|
||||
.map(|i| {
|
||||
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! {
|
||||
self.#lit.encode(&mut _w).context(#ctx)?;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
|
|||
|
||||
Ok(quote! {
|
||||
#[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
|
||||
{
|
||||
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 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 {
|
||||
|
@ -83,7 +84,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
|
|||
.map(|name| {
|
||||
let ctx = format!(
|
||||
"failed to encode field `{name}` in variant \
|
||||
`{variant_name}`",
|
||||
`{variant_name}` in `{input_name}`",
|
||||
);
|
||||
|
||||
quote! {
|
||||
|
@ -111,7 +112,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
|
|||
.map(|name| {
|
||||
let ctx = format!(
|
||||
"failed to encode field `{name}` in variant \
|
||||
`{variant_name}`"
|
||||
`{variant_name}` in `{input_name}`"
|
||||
);
|
||||
|
||||
quote! {
|
||||
|
@ -142,7 +143,7 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
|
|||
|
||||
Ok(quote! {
|
||||
#[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
|
||||
{
|
||||
fn encode(&self, mut _w: impl ::std::io::Write) -> ::valence_protocol::__private::Result<()> {
|
||||
|
|
|
@ -15,7 +15,6 @@ struct TopLevel {
|
|||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
struct Block {
|
||||
#[allow(unused)]
|
||||
id: u16,
|
||||
item_id: u16,
|
||||
translation_key: String,
|
||||
|
@ -362,6 +361,18 @@ pub fn build() -> anyhow::Result<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 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.
|
||||
pub const ALL: [Self; #block_kind_count] = [#(Self::#block_kind_variants,)*];
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ mod item;
|
|||
mod translation_key;
|
||||
|
||||
pub fn main() -> anyhow::Result<()> {
|
||||
println!("cargo:rerun-if-changed=../extracted/");
|
||||
println!("cargo:rerun-if-changed=../../extracted/");
|
||||
|
||||
let generators = [
|
||||
(block::build as fn() -> _, "block.rs"),
|
||||
|
|
|
@ -51,7 +51,7 @@ fn fmt_block_state(bs: BlockState, f: &mut fmt::Formatter) -> fmt::Result {
|
|||
|
||||
impl Encode for BlockState {
|
||||
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)]
|
||||
pub enum BlockFace {
|
||||
/// -Y
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::io::Write;
|
||||
use std::mem::MaybeUninit;
|
||||
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 ==== //
|
||||
|
||||
impl Encode for str {
|
||||
|
|
|
@ -112,6 +112,7 @@ pub mod packets;
|
|||
pub mod particle;
|
||||
pub mod player_list;
|
||||
mod raw_bytes;
|
||||
pub mod recipe;
|
||||
pub mod text;
|
||||
pub mod translation_key;
|
||||
pub mod types;
|
||||
|
|
|
@ -7,11 +7,12 @@ use crate::byte_angle::ByteAngle;
|
|||
use crate::ident::Ident;
|
||||
use crate::item::ItemStack;
|
||||
use crate::raw_bytes::RawBytes;
|
||||
use crate::recipe::DeclaredRecipe;
|
||||
use crate::text::Text;
|
||||
use crate::types::{
|
||||
AttributeProperty, BossBarAction, ChunkDataBlockEntity, Difficulty, GameMode,
|
||||
GameStateChangeReason, GlobalPos, PlayerAbilitiesFlags, SignedProperty, SoundCategory,
|
||||
SyncPlayerPosLookFlags,
|
||||
AttributeProperty, BossBarAction, ChunkDataBlockEntity, Difficulty, GameEventKind, GameMode,
|
||||
GlobalPos, PlayerAbilitiesFlags, SignedProperty, SoundCategory, SyncPlayerPosLookFlags,
|
||||
TagGroup,
|
||||
};
|
||||
use crate::username::Username;
|
||||
use crate::var_int::VarInt;
|
||||
|
@ -276,7 +277,7 @@ pub mod play {
|
|||
#[derive(Copy, Clone, Debug, Encode, EncodePacket, Decode, DecodePacket)]
|
||||
#[packet_id = 0x1c]
|
||||
pub struct GameEvent {
|
||||
pub reason: GameStateChangeReason,
|
||||
pub kind: GameEventKind,
|
||||
pub value: f32,
|
||||
}
|
||||
|
||||
|
@ -673,6 +674,16 @@ pub mod play {
|
|||
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! {
|
||||
#[derive(Clone)]
|
||||
S2cPlayPacket<'a> {
|
||||
|
@ -737,6 +748,8 @@ pub mod play {
|
|||
TeleportEntity,
|
||||
UpdateAttributes<'a>,
|
||||
FeatureFlags<'a>,
|
||||
DeclareRecipes<'a>,
|
||||
UpdateTags<'a>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
392
crates/valence_protocol/src/recipe.rs
Normal file
392
crates/valence_protocol/src/recipe.rs
Normal 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)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
|
@ -241,7 +241,7 @@ pub enum SoundCategory {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub enum GameStateChangeReason {
|
||||
pub enum GameEventKind {
|
||||
NoRespawnBlockAvailable,
|
||||
EndRaining,
|
||||
BeginRaining,
|
||||
|
@ -358,3 +358,15 @@ pub struct PlayerAbilitiesFlags {
|
|||
#[bits(4)]
|
||||
_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>,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue