Replace EncodePacket and DecodePacket with Packet (#261)

## Description

Combines the `EncodePacket` and `DecodePacket` trait into a single
`Packet` trait. This makes `valence_protocol` simpler and easier to use.
This can be done because all packets were made to be bidirectional in
#253.

Additionally, a `packet_id` method has been added. This should help with
#238.

## Test Plan

Steps:
1. Run examples, packet_inspector, etc. Behavior should be the same.
This commit is contained in:
Ryan Johnson 2023-02-25 11:21:25 -08:00 committed by GitHub
parent 6e8ebbe000
commit 7af119da72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 199 additions and 244 deletions

View file

@ -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<P>
where
P: DecodePacket<'a> + EncodePacket,
P: Packet<'a>,
{
while !self.dec.has_next_packet()? {
self.dec.reserve(4096);

View file

@ -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<P>(&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<P>(&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)
}

View file

@ -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<P>(&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<P>(&mut self, pkt: &P, pos: impl Into<ChunkPos>)
pub fn write_packet_at<'a, P>(&mut self, pkt: &P, pos: impl Into<ChunkPos>)
where
P: EncodePacket + ?Sized,
P: Packet<'a>,
{
let pos = pos.into();
if let Some(cell) = self.partition.get_mut(&pos) {

View file

@ -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<P>(&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<W: WritePacket> WritePacket for &mut W {
fn write_packet<P>(&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<P>(&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<P>(&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:#}");

View file

@ -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<P>(&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<P>
where
P: DecodePacket<'a>,
P: Packet<'a>,
{
timeout(self.timeout, async {
while !self.dec.has_next_packet()? {

View file

@ -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");

View file

@ -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<P>(&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<P>(&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<P>(buf: &mut Vec<u8>, pkt: &P) -> Result<()>
pub fn encode_packet<'a, P>(buf: &mut Vec<u8>, 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<P>(
pub fn encode_packet_compressed<'a, P>(
buf: &mut Vec<u8>,
pkt: &P,
threshold: u32,
scratch: &mut Vec<u8>,
) -> 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<Option<P>>
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<Vec<P>>
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,

View file

@ -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<Self>;
}
/// 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<Self>;
/// 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::<RegularStruct>();
has_impls::<UnitStruct>();

View file

@ -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<Self> {
@ -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<Self> {
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<Self> {
@ -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<Self> {
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)),

View file

@ -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<TokenStream> {
let mut input = parse2::<DeriveInput>(item)?;
@ -167,48 +165,3 @@ pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
)),
}
}
pub fn derive_decode_packet(item: TokenStream) -> Result<TokenStream> {
let mut input = parse2::<DeriveInput>(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<Self> {
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)
}
}
})
}

View file

@ -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<TokenStream> {
let mut input = parse2::<DeriveInput>(item)?;
@ -163,41 +163,3 @@ pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
)),
}
}
pub fn derive_encode_packet(item: TokenStream) -> Result<TokenStream> {
let mut input = parse2::<DeriveInput>(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)
}
}
})
}

View file

@ -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<Option<LitInt>> {
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<Item = Variant>,
) -> Result<Vec<(i32, Variant)>> {

View file

@ -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<TokenStream> {
let mut input = parse2::<DeriveInput>(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<Self> {
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<Option<LitInt>> {
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)
}