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}