1
+ use crate :: static_loading:: packets:: { get_packet_id, PacketBoundiness } ;
1
2
use colored:: Colorize ;
2
3
use proc_macro:: TokenStream ;
3
- use quote:: quote;
4
+ use quote:: { quote, ToTokens } ;
5
+ use regex:: Regex ;
4
6
use std:: env;
5
7
use std:: ops:: Add ;
6
- use syn:: { parse_macro_input, LitInt , LitStr } ;
8
+ use syn:: { parse_macro_input, Attribute } ;
9
+
10
+ /// Returns: (state, packet_id)
11
+ fn parse_packet_attribute ( attr : & Attribute ) -> Option < ( String , String ) > {
12
+ let attr_str = attr. to_token_stream ( ) . to_string ( ) ;
13
+
14
+ // This regex matches both formats:
15
+ // #[packet(packet_id = "something", state = "play")]
16
+ let re = Regex :: new ( r#"packet_id\s*=\s*"([^"]+)"(?:\s*,\s*)?state\s*=\s*"([^"]+)""# ) . unwrap ( ) ;
17
+
18
+ if let Some ( caps) = re. captures ( & attr_str) {
19
+ let packet_id = caps. get ( 1 ) . map ( |m| m. as_str ( ) . to_string ( ) ) ?;
20
+ let state = caps. get ( 2 ) . map ( |m| m. as_str ( ) . to_string ( ) ) ?;
21
+ Some ( ( state, packet_id) )
22
+ } else {
23
+ None
24
+ }
25
+ }
26
+
27
+ /// Returns: (state, packet_id)
28
+ pub ( crate ) fn get_packet_details_from_attributes (
29
+ attrs : & [ Attribute ] ,
30
+ bound_to : PacketBoundiness ,
31
+ ) -> Option < ( String , u8 ) > {
32
+ let mut val = Option :: < ( String , String ) > :: None ;
33
+
34
+ for attr in attrs {
35
+ if !attr. path ( ) . is_ident ( "packet" ) {
36
+ continue ;
37
+ }
38
+
39
+ val = parse_packet_attribute ( attr) ;
40
+ }
41
+
42
+ let ( state, packet_id) = val?;
43
+
44
+ let packet_id =
45
+ parse_packet_id ( state. as_str ( ) , packet_id, bound_to) . expect ( "parse_packet_id failed" ) ;
46
+
47
+ Some ( ( state, packet_id) )
48
+ }
7
49
8
50
/// Essentially, this just reads all the files in the directory and generates a match arm for each packet.
9
51
/// (packet_id, state) => { ... }
@@ -40,11 +82,21 @@ pub fn bake_registry(input: TokenStream) -> TokenStream {
40
82
41
83
let start = std:: time:: Instant :: now ( ) ;
42
84
43
- for entry in std:: fs:: read_dir ( dir_path) . expect ( "read_dir call failed" ) {
85
+ let entries = std:: fs:: read_dir ( dir_path) . expect ( "read_dir call failed" ) ;
86
+
87
+ for entry in entries {
44
88
let entry = entry. expect ( "entry failed" ) ;
45
89
let path = entry. path ( ) ;
46
90
let file_name = path. file_name ( ) . expect ( "file_name failed" ) . to_os_string ( ) ;
47
91
92
+ println ! (
93
+ " {} {}" ,
94
+ "[FERRUMC_MACROS]" . bold( ) . blue( ) ,
95
+ format!( "Parsing file: {}" , file_name. to_string_lossy( ) )
96
+ . white( )
97
+ . bold( )
98
+ ) ;
99
+
48
100
if !path. is_file ( ) {
49
101
continue ;
50
102
}
@@ -57,70 +109,47 @@ pub fn bake_registry(input: TokenStream) -> TokenStream {
57
109
continue ;
58
110
} ;
59
111
60
- // format: #[packet(packet_id = 0x00, state = "handshake")]
112
+ // If the struct does not have the #[packet(...)] attribute, then skip it.
113
+ if !item_struct
114
+ . attrs
115
+ . iter ( )
116
+ . any ( |attr| attr. path ( ) . is_ident ( "packet" ) )
117
+ {
118
+ continue ;
119
+ }
61
120
62
- let mut packet_id: Option < u8 > = None ;
63
- let mut state: Option < String > = None ;
64
-
65
- for attr in item_struct. attrs {
66
- // #[packet(...)] part.
67
- if !attr. path ( ) . is_ident ( "packet" ) {
68
- continue ;
69
- }
70
-
71
- attr. parse_nested_meta ( |meta| {
72
- let Some ( ident) = meta. path . get_ident ( ) else {
73
- return Ok ( ( ) ) ;
74
- } ;
75
-
76
- match ident. to_string ( ) . as_str ( ) {
77
- "packet_id" => {
78
- let value = meta. value ( ) . expect ( "value failed" ) ;
79
- let value = value. parse :: < LitInt > ( ) . expect ( "parse failed" ) ;
80
- let n: u8 = value. base10_parse ( ) . expect ( "base10_parse failed" ) ;
81
- packet_id = Some ( n) ;
82
- }
83
- "state" => {
84
- let value = meta. value ( ) . expect ( "value failed" ) ;
85
- let value = value. parse :: < LitStr > ( ) . expect ( "parse failed" ) ;
86
- let n = value. value ( ) ;
87
- state = Some ( n) ;
88
- }
89
- & _ => {
90
- return Ok ( ( ) ) ;
91
- }
92
- }
93
-
94
- Ok ( ( ) )
95
- } )
96
- . unwrap ( ) ;
97
-
98
- let packet_id = packet_id. expect ( "packet_id not found" ) ;
99
-
100
- let state = state. clone ( ) . expect ( "state not found" ) ;
101
- let struct_name = & item_struct. ident ;
102
-
103
- println ! (
104
- " {} {} (ID: {}, State: {}, Struct Name: {})" ,
105
- "[FERRUMC_MACROS]" . bold( ) . blue( ) ,
106
- "Found Packet" . white( ) . bold( ) ,
107
- format!( "0x{:02X}" , packet_id) . cyan( ) ,
108
- state. green( ) ,
109
- struct_name. to_string( ) . yellow( )
110
- ) ;
111
-
112
- let path = format ! (
113
- // "crate::net::packets::incoming::{}",
114
- "{}::{}" ,
115
- base_path,
116
- file_name. to_string_lossy( ) . replace( ".rs" , "" )
117
- ) ;
118
- let struct_path = format ! ( "{}::{}" , path, struct_name) ;
119
-
120
- let struct_path =
121
- syn:: parse_str :: < syn:: Path > ( & struct_path) . expect ( "parse_str failed" ) ;
122
-
123
- match_arms. push ( quote ! {
121
+ // format: #[packet(packet_id = 0x00, state = "handshake")]
122
+ let ( state, packet_id) = get_packet_details_from_attributes (
123
+ & item_struct. attrs ,
124
+ PacketBoundiness :: Serverbound ,
125
+ )
126
+ . expect (
127
+ "parse_packet_attribute failed\
128
+ \n Please provide the packet_id and state fields in the #[packet(...)] attribute.\
129
+ \n Example: #[packet(packet_id = 0x00, state = \" handshake\" )]",
130
+ ) ;
131
+
132
+ let struct_name = & item_struct. ident ;
133
+
134
+ println ! (
135
+ " {} {} (ID: {}, State: {}, Struct Name: {})" ,
136
+ "[FERRUMC_MACROS]" . bold( ) . blue( ) ,
137
+ "Found Packet" . white( ) . bold( ) ,
138
+ format!( "0x{:02X}" , packet_id) . cyan( ) ,
139
+ state. green( ) ,
140
+ struct_name. to_string( ) . yellow( )
141
+ ) ;
142
+
143
+ let path = format ! (
144
+ "{}::{}" ,
145
+ base_path,
146
+ file_name. to_string_lossy( ) . replace( ".rs" , "" )
147
+ ) ;
148
+ let struct_path = format ! ( "{}::{}" , path, struct_name) ;
149
+
150
+ let struct_path = syn:: parse_str :: < syn:: Path > ( & struct_path) . expect ( "parse_str failed" ) ;
151
+
152
+ match_arms. push ( quote ! {
124
153
( #packet_id, #state) => {
125
154
// let packet= #struct_path::net_decode(cursor).await?;
126
155
let packet = <#struct_path as ferrumc_net_codec:: decode:: NetDecode >:: decode( cursor, & ferrumc_net_codec:: decode:: NetDecodeOpts :: None ) ?;
@@ -129,7 +158,6 @@ pub fn bake_registry(input: TokenStream) -> TokenStream {
129
158
// tracing::debug!("Received packet: {:?}", packet);
130
159
} ,
131
160
} ) ;
132
- }
133
161
}
134
162
}
135
163
@@ -168,6 +196,23 @@ pub fn bake_registry(input: TokenStream) -> TokenStream {
168
196
TokenStream :: from ( output)
169
197
}
170
198
199
+ fn parse_packet_id ( state : & str , value : String , bound_to : PacketBoundiness ) -> syn:: Result < u8 > {
200
+ //! Sorry to anyone reading this code. The get_packet_id method PANICS if there is any type of error.
201
+ //! these macros are treated like trash gah damn. they need better care 😔
202
+
203
+ // If the user provided a direct integer (like 0x01, or any number) value.
204
+ if value. starts_with ( "0x" ) {
205
+ let value = value. strip_prefix ( "0x" ) . expect ( "strip_prefix failed" ) ;
206
+ let n = u8:: from_str_radix ( value, 16 ) . expect ( "from_str_radix failed" ) ;
207
+ return Ok ( n) ;
208
+ }
209
+
210
+ // If the user provided referencing packet id, then just get that.
211
+ let n = get_packet_id ( state, bound_to, value. as_str ( ) ) ;
212
+
213
+ Ok ( n)
214
+ }
215
+
171
216
/// `#[packet]` attribute is used to declare an incoming/outgoing packet.
172
217
///
173
218
/// <b>packet_id</b> => The packet id of the packet. In hexadecimal.
@@ -208,7 +253,7 @@ pub fn attribute(args: TokenStream, input: TokenStream) -> TokenStream {
208
253
209
254
if !& [ "packet_id" , "state" ]
210
255
. iter ( )
211
- . any ( |x| args. to_string ( ) . contains ( x) )
256
+ . all ( |x| args. to_string ( ) . contains ( x) )
212
257
{
213
258
return TokenStream :: from ( quote ! {
214
259
compile_error!( #E ) ;
0 commit comments