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::login::LoginSuccessS2c;
use valence_protocol::packet::s2c::status::{QueryPongS2c, QueryResponseS2c}; use valence_protocol::packet::s2c::status::{QueryPongS2c, QueryResponseS2c};
use valence_protocol::packet::{C2sPlayPacket, S2cLoginPacket, S2cPlayPacket}; use valence_protocol::packet::{C2sPlayPacket, S2cLoginPacket, S2cPlayPacket};
use valence_protocol::{DecodePacket, EncodePacket}; use valence_protocol::Packet;
#[derive(Parser, Clone, Debug)] #[derive(Parser, Clone, Debug)]
#[clap(author, version, about)] #[clap(author, version, about)]
@ -65,7 +65,7 @@ struct State {
impl State { impl State {
pub async fn rw_packet<'a, P>(&'a mut self) -> anyhow::Result<P> pub async fn rw_packet<'a, P>(&'a mut self) -> anyhow::Result<P>
where where
P: DecodePacket<'a> + EncodePacket, P: Packet<'a>,
{ {
while !self.dec.has_next_packet()? { while !self.dec.has_next_packet()? {
self.dec.reserve(4096); 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::types::{GameMode, GlobalPos, Property, SoundCategory};
use valence_protocol::username::Username; use valence_protocol::username::Username;
use valence_protocol::var_int::VarInt; use valence_protocol::var_int::VarInt;
use valence_protocol::EncodePacket; use valence_protocol::Packet;
use crate::dimension::DimensionId; use crate::dimension::DimensionId;
use crate::entity::data::Player; use crate::entity::data::Player;
@ -173,9 +173,9 @@ impl Client {
/// ///
/// If encoding the packet fails, the client is disconnected. Has no /// If encoding the packet fails, the client is disconnected. Has no
/// effect if the client is already disconnected. /// 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 where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
self.enc.write_packet(pkt); self.enc.write_packet(pkt);
} }
@ -606,9 +606,9 @@ impl Client {
} }
impl WritePacket for Client { impl WritePacket for Client {
fn write_packet<P>(&mut self, packet: &P) fn write_packet<'a, P>(&mut self, packet: &P)
where where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
self.enc.write_packet(packet) 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::sound::Sound;
use valence_protocol::text::Text; use valence_protocol::text::Text;
use valence_protocol::types::SoundCategory; use valence_protocol::types::SoundCategory;
use valence_protocol::EncodePacket; use valence_protocol::Packet;
use crate::dimension::DimensionId; use crate::dimension::DimensionId;
use crate::entity::McEntity; use crate::entity::McEntity;
@ -313,9 +313,9 @@ impl Instance {
/// ///
/// This is more efficient than sending the packet to each client /// This is more efficient than sending the packet to each client
/// individually. /// individually.
pub fn write_packet<P>(&mut self, pkt: &P) pub fn write_packet<'a, P>(&mut self, pkt: &P)
where where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
PacketWriter::new( PacketWriter::new(
&mut self.packet_buf, &mut self.packet_buf,
@ -340,9 +340,9 @@ impl Instance {
/// ///
/// This is more efficient than sending the packet to each client /// This is more efficient than sending the packet to each client
/// individually. /// 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 where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
let pos = pos.into(); let pos = pos.into();
if let Some(cell) = self.partition.get_mut(&pos) { if let Some(cell) = self.partition.get_mut(&pos) {

View file

@ -2,20 +2,20 @@ use std::io::Write;
use tracing::warn; use tracing::warn;
use valence_protocol::codec::{encode_packet, encode_packet_compressed, PacketEncoder}; use valence_protocol::codec::{encode_packet, encode_packet_compressed, PacketEncoder};
use valence_protocol::EncodePacket; use valence_protocol::Packet;
pub(crate) trait WritePacket { pub(crate) trait WritePacket {
fn write_packet<P>(&mut self, packet: &P) fn write_packet<'a, P>(&mut self, packet: &P)
where where
P: EncodePacket + ?Sized; P: Packet<'a>;
fn write_packet_bytes(&mut self, bytes: &[u8]); fn write_packet_bytes(&mut self, bytes: &[u8]);
} }
impl<W: WritePacket> WritePacket for &mut W { 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 where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
(*self).write_packet(packet) (*self).write_packet(packet)
} }
@ -42,9 +42,9 @@ impl<'a> PacketWriter<'a> {
} }
impl WritePacket for PacketWriter<'_> { impl WritePacket for PacketWriter<'_> {
fn write_packet<P>(&mut self, pkt: &P) fn write_packet<'a, P>(&mut self, pkt: &P)
where where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
let res = if let Some(threshold) = self.threshold { let res = if let Some(threshold) = self.threshold {
encode_packet_compressed(self.buf, pkt, threshold, self.scratch) encode_packet_compressed(self.buf, pkt, threshold, self.scratch)
@ -65,9 +65,9 @@ impl WritePacket for PacketWriter<'_> {
} }
impl WritePacket for PacketEncoder { impl WritePacket for PacketEncoder {
fn write_packet<P>(&mut self, packet: &P) fn write_packet<'a, P>(&mut self, packet: &P)
where where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
if let Err(e) = self.append_packet(packet) { if let Err(e) = self.append_packet(packet) {
warn!("failed to write packet: {e:#}"); warn!("failed to write packet: {e:#}");

View file

@ -10,7 +10,7 @@ use tokio::task::JoinHandle;
use tokio::time::timeout; use tokio::time::timeout;
use tracing::debug; use tracing::debug;
use valence_protocol::codec::{PacketDecoder, PacketEncoder}; use valence_protocol::codec::{PacketDecoder, PacketEncoder};
use valence_protocol::{DecodePacket, EncodePacket}; use valence_protocol::Packet;
use crate::client::{Client, ClientConnection}; use crate::client::{Client, ClientConnection};
use crate::server::byte_channel::{ 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 where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
self.enc.append_packet(pkt)?; self.enc.append_packet(pkt)?;
let bytes = self.enc.take(); let bytes = self.enc.take();
@ -64,7 +64,7 @@ where
pub async fn recv_packet<'a, P>(&'a mut self) -> anyhow::Result<P> pub async fn recv_packet<'a, P>(&'a mut self) -> anyhow::Result<P>
where where
P: DecodePacket<'a>, P: Packet<'a>,
{ {
timeout(self.timeout, async { timeout(self.timeout, async {
while !self.dec.has_next_packet()? { while !self.dec.has_next_packet()? {

View file

@ -6,7 +6,7 @@ use bytes::BytesMut;
use valence_protocol::codec::{PacketDecoder, PacketEncoder}; use valence_protocol::codec::{PacketDecoder, PacketEncoder};
use valence_protocol::packet::S2cPlayPacket; use valence_protocol::packet::S2cPlayPacket;
use valence_protocol::username::Username; use valence_protocol::username::Username;
use valence_protocol::EncodePacket; use valence_protocol::Packet;
use crate::client::{Client, ClientConnection}; use crate::client::{Client, ClientConnection};
use crate::config::{ConnectionMode, ServerPlugin}; 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 /// Inject a packet to be treated as a packet inbound to the server. Panics
/// if the packet cannot be sent. /// 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 self.enc
.append_packet(packet) .append_packet(packet)
.expect("failed to encode packet"); .expect("failed to encode packet");

View file

@ -5,7 +5,7 @@ use bytes::{Buf, BufMut, BytesMut};
use tracing::debug; use tracing::debug;
use crate::var_int::{VarInt, VarIntDecodeError}; 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 /// The AES block cipher with a 128 bit key, using the CFB-8 mode of
/// operation. /// operation.
@ -33,9 +33,9 @@ impl PacketEncoder {
self.buf.extend_from_slice(bytes) 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 where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
let start_len = self.buf.len(); let start_len = self.buf.len();
self.append_packet(pkt)?; self.append_packet(pkt)?;
@ -54,9 +54,9 @@ impl PacketEncoder {
Ok(()) Ok(())
} }
pub fn append_packet<P>(&mut self, pkt: &P) -> Result<()> pub fn append_packet<'a, P>(&mut self, pkt: &P) -> Result<()>
where where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
let start_len = self.buf.len(); 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 where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
let start_len = buf.len(); let start_len = buf.len();
@ -201,14 +201,14 @@ where
} }
#[cfg(feature = "compression")] #[cfg(feature = "compression")]
pub fn encode_packet_compressed<P>( pub fn encode_packet_compressed<'a, P>(
buf: &mut Vec<u8>, buf: &mut Vec<u8>,
pkt: &P, pkt: &P,
threshold: u32, threshold: u32,
scratch: &mut Vec<u8>, scratch: &mut Vec<u8>,
) -> Result<()> ) -> Result<()>
where where
P: EncodePacket + ?Sized, P: Packet<'a>,
{ {
use std::io::Read; use std::io::Read;
@ -287,7 +287,7 @@ impl PacketDecoder {
pub fn try_next_packet<'a, P>(&'a mut self) -> Result<Option<P>> pub fn try_next_packet<'a, P>(&'a mut self) -> Result<Option<P>>
where where
P: DecodePacket<'a>, P: Packet<'a>,
{ {
self.buf.advance(self.cursor); self.buf.advance(self.cursor);
self.cursor = 0; self.cursor = 0;
@ -368,7 +368,7 @@ impl PacketDecoder {
#[track_caller] #[track_caller]
pub fn collect_into_vec<'a, P>(&'a mut self) -> Result<Vec<P>> pub fn collect_into_vec<'a, P>(&'a mut self) -> Result<Vec<P>>
where where
P: DecodePacket<'a>, P: Packet<'a>,
{ {
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
assert!( assert!(
@ -512,7 +512,7 @@ mod tests {
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
const CRYPT_KEY: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; 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] #[packet_id = 42]
struct TestPacket<'a> { struct TestPacket<'a> {
a: bool, a: bool,

View file

@ -75,7 +75,7 @@ use std::io::Write;
use std::{fmt, io}; use std::{fmt, io};
pub use anyhow::{Error, Result}; 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}; pub use {uuid, valence_nbt as nbt};
/// The Minecraft protocol version this library currently targets. /// 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 anyhow::{anyhow, bail, ensure, Context, Result};
pub use crate::var_int::VarInt; 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. /// 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>; fn decode(r: &mut &'a [u8]) -> Result<Self>;
} }
/// Like [`Encode`], but implementations must write a leading [`VarInt`] packet /// Like [`Encode`] + [`Decode`], but implementations must read and write a
/// ID before any other data. /// leading [`VarInt`] packet ID before any other data.
/// ///
/// # Deriving /// # Deriving
/// ///
/// This trait can be implemented automatically by using the /// This trait can be implemented automatically by using the
/// [`EncodePacket`][macro] derive macro. The trait is implemented by writing /// [`Packet`][macro] derive macro. The trait is implemented by reading or
/// the packet ID provided in the `#[packet_id = ...]` helper attribute followed /// writing the packet ID provided in the `#[packet_id = ...]` helper attribute
/// by a call to [`Encode::encode`]. /// 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] /// #[packet_id = 42]
/// struct MyStruct { /// struct MyStruct {
/// first: i32, /// first: i32,
@ -275,60 +276,24 @@ pub trait Decode<'a>: Sized {
/// println!("{buf:?}"); /// println!("{buf:?}");
/// ``` /// ```
/// ///
/// [macro]: valence_protocol_macros::DecodePacket /// [macro]: valence_protocol_macros::Packet
/// [`VarInt`]: var_int::VarInt /// [`VarInt`]: var_int::VarInt
pub trait EncodePacket: fmt::Debug { pub trait Packet<'a>: Sized + fmt::Debug {
/// The packet ID that is written when [`Self::encode_packet`] is called. A /// The packet returned by [`Self::packet_id`]. If the packet ID is not
/// negative value indicates that the packet ID is not statically known. /// statically known, then a negative value is used instead.
const PACKET_ID: i32 = -1; const PACKET_ID: i32 = -1;
/// Like [`Encode::encode`], but a leading [`VarInt`] packet ID must be /// Like [`Encode::encode`], but a leading [`VarInt`] packet ID must be
/// written first. /// written first.
/// ///
/// [`VarInt`]: var_int::VarInt /// [`VarInt`]: var_int::VarInt
fn encode_packet(&self, w: impl Write) -> Result<()>; 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 /// Like [`Decode::decode`], but a leading [`VarInt`] packet ID must be read
/// first. /// first.
/// ///
/// [`VarInt`]: var_int::VarInt /// [`VarInt`]: var_int::VarInt
fn decode_packet(r: &mut &'a [u8]) -> Result<Self>; fn decode_packet(r: &mut &'a [u8]) -> Result<Self>;
/// Returns the ID of this packet.
fn packet_id(&self) -> i32;
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -336,7 +301,7 @@ pub trait DecodePacket<'a>: Sized + fmt::Debug {
mod derive_tests { mod derive_tests {
use super::*; use super::*;
#[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] #[derive(Encode, Decode, Packet, Debug)]
#[packet_id = 1] #[packet_id = 1]
struct RegularStruct { struct RegularStruct {
foo: i32, foo: i32,
@ -344,30 +309,30 @@ mod derive_tests {
baz: f64, baz: f64,
} }
#[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] #[derive(Encode, Decode, Packet, Debug)]
#[packet_id = 2] #[packet_id = 2]
struct UnitStruct; struct UnitStruct;
#[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] #[derive(Encode, Decode, Packet, Debug)]
#[packet_id = 3] #[packet_id = 3]
struct EmptyStruct {} struct EmptyStruct {}
#[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] #[derive(Encode, Decode, Packet, Debug)]
#[packet_id = 4] #[packet_id = 4]
struct TupleStruct(i32, bool, f64); struct TupleStruct(i32, bool, f64);
#[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] #[derive(Encode, Decode, Packet, Debug)]
#[packet_id = 5] #[packet_id = 5]
struct StructWithGenerics<'z, T: fmt::Debug = ()> { struct StructWithGenerics<'z, T = ()> {
foo: &'z str, foo: &'z str,
bar: T, bar: T,
} }
#[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] #[derive(Encode, Decode, Packet, Debug)]
#[packet_id = 6] #[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] #[packet_id = 7]
enum RegularEnum { enum RegularEnum {
Empty, Empty,
@ -375,13 +340,13 @@ mod derive_tests {
Fields { foo: i32, bar: bool, baz: f64 }, Fields { foo: i32, bar: bool, baz: f64 },
} }
#[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] #[derive(Encode, Decode, Packet, Debug)]
#[packet_id = 8] #[packet_id = 8]
enum EmptyEnum {} enum EmptyEnum {}
#[derive(Encode, EncodePacket, Decode, DecodePacket, Debug)] #[derive(Encode, Decode, Packet, Debug)]
#[packet_id = 0xbeef] #[packet_id = 0xbeef]
enum EnumWithGenericsAndTags<'z, T: fmt::Debug = ()> { enum EnumWithGenericsAndTags<'z, T = ()> {
#[tag = 5] #[tag = 5]
First { First {
foo: &'z str, foo: &'z str,
@ -396,7 +361,7 @@ mod derive_tests {
#[allow(unconditional_recursion)] #[allow(unconditional_recursion)]
fn has_impls<'a, T>() fn has_impls<'a, T>()
where where
T: Encode + EncodePacket + Decode<'a> + DecodePacket<'a>, T: Encode + Decode<'a> + Packet<'a>,
{ {
has_impls::<RegularStruct>(); has_impls::<RegularStruct>();
has_impls::<UnitStruct>(); has_impls::<UnitStruct>();

View file

@ -12,8 +12,7 @@ pub use s2c::login::S2cLoginPacket;
pub use s2c::play::S2cPlayPacket; pub use s2c::play::S2cPlayPacket;
pub use s2c::status::S2cStatusPacket; pub use s2c::status::S2cStatusPacket;
/// Defines an enum of packets and implements `EncodePacket` and `DecodePacket` /// Defines an enum of packets and implements `Packet` for each.
/// for each.
macro_rules! packet_group { macro_rules! packet_group {
( (
$(#[$attrs:meta])* $(#[$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; const PACKET_ID: i32 = $packet_id;
fn packet_id(&self) -> i32 {
$packet_id
}
#[allow(unused_imports)] #[allow(unused_imports)]
fn encode_packet(&self, mut w: impl std::io::Write) -> crate::Result<()> { fn encode_packet(&self, mut w: impl std::io::Write) -> crate::Result<()> {
use ::valence_protocol::__private::{Encode, Context, VarInt}; use ::valence_protocol::__private::{Encode, Context, VarInt};
@ -48,10 +51,6 @@ macro_rules! packet_group {
self.encode(w) self.encode(w)
} }
}
impl<$enum_life> crate::DecodePacket<$enum_life> for $packet$(<$life>)? {
const PACKET_ID: i32 = $packet_id;
#[allow(unused_imports)] #[allow(unused_imports)]
fn decode_packet(r: &mut &$enum_life [u8]) -> ::valence_protocol::__private::Result<Self> { 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<()> { fn encode_packet(&self, mut w: impl std::io::Write) -> crate::Result<()> {
use crate::Encode; use crate::Encode;
use crate::var_int::VarInt; use crate::var_int::VarInt;
@ -73,7 +80,7 @@ macro_rules! packet_group {
match self { match self {
$( $(
Self::$packet(pkt) => { 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)?; pkt.encode(w)?;
} }
)* )*
@ -81,9 +88,7 @@ macro_rules! packet_group {
Ok(()) Ok(())
} }
}
impl<$enum_life> crate::DecodePacket<$enum_life> for $enum_name<$enum_life> {
fn decode_packet(r: &mut &$enum_life [u8]) -> crate::Result<Self> { fn decode_packet(r: &mut &$enum_life [u8]) -> crate::Result<Self> {
use crate::Decode; use crate::Decode;
use crate::var_int::VarInt; use crate::var_int::VarInt;
@ -91,7 +96,7 @@ macro_rules! packet_group {
let id = VarInt::decode(r)?.0; let id = VarInt::decode(r)?.0;
Ok(match id { Ok(match id {
$( $(
<$packet as crate::DecodePacket>::PACKET_ID => <$packet as crate::Packet>::PACKET_ID =>
Self::$packet($packet::decode(r)?), Self::$packet($packet::decode(r)?),
)* )*
id => anyhow::bail!("unknown packet ID {} while decoding {}", id, stringify!($enum_name)), 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; const PACKET_ID: i32 = $packet_id;
fn packet_id(&self) -> i32 {
$packet_id
}
#[allow(unused_imports)] #[allow(unused_imports)]
fn encode_packet(&self, mut w: impl std::io::Write) -> crate::Result<()> { fn encode_packet(&self, mut w: impl std::io::Write) -> crate::Result<()> {
use ::valence_protocol::__private::{Encode, Context, VarInt}; use ::valence_protocol::__private::{Encode, Context, VarInt};
@ -143,10 +152,6 @@ macro_rules! packet_group {
self.encode(w) self.encode(w)
} }
}
impl crate::DecodePacket<'_> for $packet {
const PACKET_ID: i32 = $packet_id;
#[allow(unused_imports)] #[allow(unused_imports)]
fn decode_packet(r: &mut &[u8]) -> ::valence_protocol::__private::Result<Self> { 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<()> { fn encode_packet(&self, mut w: impl std::io::Write) -> crate::Result<()> {
use crate::Encode; use crate::Encode;
use crate::var_int::VarInt; use crate::var_int::VarInt;
@ -168,7 +181,7 @@ macro_rules! packet_group {
match self { match self {
$( $(
Self::$packet(pkt) => { 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)?; pkt.encode(w)?;
} }
)* )*
@ -176,9 +189,7 @@ macro_rules! packet_group {
Ok(()) Ok(())
} }
}
impl crate::DecodePacket<'_> for $enum_name {
fn decode_packet(r: &mut &[u8]) -> crate::Result<Self> { fn decode_packet(r: &mut &[u8]) -> crate::Result<Self> {
use crate::Decode; use crate::Decode;
use crate::var_int::VarInt; use crate::var_int::VarInt;
@ -186,7 +197,7 @@ macro_rules! packet_group {
let id = VarInt::decode(r)?.0; let id = VarInt::decode(r)?.0;
Ok(match id { Ok(match id {
$( $(
<$packet as crate::DecodePacket>::PACKET_ID => <$packet as crate::Packet>::PACKET_ID =>
Self::$packet($packet::decode(r)?), Self::$packet($packet::decode(r)?),
)* )*
id => anyhow::bail!("unknown packet ID {} while decoding {}", id, stringify!($enum_name)), 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::spanned::Spanned;
use syn::{parse2, parse_quote, Data, DeriveInput, Error, Fields, Result}; use syn::{parse2, parse_quote, Data, DeriveInput, Error, Fields, Result};
use crate::{ use crate::{add_trait_bounds, decode_split_for_impl, pair_variants_with_discriminants};
add_trait_bounds, decode_split_for_impl, find_packet_id_attr, pair_variants_with_discriminants,
};
pub fn derive_decode(item: TokenStream) -> Result<TokenStream> { pub fn derive_decode(item: TokenStream) -> Result<TokenStream> {
let mut input = parse2::<DeriveInput>(item)?; let mut input = parse2::<DeriveInput>(item)?;
@ -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::spanned::Spanned;
use syn::{parse2, Data, DeriveInput, Error, Fields, LitInt, Result}; 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> { pub fn derive_encode(item: TokenStream) -> Result<TokenStream> {
let mut input = parse2::<DeriveInput>(item)?; 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`], //! This crate provides derive macros for [`Encode`], [`Decode`], and
//! [`EncodePacket`], and [`DecodePacket`]. //! [`Packet`]. It also provides the procedural macro [`ident_str!`] for parsing
//! It also provides the procedural macro [`ident_str!`] //! identifiers at compile time.
//! //!
//! See `valence_protocol`'s documentation for more information. //! See `valence_protocol`'s documentation for more information.
@ -8,13 +8,14 @@ use proc_macro::TokenStream as StdTokenStream;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::ToTokens; use quote::ToTokens;
use syn::{ use syn::{
parse_quote, Attribute, Error, GenericParam, Generics, Lifetime, LifetimeDef, Lit, LitInt, parse_quote, Attribute, Error, GenericParam, Generics, Lifetime, LifetimeDef, Lit, Meta,
Meta, Result, Variant, Result, Variant,
}; };
mod decode; mod decode;
mod encode; mod encode;
mod ident_str; mod ident_str;
mod packet;
#[proc_macro_derive(Encode, attributes(tag))] #[proc_macro_derive(Encode, attributes(tag))]
pub fn derive_encode(item: StdTokenStream) -> StdTokenStream { 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))] #[proc_macro_derive(Decode, attributes(tag))]
pub fn derive_decode(item: StdTokenStream) -> StdTokenStream { pub fn derive_decode(item: StdTokenStream) -> StdTokenStream {
match decode::derive_decode(item.into()) { match decode::derive_decode(item.into()) {
@ -40,9 +33,9 @@ pub fn derive_decode(item: StdTokenStream) -> StdTokenStream {
} }
} }
#[proc_macro_derive(DecodePacket, attributes(packet_id))] #[proc_macro_derive(Packet, attributes(packet_id))]
pub fn derive_decode_packet(item: StdTokenStream) -> StdTokenStream { pub fn derive_packet(item: StdTokenStream) -> StdTokenStream {
match decode::derive_decode_packet(item.into()) { match packet::derive_packet(item.into()) {
Ok(tokens) => tokens.into(), Ok(tokens) => tokens.into(),
Err(e) => e.into_compile_error().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( fn pair_variants_with_discriminants(
variants: impl IntoIterator<Item = Variant>, variants: impl IntoIterator<Item = Variant>,
) -> Result<Vec<(i32, 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)
}