diff --git a/Cargo.lock b/Cargo.lock index 3df73ba4..03fc570f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + [[package]] name = "haya_collection" version = "0.4.0" @@ -70,6 +76,7 @@ dependencies = [ name = "haya_palette" version = "0.5.0" dependencies = [ + "hashbrown", "minecraft_data", "mser", ] diff --git a/haya_collection/src/lib.rs b/haya_collection/src/lib.rs index 9e29e40f..2d9e3f31 100644 --- a/haya_collection/src/lib.rs +++ b/haya_collection/src/lib.rs @@ -4,7 +4,7 @@ extern crate alloc; use alloc::boxed::Box; use alloc::vec::Vec; -use mser::{Error, Read, Reader, V21, Write, Writer}; +use mser::{Error, Read, Reader, V21, Write, Writer, read_v21_len}; pub enum List<'a, T: 'a, const MAX: usize = { usize::MAX }> { Borrowed(&'a [T]), @@ -95,10 +95,7 @@ pub fn capacity_fix(len: usize) -> usize { impl<'a, T: Read<'a>, const MAX: usize> Read<'a> for List<'a, T, MAX> { fn read(buf: &mut Reader<'a>) -> Result { - let len = V21::read(buf)?.0 as usize; - if len > MAX { - return Err(Error); - } + let len = read_v21_len(buf, MAX)?; let mut vec = Vec::with_capacity(capacity_fix(len)); for _ in 0..len { vec.push(T::read(buf)?); @@ -135,10 +132,7 @@ impl<'a, K: Write + 'a, V: Write + 'a, const MAX: usize> Write for Map<'a, K, V, impl<'a, K: Read<'a>, V: Read<'a>, const MAX: usize> Read<'a> for Map<'a, K, V, MAX> { fn read(buf: &mut Reader<'a>) -> Result { - let len = V21::read(buf)?.0 as usize; - if len > MAX { - return Err(Error); - } + let len = read_v21_len(buf, MAX)?; let mut vec = Vec::with_capacity(capacity_fix(len)); for _ in 0..len { let k = K::read(buf)?; @@ -179,25 +173,3 @@ impl<'a, T: Read<'a>> Read<'a> for Cow<'a, T> { Ok(Self::Owned(Box::new(T::read(buf)?))) } } - -#[derive(Debug, Clone, Copy)] -pub struct FixedByteArray<'a, const L: usize>(pub &'a [u8; L]); - -impl<'a, const L: usize> Read<'a> for FixedByteArray<'a, L> { - fn read(buf: &mut Reader<'a>) -> Result { - match buf.read_array() { - Ok(x) => Ok(Self(x)), - Err(e) => Err(e), - } - } -} - -impl<'a, const L: usize> Write for FixedByteArray<'a, L> { - unsafe fn write(&self, w: &mut Writer) { - unsafe { w.write(self.0) } - } - - fn len_s(&self) -> usize { - self.0.len() - } -} diff --git a/haya_math/src/lib.rs b/haya_math/src/lib.rs index 8c0833a9..452bab40 100644 --- a/haya_math/src/lib.rs +++ b/haya_math/src/lib.rs @@ -325,8 +325,8 @@ impl From for LpVec3 { pub struct ByteAngle(pub u8); impl ByteAngle { - pub fn new(f: f32) -> ByteAngle { - ByteAngle(libm::floorf(f * 256.0 / 360.0) as u8) + pub fn new(f: f32) -> Self { + Self(libm::floorf(f * 256.0 / 360.0) as u8) } pub fn to_degrees(self) -> f32 { @@ -562,6 +562,32 @@ pub enum Direction { East, } +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct ByteDirection(pub Direction); + +impl<'a> Read<'a> for ByteDirection { + fn read(buf: &mut Reader<'a>) -> Result { + Ok(Self(match buf.read_byte()? { + 1 => Direction::Up, + 2 => Direction::North, + 3 => Direction::South, + 4 => Direction::West, + 5 => Direction::East, + _ => Direction::Down, + })) + } +} + +impl Write for ByteDirection { + unsafe fn write(&self, w: &mut Writer) { + unsafe { w.write_byte(self.0 as u8) } + } + + fn len_s(&self) -> usize { + 1 + } +} + impl<'a> Read<'a> for Direction { fn read(buf: &mut Reader<'a>) -> Result { Ok(match V21::read(buf)?.0 { diff --git a/haya_nbt/src/compound.rs b/haya_nbt/src/compound.rs index 8e8795dc..967cee5d 100644 --- a/haya_nbt/src/compound.rs +++ b/haya_nbt/src/compound.rs @@ -7,42 +7,21 @@ use haya_mutf8::{as_mutf8_ascii, decode_mutf8_len}; use haya_str::HayaStr; use mser::{Error, Read, Reader, Write, Writer}; -enum CowVec { - Thin(HayaStr), - Heap(Vec), -} - impl<'a> Read<'a> for Name { fn read(buf: &mut Reader<'a>) -> Result { let len = u16::read(buf)? as usize; let data = buf.read_slice(len)?; if let Some(x) = as_mutf8_ascii(data) { - Ok(Self::new(x)) + unsafe { Ok(Self::new(x)) } } else { let len = decode_mutf8_len(data)?; - let mut ptr = if len <= haya_str::MAX { - CowVec::Thin(HayaStr::new()) - } else { - CowVec::Heap(Vec::with_capacity(len)) - }; + let mut vec = Vec::with_capacity(len); unsafe { - mser::write_unchecked( - match &mut ptr { - CowVec::Thin(s) => s.as_mut_ptr(), - CowVec::Heap(x) => x.as_mut_ptr(), - }, - &(DecodeMutf8(data, len)), - ); - match ptr { - CowVec::Thin(mut s) => { - s.set_len(len); - Ok(Self::Thin(s)) - } - CowVec::Heap(mut x) => { - x.set_len(len); - Ok(Self::Heap(String::from_utf8_unchecked(x).into_boxed_str())) - } - } + mser::write_unchecked(vec.as_mut_ptr(), &(DecodeMutf8(data, len))); + vec.set_len(len); + Ok(Self(crate::Inner::Heap( + String::from_utf8_unchecked(vec).into_boxed_str(), + ))) } } } @@ -50,9 +29,9 @@ impl<'a> Read<'a> for Name { impl AsRef for Name { fn as_ref(&self) -> &str { - match self { - Self::Thin(x) => x, - Self::Heap(x) => x, + match &self.0 { + crate::Inner::Thin(x) => x, + crate::Inner::Heap(x) => x, } } } @@ -66,10 +45,10 @@ impl core::ops::Deref for Name { } impl Name { - pub fn new(s: &str) -> Self { + pub(crate) unsafe fn new(s: &str) -> Self { match HayaStr::copy_from(s) { - Ok(x) => Self::Thin(x), - Err(_) => Self::Heap(s.to_owned().into_boxed_str()), + Ok(x) => Self(crate::Inner::Thin(x)), + Err(_) => Self(crate::Inner::Heap(s.to_owned().into_boxed_str())), } } } diff --git a/haya_nbt/src/lib.rs b/haya_nbt/src/lib.rs index 9cdd911a..36a52e63 100644 --- a/haya_nbt/src/lib.rs +++ b/haya_nbt/src/lib.rs @@ -53,7 +53,10 @@ pub struct Compound(Vec<(Name, Tag)>); pub struct CompoundStringify(pub Compound); #[derive(Clone, Debug)] -pub enum Name { +pub struct Name(Inner); + +#[derive(Clone, Debug)] +enum Inner { Thin(HayaStr), Heap(Box), } diff --git a/haya_nbt/src/string.rs b/haya_nbt/src/string.rs index 0a3dbe13..6094776f 100644 --- a/haya_nbt/src/string.rs +++ b/haya_nbt/src/string.rs @@ -1,4 +1,4 @@ -use crate::{Error, Name, RawStringTag, Read, RefStringTag, StringTag, Write, Writer}; +use crate::{Error, Inner, Name, RawStringTag, Read, RefStringTag, StringTag, Write, Writer}; use alloc::borrow::ToOwned; use alloc::vec::Vec; use haya_mutf8::{as_mutf8_ascii, decode_mutf8, decode_mutf8_len, encode_mutf8, encode_mutf8_len}; @@ -129,21 +129,27 @@ impl Write for Name { #[inline] unsafe fn write(&self, w: &mut Writer) { unsafe { - if let Some(x) = RawStringTag::new(self.as_bytes()) { - x.write(w); - } else { - (encode_mutf8_len(self) as u16).write(w); - encode_mutf8(self, w); + match &self.0 { + Inner::Thin(direct) => RawStringTag::new_unchecked(direct).write(w), + Inner::Heap(x) => match RawStringTag::new(x.as_bytes()) { + Some(x) => x.write(w), + None => { + (encode_mutf8_len(self) as u16).write(w); + encode_mutf8(self, w); + } + }, } } } #[inline] fn len_s(&self) -> usize { - if let Some(x) = RawStringTag::new(self.as_bytes()) { - x.len_s() - } else { - encode_mutf8_len(self) + 2 + match &self.0 { + Inner::Thin(direct) => RawStringTag::new_unchecked(direct).len_s(), + Inner::Heap(x) => match RawStringTag::new(x.as_bytes()) { + Some(x) => x.len_s(), + None => encode_mutf8_len(self) + 2, + }, } } } diff --git a/haya_palette/Cargo.toml b/haya_palette/Cargo.toml index 3256bbe0..5f345993 100644 --- a/haya_palette/Cargo.toml +++ b/haya_palette/Cargo.toml @@ -10,3 +10,5 @@ edition.workspace = true [dependencies] mser = { workspace = true } minecraft_data = { workspace = true } + +hashbrown = { version = "0", default-features = false } diff --git a/haya_palette/src/chunk.rs b/haya_palette/src/chunk.rs index 31966d32..e5fee5d9 100644 --- a/haya_palette/src/chunk.rs +++ b/haya_palette/src/chunk.rs @@ -1,6 +1,7 @@ use crate::Palette; use alloc::vec::Vec; -use core::iter::FusedIterator; +use hashbrown::HashTable; +use mser::cold_path; const BLOCK_PER_CHUNK: usize = 4 * 4 * 4; const INDIRECT4_PER_CHUNK: usize = BLOCK_PER_CHUNK / 2; @@ -29,36 +30,52 @@ pub struct ChunkCache { pub direct: Vec>, pub indirect2: Vec>, pub indirect4: Vec>, - pub chunks: Int64Map, + pub chunks: HashTable<(u64, u64)>, pub direct_key: Vec, pub indirect4_key: Vec, pub indirect2_key: Vec, pub single_key: Vec, } +impl Default for ChunkCache { + fn default() -> Self { + Self::new() + } +} + impl ChunkCache { - pub fn get(&self, x: i32, y: i32, z: i32) -> Option { + pub const fn new() -> Self { + Self { + direct: Vec::new(), + indirect2: Vec::new(), + indirect4: Vec::new(), + chunks: HashTable::new(), + direct_key: Vec::new(), + indirect4_key: Vec::new(), + indirect2_key: Vec::new(), + single_key: Vec::new(), + } + } + + pub fn get_block(&self, x: i32, y: i32, z: i32) -> T { let j = ((x & 3) | ((y & 3) << 2) | ((z & 3) << 4)) as usize; - let sx = ((x >> 2) & 0x3FF_FFFF) as i64; - let sy = ((y >> 2) & 0xFFF) as i64; - let sz = ((z >> 2) & 0x3FF_FFFF) as i64; - let i = ((sx << 38) | (sz << 12) | sy) as u64; - let t = match self.chunks.get(i) { - Some(t) => t, + let chunk = pack(x >> 2, y >> 2, z >> 2); + let t = match self.chunks.find(mix(chunk), |x| x.0 == chunk) { + Some(t) => t.1, None => { - mser::cold_path(); - return None; + cold_path(); + return T::default(); } }; let n = (t & INDEX_MASK) as usize; let ty = t >> 62; unsafe { - Some(match ty { + match ty { 3 => T::from_id(n as u32), 2 => self.indirect2.get_unchecked(n).get(j), 1 => self.indirect4.get_unchecked(n).get(j), _ => *self.direct.get_unchecked(n).data.get_unchecked(j), - }) + } } } } @@ -83,393 +100,27 @@ impl Indirect4 { } } -fn mix(x: u64, mask: usize) -> usize { - (U64_PRIME_MAX.wrapping_mul(x) as usize) & mask -} - -const U64_PRIME_MAX: u64 = u64::MAX - 58; - -#[derive(Clone, Copy)] -struct Slot(u64, u64); - -impl Slot { - const EMPTY: Self = Self(u64::MAX, u64::MAX); - - fn is_empty(self) -> bool { - self.0 == u64::MAX - } -} - -#[derive(Clone)] -pub struct Int64Map { - slots: Vec, - mask: usize, - len: usize, -} - -impl Int64Map { - pub fn with_capacity(capacity: usize) -> Self { - let size = capacity.next_power_of_two(); - let mask = size - 1; - let slots = alloc::vec![Slot::EMPTY; size]; - Self { - slots, - len: 0, - mask, - } - } - - pub fn reserve(&mut self, additional: usize) { - let capacity = self.len + additional; - while self.mask < capacity { - self.rehash(); - } - } - - pub fn insert(&mut self, key: u64, value: u64) -> Option { - debug_assert_ne!(key, u64::MAX); - while self.len * 4 > self.slots.len() * 3 { - self.rehash(); - } - let mut i = mix(key, self.mask); - loop { - let slot = unsafe { self.slots.get_unchecked_mut(i) }; - if slot.is_empty() { - *slot = Slot(key, value); - self.len += 1; - return None; - } - if slot.0 == key { - let old = slot.1; - *slot = Slot(key, value); - return Some(old); - } - i = (i + 1) & self.mask; - } - } - - pub fn get(&self, key: u64) -> Option { - let mut i = mix(key, self.mask); - loop { - let slot = unsafe { self.slots.get_unchecked(i) }; - if slot.0 == key { - return Some(slot.1); - } - if slot.is_empty() { - return None; - } - i = (i + 1) & self.mask; - } - } - - pub fn get_mut(&mut self, key: u64) -> Option<&mut u64> { - let mut i = mix(key, self.mask); - loop { - let slot = unsafe { self.slots.get_unchecked_mut(i) }; - if slot.0 == key { - return Some(unsafe { &mut self.slots.get_unchecked_mut(i).1 }); - } - if slot.is_empty() { - return None; - } - i = (i + 1) & self.mask; - } - } - - pub fn remove(&mut self, key: u64) -> Option { - let mut i = mix(key, self.mask); - let mut slot = unsafe { self.slots.get_unchecked_mut(i) }; - loop { - if slot.0 == key { - break; - } - if slot.is_empty() { - return None; - } - i = (i + 1) & self.mask; - slot = unsafe { self.slots.get_unchecked_mut(i) }; - } - self.len -= 1; - let v = slot.1; - *slot = Slot::EMPTY; - let mut j = (i + 1) & self.mask; - loop { - let v = unsafe { *self.slots.get_unchecked(j) }; - if v.is_empty() { - break; - } - let natural = mix(v.0, self.mask); - let remove = if natural <= i { - i < j || j < natural - } else { - j < natural && i < j - }; - if remove { - unsafe { - *self.slots.get_unchecked_mut(i) = v; - *self.slots.get_unchecked_mut(j) = Slot::EMPTY; - } - i = j; - } - j = (j + 1) & self.mask; - } - Some(v) - } - - pub fn contains_key(&self, key: u64) -> bool { - self.get(key).is_some() - } - - pub fn clear(&mut self) { - self.slots.fill(Slot::EMPTY); - self.len = 0; - } - - pub fn is_empty(&self) -> bool { - self.len == 0 - } - - pub fn iter(&self) -> Iter<'_> { - Iter::new(&self.slots) - } - - pub fn iter_mut(&mut self) -> IterMut<'_> { - IterMut::new(&mut self.slots) - } - - pub fn keys(&self) -> Keys<'_> { - Keys { inner: self.iter() } - } - - pub fn values(&self) -> Values<'_> { - Values { inner: self.iter() } - } - - pub fn values_mut(&mut self) -> ValuesMut<'_> { - ValuesMut { - inner: self.iter_mut(), - } - } - - #[cold] - fn rehash(&mut self) { - let new_size = (self.mask + 1) << 1; - self.mask = new_size - 1; - let old = core::mem::replace(&mut self.slots, alloc::vec![Slot::EMPTY; new_size]); - for slot in old { - if slot.is_empty() { - continue; - } - let mut i = mix(slot.0, self.mask); - loop { - let j = unsafe { self.slots.get_unchecked_mut(i) }; - if j.is_empty() { - *j = slot; - break; - } - i = (i + 1) & self.mask; - } - } - } - - pub fn len(&self) -> usize { - self.len - } - - pub fn capacity(&self) -> usize { - self.slots.len() - } -} - -impl Default for Int64Map { - fn default() -> Self { - Self::with_capacity(8) - } -} +const PRIME_MAX_A: u32 = u32::MAX - 4; +const PRIME_MAX_B: u32 = u32::MAX - 16; +const PRIME_MAX_C: u32 = u32::MAX - 64; -impl PartialEq for Int64Map { - fn eq(&self, other: &Int64Map) -> bool { - self.len == other.len - && self - .slots - .iter() - .filter(|x| !x.is_empty()) - .all(|slot| other.get(slot.0) == Some(slot.1)) - } +#[inline] +fn pack(x: i32, y: i32, z: i32) -> u64 { + let sx = (x & 0x3FF_FFFF) as i64; + let sy = (y & 0xFFF) as i64; + let sz = (z & 0x3FF_FFFF) as i64; + ((sx << 38) | (sz << 12) | sy) as u64 } -impl Eq for Int64Map {} - -impl core::fmt::Debug for Int64Map { - fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { - fmt.debug_map().entries(self.iter()).finish() - } -} - -#[derive(Clone)] -pub struct Iter<'a> { - inner: core::slice::Iter<'a, Slot>, -} - -impl<'a> Iter<'a> { - fn new(vec: &'a [Slot]) -> Self { - Iter { inner: vec.iter() } - } -} - -impl<'a> Iterator for Iter<'a> { - type Item = (u64, u64); - - fn next(&mut self) -> Option { - self.inner.find(|&x| !x.is_empty()).map(|r| (r.0, r.1)) - } -} - -impl<'a> DoubleEndedIterator for Iter<'a> { - fn next_back(&mut self) -> Option { - self.inner.rfind(|&x| !x.is_empty()).map(|r| (r.0, r.1)) - } -} - -impl<'a> FusedIterator for Iter<'a> {} - -impl<'a> IntoIterator for &'a mut Int64Map { - type Item = (&'a mut u64, &'a mut u64); - type IntoIter = IterMut<'a>; - - fn into_iter(self) -> Self::IntoIter { - IterMut::new(&mut self.slots) - } -} - -pub struct IterMut<'a> { - inner: core::slice::IterMut<'a, Slot>, -} - -impl<'a> IterMut<'a> { - fn new(vec: &'a mut [Slot]) -> Self { - IterMut { - inner: vec.iter_mut(), - } - } -} - -impl<'a> Iterator for IterMut<'a> { - type Item = (&'a mut u64, &'a mut u64); - - fn next(&mut self) -> Option { - self.inner - .find(|x| !x.is_empty()) - .map(|x| (&mut x.0, &mut x.1)) - } -} - -impl<'a> DoubleEndedIterator for IterMut<'a> { - fn next_back(&mut self) -> Option { - self.inner - .rfind(|x| !x.is_empty()) - .map(|x| (&mut x.0, &mut x.1)) - } -} - -impl<'a> FusedIterator for IterMut<'a> {} - -pub struct Keys<'a> { - inner: Iter<'a>, -} - -impl<'a> Iterator for Keys<'a> { - type Item = u64; - - fn next(&mut self) -> Option { - self.inner.next().map(|kv| kv.0) - } -} - -impl<'a> DoubleEndedIterator for Keys<'a> { - fn next_back(&mut self) -> Option { - self.inner.next_back().map(|(k, _)| k) - } -} - -pub struct Values<'a> { - inner: Iter<'a>, -} - -impl<'a> Iterator for Values<'a> { - type Item = u64; - - fn next(&mut self) -> Option { - self.inner.next().map(|(_, v)| v) - } -} - -pub struct ValuesMut<'a> { - pub(crate) inner: IterMut<'a>, -} - -impl<'a> Iterator for ValuesMut<'a> { - type Item = &'a mut u64; - - fn next(&mut self) -> Option { - self.inner.next().map(|x| x.1) - } -} - -impl IntoIterator for Int64Map { - type Item = (u64, u64); - type IntoIter = IntoIter; - - fn into_iter(self) -> Self::IntoIter { - IntoIter::new(self.slots) - } -} - -pub struct IntoIter { - inner: alloc::vec::IntoIter, -} - -impl IntoIter { - fn new(vec: Vec) -> Self { - IntoIter { - inner: vec.into_iter(), - } - } -} - -impl Iterator for IntoIter { - type Item = (u64, u64); - - fn next(&mut self) -> Option<(u64, u64)> { - self.inner.find(|x| !x.is_empty()).map(|kv| (kv.0, kv.1)) - } -} - -impl<'a> FusedIterator for Keys<'a> {} - -impl<'a> FusedIterator for Values<'a> {} - -impl<'a> FusedIterator for ValuesMut<'a> {} - -impl FusedIterator for IntoIter {} - -impl Extend<(u64, u64)> for Int64Map { - #[inline] - fn extend>(&mut self, iter: T) { - for elem in iter { - self.insert(elem.0, elem.1); - } - } -} - -impl core::iter::FromIterator<(u64, u64)> for Int64Map { - fn from_iter>(iter: T) -> Self { - let iterator = iter.into_iter(); - let (lower_bound, _) = iterator.size_hint(); - let mut map = Int64Map::with_capacity(lower_bound); - for elem in iterator { - map.insert(elem.0, elem.1); - } - map - } +// 32 bits +#[inline] +fn mix(v: u64) -> u64 { + let x = (v >> 38) as i32 as u32; + let y = ((v << 52) >> 52) as i32 as u32; + let z = ((v << 26) >> 38) as i32 as u32; + let m = PRIME_MAX_A + .wrapping_mul(x) + .wrapping_add(PRIME_MAX_B.wrapping_mul(y)) + .wrapping_add(PRIME_MAX_C.wrapping_mul(z)); + m as u64 } diff --git a/haya_palette/src/lib.rs b/haya_palette/src/lib.rs index 28162fcd..2d802fc0 100644 --- a/haya_palette/src/lib.rs +++ b/haya_palette/src/lib.rs @@ -4,17 +4,14 @@ mod chunk; extern crate alloc; -pub use self::chunk::{ - ChunkCache, Direct, Indirect2, Indirect4, Int64Map, IntoIter, Iter, IterMut, Keys, Values, - ValuesMut, -}; +pub use self::chunk::{ChunkCache, Direct, Indirect2, Indirect4}; use alloc::alloc::{alloc, alloc_zeroed, dealloc, handle_alloc_error}; use core::alloc::Layout; use core::array::from_fn; use core::mem::{align_of, size_of}; use core::ptr::NonNull; use core::slice::from_raw_parts; -use minecraft_data::block_state; +use minecraft_data::{block, block_state}; use mser::{Error, Read, Reader, V21, V32, Write, Writer}; pub trait Palette: Copy { @@ -23,6 +20,7 @@ pub trait Palette: Copy { /// `value` must be a valid id. unsafe fn from_id(value: u32) -> Self; fn to_id(self) -> u32; + fn default() -> Self; } impl Palette for block_state { @@ -33,6 +31,10 @@ impl Palette for block_state { fn to_id(self) -> u32 { self.id() as u32 } + + fn default() -> Self { + block::void_air.state_default() + } } impl Palette for Biome { @@ -43,6 +45,10 @@ impl Palette for Biome { fn to_id(self) -> u32 { self as u32 } + + fn default() -> Self { + Biome::TheVoid + } } #[derive(Clone, Default, Copy, PartialEq, Eq)] diff --git a/haya_protocol/src/advancement.rs b/haya_protocol/src/advancement.rs index 34a45eba..e25c7192 100644 --- a/haya_protocol/src/advancement.rs +++ b/haya_protocol/src/advancement.rs @@ -1,5 +1,5 @@ -use crate::item::{ItemStack, TypedDataComponent}; -use crate::{Component, HolderSet, ResourceTexture}; +use crate::item_stack::{ItemStack, TypedDataComponent}; +use crate::{Component, HolderSet, MilliSeconds, ResourceTexture}; use haya_collection::{List, Map}; use haya_ident::Ident; use haya_nbt::Tag; @@ -183,5 +183,5 @@ pub struct AdvancementProgress<'a> { #[derive(Clone, Serialize, Deserialize)] pub struct CriterionProgress { - pub obtained: Option, + pub obtained: Option, } diff --git a/haya_protocol/src/block.rs b/haya_protocol/src/block.rs index e5855722..b020e182 100644 --- a/haya_protocol/src/block.rs +++ b/haya_protocol/src/block.rs @@ -1,4 +1,4 @@ -use crate::item::TypedEntityDataEntity; +use crate::item_stack::TypedEntityDataEntity; use crate::registry::BannerPatternRef; use crate::{DyeColor, Holder}; use haya_collection::List; diff --git a/haya_protocol/src/chat.rs b/haya_protocol/src/chat.rs index 6e6b7807..6d1810dd 100644 --- a/haya_protocol/src/chat.rs +++ b/haya_protocol/src/chat.rs @@ -1,7 +1,7 @@ use crate::registry::ChatTypeRef; -use crate::{BitSet, Component, Holder, Style}; -use haya_collection::{FixedByteArray, List}; -use mser::{ByteArray, Read, Utf8, V32, Write}; +use crate::{BitSet, Component, Holder, MilliSeconds, Style}; +use haya_collection::List; +use mser::{ByteArray, FixedByteArray, Read, Utf8, V32, Write}; use uuid::Uuid; #[derive(Clone, Copy, Serialize, Deserialize)] @@ -91,7 +91,7 @@ impl Parameter { #[derive(Clone, Serialize, Deserialize)] pub struct SignedMessageBodyPacked<'a> { pub content: Utf8<'a, 256>, - pub timestamp: u64, + pub timestamp: MilliSeconds, pub salt: u64, pub last_seen: LastSeenMessagesPacked<'a>, } @@ -136,7 +136,15 @@ pub struct RemoteChatSession<'a> { #[derive(Clone, Serialize, Deserialize)] pub struct ProfilePublicKey<'a> { - pub expires_at: u64, + pub expires_at: MilliSeconds, pub key: ByteArray<'a, 512>, pub key_signature: ByteArray<'a, 4096>, } + +#[derive(Clone, Serialize, Deserialize)] +pub struct LastSeenMessagesUpdate<'a> { + #[mser(varint)] + pub offset: u32, + pub acknowledged: FixedByteArray<'a, 3>, + pub checksum: u8, +} diff --git a/haya_protocol/src/clientbound/game.rs b/haya_protocol/src/clientbound/game.rs index d8616778..995c685e 100644 --- a/haya_protocol/src/clientbound/game.rs +++ b/haya_protocol/src/clientbound/game.rs @@ -10,7 +10,8 @@ use crate::crafting::{ }; use crate::debug::{DebugSubscriptionEvent, DebugSubscriptionUpdate, RemoteDebugSampleType}; use crate::entity_data::EntityDataSerializer; -use crate::item::OptionalItemStack; +use crate::inventory::{ContainerId, EquipmentSlot, InteractionHand}; +use crate::item_stack::OptionalItemStack; use crate::map::{MapDecoration, MapId, MapPatch}; use crate::minecart::MinecartStep; use crate::particle::{ExplosionParticleInfo, Particle}; @@ -22,9 +23,8 @@ use crate::stat::Stat; use crate::trading::MerchantOffer; use crate::waypoint::TrackedWaypoint; use crate::{ - BitSet, ChatFormatting, Component, ContainerId, Difficulty, EntityAnchor, EquipmentSlot, - GameType, GlobalPos, HeightmapType, Holder, InteractionHand, OptionalGameType, Relatives, - RespawnData, V32List, WeightedList, + BitSet, ChatFormatting, Component, Difficulty, EntityAnchor, GameType, GlobalPos, + HeightmapType, Holder, OptionalGameType, Relatives, RespawnData, V32List, WeightedList, }; use alloc::vec::Vec; use haya_collection::{List, Map, capacity_fix}; diff --git a/haya_protocol/src/command.rs b/haya_protocol/src/command.rs index 61d21010..bb05087c 100644 --- a/haya_protocol/src/command.rs +++ b/haya_protocol/src/command.rs @@ -1,3 +1,4 @@ +use crate::chat::MessageSignature; use haya_collection::List; use haya_ident::{Ident, ResourceKey}; use minecraft_data::command_argument_type; @@ -709,3 +710,12 @@ impl<'a> Read<'a> for CommandNode<'a> { }) } } + +#[derive(Clone, Serialize, Deserialize)] +pub struct ArgumentSignatures<'a>(List<'a, ArgumentSignature<'a>, 8>); + +#[derive(Clone, Serialize, Deserialize)] +pub struct ArgumentSignature<'a> { + pub name: Utf8<'a, 16>, + pub signature: MessageSignature<'a>, +} diff --git a/haya_protocol/src/crafting.rs b/haya_protocol/src/crafting.rs index 4f823e4a..06e0f70e 100644 --- a/haya_protocol/src/crafting.rs +++ b/haya_protocol/src/crafting.rs @@ -1,4 +1,4 @@ -use crate::item::ItemStack; +use crate::item_stack::ItemStack; use crate::registry::TrimPatternRef; use crate::trim::TrimPattern; use crate::{Holder, HolderSet, OptionalV32}; diff --git a/haya_protocol/src/entity_data.rs b/haya_protocol/src/entity_data.rs index 2047a745..21036fec 100644 --- a/haya_protocol/src/entity_data.rs +++ b/haya_protocol/src/entity_data.rs @@ -2,15 +2,14 @@ use crate::entity::{ ArmadilloState, CopperGolemState, EntityReference, PaintingVariant, Pose, SnifferState, VillagerData, }; -use crate::item::OptionalItemStack; +use crate::inventory::HumanoidArm; +use crate::item_stack::OptionalItemStack; use crate::profile::ResolvableProfile; use crate::registry::{ CatVariantRef, ChickenVariantRef, CowVariantRef, FrogVariantRef, PaintingVariantRef, PigVariantRef, WolfSoundVariantRef, WolfVariantRef, ZombieNautilusVariantRef, }; -use crate::{ - Component, GlobalPos, Holder, HumanoidArm, OptionalV32, Rotations, WeatheringCopperState, -}; +use crate::{Component, GlobalPos, Holder, OptionalV32, Rotations, WeatheringCopperState}; use haya_collection::List; use haya_math::{BlockPosPacked, Direction, FQuat, FVec3}; use minecraft_data::{block_state, particle_type}; diff --git a/haya_protocol/src/inventory.rs b/haya_protocol/src/inventory.rs new file mode 100644 index 00000000..b2def68f --- /dev/null +++ b/haya_protocol/src/inventory.rs @@ -0,0 +1,118 @@ +use crate::Translatable; + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum HumanoidArm { + Left, + Right, +} + +impl HumanoidArm { + pub const fn translation_key(self) -> Translatable<'static> { + Translatable(match self { + Self::Left => "options.mainHand.left", + Self::Right => "options.mainHand.right", + }) + } + + pub const fn name(self) -> &'static str { + match self { + Self::Left => "left", + Self::Right => "right", + } + } +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +pub struct ContainerId(#[mser(varint)] pub u32); + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum EquipmentSlotGroup { + Any, + Mainhand, + Offhand, + Hand, + Feet, + Legs, + Chest, + Head, + Armor, + Body, + Saddle, +} + +impl EquipmentSlotGroup { + pub const fn name(self) -> &'static str { + match self { + Self::Any => "any", + Self::Mainhand => "mainhand", + Self::Offhand => "offhand", + Self::Hand => "hand", + Self::Feet => "feet", + Self::Legs => "legs", + Self::Chest => "chest", + Self::Head => "head", + Self::Armor => "armor", + Self::Body => "body", + Self::Saddle => "saddle", + } + } +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum EquipmentSlot { + Mainhand, + Feet, + Legs, + Chest, + Head, + Offhand, + Body, + Saddle, +} + +impl EquipmentSlot { + pub const fn new(n: u8) -> Self { + if n > Self::Saddle as u8 { + Self::Mainhand + } else { + unsafe { core::mem::transmute::(n) } + } + } + + pub const fn name(self) -> &'static str { + match self { + Self::Mainhand => "mainhand", + Self::Feet => "feet", + Self::Legs => "legs", + Self::Chest => "chest", + Self::Head => "head", + Self::Offhand => "offhand", + Self::Body => "body", + Self::Saddle => "saddle", + } + } +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum InteractionHand { + MainHand, + OffHand, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum RecipeBookType { + Crafting, + Furnace, + BlastFurnace, + Smoker, +} diff --git a/haya_protocol/src/item.rs b/haya_protocol/src/item_stack.rs similarity index 91% rename from haya_protocol/src/item.rs rename to haya_protocol/src/item_stack.rs index b474829f..9ae28c2f 100644 --- a/haya_protocol/src/item.rs +++ b/haya_protocol/src/item_stack.rs @@ -1,3 +1,4 @@ +pub mod consumable; pub mod consume_effect; pub mod firework_explosion; pub mod item_attribute_modifiers; @@ -6,6 +7,14 @@ pub mod kinetic_weapon; pub mod suspicious_stew_effects; pub mod tool; +use self::consumable::Consumable; +use self::consume_effect::ConsumeEffect; +use self::firework_explosion::FireworkExplosion; +use self::item_attribute_modifiers::ItemAttributeModifiers; +use self::item_enchantments::ItemEnchantments; +use self::kinetic_weapon::KineticWeapon; +use self::suspicious_stew_effects::SuspiciousStewEffects; +use self::tool::Tool; use crate::advancement::BlockPredicate; use crate::block::{BannerPatternLayers, BeehiveOccupant}; use crate::effect::MobEffect; @@ -14,13 +23,7 @@ use crate::entity::{ ParrotVariant, RabbitVariant, SalmonVariant, TropicalFishPattern, }; use crate::food::FoodProperties; -use crate::item::consume_effect::ConsumeEffect; -use crate::item::firework_explosion::FireworkExplosion; -use crate::item::item_attribute_modifiers::ItemAttributeModifiers; -use crate::item::item_enchantments::ItemEnchantments; -use crate::item::kinetic_weapon::KineticWeapon; -use crate::item::suspicious_stew_effects::SuspiciousStewEffects; -use crate::item::tool::Tool; +use crate::inventory::EquipmentSlot; use crate::map::MapId; use crate::profile::ResolvableProfile; use crate::registry::{ @@ -30,7 +33,7 @@ use crate::registry::{ }; use crate::sound::SoundEvent; use crate::trim::{TrimMaterial, TrimPattern}; -use crate::{Component, DyeColor, EquipmentSlot, Filterable, Holder, HolderSet, LockCode, Rarity}; +use crate::{Component, DyeColor, Filterable, Holder, HolderSet, LockCode, Rarity}; use alloc::vec::Vec; use haya_collection::{List, Map, capacity_fix}; use haya_ident::{Ident, ResourceKey, TagKey}; @@ -173,50 +176,6 @@ pub struct TooltipDisplay<'a> { pub hidden_components: List<'a, TypedDataComponent<'a>>, } -#[derive(Clone, Serialize, Deserialize)] -pub struct Consumable<'a> { - pub consume_seconds: f32, - pub animation: ItemUseAnimation, - pub sound: Holder, sound_event>, -} - -#[derive(Clone, Copy, Serialize, Deserialize)] -#[mser(varint)] -#[repr(u8)] -pub enum ItemUseAnimation { - None, - Eat, - Drink, - Block, - Bow, - Trident, - Crossbow, - Spyglass, - TootHorn, - Brush, - Bundle, - Spear, -} - -impl ItemUseAnimation { - pub const fn name(self) -> &'static str { - match self { - Self::None => "none", - Self::Eat => "eat", - Self::Drink => "drink", - Self::Block => "block", - Self::Bow => "bow", - Self::Trident => "trident", - Self::Crossbow => "crossbow", - Self::Spyglass => "spyglass", - Self::TootHorn => "toot_horn", - Self::Brush => "brush", - Self::Bundle => "bundle", - Self::Spear => "spear", - } - } -} - #[derive(Clone, Serialize, Deserialize)] pub struct UseRemainder<'a> { pub convert_into: OptionalItemStack<'a>, diff --git a/haya_protocol/src/item_stack/consumable.rs b/haya_protocol/src/item_stack/consumable.rs new file mode 100644 index 00000000..0648eb64 --- /dev/null +++ b/haya_protocol/src/item_stack/consumable.rs @@ -0,0 +1,47 @@ +use crate::Holder; +use crate::sound::SoundEvent; +use minecraft_data::sound_event; + +#[derive(Clone, Serialize, Deserialize)] +pub struct Consumable<'a> { + pub consume_seconds: f32, + pub animation: ItemUseAnimation, + pub sound: Holder, sound_event>, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[mser(varint)] +#[repr(u8)] +pub enum ItemUseAnimation { + None, + Eat, + Drink, + Block, + Bow, + Trident, + Crossbow, + Spyglass, + TootHorn, + Brush, + Bundle, + Spear, +} + +impl ItemUseAnimation { + pub const fn name(self) -> &'static str { + match self { + Self::None => "none", + Self::Eat => "eat", + Self::Drink => "drink", + Self::Block => "block", + Self::Bow => "bow", + Self::Trident => "trident", + Self::Crossbow => "crossbow", + Self::Spyglass => "spyglass", + Self::TootHorn => "toot_horn", + Self::Brush => "brush", + Self::Bundle => "bundle", + Self::Spear => "spear", + } + } +} diff --git a/haya_protocol/src/item/consume_effect.rs b/haya_protocol/src/item_stack/consume_effect.rs similarity index 100% rename from haya_protocol/src/item/consume_effect.rs rename to haya_protocol/src/item_stack/consume_effect.rs diff --git a/haya_protocol/src/item/firework_explosion.rs b/haya_protocol/src/item_stack/firework_explosion.rs similarity index 100% rename from haya_protocol/src/item/firework_explosion.rs rename to haya_protocol/src/item_stack/firework_explosion.rs diff --git a/haya_protocol/src/item/item_attribute_modifiers.rs b/haya_protocol/src/item_stack/item_attribute_modifiers.rs similarity index 95% rename from haya_protocol/src/item/item_attribute_modifiers.rs rename to haya_protocol/src/item_stack/item_attribute_modifiers.rs index f512600d..bc7810fc 100644 --- a/haya_protocol/src/item/item_attribute_modifiers.rs +++ b/haya_protocol/src/item_stack/item_attribute_modifiers.rs @@ -1,5 +1,6 @@ +use crate::Component; use crate::attribute::AttributeModifier; -use crate::{Component, EquipmentSlotGroup}; +use crate::inventory::EquipmentSlotGroup; use haya_collection::List; use minecraft_data::attribute; use mser::{Error, Read, Reader, V21, Write, Writer}; diff --git a/haya_protocol/src/item/item_enchantments.rs b/haya_protocol/src/item_stack/item_enchantments.rs similarity index 100% rename from haya_protocol/src/item/item_enchantments.rs rename to haya_protocol/src/item_stack/item_enchantments.rs diff --git a/haya_protocol/src/item/kinetic_weapon.rs b/haya_protocol/src/item_stack/kinetic_weapon.rs similarity index 100% rename from haya_protocol/src/item/kinetic_weapon.rs rename to haya_protocol/src/item_stack/kinetic_weapon.rs diff --git a/haya_protocol/src/item/suspicious_stew_effects.rs b/haya_protocol/src/item_stack/suspicious_stew_effects.rs similarity index 100% rename from haya_protocol/src/item/suspicious_stew_effects.rs rename to haya_protocol/src/item_stack/suspicious_stew_effects.rs diff --git a/haya_protocol/src/item/tool.rs b/haya_protocol/src/item_stack/tool.rs similarity index 100% rename from haya_protocol/src/item/tool.rs rename to haya_protocol/src/item_stack/tool.rs diff --git a/haya_protocol/src/lib.rs b/haya_protocol/src/lib.rs index f59c4d83..4bc06e84 100644 --- a/haya_protocol/src/lib.rs +++ b/haya_protocol/src/lib.rs @@ -1,10 +1,12 @@ #![no_std] +use crate::inventory::HumanoidArm; use alloc::vec::Vec; -use haya_collection::{List, capacity_fix}; +use haya_collection::{List, Map, capacity_fix}; use haya_ident::{Ident, ResourceKey}; -use haya_math::BlockPosPacked; +use haya_math::{BlockPosPacked, Direction, FVec3, IVec3}; use haya_nbt::Tag; +use minecraft_data::data_component_type; use mser::{Either, Error, Read, Reader, Utf8, V21, V32, Write, Writer}; pub mod advancement; @@ -20,7 +22,8 @@ pub mod entity; pub mod entity_data; pub mod food; pub mod game_event; -pub mod item; +pub mod inventory; +pub mod item_stack; pub mod level_event; pub mod map; pub mod minecart; @@ -167,30 +170,6 @@ impl ChatVisibility { } } -#[derive(Clone, Copy, Serialize, Deserialize)] -#[repr(u8)] -#[mser(varint)] -pub enum HumanoidArm { - Left, - Right, -} - -impl HumanoidArm { - pub const fn translation_key(self) -> Translatable<'static> { - Translatable(match self { - Self::Left => "options.mainHand.left", - Self::Right => "options.mainHand.right", - }) - } - - pub const fn name(self) -> &'static str { - match self { - Self::Left => "left", - Self::Right => "right", - } - } -} - #[derive(Clone, Copy, Serialize, Deserialize)] #[repr(u8)] #[mser(varint)] @@ -250,9 +229,6 @@ impl Difficulty { } } -#[derive(Clone, Copy, Serialize, Deserialize)] -pub struct ContainerId(#[mser(varint)] pub u32); - #[derive(Clone, Copy, Serialize, Deserialize)] #[repr(u8)] #[mser(varint)] @@ -333,78 +309,6 @@ pub enum Holder { Direct(T), } -#[derive(Clone, Copy, Serialize, Deserialize)] -#[repr(u8)] -#[mser(varint)] -pub enum EquipmentSlotGroup { - Any, - Mainhand, - Offhand, - Hand, - Feet, - Legs, - Chest, - Head, - Armor, - Body, - Saddle, -} - -impl EquipmentSlotGroup { - pub const fn name(self) -> &'static str { - match self { - Self::Any => "any", - Self::Mainhand => "mainhand", - Self::Offhand => "offhand", - Self::Hand => "hand", - Self::Feet => "feet", - Self::Legs => "legs", - Self::Chest => "chest", - Self::Head => "head", - Self::Armor => "armor", - Self::Body => "body", - Self::Saddle => "saddle", - } - } -} - -#[derive(Clone, Copy, Serialize, Deserialize)] -#[repr(u8)] -#[mser(varint)] -pub enum EquipmentSlot { - Mainhand, - Feet, - Legs, - Chest, - Head, - Offhand, - Body, - Saddle, -} - -impl EquipmentSlot { - pub const fn new(n: u8) -> Self { - if n > Self::Saddle as u8 { - Self::Mainhand - } else { - unsafe { core::mem::transmute::(n) } - } - } - - pub const fn name(self) -> &'static str { - match self { - Self::Mainhand => "mainhand", - Self::Feet => "feet", - Self::Legs => "legs", - Self::Chest => "chest", - Self::Head => "head", - Self::Offhand => "offhand", - Self::Body => "body", - Self::Saddle => "saddle", - } - } -} - #[derive(Clone, Copy, Serialize, Deserialize)] #[repr(u8)] #[mser(varint)] @@ -614,14 +518,6 @@ pub struct GlobalPos<'a> { pub pos: BlockPosPacked, } -#[derive(Clone, Copy, Serialize, Deserialize)] -#[repr(u8)] -#[mser(varint)] -pub enum InteractionHand { - MainHand, - OffHand, -} - #[derive(Clone, Copy, Serialize, Deserialize)] #[repr(u8)] #[mser(varint)] @@ -840,6 +736,233 @@ pub struct RgbColor { pub b: u8, } +#[derive(Clone, Copy, Serialize, Deserialize)] +pub struct MilliSeconds(pub u64); + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum ClickType { + Pickup, + QuickMove, + Swap, + Clone, + Throw, + QuickCraft, + PickupAll, +} + +// CRC32C +#[derive(Clone, Serialize, Deserialize)] +pub struct HashedPatchMap<'a> { + pub added_components: Map<'a, data_component_type, u32, 256>, + pub removed_components: List<'a, data_component_type, 256>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct HashedStack<'a> { + pub item: minecraft_data::item, + #[mser(varint)] + pub count: u32, + pub components: HashedPatchMap<'a>, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +pub struct Input(pub u8); + +impl Input { + pub const FORWARD: u8 = 1; + pub const BACKWARD: u8 = 2; + pub const LEFT: u8 = 4; + pub const RIGHT: u8 = 8; + pub const JUMP: u8 = 16; + pub const SHIFT: u8 = 32; + pub const SPRINT: u8 = 64; +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum CommandBlockEntityMode { + Sequence, + Auto, + Redstone, +} + +#[derive(Serialize, Deserialize, Clone, Copy)] +#[repr(u8)] +#[mser(varint)] +pub enum JointType { + Rollable, + Aligned, +} + +#[derive(Clone, Copy)] +pub struct JointTypeName(pub JointType); + +impl<'a> Read<'a> for JointTypeName { + fn read(buf: &mut Reader<'a>) -> Result { + let a: Utf8 = Utf8::read(buf)?; + Ok(match a.0 { + "rollable" => Self(JointType::Rollable), + _ => Self(JointType::Aligned), + }) + } +} + +impl Write for JointTypeName { + unsafe fn write(&self, w: &mut Writer) { + unsafe { + let a: Utf8 = Utf8(self.0.name()); + a.write(w); + } + } + + fn len_s(&self) -> usize { + let a: Utf8 = Utf8(self.0.name()); + a.len_s() + } +} + +impl JointType { + pub const fn name(self) -> &'static str { + match self { + Self::Rollable => "rollable", + Self::Aligned => "aligned", + } + } +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum StructureUpdateType { + UpdateData, + SaveArea, + LoadArea, + ScanArea, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum StructureMode { + Save, + Load, + Corner, + Data, +} + +impl StructureMode { + pub const fn name(self) -> &'static str { + match self { + Self::Save => "save", + Self::Load => "load", + Self::Corner => "corner", + Self::Data => "data", + } + } +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum Mirror { + None, + LeftRight, + FrontBack, +} + +impl Mirror { + pub const fn name(self) -> &'static str { + match self { + Self::None => "none", + Self::LeftRight => "left_right", + Self::FrontBack => "front_back", + } + } +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum Rotation { + None, + Clockwise90, + Clockwise180, + Counterclockwise90, +} + +impl Rotation { + pub const fn name(self) -> &'static str { + match self { + Self::None => "none", + Self::Clockwise90 => "clockwise_90", + Self::Clockwise180 => "180", + Self::Counterclockwise90 => "counterclockwise_90", + } + } +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum TestBlockMode { + Start, + Log, + Fail, + Accept, +} + +impl TestBlockMode { + pub const fn name(self) -> &'static str { + match self { + Self::Start => "start", + Self::Log => "log", + Self::Fail => "fail", + Self::Accept => "accept", + } + } +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct TestInstanceData<'a> { + pub test: Option>, + pub size: IVec3, + pub rotation: Rotation, + pub ignore_entities: bool, + pub status: TestInstanceStatus, + pub error_message: Option, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum TestInstanceStatus { + Cleared, + Running, + Finished, +} + +impl TestInstanceStatus { + pub const fn name(self) -> &'static str { + match self { + Self::Cleared => "cleared", + Self::Running => "running", + Self::Finished => "finished", + } + } +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct BlockHitResult { + pub block_pos: BlockPosPacked, + pub face: Direction, + pub click: FVec3, + pub inside: bool, + pub world_border_hit: bool, +} + #[cfg(test)] mod tests { use super::*; diff --git a/haya_protocol/src/particle.rs b/haya_protocol/src/particle.rs index 8b081f95..88c997f6 100644 --- a/haya_protocol/src/particle.rs +++ b/haya_protocol/src/particle.rs @@ -1,5 +1,5 @@ use crate::game_event::PositionSource; -use crate::item::ItemStack; +use crate::item_stack::ItemStack; use haya_math::Vec3; use minecraft_data::{block_state, particle_type}; diff --git a/haya_protocol/src/registry.rs b/haya_protocol/src/registry.rs index a0b939ab..8ebd872f 100644 --- a/haya_protocol/src/registry.rs +++ b/haya_protocol/src/registry.rs @@ -1,7 +1,7 @@ use crate::block::BannerPattern; use crate::chat::ChatType; use crate::entity::PaintingVariant; -use crate::item::{Instrument, JukeboxSong}; +use crate::item_stack::{Instrument, JukeboxSong}; use crate::sound::SoundEvent; use crate::trim::{TrimMaterial, TrimPattern}; use crate::{Dialog, Holder}; diff --git a/haya_protocol/src/serverbound.rs b/haya_protocol/src/serverbound.rs index e10ee3bb..36c6aaeb 100644 --- a/haya_protocol/src/serverbound.rs +++ b/haya_protocol/src/serverbound.rs @@ -1,10 +1,12 @@ use minecraft_data::{ - serverbound__configuration, serverbound__handshake, serverbound__login, serverbound__status, + serverbound__configuration, serverbound__handshake, serverbound__login, serverbound__play, + serverbound__status, }; pub mod common; pub mod configuration; pub mod cookie; +pub mod game; pub mod handshake; pub mod login; pub mod ping; @@ -37,6 +39,8 @@ macro_rules! packets { self.$variant(e); } )+ + #[allow(unreachable_patterns)] + _ => todo!(), } Ok(()) } @@ -57,7 +61,7 @@ packets! { StatusHandler, handle, status_request = status::StatusRequest, - ping_request = ping::PingRequest, + ping_request = ping::StatusPingRequest, } packets! { serverbound__login, @@ -67,7 +71,7 @@ packets! { key = login::Key<'_>, custom_query_answer = login::CustomQueryAnswer<'_>, login_acknowledged = login::LoginAcknowledged, - cookie_response = cookie::CookieResponse<'_>, + cookie_response = cookie::LoginCookieResponse<'_>, } packets! { serverbound__configuration, @@ -75,12 +79,83 @@ packets! { handle, client_information = common::ConfigurationClientInformation<'_>, cookie_response = cookie::ConfigurationCookieResponse<'_>, - custom_payload = common::CustomPayload<'_>, + custom_payload = common::ConfigurationCustomPayload<'_>, finish_configuration = configuration::FinishConfiguration, - keep_alive = common::KeepAlive, - pong = common::Pong, - resource_pack = common::ResourcePack, + keep_alive = common::ConfigurationKeepAlive, + pong = common::ConfigurationPong, + resource_pack = common::ConfigurationResourcePack, select_known_packs = configuration::SelectKnownPacks<'_>, custom_click_action = common::CustomClickAction<'_>, accept_code_of_conduct = configuration::AcceptCodeOfConduct, } +packets! { + serverbound__play, + GameHandler, + handle, + accept_teleportation = game::AcceptTeleportation, + block_entity_tag_query = game::BlockEntityTagQuery, + bundle_item_selected = game::BundleItemSelected, + change_difficulty = game::ChangeDifficulty, + change_game_mode = game::ChangeGameMode, + chat_ack = game::ChatAck, + chat_command = game::ChatCommand<'_>, + chat_command_signed = game::ChatCommandSigned<'_>, + chat = game::Chat<'_>, + chat_session_update = game::ChatSessionUpdate<'_>, + chunk_batch_received = game::ChunkBatchReceived, + client_command = game::ClientCommand, + client_tick_end = game::ClientTickEnd, + client_information = game::ClientInformation<'_>, + command_suggestion = game::CommandSuggestion<'_>, + configuration_acknowledged = game::ConfigurationAcknowledged, + container_button_click = game::ContainerButtonClick, + container_click = game::ContainerClick<'_>, + container_close = game::ContainerClose, + container_slot_state_changed = game::ContainerSlotStateChanged, + cookie_response = cookie::GameCookieResponse<'_>, + custom_payload = common::GameCustomPayload<'_>, + debug_subscription_request = game::DebugSubscriptionRequest<'_>, + edit_book = game::EditBook<'_>, + entity_tag_query = game::EntityTagQuery, + interact = game::Interact, + jigsaw_generate = game::JigsawGenerate, + keep_alive = common::GameKeepAlive, + lock_difficulty = game::LockDifficulty, + move_player_pos = game::MovePlayerPos, + move_player_pos_rot = game::MovePlayerPosRot, + move_player_rot = game::MovePlayerRot, + move_player_status_only = game::MovePlayerStatusOnly, + move_vehicle = game::MoveVehicle, + paddle_boat = game::PaddleBoat, + pick_item_from_block = game::PickItemFromBlock, + pick_item_from_entity = game::PickItemFromEntity, + ping_request = ping::GamePingRequest, + place_recipe = game::PlaceRecipe, + player_abilities = game::PlayerAbilities, + player_action = game::PlayerAction, + player_command = game::PlayerCommand, + player_input = game::PlayerInput, + player_loaded = game::PlayerLoaded, + pong = common::GamePong, + recipe_book_change_settings = game::RecipeBookChangeSettings, + recipe_book_seen_recipe = game::RecipeBookSeenRecipe, + rename_item = game::RenameItem<'_>, + resource_pack = common::GameResourcePack, + seen_advancements = game::SeenAdvancements<'_>, + select_trade = game::SelectTrade, + set_beacon = game::SetBeacon, + set_carried_item = game::SetCarriedItem, + set_command_block = game::SetCommandBlock<'_>, + set_command_minecart = game::SetCommandMinecart<'_>, + set_creative_mode_slot = game::SetCreativeModeSlot<'_>, + set_jigsaw_block = game::SetJigsawBlock<'_>, + set_structure_block = game::SetStructureBlock<'_>, + set_test_block = game::SetTestBlock<'_>, + sign_update = game::SignUpdate<'_>, + swing = game::Swing, + teleport_to_entity = game::TeleportToEntity, + test_instance_block_action = game::TestInstanceBlockAction<'_>, + use_item_on = game::UseItemOn, + use_item = game::UseItem, + custom_click_action = game::CustomClickAction<'_>, +} diff --git a/haya_protocol/src/serverbound/common.rs b/haya_protocol/src/serverbound/common.rs index b1b37253..bba9630b 100644 --- a/haya_protocol/src/serverbound/common.rs +++ b/haya_protocol/src/serverbound/common.rs @@ -7,22 +7,46 @@ use uuid::Uuid; #[derive(Clone, Serialize, Deserialize)] pub struct ConfigurationClientInformation<'a>(pub ClientInformation<'a>); +#[derive(Clone, Serialize, Deserialize)] +pub struct ConfigurationCustomPayload<'a>(pub CustomPayload<'a>); + +#[derive(Clone, Serialize, Deserialize)] +pub struct GameCustomPayload<'a>(pub CustomPayload<'a>); + #[derive(Clone, Serialize, Deserialize)] pub struct CustomPayload<'a> { pub id: Ident<'a>, pub payload: Rest<'a, 32767>, } +#[derive(Clone, Serialize, Deserialize)] +pub struct ConfigurationKeepAlive(pub KeepAlive); + +#[derive(Clone, Serialize, Deserialize)] +pub struct GameKeepAlive(pub KeepAlive); + #[derive(Clone, Serialize, Deserialize)] pub struct KeepAlive { pub id: u64, } +#[derive(Clone, Serialize, Deserialize)] +pub struct ConfigurationPong(pub Pong); + +#[derive(Clone, Serialize, Deserialize)] +pub struct GamePong(pub Pong); + #[derive(Clone, Serialize, Deserialize)] pub struct Pong { pub id: u32, } +#[derive(Clone, Serialize, Deserialize)] +pub struct ConfigurationResourcePack(pub ResourcePack); + +#[derive(Clone, Serialize, Deserialize)] +pub struct GameResourcePack(pub ResourcePack); + #[derive(Clone, Serialize, Deserialize)] pub struct ResourcePack { pub id: Uuid, diff --git a/haya_protocol/src/serverbound/cookie.rs b/haya_protocol/src/serverbound/cookie.rs index 70cf5093..39ea9bd0 100644 --- a/haya_protocol/src/serverbound/cookie.rs +++ b/haya_protocol/src/serverbound/cookie.rs @@ -7,6 +7,9 @@ pub struct LoginCookieResponse<'a>(pub CookieResponse<'a>); #[derive(Clone, Serialize, Deserialize)] pub struct ConfigurationCookieResponse<'a>(pub CookieResponse<'a>); +#[derive(Clone, Serialize, Deserialize)] +pub struct GameCookieResponse<'a>(pub CookieResponse<'a>); + #[derive(Clone, Serialize, Deserialize)] pub struct CookieResponse<'a> { pub key: Ident<'a>, diff --git a/haya_protocol/src/serverbound/game.rs b/haya_protocol/src/serverbound/game.rs new file mode 100644 index 00000000..d5ddb20d --- /dev/null +++ b/haya_protocol/src/serverbound/game.rs @@ -0,0 +1,554 @@ +use crate::chat::{LastSeenMessagesUpdate, MessageSignature, RemoteChatSession}; +use crate::command::ArgumentSignatures; +use crate::crafting::RecipeDisplayId; +use crate::inventory::{ContainerId, InteractionHand, RecipeBookType}; +use crate::{ + BlockHitResult, ClickType, CommandBlockEntityMode, Difficulty, GameType, HashedStack, Input, + JointTypeName, MilliSeconds, Mirror, Rotation, StructureMode, StructureUpdateType, + TestBlockMode, TestInstanceData, +}; +use haya_collection::{List, Map}; +use haya_ident::Ident; +use haya_math::{BlockPosPacked, ByteDirection, FVec3, Vec3}; +use minecraft_data::{debug_subscription, mob_effect}; +use mser::{ByteArray, Rest, Utf8}; +use uuid::Uuid; + +#[derive(Clone, Serialize, Deserialize)] +pub struct AcceptTeleportation { + #[mser(varint)] + id: u32, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct BlockEntityTagQuery { + #[mser(varint)] + pub transaction_id: u32, + pub pos: BlockPosPacked, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct BundleItemSelected { + #[mser(varint)] + pub slot_id: u32, + #[mser(varint, filter = valicate_bundle_item_selected)] + pub selected_item_index: u32, +} + +fn valicate_bundle_item_selected(x: &u32) -> bool { + let x = *x as i32; + x >= 0 || x == -1 +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ChangeDifficulty { + pub difficulty: Difficulty, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ChangeGameMode { + pub mode: GameType, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ChatAck { + pub offset: u32, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ChatCommand<'a> { + pub command: Utf8<'a>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ChatCommandSigned<'a> { + pub command: Utf8<'a>, + pub time_stamp: MilliSeconds, + pub salt: u64, + pub argument_signatures: ArgumentSignatures<'a>, + pub last_seen_messages: LastSeenMessagesUpdate<'a>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct Chat<'a> { + pub message: Utf8<'a, 256>, + pub time_stamp: MilliSeconds, + pub salt: u64, + pub signature: Option>, + pub last_seen_messages: LastSeenMessagesUpdate<'a>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ChatSessionUpdate<'a> { + pub chat_session: RemoteChatSession<'a>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ChunkBatchReceived { + pub desired_chunks_per_tick: f32, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ClientCommand { + pub action: ClientCommandAction, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum ClientCommandAction { + PerformRespawn, + RequestStats, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ClientTickEnd {} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ClientInformation<'a> { + pub information: crate::ClientInformation<'a>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct CommandSuggestion<'a> { + #[mser(varint)] + pub id: u32, + pub command: Utf8<'a, 32500>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ConfigurationAcknowledged {} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ContainerButtonClick { + pub container_id: ContainerId, + #[mser(varint)] + pub button_id: u32, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ContainerClick<'a> { + pub container_id: ContainerId, + #[mser(varint)] + pub state_id: u32, + pub slot_num: u16, + pub button_num: u8, + pub click_type: ClickType, + pub changed_slots: Map<'a, u16, Option>, 128>, + pub carried_item: Option>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ContainerClose { + pub container_id: ContainerId, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct ContainerSlotStateChanged { + #[mser(varint)] + pub slot_id: u32, + pub container_id: ContainerId, + pub new_state: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct DebugSubscriptionRequest<'a> { + pub subscriptions: List<'a, debug_subscription>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct EditBook<'a> { + #[mser(varint)] + pub slot: u32, + pub pages: List<'a, Utf8<'a, 1024>, 100>, + pub title: Option>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct EntityTagQuery { + #[mser(varint)] + pub transaction_id: u32, + #[mser(varint)] + pub entity_id: u32, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct Interact { + #[mser(varint)] + pub entity_id: u32, + pub action: InteractAction, + pub using_secondary_action: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +#[mser(header = InteractActionType, camel_case)] +pub enum InteractAction { + Interact { + hand: InteractionHand, + }, + Attack, + InteractAt { + location: FVec3, + hand: InteractionHand, + }, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum InteractActionType { + Interact, + Attack, + InteractAt, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct JigsawGenerate { + pub pos: BlockPosPacked, + #[mser(varint)] + pub levels: u32, + pub keep_jigsaws: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct LockDifficulty { + pub locked: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct MovePlayerPos { + pub pos: Vec3, + pub flags: MovePlayerFlags, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +pub struct MovePlayerFlags(pub u8); + +impl MovePlayerFlags { + pub const ON_GROUND: u8 = 1; + pub const HORIZONTAL_COLLISION: u8 = 2; +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct MovePlayerPosRot { + pub pos: Vec3, + pub y_rot: f32, + pub x_rot: f32, + pub flags: MovePlayerFlags, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct MovePlayerRot { + pub y_rot: f32, + pub x_rot: f32, + pub flags: MovePlayerFlags, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct MovePlayerStatusOnly { + pub flags: MovePlayerFlags, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct MoveVehicle { + pub position: Vec3, + pub y_rot: f32, + pub x_rot: f32, + pub on_ground: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct PaddleBoat { + pub left: bool, + pub right: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct PickItemFromBlock { + pub pos: BlockPosPacked, + pub include_data: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct PickItemFromEntity { + #[mser(varint)] + pub id: u32, + pub include_data: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct PlaceRecipe { + pub container_id: ContainerId, + pub recipe: RecipeDisplayId, + pub use_max_items: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct PlayerAbilities { + pub flags: PlayerAbilitiesFlags, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +pub struct PlayerAbilitiesFlags(pub u8); + +impl PlayerAbilitiesFlags { + pub const FLYING: u8 = 2; + + pub const fn flying(self) -> bool { + self.0 & Self::FLYING != 0 + } +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct PlayerAction { + pub action: PlayerActionType, + pub pos: BlockPosPacked, + pub direction: ByteDirection, + #[mser(varint)] + pub sequence: u32, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum PlayerActionType { + StartDestroyBlock, + AbortDestroyBlock, + StopDestroyBlock, + DropAllItems, + DropItem, + ReleaseUseItem, + SwapItemWithOffhand, + Stab, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct PlayerCommand { + #[mser(varint)] + pub id: u32, + pub action: PlayerCommandAction, + #[mser(varint)] + pub data: u32, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum PlayerCommandAction { + StopSleeping, + StartSprinting, + StopSprinting, + StartRidingJump, + StopRidingJump, + OpenInventory, + StartFallFlying, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct PlayerInput { + pub input: Input, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct PlayerLoaded {} + +#[derive(Clone, Serialize, Deserialize)] +pub struct RecipeBookChangeSettings { + pub book_type: RecipeBookType, + pub is_open: bool, + pub is_filtering: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct RecipeBookSeenRecipe { + pub recipe: RecipeDisplayId, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct RenameItem<'a> { + pub name: Utf8<'a>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SeenAdvancements<'a> { + pub action: SeenAdvancementsAction<'a>, +} + +#[derive(Clone, Serialize, Deserialize)] +#[mser(header = SeenAdvancementsType, camel_case)] +pub enum SeenAdvancementsAction<'a> { + OpenedTab { tab: Ident<'a> }, + ClosedScreen, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum SeenAdvancementsType { + OpenedTab, + ClosedScreen, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SelectTrade { + #[mser(varint)] + pub item: u32, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SetBeacon { + pub primary: Option, + pub secondary: Option, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SetCarriedItem { + pub slot: u16, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SetCommandBlock<'a> { + pub pos: BlockPosPacked, + pub command: Utf8<'a>, + pub mode: CommandBlockEntityMode, + pub flags: SetCommandBlockFlags, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SetCommandBlockFlags(pub u8); + +impl SetCommandBlockFlags { + pub const TRACK_OUTPUT: u8 = 1; + pub const CONDITIONAL: u8 = 2; + pub const AUTOMATIC: u8 = 4; +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SetCommandMinecart<'a> { + #[mser(varint)] + pub entity: u32, + pub command: Utf8<'a>, + pub track_output: bool, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SetCreativeModeSlot<'a> { + pub slot_num: u16, + // pub item_stack: OptionalItemStack<'a>, + pub item_stack: Rest<'a>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SetJigsawBlock<'a> { + pub pos: BlockPosPacked, + pub name: Ident<'a>, + pub target: Ident<'a>, + pub pool: Ident<'a>, + pub final_state: Utf8<'a>, + pub joint: JointTypeName, + #[mser(varint)] + pub selection_priority: u32, + #[mser(varint)] + pub placement_priority: u32, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SetStructureBlock<'a> { + pub pos: BlockPosPacked, + pub update_type: StructureUpdateType, + pub mode: StructureMode, + pub name: Utf8<'a>, + pub offset_x: i8, + pub offset_y: i8, + pub offset_z: i8, + pub size_x: i8, + pub size_y: i8, + pub size_z: i8, + pub mirror: Mirror, + pub rotation: Rotation, + pub data: Utf8<'a, 128>, + pub integrity: f32, + #[mser(varint)] + pub seed: u64, + pub flags: SetStructureBlockFlags, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +pub struct SetStructureBlockFlags(pub u8); + +impl SetStructureBlockFlags { + pub const IGNORE_ENTITIES: u8 = 1; + pub const SHOW_AIR: u8 = 2; + pub const SHOW_BOUNDING_BOX: u8 = 4; + pub const STRICT: u8 = 8; +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SetTestBlock<'a> { + pub position: BlockPosPacked, + pub mode: TestBlockMode, + pub message: Utf8<'a>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct SignUpdate<'a> { + pub pos: BlockPosPacked, + pub is_front_text: bool, + pub line1: Utf8<'a, 384>, + pub line2: Utf8<'a, 384>, + pub line3: Utf8<'a, 384>, + pub line4: Utf8<'a, 384>, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct Swing { + pub hand: InteractionHand, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct TeleportToEntity { + pub uuid: Uuid, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct TestInstanceBlockAction<'a> { + pub pos: BlockPosPacked, + pub action: TestInstanceBlockActionType, + pub data: TestInstanceData<'a>, +} + +#[derive(Clone, Copy, Serialize, Deserialize)] +#[repr(u8)] +#[mser(varint)] +pub enum TestInstanceBlockActionType { + Init, + Query, + Set, + Reset, + Save, + Export, + Run, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct UseItemOn { + pub hand: InteractionHand, + pub block_hit: BlockHitResult, + #[mser(varint)] + pub sequence: u32, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct UseItem { + pub hand: InteractionHand, + #[mser(varint)] + pub sequence: u32, + pub y_rot: f32, + pub x_rot: f32, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct CustomClickAction<'a> { + pub id: Ident<'a>, + pub payload: ByteArray<'a, 65536>, +} diff --git a/haya_protocol/src/serverbound/ping.rs b/haya_protocol/src/serverbound/ping.rs index 8f107765..53c7a80b 100644 --- a/haya_protocol/src/serverbound/ping.rs +++ b/haya_protocol/src/serverbound/ping.rs @@ -1,3 +1,9 @@ +#[derive(Clone, Serialize, Deserialize)] +pub struct StatusPingRequest(pub PingRequest); + +#[derive(Clone, Serialize, Deserialize)] +pub struct GamePingRequest(pub PingRequest); + #[derive(Clone, Serialize, Deserialize)] pub struct PingRequest { pub time: u64, diff --git a/haya_protocol/src/trading.rs b/haya_protocol/src/trading.rs index 323a7d7c..3e5961f2 100644 --- a/haya_protocol/src/trading.rs +++ b/haya_protocol/src/trading.rs @@ -1,4 +1,4 @@ -use crate::item::{DataComponentExactPredicate, ItemStack}; +use crate::item_stack::{DataComponentExactPredicate, ItemStack}; use minecraft_data::item; #[derive(Clone, Serialize, Deserialize)] diff --git a/haya_ser/src/lib.rs b/haya_ser/src/lib.rs index 05b64763..adf6c3ea 100644 --- a/haya_ser/src/lib.rs +++ b/haya_ser/src/lib.rs @@ -90,10 +90,7 @@ impl<'a, const MAX: usize> Write for ByteArray<'a, MAX> { impl<'a, const MAX: usize> Read<'a> for ByteArray<'a, MAX> { fn read(buf: &mut Reader<'a>) -> Result { - let len = V21::read(buf)?.0 as usize; - if len > MAX { - return Err(Error); - } + let len = read_v21_len(buf, MAX)?; Ok(Self(buf.read_slice(len)?)) } } @@ -101,10 +98,6 @@ impl<'a, const MAX: usize> Read<'a> for ByteArray<'a, MAX> { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Utf8<'a, const MAX: usize = 32767>(pub &'a str); -impl<'a, const MAX: usize> Utf8<'a, MAX> { - const MAX_BYTES: usize = MAX.checked_mul(3).unwrap(); -} - impl<'a, const MAX: usize> Write for Utf8<'a, MAX> { #[inline] unsafe fn write(&self, w: &mut Writer) { @@ -123,17 +116,14 @@ impl<'a, const MAX: usize> Write for Utf8<'a, MAX> { impl<'a, const MAX: usize> Read<'a> for Utf8<'a, MAX> { #[inline] fn read(buf: &mut Reader<'a>) -> Result { - let len = V21::read(buf)?.0 as usize; - if len > Self::MAX_BYTES { - return Err(Error); - } + let len = read_v21_len(buf, MAX.saturating_mul(3))?; let bytes = buf.read_slice(len)?; let s = match core::str::from_utf8(bytes) { Ok(x) => x, Err(_) => return Err(Error), }; - if s.chars().map(|x| x.len_utf16()).sum::() <= MAX { - Ok(Utf8(s)) + if s.chars().map(char::len_utf16).sum::() <= MAX { + Ok(Self(s)) } else { Err(Error) } @@ -202,3 +192,35 @@ impl<'a, const MAX: usize> Write for Rest<'a, MAX> { self.0.len() } } + +#[inline(always)] +pub fn read_v21_len(buf: &mut Reader<'_>, max: usize) -> Result { + let len = V21::read(buf)?.0 as usize; + if len > max { + cold_path(); + return Err(Error); + } + Ok(len) +} + +#[derive(Debug, Clone, Copy)] +pub struct FixedByteArray<'a, const L: usize>(pub &'a [u8; L]); + +impl<'a, const L: usize> Read<'a> for FixedByteArray<'a, L> { + fn read(buf: &mut Reader<'a>) -> Result { + match buf.read_array() { + Ok(x) => Ok(Self(x)), + Err(e) => Err(e), + } + } +} + +impl<'a, const L: usize> Write for FixedByteArray<'a, L> { + unsafe fn write(&self, w: &mut Writer) { + unsafe { w.write(self.0) } + } + + fn len_s(&self) -> usize { + self.0.len() + } +} diff --git a/haya_ser_macro/src/deserialize.rs b/haya_ser_macro/src/deserialize.rs index 53ef974c..c0d242a3 100644 --- a/haya_ser_macro/src/deserialize.rs +++ b/haya_ser_macro/src/deserialize.rs @@ -6,6 +6,7 @@ use quote::{ToTokens, TokenStreamExt, quote}; pub fn deserialize_struct( input: syn::DeriveInput, cratename: syn::Path, + attrs: Attrs, ) -> syn::Result { let name = &input.ident; let generics = &input.generics; @@ -26,14 +27,27 @@ pub fn deserialize_struct( let read = fields .iter() .map(|(field, attrs, m)| read_field(&cratename, field, attrs, m)); + let read = quote!(Self { + #(#read,)* + }); + let read = if let Some(filter) = attrs.filter { + quote! { + let __v = #read; + if #filter(&__v) { + ::core::result::Result::Ok(__v) + } else { + ::core::result::Result::Err(::#cratename::Error) + } + } + } else { + quote!(::core::result::Result::Ok(#read)) + }; Ok(quote! { #[automatically_derived] impl <'__a #impl_generics ::#cratename::Read<'__a> for #name #tok { #[inline] fn read(__r: &mut ::#cratename::Reader<'__a>) -> ::core::result::Result { - ::core::result::Result::Ok(Self { - #(#read,)* - }) + #read } } }) @@ -98,9 +112,9 @@ pub fn deserialize_enum( } read = Some(quote! { - ::core::result::Result::Ok(match <#header as ::#cratename::Read>::read(__r)? { + match <#header as ::#cratename::Read>::read(__r)? { #(#match_arms,)* - }) + } }); } @@ -113,6 +127,19 @@ pub fn deserialize_enum( )); } }; + let read = if let Some(filter) = attrs.filter { + quote! { + let __v = #read; + if #filter(&__v) { + ::core::result::Result::Ok(__v) + } else { + ::core::result::Result::Err(::#cratename::Error) + } + } + } else { + quote!(::core::result::Result::Ok(#read)) + }; + let generics = &input.generics; let has_lifetimes = generics.lifetimes().next().is_some(); @@ -173,41 +200,41 @@ fn repr_enum( lit: syn::LitInt, ) -> TokenStream { if !varint { - quote! { + quote! {{ let __x = <#path as ::#cratename::Read>::read(__r)?; if __x < #lit { - unsafe { ::core::result::Result::Ok(::core::mem::transmute::<#path, Self>(__x as #path) ) } + unsafe { ::core::mem::transmute::<#path, Self>(__x as #path) } } else { - unsafe { ::core::result::Result::Ok(::core::mem::transmute::<#path, Self>(0) ) } + unsafe { ::core::mem::transmute::<#path, Self>(0) } } - } + }} } else if path == "u64" { - quote! { + quote! {{ let __x = <::#cratename::V64 as ::#cratename::Read>::read(__r)?.0; if __x < #lit { - unsafe { ::core::result::Result::Ok(::core::mem::transmute::(__x) ) } + unsafe { ::core::mem::transmute::(__x) } } else { - unsafe { ::core::result::Result::Ok(::core::mem::transmute::(0) ) } + unsafe { ::core::mem::transmute::(0) } } - } + }} } else if len > V21MAX { - quote! { + quote! {{ let __x = <::#cratename::V32 as ::#cratename::Read>::read(__r)?.0; if __x < #lit { - unsafe { ::core::result::Result::Ok(::core::mem::transmute::(__x) ) } + unsafe { ::core::mem::transmute::(__x) } } else { - unsafe { ::core::result::Result::Ok(::core::mem::transmute::(0) ) } + unsafe { ::core::mem::transmute::(0) } } - } + }} } else { - quote! { + quote! {{ let __x = <::#cratename::V21 as ::#cratename::Read>::read(__r)?.0; if __x < #lit { - unsafe { ::core::result::Result::Ok(::core::mem::transmute::<#path, Self>(__x as #path) ) } + unsafe { ::core::mem::transmute::<#path, Self>(__x as #path) } } else { - unsafe { ::core::result::Result::Ok(::core::mem::transmute::<#path, Self>(0) ) } + unsafe { ::core::mem::transmute::<#path, Self>(0) } } - } + }} } } diff --git a/haya_ser_macro/src/lib.rs b/haya_ser_macro/src/lib.rs index 6cd77489..1a8cfe97 100644 --- a/haya_ser_macro/src/lib.rs +++ b/haya_ser_macro/src/lib.rs @@ -28,6 +28,7 @@ struct Attrs { varint: bool, header: Option, camel_case: bool, + filter: Option, } impl syn::parse::Parse for Attrs { @@ -45,6 +46,10 @@ impl syn::parse::Parse for Attrs { } else if lookahead.peek(kw::camel_case) { let _: kw::camel_case = input.parse()?; attr.camel_case = true; + } else if lookahead.peek(kw::filter) { + let _: kw::filter = input.parse()?; + let _: syn::Token![=] = input.parse()?; + attr.filter = Some(input.parse::()?); } else { return Err(lookahead.error()); } @@ -100,7 +105,7 @@ pub fn serialize(input: TokenStream) -> TokenStream { #[proc_macro_derive(Deserialize, attributes(mser))] pub fn deserialize(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as syn::DeriveInput); - let (attr, cratename) = match crate_name(&input) { + let (attrs, cratename) = match crate_name(&input) { Ok(cratename) => cratename, Err(err) => { return err.to_compile_error().into(); @@ -108,8 +113,8 @@ pub fn deserialize(input: TokenStream) -> TokenStream { }; let x = match input.data { - syn::Data::Struct(_) => deserialize::deserialize_struct(input, cratename), - syn::Data::Enum(_) => deserialize::deserialize_enum(input, cratename, attr), + syn::Data::Struct(_) => deserialize::deserialize_struct(input, cratename, attrs), + syn::Data::Enum(_) => deserialize::deserialize_enum(input, cratename, attrs), syn::Data::Union(_) => Err(syn::Error::new_spanned(input, "unions are not supported")), }; match x { @@ -119,8 +124,8 @@ pub fn deserialize(input: TokenStream) -> TokenStream { } struct FieldAttrs { - pub filter: Option, - pub varint: bool, + filter: Option, + varint: bool, } impl syn::parse::Parse for FieldAttrs {