luanti_protocol/commands/
macros.rs

1#[macro_export]
2macro_rules! as_item {
3    ($i:item) => {
4        $i
5    };
6}
7
8// #[macro_export]
9// macro_rules! default_serializer {
10//     ($spec_ty: ident { }) => {
11//         impl Serialize for $spec_ty {
12//             type Input = Self;
13//             fn serialize<S: Serializer>(value: &Self::Input, _: &mut S) -> SerializeResult {
14//                 Ok(())
15//             }
16//         }
17//     };
18//     ($spec_ty: ident { $($fname: ident: $ftyp: ty ),+ }) => {
19//         impl Serialize for $spec_ty {
20//             type Input = Self;
21//             fn serialize<S: Serializer>(value: &Self::Input, ser: &mut S) -> SerializeResult {
22//                 $(
23//                     <$ftyp as Serialize>::serialize(&value.$fname, ser)?;
24//                 )+
25//                 Ok(())
26//             }
27//         }
28//     };
29// }
30
31// #[macro_export]
32// macro_rules! default_deserializer {
33//     ($spec_ty: ident { }) => {
34//         impl Deserialize for $spec_ty {
35//             type Output = Self;
36//             fn deserialize(_deserializer: &mut Deserializer) -> DeserializeResult<Self> {
37//                 log::trace!(stringify!("deserializing ", $spec_ty));
38//                 Ok($spec_ty)
39//             }
40//         }
41//     };
42//     ($spec_ty: ident { $($fname: ident: $ftyp: ty ),+ }) => {
43//         impl Deserialize for $spec_ty {
44//             type Output = Self;
45//             fn deserialize(deserializer: &mut Deserializer) -> DeserializeResult<Self> {
46//                 log::trace!(stringify!("deserializing ", $spec_ty));
47//                 $(
48//                     log::trace!(stringify!("deserializing field ", $fname, ": ", $ftyp));
49//                     let $fname = <$ftyp>::deserialize(deser)?;
50//                 )+
51//                 Ok($spec_ty { $($fname, )+ })
52//             }
53//         }
54//     };
55// }
56
57#[macro_export]
58macro_rules! implicit_from {
59    ($command_ty: ident, $name: ident, $spec_ty: ident) => {
60        impl From<$spec_ty> for $command_ty {
61            fn from(value: $spec_ty) -> Self {
62                $command_ty::$name(Box::new(value))
63            }
64        }
65    };
66}
67
68macro_rules! define_protocol {
69    ($version: literal,
70     $protocol_id: literal,
71     $dir: ident,
72     $command_ty: ident => {
73         $($name: ident, $id: literal, $channel: ident, $reliable: literal => $spec_ty: ident),*
74    }) => {
75        $crate::as_item! {
76            #[derive(Debug, PartialEq, Clone)]
77            pub enum $command_ty {
78                $($name(Box<$spec_ty>)),*,
79            }
80        }
81
82        $crate::as_item! {
83            impl CommandProperties for $command_ty {
84                fn direction(&self) -> CommandDirection {
85                    CommandDirection::$dir
86                }
87
88                fn default_channel(&self) -> ChannelId {
89                    match self {
90                        $($command_ty::$name(_) => ChannelId::$channel),*,
91                    }
92                }
93
94                fn default_reliability(&self) -> bool {
95                    match self {
96                        $($command_ty::$name(_) => $reliable),*,
97                    }
98                }
99
100                fn command_name(&self) -> &'static str {
101                    match self {
102                        $($command_ty::$name(_) => stringify!($name)),*,
103                    }
104                }
105            }
106        }
107
108        $crate::as_item! {
109            impl Serialize for $command_ty {
110                type Input = Self;
111                fn serialize<S: Serializer>(value: &Self::Input, ser: &mut S) -> SerializeResult {
112                    match value {
113                        $($command_ty::$name(spec) => {
114                            u16::serialize(&$id, ser)?;
115                            anyhow::Context::context(<$spec_ty as Serialize>::serialize(Deref::deref(spec), ser), stringify!($name))
116                        }),*,
117                    }
118                }
119            }
120        }
121
122        $crate::as_item! {
123            impl Deserialize for $command_ty {
124                type Output = Option<Self>;
125                fn deserialize(deserializer: &mut Deserializer<'_>) -> DeserializeResult<Self::Output> {
126                    // The first packet a client sends doesn't contain a command but has an empty payload.
127                    // It only serves the purpose of triggering the creation of a peer entry within the server.
128                    // Rather than requesting every caller to perform a pre-check for a non-empty payload,
129                    // we just return an `Option` to force the caller to handle this case.
130                    if !deserializer.has_remaining() {
131                        return Ok(None);
132                    }
133                    let orig_buffer = deserializer.peek_all();
134                    // log::trace!("orig_buffer: {:?}", &orig_buffer[0..(orig_buffer.len().min(64))]);
135                    let command_id = u16::deserialize(deserializer)?;
136                    let dir = deserializer.direction();
137                    let result = match (dir, command_id) {
138                        $( (CommandDirection::$dir, $id) => $command_ty::$name(Box::new(<$spec_ty as Deserialize>::deserialize(deserializer)?)) ),*,
139                        _ => bail!(DeserializeError::BadPacketId(dir, command_id)),
140                    };
141                    // there might be more bytes to read if new fields have been added to the protocol
142                    // those will be stripped off and might trip the receiver
143                    if deserializer.has_remaining() {
144                        log::warn!("left-over bytes after deserialization of {:#?}: {:?}", result, deserializer.peek_all());
145                    }
146                    audit_command(deserializer.context(), orig_buffer, &result);
147                    Ok(Some(result))
148                }
149            }
150        }
151
152        $($crate::implicit_from!($command_ty, $name, $spec_ty);)*
153    };
154}