Skip to content

Commit aab2a99

Browse files
committed
feat: basic Decode derive
1 parent 089d970 commit aab2a99

File tree

13 files changed

+272
-141
lines changed

13 files changed

+272
-141
lines changed

crawlspace-macro/src/lib.rs

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use proc_macro::{Span, TokenStream};
2-
use quote::{quote, TokenStreamExt};
3-
use syn::{parse_macro_input, DeriveInput, Fields, Ident, Index, Lit};
2+
use quote::quote;
3+
use syn::{parse_macro_input, parse_quote, DeriveInput, Fields, Ident, Index, Lit};
44

55
#[proc_macro_derive(Packet, attributes(packet))]
66
pub fn derive_packet(input: TokenStream) -> TokenStream {
@@ -182,3 +182,122 @@ pub fn derive_encode(input: TokenStream) -> TokenStream {
182182
}
183183
.into()
184184
}
185+
186+
/// Automatically implements "straight-across" decoding for the given struct, i.e. fields are
187+
/// deserialized in order as is. Supports #[varint] and #[varlong] attributes on integer types to
188+
/// deserialize as those formats instead.
189+
#[proc_macro_derive(Decode, attributes(varint, varlong))]
190+
pub fn derive_decode(input: TokenStream) -> TokenStream {
191+
let input = parse_macro_input!(input as DeriveInput);
192+
193+
let syn::Data::Struct(data) = input.data else {
194+
panic!("Can only derive Decode on a struct");
195+
};
196+
197+
let name = input.ident;
198+
199+
let struct_tokens = match data.fields {
200+
Fields::Named(fields) => {
201+
let mut field_tokens = proc_macro2::TokenStream::new();
202+
203+
for field in fields.named {
204+
let field_name = field.ident.expect("couldn't get ident for named field");
205+
let ty = field.ty;
206+
207+
let wrapped = format!("for field {field_name} in {name}");
208+
209+
if field
210+
.attrs
211+
.iter()
212+
.any(|attr| attr.meta.path().is_ident("varint"))
213+
{
214+
field_tokens.extend(quote! {
215+
#field_name: VarInt::decode(r)
216+
.wrap_err(#wrapped)?
217+
.try_into()?,
218+
});
219+
} else if field
220+
.attrs
221+
.iter()
222+
.any(|attr| attr.meta.path().is_ident("varlong"))
223+
{
224+
field_tokens.extend(quote! {
225+
#field_name: VarLong::decode(r)
226+
.wrap_err(#wrapped)?
227+
.try_into()?,
228+
});
229+
} else {
230+
field_tokens.extend(quote! {
231+
#field_name: <#ty as Decode>::decode(r)
232+
.wrap_err(#wrapped)?,
233+
});
234+
}
235+
}
236+
quote! {
237+
Self {
238+
#field_tokens
239+
}
240+
}
241+
}
242+
Fields::Unnamed(fields) => {
243+
let mut field_tokens = proc_macro2::TokenStream::new();
244+
for (i, field) in fields.unnamed.into_iter().enumerate() {
245+
let ty = field.ty;
246+
247+
let wrapped = format!("for field {i} in {name}");
248+
249+
if field
250+
.attrs
251+
.iter()
252+
.any(|attr| attr.meta.path().is_ident("varint"))
253+
{
254+
field_tokens.extend(quote! {
255+
VarInt::decode(r)
256+
.wrap_err(#wrapped)?
257+
.try_into()?,
258+
});
259+
} else if field
260+
.attrs
261+
.iter()
262+
.any(|attr| attr.meta.path().is_ident("varlong"))
263+
{
264+
field_tokens.extend(quote! {
265+
VarLong::decode(r)
266+
.wrap_err(#wrapped)?
267+
.try_into()?,
268+
});
269+
} else {
270+
field_tokens.extend(quote! {
271+
<#ty as Decode>::decode(r)
272+
.wrap_err(#wrapped)?,
273+
});
274+
}
275+
}
276+
quote! {
277+
Self(#field_tokens)
278+
}
279+
}
280+
Fields::Unit => quote! { Self },
281+
};
282+
283+
let struct_generics = input.generics;
284+
let where_clause = struct_generics.where_clause.clone();
285+
286+
let mut impl_generics = struct_generics.clone();
287+
if impl_generics.lifetimes().count() == 0 {
288+
impl_generics.params.push(parse_quote!('a));
289+
}
290+
291+
quote! {
292+
impl #impl_generics Decode #impl_generics for #name #struct_generics #where_clause {
293+
fn decode(r: &mut &'a [u8]) -> color_eyre::Result<Self>
294+
where
295+
Self: Sized,
296+
{
297+
use color_eyre::eyre::WrapErr;
298+
Ok(#struct_tokens)
299+
}
300+
}
301+
}
302+
.into()
303+
}

crawlspace/src/protocol/datatypes/impls.rs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,32 +46,80 @@ impl Encode for bool {
4646
}
4747
}
4848

49+
impl<'a> Decode<'a> for i8 {
50+
fn decode(r: &mut &'a [u8]) -> Result<Self>
51+
where
52+
Self: Sized,
53+
{
54+
Ok(r.read_i8()?)
55+
}
56+
}
57+
4958
impl Encode for i8 {
5059
fn encode(&self, mut w: impl std::io::Write) -> Result<()> {
5160
Ok(w.write_i8(*self)?)
5261
}
5362
}
5463

64+
impl<'a> Decode<'a> for u8 {
65+
fn decode(r: &mut &'a [u8]) -> Result<Self>
66+
where
67+
Self: Sized,
68+
{
69+
Ok(r.read_u8()?)
70+
}
71+
}
72+
5573
impl Encode for u8 {
5674
fn encode(&self, mut w: impl std::io::Write) -> Result<()> {
5775
Ok(w.write_u8(*self)?)
5876
}
5977
}
6078

79+
impl<'a> Decode<'a> for i16 {
80+
fn decode(r: &mut &'a [u8]) -> Result<Self>
81+
where
82+
Self: Sized,
83+
{
84+
Ok(r.read_i16::<BigEndian>()?)
85+
}
86+
}
87+
6188
impl Encode for i16 {
6289
fn encode(&self, mut w: impl std::io::Write) -> Result<()> {
6390
Ok(w.write_i16::<BigEndian>(*self)?)
6491
}
6592
}
6693

94+
impl<'a> Decode<'a> for u16 {
95+
fn decode(r: &mut &'a [u8]) -> Result<Self>
96+
where
97+
Self: Sized,
98+
{
99+
Ok(r.read_u16::<BigEndian>()?)
100+
}
101+
}
102+
103+
impl<'a> Decode<'a> for i32 {
104+
fn decode(r: &mut &'a [u8]) -> Result<Self>
105+
where
106+
Self: Sized,
107+
{
108+
Ok(r.read_i32::<BigEndian>()?)
109+
}
110+
}
111+
67112
impl Encode for i32 {
68113
fn encode(&self, mut w: impl std::io::Write) -> Result<()> {
69114
Ok(w.write_i32::<BigEndian>(*self)?)
70115
}
71116
}
72117

73118
impl<'a> Decode<'a> for i64 {
74-
fn decode(r: &mut &'a [u8]) -> Result<Self> {
119+
fn decode(r: &mut &'a [u8]) -> Result<Self>
120+
where
121+
Self: Sized,
122+
{
75123
Ok(r.read_i64::<BigEndian>()?)
76124
}
77125
}
@@ -82,24 +130,60 @@ impl Encode for i64 {
82130
}
83131
}
84132

133+
impl<'a> Decode<'a> for u64 {
134+
fn decode(r: &mut &'a [u8]) -> Result<Self>
135+
where
136+
Self: Sized,
137+
{
138+
Ok(r.read_u64::<BigEndian>()?)
139+
}
140+
}
141+
85142
impl Encode for u64 {
86143
fn encode(&self, mut w: impl std::io::Write) -> Result<()> {
87144
Ok(w.write_u64::<BigEndian>(*self)?)
88145
}
89146
}
90147

148+
impl<'a> Decode<'a> for u128 {
149+
fn decode(r: &mut &'a [u8]) -> Result<Self>
150+
where
151+
Self: Sized,
152+
{
153+
Ok(r.read_u128::<BigEndian>()?)
154+
}
155+
}
156+
91157
impl Encode for u128 {
92158
fn encode(&self, mut w: impl std::io::Write) -> Result<()> {
93159
Ok(w.write_u128::<BigEndian>(*self)?)
94160
}
95161
}
96162

163+
impl<'a> Decode<'a> for f32 {
164+
fn decode(r: &mut &'a [u8]) -> Result<Self>
165+
where
166+
Self: Sized,
167+
{
168+
Ok(r.read_f32::<BigEndian>()?)
169+
}
170+
}
171+
97172
impl Encode for f32 {
98173
fn encode(&self, mut w: impl std::io::Write) -> Result<()> {
99174
Ok(w.write_f32::<BigEndian>(*self)?)
100175
}
101176
}
102177

178+
impl<'a> Decode<'a> for f64 {
179+
fn decode(r: &mut &'a [u8]) -> Result<Self>
180+
where
181+
Self: Sized,
182+
{
183+
Ok(r.read_f64::<BigEndian>()?)
184+
}
185+
}
186+
103187
impl Encode for f64 {
104188
fn encode(&self, mut w: impl std::io::Write) -> Result<()> {
105189
Ok(w.write_f64::<BigEndian>(*self)?)

crawlspace/src/protocol/datatypes/variable.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,9 @@ impl Encode for VarLong {
296296
}
297297
}
298298
}
299+
300+
impl From<VarInt> for i32 {
301+
fn from(value: VarInt) -> Self {
302+
value.0
303+
}
304+
}

crawlspace/src/protocol/mod.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ mod decoder;
7878
mod encoder;
7979

8080
use bit_vec::BitVec;
81-
use color_eyre::eyre::{Context, Result};
81+
use color_eyre::eyre::{bail, Context, Result};
8282
use datatypes::{Bounded, VarInt};
8383
use serde::{Deserialize, Serialize};
8484
use std::collections::HashMap;
@@ -216,6 +216,22 @@ pub enum ProtocolState {
216216
Transfer,
217217
}
218218

219+
impl Decode<'_> for ProtocolState {
220+
fn decode(r: &mut &'_ [u8]) -> Result<Self>
221+
where
222+
Self: Sized,
223+
{
224+
let state = VarInt::decode(r)?;
225+
226+
match state.0 {
227+
1 => Ok(ProtocolState::Status),
228+
2 => Ok(ProtocolState::Login),
229+
3 => Ok(ProtocolState::Transfer),
230+
e => bail!("Invalid next state requested: {e}"),
231+
}
232+
}
233+
}
234+
219235
#[derive(Error, Debug)]
220236
pub enum ProtocolStateDecodeError {
221237
#[error("Unable to decode {0} into a PacketState")]

crawlspace/src/protocol/packets/login/config.rs

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
use color_eyre::eyre::{ensure, Result};
21-
use crawlspace_macro::{Encode, Packet};
21+
use crawlspace_macro::{Decode, Encode, Packet};
2222

2323
use crate::protocol::{
2424
datatypes::{Bounded, VarInt},
@@ -36,7 +36,7 @@ pub struct KnownPacksC<'a> {
3636
known_packs: Vec<KnownPack<'a>>,
3737
}
3838

39-
#[derive(Debug, Encode)]
39+
#[derive(Debug, Encode, Decode)]
4040
pub struct KnownPack<'a> {
4141
namespace: Bounded<&'a str>,
4242
id: Bounded<&'a str>,
@@ -86,16 +86,6 @@ impl<'a> Decode<'a> for KnownPacksS<'a> {
8686
}
8787
}
8888

89-
impl<'a> Decode<'a> for KnownPack<'a> {
90-
fn decode(r: &mut &'a [u8]) -> Result<Self> {
91-
Ok(Self {
92-
namespace: Bounded::<&'a str>::decode(r)?,
93-
id: Bounded::<&'a str>::decode(r)?,
94-
version: Bounded::<&'a str>::decode(r)?,
95-
})
96-
}
97-
}
98-
9989
#[derive(Debug, Packet, Encode)]
10090
#[packet(
10191
id = "minecraft:finish_configuration",
@@ -104,16 +94,10 @@ impl<'a> Decode<'a> for KnownPack<'a> {
10494
)]
10595
pub struct FinishConfigurationC;
10696

107-
#[derive(Debug, Packet)]
97+
#[derive(Debug, Packet, Decode)]
10898
#[packet(
10999
id = "minecraft:finish_configuration",
110100
serverbound,
111101
state = "PacketState::Configuration"
112102
)]
113103
pub struct FinishConfigurationAckS;
114-
115-
impl<'a> Decode<'a> for FinishConfigurationAckS {
116-
fn decode(_r: &mut &'a [u8]) -> Result<Self> {
117-
Ok(Self)
118-
}
119-
}

0 commit comments

Comments
 (0)