diff --git a/crates/packet_inspector/src/main.rs b/crates/packet_inspector/src/main.rs index d13a221..6979001 100644 --- a/crates/packet_inspector/src/main.rs +++ b/crates/packet_inspector/src/main.rs @@ -23,7 +23,7 @@ use valence_protocol::packet::c2s::status::{QueryPingC2s, QueryRequestC2s}; use valence_protocol::packet::s2c::login::LoginSuccessS2c; use valence_protocol::packet::s2c::status::{QueryPongS2c, QueryResponseS2c}; use valence_protocol::packet::{C2sPlayPacket, S2cLoginPacket, S2cPlayPacket}; -use valence_protocol::{DecodePacket, EncodePacket}; +use valence_protocol::Packet; #[derive(Parser, Clone, Debug)] #[clap(author, version, about)] @@ -65,7 +65,7 @@ struct State { impl State { pub async fn rw_packet<'a, P>(&'a mut self) -> anyhow::Result

where - P: DecodePacket<'a> + EncodePacket, + P: Packet<'a>, { while !self.dec.has_next_packet()? { self.dec.reserve(4096); diff --git a/crates/valence/src/client.rs b/crates/valence/src/client.rs index c20c5e1..8c1b529 100644 --- a/crates/valence/src/client.rs +++ b/crates/valence/src/client.rs @@ -29,7 +29,7 @@ use valence_protocol::text::Text; use valence_protocol::types::{GameMode, GlobalPos, Property, SoundCategory}; use valence_protocol::username::Username; use valence_protocol::var_int::VarInt; -use valence_protocol::EncodePacket; +use valence_protocol::Packet; use crate::dimension::DimensionId; use crate::entity::data::Player; @@ -173,9 +173,9 @@ impl Client { /// /// If encoding the packet fails, the client is disconnected. Has no /// effect if the client is already disconnected. - pub fn write_packet

(&mut self, pkt: &P) + pub fn write_packet<'a, P>(&mut self, pkt: &P) where - P: EncodePacket + ?Sized, + P: Packet<'a>, { self.enc.write_packet(pkt); } @@ -606,9 +606,9 @@ impl Client { } impl WritePacket for Client { - fn write_packet

(&mut self, packet: &P) + fn write_packet<'a, P>(&mut self, packet: &P) where - P: EncodePacket + ?Sized, + P: Packet<'a>, { self.enc.write_packet(packet) } diff --git a/crates/valence/src/instance.rs b/crates/valence/src/instance.rs index 504dba6..b62a76f 100644 --- a/crates/valence/src/instance.rs +++ b/crates/valence/src/instance.rs @@ -16,7 +16,7 @@ use valence_protocol::packet::s2c::play::{OverlayMessageS2c, ParticleS2c, PlaySo use valence_protocol::sound::Sound; use valence_protocol::text::Text; use valence_protocol::types::SoundCategory; -use valence_protocol::EncodePacket; +use valence_protocol::Packet; use crate::dimension::DimensionId; use crate::entity::McEntity; @@ -313,9 +313,9 @@ impl Instance { /// /// This is more efficient than sending the packet to each client /// individually. - pub fn write_packet

(&mut self, pkt: &P) + pub fn write_packet<'a, P>(&mut self, pkt: &P) where - P: EncodePacket + ?Sized, + P: Packet<'a>, { PacketWriter::new( &mut self.packet_buf, @@ -340,9 +340,9 @@ impl Instance { /// /// This is more efficient than sending the packet to each client /// individually. - pub fn write_packet_at

(&mut self, pkt: &P, pos: impl Into) + pub fn write_packet_at<'a, P>(&mut self, pkt: &P, pos: impl Into) where - P: EncodePacket + ?Sized, + P: Packet<'a>, { let pos = pos.into(); if let Some(cell) = self.partition.get_mut(&pos) { diff --git a/crates/valence/src/packet.rs b/crates/valence/src/packet.rs index 744d518..d85833b 100644 --- a/crates/valence/src/packet.rs +++ b/crates/valence/src/packet.rs @@ -2,20 +2,20 @@ use std::io::Write; use tracing::warn; use valence_protocol::codec::{encode_packet, encode_packet_compressed, PacketEncoder}; -use valence_protocol::EncodePacket; +use valence_protocol::Packet; pub(crate) trait WritePacket { - fn write_packet

(&mut self, packet: &P) + fn write_packet<'a, P>(&mut self, packet: &P) where - P: EncodePacket + ?Sized; + P: Packet<'a>; fn write_packet_bytes(&mut self, bytes: &[u8]); } impl WritePacket for &mut W { - fn write_packet

(&mut self, packet: &P) + fn write_packet<'a, P>(&mut self, packet: &P) where - P: EncodePacket + ?Sized, + P: Packet<'a>, { (*self).write_packet(packet) } @@ -42,9 +42,9 @@ impl<'a> PacketWriter<'a> { } impl WritePacket for PacketWriter<'_> { - fn write_packet

(&mut self, pkt: &P) + fn write_packet<'a, P>(&mut self, pkt: &P) where - P: EncodePacket + ?Sized, + P: Packet<'a>, { let res = if let Some(threshold) = self.threshold { encode_packet_compressed(self.buf, pkt, threshold, self.scratch) @@ -65,9 +65,9 @@ impl WritePacket for PacketWriter<'_> { } impl WritePacket for PacketEncoder { - fn write_packet

(&mut self, packet: &P) + fn write_packet<'a, P>(&mut self, packet: &P) where - P: EncodePacket + ?Sized, + P: Packet<'a>, { if let Err(e) = self.append_packet(packet) { warn!("failed to write packet: {e:#}"); diff --git a/crates/valence/src/server/connection.rs b/crates/valence/src/server/connection.rs index a77cf28..1578bc2 100644 --- a/crates/valence/src/server/connection.rs +++ b/crates/valence/src/server/connection.rs @@ -10,7 +10,7 @@ use tokio::task::JoinHandle; use tokio::time::timeout; use tracing::debug; use valence_protocol::codec::{PacketDecoder, PacketEncoder}; -use valence_protocol::{DecodePacket, EncodePacket}; +use valence_protocol::Packet; use crate::client::{Client, ClientConnection}; use crate::server::byte_channel::{ @@ -52,9 +52,9 @@ where } } - pub async fn send_packet

(&mut self, pkt: &P) -> anyhow::Result<()> + pub async fn send_packet<'a, P>(&mut self, pkt: &P) -> anyhow::Result<()> where - P: EncodePacket + ?Sized, + P: Packet<'a>, { self.enc.append_packet(pkt)?; let bytes = self.enc.take(); @@ -64,7 +64,7 @@ where pub async fn recv_packet<'a, P>(&'a mut self) -> anyhow::Result

where - P: DecodePacket<'a>, + P: Packet<'a>, { timeout(self.timeout, async { while !self.dec.has_next_packet()? { diff --git a/crates/valence/src/unit_test/util.rs b/crates/valence/src/unit_test/util.rs index dfea93f..3cff187 100644 --- a/crates/valence/src/unit_test/util.rs +++ b/crates/valence/src/unit_test/util.rs @@ -6,7 +6,7 @@ use bytes::BytesMut; use valence_protocol::codec::{PacketDecoder, PacketEncoder}; use valence_protocol::packet::S2cPlayPacket; use valence_protocol::username::Username; -use valence_protocol::EncodePacket; +use valence_protocol::Packet; use crate::client::{Client, ClientConnection}; use crate::config::{ConnectionMode, ServerPlugin}; @@ -135,7 +135,7 @@ impl MockClientHelper { /// Inject a packet to be treated as a packet inbound to the server. Panics /// if the packet cannot be sent. - pub fn send(&mut self, packet: &(impl EncodePacket + ?Sized)) { + pub fn send<'a>(&mut self, packet: &impl Packet<'a>) { self.enc .append_packet(packet) .expect("failed to encode packet"); diff --git a/crates/valence_protocol/src/codec.rs b/crates/valence_protocol/src/codec.rs index b154bda..45f65c7 100644 --- a/crates/valence_protocol/src/codec.rs +++ b/crates/valence_protocol/src/codec.rs @@ -5,7 +5,7 @@ use bytes::{Buf, BufMut, BytesMut}; use tracing::debug; use crate::var_int::{VarInt, VarIntDecodeError}; -use crate::{DecodePacket, Encode, EncodePacket, Result, MAX_PACKET_SIZE}; +use crate::{Encode, Packet, Result, MAX_PACKET_SIZE}; /// The AES block cipher with a 128 bit key, using the CFB-8 mode of /// operation. @@ -33,9 +33,9 @@ impl PacketEncoder { self.buf.extend_from_slice(bytes) } - pub fn prepend_packet

(&mut self, pkt: &P) -> Result<()> + pub fn prepend_packet<'a, P>(&mut self, pkt: &P) -> Result<()> where - P: EncodePacket + ?Sized, + P: Packet<'a>, { let start_len = self.buf.len(); self.append_packet(pkt)?; @@ -54,9 +54,9 @@ impl PacketEncoder { Ok(()) } - pub fn append_packet

(&mut self, pkt: &P) -> Result<()> + pub fn append_packet<'a, P>(&mut self, pkt: &P) -> Result<()> where - P: EncodePacket + ?Sized, + P: Packet<'a>, { let start_len = self.buf.len(); @@ -171,9 +171,9 @@ impl PacketEncoder { } } -pub fn encode_packet

(buf: &mut Vec, pkt: &P) -> Result<()> +pub fn encode_packet<'a, P>(buf: &mut Vec, pkt: &P) -> Result<()> where - P: EncodePacket + ?Sized, + P: Packet<'a>, { let start_len = buf.len(); @@ -201,14 +201,14 @@ where } #[cfg(feature = "compression")] -pub fn encode_packet_compressed

( +pub fn encode_packet_compressed<'a, P>( buf: &mut Vec, pkt: &P, threshold: u32, scratch: &mut Vec, ) -> Result<()> where - P: EncodePacket + ?Sized, + P: Packet<'a>, { use std::io::Read; @@ -287,7 +287,7 @@ impl PacketDecoder { pub fn try_next_packet<'a, P>(&'a mut self) -> Result> where - P: DecodePacket<'a>, + P: Packet<'a>, { self.buf.advance(self.cursor); self.cursor = 0; @@ -368,7 +368,7 @@ impl PacketDecoder { #[track_caller] pub fn collect_into_vec<'a, P>(&'a mut self) -> Result> where - P: DecodePacket<'a>, + P: Packet<'a>, { #[cfg(feature = "encryption")] assert!( @@ -512,7 +512,7 @@ mod tests { #[cfg(feature = "encryption")] const CRYPT_KEY: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - #[derive(PartialEq, Debug, Encode, EncodePacket, Decode, DecodePacket)] + #[derive(PartialEq, Debug, Encode, Decode, Packet)] #[packet_id = 42] struct TestPacket<'a> { a: bool, diff --git a/crates/valence_protocol/src/lib.rs b/crates/valence_protocol/src/lib.rs index 4b2b6f9..6f02f34 100644 --- a/crates/valence_protocol/src/lib.rs +++ b/crates/valence_protocol/src/lib.rs @@ -75,7 +75,7 @@ use std::io::Write; use std::{fmt, io}; pub use anyhow::{Error, Result}; -pub use valence_protocol_macros::{ident_str, Decode, DecodePacket, Encode, EncodePacket}; +pub use valence_protocol_macros::{ident_str, Decode, Encode, Packet}; pub use {uuid, valence_nbt as nbt}; /// The Minecraft protocol version this library currently targets. @@ -111,7 +111,7 @@ pub mod __private { pub use anyhow::{anyhow, bail, ensure, Context, Result}; pub use crate::var_int::VarInt; - pub use crate::{Decode, DecodePacket, Encode, EncodePacket}; + pub use crate::{Decode, Encode, Packet}; } /// The maximum number of bytes in a single Minecraft packet. @@ -249,20 +249,21 @@ pub trait Decode<'a>: Sized { fn decode(r: &mut &'a [u8]) -> Result; } -/// Like [`Encode`], but implementations must write a leading [`VarInt`] packet -/// ID before any other data. +/// Like [`Encode`] + [`Decode`], but implementations must read and write a +/// leading [`VarInt`] packet ID before any other data. /// /// # Deriving /// /// This trait can be implemented automatically by using the -/// [`EncodePacket`][macro] derive macro. The trait is implemented by writing -/// the packet ID provided in the `#[packet_id = ...]` helper attribute followed -/// by a call to [`Encode::encode`]. +/// [`Packet`][macro] derive macro. The trait is implemented by reading or +/// writing the packet ID provided in the `#[packet_id = ...]` helper attribute +/// followed by a call to [`Encode::encode`] or [`Decode::decode`]. The target +/// type must implement [`Encode`], [`Decode`], and [`fmt::Debug`]. /// /// ``` -/// use valence_protocol::{Encode, EncodePacket}; +/// use valence_protocol::{Encode, Packet}; /// -/// #[derive(Encode, EncodePacket, Debug)] +/// #[derive(Encode, Packet, Debug)] /// #[packet_id = 42] /// struct MyStruct { /// first: i32, @@ -275,60 +276,24 @@ pub trait Decode<'a>: Sized { /// println!("{buf:?}"); /// ``` /// -/// [macro]: valence_protocol_macros::DecodePacket +/// [macro]: valence_protocol_macros::Packet /// [`VarInt`]: var_int::VarInt -pub trait EncodePacket: fmt::Debug { - /// The packet ID that is written when [`Self::encode_packet`] is called. A - /// negative value indicates that the packet ID is not statically known. +pub trait Packet<'a>: Sized + fmt::Debug { + /// The packet returned by [`Self::packet_id`]. If the packet ID is not + /// statically known, then a negative value is used instead. const PACKET_ID: i32 = -1; - /// Like [`Encode::encode`], but a leading [`VarInt`] packet ID must be /// written first. /// /// [`VarInt`]: var_int::VarInt fn encode_packet(&self, w: impl Write) -> Result<()>; -} - -/// Like [`Decode`], but implementations must read a leading [`VarInt`] packet -/// ID before any other data. -/// -/// # Deriving -/// -/// This trait can be implemented automatically by using the -/// [`DecodePacket`][macro] derive macro. The trait is implemented by reading -/// the packet ID provided in the `#[packet_id = ...]` helper attribute followed -/// by a call to [`Decode::decode`]. -/// -/// ``` -/// use valence_protocol::{Decode, DecodePacket}; -/// -/// #[derive(Decode, DecodePacket, Debug)] -/// #[packet_id = 42] -/// struct MyStruct { -/// first: i32, -/// } -/// -/// let buf = [42, 0, 0, 0, 0]; -/// let mut r = buf.as_slice(); -/// -/// let value = MyStruct::decode_packet(&mut r).unwrap(); -/// -/// assert_eq!(value.first, 0); -/// assert!(r.is_empty()); -/// ``` -/// -/// [macro]: valence_protocol::DecodePacket -/// [`VarInt`]: var_int::VarInt -pub trait DecodePacket<'a>: Sized + fmt::Debug { - /// The packet ID that is read when [`Self::decode_packet`] is called. A - /// negative value indicates that the packet ID is not statically known. - const PACKET_ID: i32 = -1; - /// Like [`Decode::decode`], but a leading [`VarInt`] packet ID must be read /// first. /// /// [`VarInt`]: var_int::VarInt fn decode_packet(r: &mut &'a [u8]) -> Result; + /// Returns the ID of this packet. + fn packet_id(&self) -> i32; } #[allow(dead_code)] @@ -336,7 +301,7 @@ pub trait DecodePacket<'a>: Sized + fmt::Debug { mod derive_tests { use super::*; - #[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] + #[derive(Encode, Decode, Packet, Debug)] #[packet_id = 1] struct RegularStruct { foo: i32, @@ -344,30 +309,30 @@ mod derive_tests { baz: f64, } - #[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] + #[derive(Encode, Decode, Packet, Debug)] #[packet_id = 2] struct UnitStruct; - #[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] + #[derive(Encode, Decode, Packet, Debug)] #[packet_id = 3] struct EmptyStruct {} - #[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] + #[derive(Encode, Decode, Packet, Debug)] #[packet_id = 4] struct TupleStruct(i32, bool, f64); - #[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] + #[derive(Encode, Decode, Packet, Debug)] #[packet_id = 5] - struct StructWithGenerics<'z, T: fmt::Debug = ()> { + struct StructWithGenerics<'z, T = ()> { foo: &'z str, bar: T, } - #[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] + #[derive(Encode, Decode, Packet, Debug)] #[packet_id = 6] - struct TupleStructWithGenerics<'z, T: fmt::Debug = ()>(&'z str, i32, T); + struct TupleStructWithGenerics<'z, T = ()>(&'z str, i32, T); - #[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] + #[derive(Encode, Decode, Packet, Debug)] #[packet_id = 7] enum RegularEnum { Empty, @@ -375,13 +340,13 @@ mod derive_tests { Fields { foo: i32, bar: bool, baz: f64 }, } - #[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] + #[derive(Encode, Decode, Packet, Debug)] #[packet_id = 8] enum EmptyEnum {} - #[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] + #[derive(Encode, Decode, Packet, Debug)] #[packet_id = 0xbeef] - enum EnumWithGenericsAndTags<'z, T: fmt::Debug = ()> { + enum EnumWithGenericsAndTags<'z, T = ()> { #[tag = 5] First { foo: &'z str, @@ -396,7 +361,7 @@ mod derive_tests { #[allow(unconditional_recursion)] fn has_impls<'a, T>() where - T: Encode + EncodePacket + Decode<'a> + DecodePacket<'a>, + T: Encode + Decode<'a> + Packet<'a>, { has_impls::(); has_impls::(); diff --git a/crates/valence_protocol/src/packet.rs b/crates/valence_protocol/src/packet.rs index 45b3787..bdb3772 100644 --- a/crates/valence_protocol/src/packet.rs +++ b/crates/valence_protocol/src/packet.rs @@ -12,8 +12,7 @@ pub use s2c::login::S2cLoginPacket; pub use s2c::play::S2cPlayPacket; pub use s2c::status::S2cStatusPacket; -/// Defines an enum of packets and implements `EncodePacket` and `DecodePacket` -/// for each. +/// Defines an enum of packets and implements `Packet` for each. macro_rules! packet_group { ( $(#[$attrs:meta])* @@ -35,9 +34,13 @@ macro_rules! packet_group { } } - impl$(<$life>)? crate::EncodePacket for $packet$(<$life>)? { + impl<$enum_life> crate::Packet<$enum_life> for $packet$(<$life>)? { const PACKET_ID: i32 = $packet_id; + fn packet_id(&self) -> i32 { + $packet_id + } + #[allow(unused_imports)] fn encode_packet(&self, mut w: impl std::io::Write) -> crate::Result<()> { use ::valence_protocol::__private::{Encode, Context, VarInt}; @@ -48,10 +51,6 @@ macro_rules! packet_group { self.encode(w) } - } - - impl<$enum_life> crate::DecodePacket<$enum_life> for $packet$(<$life>)? { - const PACKET_ID: i32 = $packet_id; #[allow(unused_imports)] fn decode_packet(r: &mut &$enum_life [u8]) -> ::valence_protocol::__private::Result { @@ -65,7 +64,15 @@ macro_rules! packet_group { } )* - impl<$enum_life> crate::EncodePacket for $enum_name<$enum_life> { + impl<$enum_life> crate::Packet<$enum_life> for $enum_name<$enum_life> { + fn packet_id(&self) -> i32 { + match self { + $( + Self::$packet(_) => <$packet as crate::Packet>::PACKET_ID, + )* + } + } + fn encode_packet(&self, mut w: impl std::io::Write) -> crate::Result<()> { use crate::Encode; use crate::var_int::VarInt; @@ -73,7 +80,7 @@ macro_rules! packet_group { match self { $( Self::$packet(pkt) => { - VarInt(<$packet as crate::EncodePacket>::PACKET_ID).encode(&mut w)?; + VarInt(<$packet as crate::Packet>::PACKET_ID).encode(&mut w)?; pkt.encode(w)?; } )* @@ -81,9 +88,7 @@ macro_rules! packet_group { Ok(()) } - } - impl<$enum_life> crate::DecodePacket<$enum_life> for $enum_name<$enum_life> { fn decode_packet(r: &mut &$enum_life [u8]) -> crate::Result { use crate::Decode; use crate::var_int::VarInt; @@ -91,7 +96,7 @@ macro_rules! packet_group { let id = VarInt::decode(r)?.0; Ok(match id { $( - <$packet as crate::DecodePacket>::PACKET_ID => + <$packet as crate::Packet>::PACKET_ID => Self::$packet($packet::decode(r)?), )* id => anyhow::bail!("unknown packet ID {} while decoding {}", id, stringify!($enum_name)), @@ -130,9 +135,13 @@ macro_rules! packet_group { } } - impl crate::EncodePacket for $packet { + impl crate::Packet<'_> for $packet { const PACKET_ID: i32 = $packet_id; + fn packet_id(&self) -> i32 { + $packet_id + } + #[allow(unused_imports)] fn encode_packet(&self, mut w: impl std::io::Write) -> crate::Result<()> { use ::valence_protocol::__private::{Encode, Context, VarInt}; @@ -143,10 +152,6 @@ macro_rules! packet_group { self.encode(w) } - } - - impl crate::DecodePacket<'_> for $packet { - const PACKET_ID: i32 = $packet_id; #[allow(unused_imports)] fn decode_packet(r: &mut &[u8]) -> ::valence_protocol::__private::Result { @@ -160,7 +165,15 @@ macro_rules! packet_group { } )* - impl crate::EncodePacket for $enum_name { + impl crate::Packet<'_> for $enum_name { + fn packet_id(&self) -> i32 { + match self { + $( + Self::$packet(_) => <$packet as crate::Packet>::PACKET_ID, + )* + } + } + fn encode_packet(&self, mut w: impl std::io::Write) -> crate::Result<()> { use crate::Encode; use crate::var_int::VarInt; @@ -168,7 +181,7 @@ macro_rules! packet_group { match self { $( Self::$packet(pkt) => { - VarInt(<$packet as crate::EncodePacket>::PACKET_ID).encode(&mut w)?; + VarInt(<$packet as crate::Packet>::PACKET_ID).encode(&mut w)?; pkt.encode(w)?; } )* @@ -176,9 +189,7 @@ macro_rules! packet_group { Ok(()) } - } - impl crate::DecodePacket<'_> for $enum_name { fn decode_packet(r: &mut &[u8]) -> crate::Result { use crate::Decode; use crate::var_int::VarInt; @@ -186,7 +197,7 @@ macro_rules! packet_group { let id = VarInt::decode(r)?.0; Ok(match id { $( - <$packet as crate::DecodePacket>::PACKET_ID => + <$packet as crate::Packet>::PACKET_ID => Self::$packet($packet::decode(r)?), )* id => anyhow::bail!("unknown packet ID {} while decoding {}", id, stringify!($enum_name)), diff --git a/crates/valence_protocol_macros/src/decode.rs b/crates/valence_protocol_macros/src/decode.rs index c400f8a..8c1b25e 100644 --- a/crates/valence_protocol_macros/src/decode.rs +++ b/crates/valence_protocol_macros/src/decode.rs @@ -3,9 +3,7 @@ use quote::quote; use syn::spanned::Spanned; use syn::{parse2, parse_quote, Data, DeriveInput, Error, Fields, Result}; -use crate::{ - add_trait_bounds, decode_split_for_impl, find_packet_id_attr, pair_variants_with_discriminants, -}; +use crate::{add_trait_bounds, decode_split_for_impl, pair_variants_with_discriminants}; pub fn derive_decode(item: TokenStream) -> Result { let mut input = parse2::(item)?; @@ -167,48 +165,3 @@ pub fn derive_decode(item: TokenStream) -> Result { )), } } - -pub fn derive_decode_packet(item: TokenStream) -> Result { - let mut input = parse2::(item)?; - - let Some(packet_id) = find_packet_id_attr(&input.attrs)? else { - return Err(Error::new( - input.ident.span(), - "cannot derive `DecodePacket` without `#[packet_id = ...]` helper attribute", - )) - }; - - let lifetime = input - .generics - .lifetimes() - .next() - .map(|l| l.lifetime.clone()) - .unwrap_or_else(|| parse_quote!('a)); - - add_trait_bounds( - &mut input.generics, - quote!(::valence_protocol::__private::Decode<#lifetime>), - ); - - let (impl_generics, ty_generics, where_clause) = - decode_split_for_impl(input.generics, lifetime.clone()); - - let name = input.ident; - - Ok(quote! { - impl #impl_generics ::valence_protocol::__private::DecodePacket<#lifetime> for #name #ty_generics - #where_clause - { - const PACKET_ID: i32 = #packet_id; - - fn decode_packet(r: &mut &#lifetime [u8]) -> ::valence_protocol::__private::Result { - use ::valence_protocol::__private::{Decode, Context, VarInt, ensure}; - - let id = VarInt::decode(r).context("failed to decode packet ID")?.0; - ensure!(id == #packet_id, "unexpected packet ID {} (expected {})", id, #packet_id); - - Self::decode(r) - } - } - }) -} diff --git a/crates/valence_protocol_macros/src/encode.rs b/crates/valence_protocol_macros/src/encode.rs index 7974bb6..c2c22ad 100644 --- a/crates/valence_protocol_macros/src/encode.rs +++ b/crates/valence_protocol_macros/src/encode.rs @@ -3,7 +3,7 @@ use quote::quote; use syn::spanned::Spanned; use syn::{parse2, Data, DeriveInput, Error, Fields, LitInt, Result}; -use crate::{add_trait_bounds, find_packet_id_attr, pair_variants_with_discriminants}; +use crate::{add_trait_bounds, pair_variants_with_discriminants}; pub fn derive_encode(item: TokenStream) -> Result { let mut input = parse2::(item)?; @@ -163,41 +163,3 @@ pub fn derive_encode(item: TokenStream) -> Result { )), } } - -pub fn derive_encode_packet(item: TokenStream) -> Result { - let mut input = parse2::(item)?; - - let Some(packet_id) = find_packet_id_attr(&input.attrs)? else { - return Err(Error::new( - input.ident.span(), - "cannot derive `EncodePacket` without `#[packet_id = ...]` helper attribute", - )) - }; - - add_trait_bounds( - &mut input.generics, - quote!(::valence_protocol::__private::Encode), - ); - - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - - let name = input.ident; - - Ok(quote! { - impl #impl_generics ::valence_protocol::__private::EncodePacket for #name #ty_generics - #where_clause - { - const PACKET_ID: i32 = #packet_id; - - fn encode_packet(&self, mut w: impl ::std::io::Write) -> ::valence_protocol::__private::Result<()> { - use ::valence_protocol::__private::{Encode, Context, VarInt}; - - VarInt(#packet_id) - .encode(&mut w) - .context("failed to encode packet ID")?; - - self.encode(w) - } - } - }) -} diff --git a/crates/valence_protocol_macros/src/lib.rs b/crates/valence_protocol_macros/src/lib.rs index 2357559..4037f81 100644 --- a/crates/valence_protocol_macros/src/lib.rs +++ b/crates/valence_protocol_macros/src/lib.rs @@ -1,6 +1,6 @@ -//! This crate provides derive macros for [`Encode`], [`Decode`], -//! [`EncodePacket`], and [`DecodePacket`]. -//! It also provides the procedural macro [`ident_str!`] +//! This crate provides derive macros for [`Encode`], [`Decode`], and +//! [`Packet`]. It also provides the procedural macro [`ident_str!`] for parsing +//! identifiers at compile time. //! //! See `valence_protocol`'s documentation for more information. @@ -8,13 +8,14 @@ use proc_macro::TokenStream as StdTokenStream; use proc_macro2::TokenStream; use quote::ToTokens; use syn::{ - parse_quote, Attribute, Error, GenericParam, Generics, Lifetime, LifetimeDef, Lit, LitInt, - Meta, Result, Variant, + parse_quote, Attribute, Error, GenericParam, Generics, Lifetime, LifetimeDef, Lit, Meta, + Result, Variant, }; mod decode; mod encode; mod ident_str; +mod packet; #[proc_macro_derive(Encode, attributes(tag))] pub fn derive_encode(item: StdTokenStream) -> StdTokenStream { @@ -24,14 +25,6 @@ pub fn derive_encode(item: StdTokenStream) -> StdTokenStream { } } -#[proc_macro_derive(EncodePacket, attributes(packet_id))] -pub fn derive_encode_packet(item: StdTokenStream) -> StdTokenStream { - match encode::derive_encode_packet(item.into()) { - Ok(tokens) => tokens.into(), - Err(e) => e.into_compile_error().into(), - } -} - #[proc_macro_derive(Decode, attributes(tag))] pub fn derive_decode(item: StdTokenStream) -> StdTokenStream { match decode::derive_decode(item.into()) { @@ -40,9 +33,9 @@ pub fn derive_decode(item: StdTokenStream) -> StdTokenStream { } } -#[proc_macro_derive(DecodePacket, attributes(packet_id))] -pub fn derive_decode_packet(item: StdTokenStream) -> StdTokenStream { - match decode::derive_decode_packet(item.into()) { +#[proc_macro_derive(Packet, attributes(packet_id))] +pub fn derive_packet(item: StdTokenStream) -> StdTokenStream { + match packet::derive_packet(item.into()) { Ok(tokens) => tokens.into(), Err(e) => e.into_compile_error().into(), } @@ -56,22 +49,6 @@ pub fn ident_str(item: StdTokenStream) -> StdTokenStream { } } -fn find_packet_id_attr(attrs: &[Attribute]) -> Result> { - for attr in attrs { - if let Meta::NameValue(nv) = attr.parse_meta()? { - if nv.path.is_ident("packet_id") { - let span = nv.lit.span(); - return match nv.lit { - Lit::Int(i) => Ok(Some(i)), - _ => Err(Error::new(span, "packet ID must be an integer literal")), - }; - } - } - } - - Ok(None) -} - fn pair_variants_with_discriminants( variants: impl IntoIterator, ) -> Result> { diff --git a/crates/valence_protocol_macros/src/packet.rs b/crates/valence_protocol_macros/src/packet.rs new file mode 100644 index 0000000..565d439 --- /dev/null +++ b/crates/valence_protocol_macros/src/packet.rs @@ -0,0 +1,87 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse2, parse_quote, Attribute, DeriveInput, Error, Lit, LitInt, Meta, Result}; + +use crate::{add_trait_bounds, decode_split_for_impl}; + +pub fn derive_packet(item: TokenStream) -> Result { + let mut input = parse2::(item)?; + + let Some(packet_id) = find_packet_id_attr(&input.attrs)? else { + return Err(Error::new( + input.ident.span(), + "cannot derive `Packet` without `#[packet_id = ...]` helper attribute", + )) + }; + + let lifetime = input + .generics + .lifetimes() + .next() + .map(|l| l.lifetime.clone()) + .unwrap_or_else(|| parse_quote!('a)); + + add_trait_bounds( + &mut input.generics, + quote!(::valence_protocol::__private::Encode), + ); + + add_trait_bounds( + &mut input.generics, + quote!(::valence_protocol::__private::Decode<#lifetime>), + ); + + add_trait_bounds(&mut input.generics, quote!(::std::fmt::Debug)); + + let (impl_generics, ty_generics, where_clause) = + decode_split_for_impl(input.generics, lifetime.clone()); + + let name = input.ident; + + Ok(quote! { + impl #impl_generics ::valence_protocol::__private::Packet<#lifetime> for #name #ty_generics + #where_clause + { + const PACKET_ID: i32 = #packet_id; + + fn packet_id(&self) -> i32 { + #packet_id + } + + fn encode_packet(&self, mut w: impl ::std::io::Write) -> ::valence_protocol::__private::Result<()> { + use ::valence_protocol::__private::{Encode, Context, VarInt}; + + VarInt(#packet_id) + .encode(&mut w) + .context("failed to encode packet ID")?; + + self.encode(w) + } + + fn decode_packet(r: &mut &#lifetime [u8]) -> ::valence_protocol::__private::Result { + use ::valence_protocol::__private::{Decode, Context, VarInt, ensure}; + + let id = VarInt::decode(r).context("failed to decode packet ID")?.0; + ensure!(id == #packet_id, "unexpected packet ID {} (expected {})", id, #packet_id); + + Self::decode(r) + } + } + }) +} + +fn find_packet_id_attr(attrs: &[Attribute]) -> Result> { + for attr in attrs { + if let Meta::NameValue(nv) = attr.parse_meta()? { + if nv.path.is_ident("packet_id") { + let span = nv.lit.span(); + return match nv.lit { + Lit::Int(i) => Ok(Some(i)), + _ => Err(Error::new(span, "packet ID must be an integer literal")), + }; + } + } + } + + Ok(None) +}