Skip to content

Commit e973a15

Browse files
committed
feat: use macro to define packets
1 parent 2db0e5e commit e973a15

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2115
-539
lines changed

Cargo.lock

Lines changed: 267 additions & 206 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,6 @@
1-
# Copyright (c) 2024 Andrew Brower.
2-
# This file is part of Crawlspace.
3-
#
4-
# Crawlspace is free software: you can redistribute it and/or
5-
# modify it under the terms of the GNU Affero General Public
6-
# License as published by the Free Software Foundation, either
7-
# version 3 of the License, or (at your option) any later version.
8-
#
9-
# Crawlspace is distributed in the hope that it will be useful,
10-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12-
# Affero General Public License for more details.
13-
#
14-
# You should have received a copy of the GNU Affero General Public
15-
# License along with Crawlspace. If not, see
16-
# <https://www.gnu.org/licenses/>.
17-
18-
[package]
19-
name = "crawlspace"
20-
version = "0.1.0"
21-
edition = "2021"
22-
license = "AGPL-3.0-or-later"
23-
24-
[dependencies]
25-
aes = { version = "0.8.4", optional = true }
26-
bit-vec = "0.8.0"
27-
bitfield-struct = "0.9.2"
28-
byteorder = "1.5.0"
29-
bytes = "1.8.0"
30-
cfb8 = { version = "0.8.1", optional = true }
31-
clap = { version = "4.5.20", features = ["derive", "env"] }
32-
color-eyre = "0.6.3"
33-
fastanvil = { git = "https://github.com/owengage/fastnbt.git" }
34-
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
35-
rand = "0.8.5"
36-
rayon = "1.10.0"
37-
serde = { version = "1.0.213", features = ["derive"] }
38-
serde_json = "1.0.132"
39-
sha2 = "0.10.8"
40-
thiserror = "1.0.65"
41-
tokio = { version = "1.43.1", features = ["full"] }
42-
tokio-util = "0.7.12"
43-
tracing = { version = "0.1.40", features = ["max_level_trace", "release_max_level_info"] }
44-
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
45-
uuid = "1.11.0"
46-
47-
[features]
48-
default = []
49-
compression = []
50-
encryption = ["dep:cfb8", "dep:aes"]
51-
modern_art = []
52-
lan = []
53-
timings = []
54-
55-
full = ["compression", "encryption"]
1+
[workspace]
2+
resolver = "2"
3+
members = [ "crawlspace-macro","crawlspace"]
564

575
[profile.release-stripped]
586
inherits = "release"

crawlspace-macro/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "crawlspace-macro"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
proc-macro = true
8+
9+
[dependencies]
10+
quote = "1.0.40"
11+
syn = "2.0.104"

crawlspace-macro/src/lib.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use proc_macro::{Span, TokenStream};
2+
use quote::quote;
3+
use syn::{parse_macro_input, DeriveInput, Ident, Lit};
4+
5+
#[proc_macro_derive(Packet, attributes(packet))]
6+
pub fn derive_packet(input: TokenStream) -> TokenStream {
7+
let input = parse_macro_input!(input as DeriveInput);
8+
9+
let syn::Data::Struct(_) = input.data else {
10+
panic!("Packet must be defined as a struct");
11+
};
12+
13+
let mut id = None;
14+
let mut state = None;
15+
let mut direction = None;
16+
17+
for attr in input.attrs {
18+
if !attr.path().is_ident("packet") {
19+
continue;
20+
}
21+
22+
attr.parse_nested_meta(|meta| {
23+
if meta.path.is_ident("id") {
24+
let lit = meta.value()?.parse()?;
25+
match lit {
26+
Lit::Str(i) => {
27+
id = Some(i);
28+
}
29+
_ => panic!("attribute value `id` must be a string"),
30+
}
31+
} else if meta.path.is_ident("state") {
32+
let lit = meta
33+
.value()
34+
.expect("no value for state")
35+
.parse()
36+
.expect("couldn't parse value for state");
37+
let Lit::Str(v) = lit else {
38+
panic!("unable to parse state as string");
39+
};
40+
state = Some(
41+
v.parse_with(syn::Path::parse_mod_style)
42+
.expect("couldn't parse state as path"),
43+
);
44+
} else if meta.path.is_ident("serverbound") {
45+
match direction {
46+
None => direction = Some("Serverbound"),
47+
Some(_) => {
48+
panic!("cannot have two directives of type `serverbound` or `clientbound`")
49+
}
50+
}
51+
} else if meta.path.is_ident("clientbound") {
52+
match direction {
53+
None => direction = Some("Clientbound"),
54+
Some(_) => {
55+
panic!("cannot have two directives of type `serverbound` or `clientbound`")
56+
}
57+
}
58+
} else {
59+
let Some(id) = meta.path.get_ident() else {
60+
panic!("unable to get ident for unrecognized directive");
61+
};
62+
panic!("unrecognized directive {}", id);
63+
}
64+
65+
Ok(())
66+
})
67+
.unwrap();
68+
}
69+
70+
let id = id.expect("id must be provided for packet");
71+
let state = state.expect("state must be provided for packet");
72+
let direction = Ident::new(
73+
direction.expect("direction must be provided for packet"),
74+
Span::call_site().into(),
75+
);
76+
77+
let name = input.ident;
78+
let where_clause = input.generics.where_clause.clone();
79+
let generics = input.generics;
80+
81+
quote! {
82+
impl #generics Packet for #name #generics #where_clause {
83+
fn id() -> &'static str {
84+
#id
85+
}
86+
87+
fn state() -> PacketState {
88+
#state
89+
}
90+
91+
fn direction() -> PacketDirection {
92+
PacketDirection::#direction
93+
}
94+
}
95+
}
96+
.into()
97+
}

0 commit comments

Comments
 (0)