1use super::audit::audit_command;
2use super::deser::Deserialize;
3use super::deser::DeserializeError;
4use super::deser::DeserializeResult;
5use super::deser::Deserializer;
6use super::ser::Serialize;
7use super::ser::SerializeResult;
8use super::ser::Serializer;
9use super::types::*;
10use anyhow::bail;
11use minetest_protocol_derive::MinetestDeserialize;
12use minetest_protocol_derive::MinetestSerialize;
13use std::ops::Deref;
14
15#[macro_export]
16macro_rules! as_item {
17 ($i:item) => {
18 $i
19 };
20}
21
22#[macro_export]
23macro_rules! default_serializer {
24 ($spec_ty: ident { }) => {
25 impl Serialize for $spec_ty {
26 type Input = Self;
27 fn serialize<S: Serializer>(value: &Self::Input, _: &mut S) -> SerializeResult {
28 Ok(())
29 }
30 }
31 };
32 ($spec_ty: ident { $($fname: ident: $ftyp: ty ),+ }) => {
33 impl Serialize for $spec_ty {
34 type Input = Self;
35 fn serialize<S: Serializer>(value: &Self::Input, ser: &mut S) -> SerializeResult {
36 $(
37 <$ftyp as Serialize>::serialize(&value.$fname, ser)?;
38 )+
39 Ok(())
40 }
41 }
42 };
43}
44
45#[macro_export]
46macro_rules! default_deserializer {
47 ($spec_ty: ident { }) => {
48 impl Deserialize for $spec_ty {
49 type Output = Self;
50 fn deserialize(_deser: &mut Deserializer) -> DeserializeResult<Self> {
51 Ok($spec_ty)
52 }
53 }
54 };
55 ($spec_ty: ident { $($fname: ident: $ftyp: ty ),+ }) => {
56 impl Deserialize for $spec_ty {
57 type Output = Self;
58 fn deserialize(deser: &mut Deserializer) -> DeserializeResult<Self> {
59 Ok($spec_ty {
60 $(
61 $fname: <$ftyp>::deserialize(deser)?,
62 )+
63 })
64 }
65 }
66 };
67}
68
69#[macro_export]
70macro_rules! implicit_from {
71 ($command_ty: ident, $name: ident, $spec_ty: ident) => {
72 impl From<$spec_ty> for $command_ty {
73 fn from(value: $spec_ty) -> Self {
74 $command_ty::$name(Box::new(value))
75 }
76 }
77 };
78}
79
80#[macro_export]
81macro_rules! proto_struct {
82 ($spec_ty: ident { }) => {
83 #[derive(Debug, Clone, PartialEq, Default, MinetestSerialize, MinetestDeserialize)]
84 pub struct $spec_ty;
85 };
86 ($spec_ty: ident {
87 $($fname: ident: $ftype: ty $([$attr:meta])? ),+
88 }) => {
89 $crate::as_item! {
90 #[derive(Debug, Clone, PartialEq, MinetestSerialize, MinetestDeserialize)]
91 pub struct $spec_ty {
92 $( $(#[$attr])? pub $fname: $ftype),+
93 }
94 }
95 };
96}
97
98macro_rules! define_protocol {
99 ($version: literal,
100 $protocol_id: literal,
101 $dir: ident,
102 $command_ty: ident => {
103 $($name: ident, $id: literal, $channel: literal, $reliable: literal => $spec_ty: ident
104 { $($fname: ident : $ftype: ty $([$attr:meta])? ),* } ),*
105 }) => {
106 $crate::as_item! {
107 #[derive(Debug, PartialEq, Clone)]
108 pub enum $command_ty {
109 $($name(Box<$spec_ty>)),*,
110 }
111 }
112
113 $crate::as_item! {
114 impl CommandProperties for $command_ty {
115 fn direction(&self) -> CommandDirection {
116 CommandDirection::$dir
117 }
118
119 fn default_channel(&self) -> u8 {
120 match self {
121 $($command_ty::$name(_) => $channel),*,
122 }
123 }
124
125 fn default_reliability(&self) -> bool {
126 match self {
127 $($command_ty::$name(_) => $reliable),*,
128 }
129 }
130
131 fn command_name(&self) -> &'static str {
132 match self {
133 $($command_ty::$name(_) => stringify!($name)),*,
134 }
135 }
136 }
137 }
138
139 $crate::as_item! {
140 impl Serialize for $command_ty {
141 type Input = Self;
142 fn serialize<S: Serializer>(value: &Self::Input, ser: &mut S) -> SerializeResult {
143 match value {
144 $($command_ty::$name(spec) => { u16::serialize(&$id, ser)?; <$spec_ty as Serialize>::serialize(Deref::deref(spec), ser) }),*,
145 }
146 }
147 }
148 }
149
150 $crate::as_item! {
151 impl Deserialize for $command_ty {
152 type Output = Self;
153 fn deserialize(deser: &mut Deserializer) -> DeserializeResult<Self> {
154 let orig_buffer = deser.peek_all();
155 let command_id = u16::deserialize(deser)?;
156 let dir = deser.direction();
157 let result = match (dir, command_id) {
158 $( (CommandDirection::$dir, $id) => $command_ty::$name(Box::new(<$spec_ty as Deserialize>::deserialize(deser)?)) ),*,
159 _ => bail!(DeserializeError::BadPacketId(dir, command_id)),
160 };
161 audit_command(deser.context(), orig_buffer, &result);
162 Ok(result)
163 }
164 }
165 }
166
167 $($crate::proto_struct!($spec_ty { $($fname: $ftype $([$attr])?),* });)*
168 $($crate::implicit_from!($command_ty, $name, $spec_ty);)*
169
170 };
171}
172
173define_protocol!(41, 0x4f457403, ToClient, ToClientCommand => {
174 Hello, 0x02, 0, true => HelloSpec {
176 serialization_ver: u8,
177 compression_mode: u16,
178 proto_ver: u16,
179 auth_mechs: AuthMechsBitset,
180 username_legacy: String
181 },
182
183 AuthAccept, 0x03, 0, true => AuthAcceptSpec {
184 player_pos: v3f,
185 map_seed: u64,
186 recommended_send_interval: f32,
187 sudo_auth_methods: u32
188 },
189
190 AcceptSudoMode, 0x04, 0, true => AcceptSudoModeSpec {
191 },
193
194 DenySudoMode, 0x05, 0, true => DenySudoModeSpec {
195 },
197
198 AccessDenied, 0x0A, 0, true => AccessDeniedSpec {
199 code: AccessDeniedCode
200 },
201
202 Blockdata, 0x20, 2, true => BlockdataSpec {
203 pos: v3s16,
204 block: MapBlock,
205 network_specific_version: u8
206 },
207 Addnode, 0x21, 0, true => AddnodeSpec {
208 pos: v3s16,
209 node: MapNode,
210 keep_metadata: bool
211 },
212
213 Removenode, 0x22, 0, true => RemovenodeSpec {
214 pos: v3s16
215 },
216
217 Inventory, 0x27, 0, true => InventorySpec {
218 inventory: Inventory
219 },
220
221 TimeOfDay, 0x29, 0, true => TimeOfDaySpec {
222 time_of_day: u16,
223 time_speed: Option<f32>
224 },
225
226 CsmRestrictionFlags, 0x2A, 0, true => CsmRestrictionFlagsSpec {
227 csm_restriction_flags: u64,
228 csm_restriction_noderange: u32
229 },
230
231 PlayerSpeed, 0x2B, 0, true => PlayerSpeedSpec {
232 added_vel: v3f
233 },
234
235 MediaPush, 0x2C, 0, true => MediaPushSpec {
236 raw_hash: String,
237 filename: String,
238 cached: bool,
239 token: u32
240 },
241
242 TCChatMessage, 0x2F, 0, true => TCChatMessageSpec {
243 version: u8,
244 message_type: u8,
245 sender: String [wrap(WString)],
246 message: String [wrap(WString)],
247 timestamp: u64
248 },
249
250 ActiveObjectRemoveAdd, 0x31, 0, true => ActiveObjectRemoveAddSpec {
251 removed_object_ids: Vec<u16> [wrap(Array16<u16>)],
252 added_objects: Vec<AddedObject> [wrap(Array16<AddedObject>)]
253 },
254
255 ActiveObjectMessages, 0x32, 0, true => ActiveObjectMessagesSpec {
256 objects: Vec<ActiveObjectMessage> [wrap(Array0<ActiveObjectMessage>)]
257 },
258
259 Hp, 0x33, 0, true => HpSpec {
260 hp: u16,
261 damage_effect: Option<bool>
262 },
263
264 MovePlayer, 0x34, 0, true => MovePlayerSpec {
265 pos: v3f,
266 pitch: f32,
267 yaw: f32
268 },
269
270 AccessDeniedLegacy, 0x35, 0, true => AccessDeniedLegacySpec {
271 reason: String [wrap(WString)]
272 },
273
274 Fov, 0x36, 0, true => FovSpec {
275 fov: f32,
276 is_multiplier: bool,
277 transition_time: Option<f32>
278 },
279
280 Deathscreen, 0x37, 0, true => DeathscreenSpec {
281 set_camera_point_target: bool,
282 camera_point_target: v3f
283 },
284
285 Media, 0x38, 2, true => MediaSpec {
286 num_bunches: u16,
287 bunch_index: u16,
288 files: Vec<MediaFileData> [wrap(Array32<MediaFileData>)]
289 },
290
291 Nodedef, 0x3a, 0, true => NodedefSpec {
292 node_def: NodeDefManager [wrap(ZLibCompressed<NodeDefManager>)]
293 },
294
295 AnnounceMedia, 0x3c, 0, true => AnnounceMediaSpec {
296 files: Vec<MediaAnnouncement> [wrap(Array16<MediaAnnouncement>)],
297 remote_servers: String
298 },
299
300 Itemdef, 0x3d, 0, true => ItemdefSpec {
301 item_def: ItemdefList [wrap(ZLibCompressed<ItemdefList>)]
302 },
303
304 PlaySound, 0x3f, 0, true => PlaySoundSpec {
305 server_id: s32,
306 spec_name: String,
307 spec_gain: f32,
308 typ: u8, pos: v3f,
310 object_id: u16,
311 spec_loop: bool,
312 spec_fade: Option<f32>,
313 spec_pitch: Option<f32>,
314 ephemeral: Option<bool>
315 },
316
317 StopSound, 0x40, 0, true => StopSoundSpec {
318 server_id: s32
319 },
320
321 Privileges, 0x41, 0, true => PrivilegesSpec {
322 privileges: Vec<String> [wrap(Array16<String>)]
323 },
324
325 InventoryFormspec, 0x42, 0, true => InventoryFormspecSpec {
326 formspec: String [wrap(LongString)]
327 },
328
329 DetachedInventory, 0x43, 0, true => DetachedInventorySpec {
330 name: String,
331 keep_inv: bool,
332 ignore: Option<u16>,
334 contents: Option<Inventory>
335 },
336
337 ShowFormspec, 0x44, 0, true => ShowFormspecSpec {
338 form_spec: String [wrap(LongString)],
339 form_name: String
340 },
341
342 Movement, 0x45, 0, true => MovementSpec {
343 acceleration_default: f32,
344 acceleration_air: f32,
345 acceleration_fast: f32,
346 speed_walk: f32,
347 speed_crouch: f32,
348 speed_fast: f32,
349 speed_climb: f32,
350 speed_jump: f32,
351 liquid_fluidity: f32,
352 liquid_fluidity_smooth: f32,
353 liquid_sink: f32,
354 gravity: f32
355 },
356
357 SpawnParticle, 0x46, 0, true => SpawnParticleSpec {
358 data: ParticleParameters
359 },
360
361 AddParticlespawner, 0x47, 0, true => AddParticlespawnerSpec {
362 legacy: AddParticleSpawnerLegacy
363 },
364
365 Hudadd, 0x49, 1, true => HudaddSpec {
366 server_id: u32,
367 typ: u8,
368 pos: v2f,
369 name: String,
370 scale: v2f,
371 text: String,
372 number: u32,
373 item: u32,
374 dir: u32,
375 align: v2f,
376 offset: v2f,
377 world_pos: Option<v3f>,
378 size: Option<v2s32>,
379 z_index: Option<s16>,
380 text2: Option<String>,
381 style: Option<u32>
382 },
383
384 Hudrm, 0x4a, 1, true => HudrmSpec {
385 server_id: u32
386 },
387
388 Hudchange, 0x4b, 1, true => HudchangeSpec {
389 server_id: u32,
390 stat: HudStat
391 },
392
393 HudSetFlags, 0x4c, 1, true => HudSetFlagsSpec {
394 flags: HudFlags, mask: HudFlags },
397
398 HudSetParam, 0x4d, 1, true => HudSetParamSpec {
399 value: HudSetParam
400 },
401
402 Breath, 0x4e, 0, true => BreathSpec {
403 breath: u16
404 },
405
406 SetSky, 0x4f, 0, true => SetSkySpec {
407 params: SkyboxParams
408 },
409
410 OverrideDayNightRatio, 0x50, 0, true => OverrideDayNightRatioSpec {
411 do_override: bool,
412 day_night_ratio: u16
413 },
414
415 LocalPlayerAnimations, 0x51, 0, true => LocalPlayerAnimationsSpec {
416 idle: v2s32,
417 walk: v2s32,
418 dig: v2s32,
419 walk_dig: v2s32,
420 frame_speed: f32
421 },
422
423 EyeOffset, 0x52, 0, true => EyeOffsetSpec {
424 eye_offset_first: v3f,
425 eye_offset_third: v3f
426 },
427
428 DeleteParticlespawner, 0x53, 0, true => DeleteParticlespawnerSpec {
429 server_id: u32
430 },
431
432 CloudParams, 0x54, 0, true => CloudParamsSpec {
433 density: f32,
434 color_bright: SColor,
435 color_ambient: SColor,
436 height: f32,
437 thickness: f32,
438 speed: v2f
439 },
440
441 FadeSound, 0x55, 0, true => FadeSoundSpec {
442 sound_id: s32,
443 step: f32,
444 gain: f32
445 },
446
447 UpdatePlayerList, 0x56, 0, true => UpdatePlayerListSpec {
448 typ: u8,
449 players: Vec<String> [wrap(Array16<String>)]
450 },
451
452 TCModchannelMsg, 0x57, 0, true => TCModchannelMsgSpec {
453 channel_name: String,
454 sender: String,
455 channel_msg: String
456 },
457
458 ModchannelSignal, 0x58, 0, true => ModchannelSignalSpec {
459 signal_tmp: u8,
460 channel: String,
461 state: Option<u8>
463 },
464
465 NodemetaChanged, 0x59, 0, true => NodemetaChangedSpec {
466 list: AbsNodeMetadataList [wrap(ZLibCompressed<AbsNodeMetadataList>)]
467 },
468
469 SetSun, 0x5a, 0, true => SetSunSpec {
470 sun: SunParams
471 },
472
473 SetMoon, 0x5b, 0, true => SetMoonSpec {
474 moon: MoonParams
475 },
476
477 SetStars, 0x5c, 0, true => SetStarsSpec {
478 stars: StarParams
479 },
480
481 SrpBytesSB, 0x60, 0, true => SrpBytesSBSpec {
482 s: Vec<u8> [wrap(BinaryData16)],
483 b: Vec<u8> [wrap(BinaryData16)]
484 },
485
486 FormspecPrepend, 0x61, 0, true => FormspecPrependSpec {
487 formspec_prepend: String
488 },
489
490 MinimapModes, 0x62, 0, true => MinimapModesSpec {
491 modes: MinimapModeList
492 },
493
494 SetLighting, 0x63, 0, true => SetLightingSpec {
495 lighting: Lighting
496 }
497});
498
499define_protocol!(41, 0x4f457403, ToServer, ToServerCommand => {
500 Null, 0x00, 0, false => NullSpec {
503 },
506
507 Init, 0x02, 1, false => InitSpec {
508 serialization_ver_max: u8,
509 supp_compr_modes: u16,
510 min_net_proto_version: u16,
511 max_net_proto_version: u16,
512 player_name: String
513 },
514
515 Init2, 0x11, 1, true => Init2Spec {
516 lang: Option<String>
517 },
518
519 ModchannelJoin, 0x17, 0, true => ModchannelJoinSpec {
520 channel_name: String
521 },
522
523 ModchannelLeave, 0x18, 0, true => ModchannelLeaveSpec {
524 channel_name: String
525 },
526
527 TSModchannelMsg, 0x19, 0, true => TSModchannelMsgSpec {
528 channel_name: String,
529 channel_msg: String
530 },
531
532 Playerpos, 0x23, 0, false => PlayerposSpec {
533 player_pos: PlayerPos
534 },
535
536 Gotblocks, 0x24, 2, true => GotblocksSpec {
537 blocks: Vec<v3s16> [wrap(Array8<v3s16>)]
538 },
539
540 Deletedblocks, 0x25, 2, true => DeletedblocksSpec {
541 blocks: Vec<v3s16> [wrap(Array8<v3s16>)]
542 },
543
544 InventoryAction, 0x31, 0, true => InventoryActionSpec {
545 action: InventoryAction
546 },
547
548 TSChatMessage, 0x32, 0, true => TSChatMessageSpec {
549 message: String [wrap(WString)]
550 },
551
552 Damage, 0x35, 0, true => DamageSpec {
553 damage: u16
554 },
555
556 Playeritem, 0x37, 0, true => PlayeritemSpec {
557 item: u16
558 },
559
560 Respawn, 0x38, 0, true => RespawnSpec {
561 },
563
564 Interact, 0x39, 0, true => InteractSpec {
565 action: InteractAction,
566 item_index: u16,
567 pointed_thing: PointedThing [wrap(Wrapped32<PointedThing>)],
568 player_pos: PlayerPos
569 },
570
571 RemovedSounds, 0x3a, 2, true => RemovedSoundsSpec {
572 ids: Vec<s32> [wrap(Array16<s32>)]
573 },
574
575 NodemetaFields, 0x3b, 0, true => NodemetaFieldsSpec {
576 p: v3s16,
577 form_name: String,
578 fields: Vec<(String, String)> [wrap(Array16<Pair<String, LongString>>)]
580 },
581
582 InventoryFields, 0x3c, 0, true => InventoryFieldsSpec {
583 client_formspec_name: String,
584 fields: Vec<(String, String)> [wrap(Array16<Pair<String, LongString>>)]
585 },
586
587 RequestMedia, 0x40, 1, true => RequestMediaSpec {
588 files: Vec<String> [wrap(Array16<String>)]
589 },
590
591 HaveMedia, 0x41, 2, true => HaveMediaSpec {
592 tokens: Vec<u32> [wrap(Array8<u32>)]
593 },
594
595 ClientReady, 0x43, 1, true => ClientReadySpec {
596 major_ver: u8,
597 minor_ver: u8,
598 patch_ver: u8,
599 reserved: u8,
600 full_ver: String,
601 formspec_ver: Option<u16>
602 },
603
604 FirstSrp, 0x50, 1, true => FirstSrpSpec {
605 salt: Vec<u8> [wrap(BinaryData16)],
606 verification_key: Vec<u8> [wrap(BinaryData16)],
607 is_empty: bool
608 },
609
610 SrpBytesA, 0x51, 1, true => SrpBytesASpec {
611 bytes_a: Vec<u8> [wrap(BinaryData16)],
612 based_on: u8
613 },
614
615 SrpBytesM, 0x52, 1, true => SrpBytesMSpec {
616 bytes_m: Vec<u8> [wrap(BinaryData16)]
617 },
618
619 UpdateClientInfo, 0x53, 1, true => UpdateClientInfoSpec {
620 render_target_size: v2u32,
621 real_gui_scaling: f32,
622 real_hud_scaling: f32,
623 max_fs_size: v2f
624 }
625});
626
627#[derive(Debug, PartialEq, Clone)]
628pub enum Command {
629 ToServer(ToServerCommand),
630 ToClient(ToClientCommand),
631}
632
633pub trait CommandProperties {
634 fn direction(&self) -> CommandDirection;
635 fn default_channel(&self) -> u8;
636 fn default_reliability(&self) -> bool;
637 fn command_name(&self) -> &'static str;
638}
639
640pub trait CommandRef: CommandProperties + std::fmt::Debug {
644 fn toserver_ref(&self) -> Option<&ToServerCommand>;
645 fn toclient_ref(&self) -> Option<&ToClientCommand>;
646}
647
648pub fn serialize_commandref<Cmd: CommandRef, S: Serializer>(
649 cmd: &Cmd,
650 ser: &mut S,
651) -> SerializeResult {
652 if let Some(r) = cmd.toserver_ref() {
653 ToServerCommand::serialize(r, ser)?;
654 }
655 if let Some(r) = cmd.toclient_ref() {
656 ToClientCommand::serialize(r, ser)?;
657 }
658 Ok(())
659}
660
661impl CommandProperties for Command {
662 fn direction(&self) -> CommandDirection {
663 match self {
664 Command::ToServer(_) => CommandDirection::ToServer,
665 Command::ToClient(_) => CommandDirection::ToClient,
666 }
667 }
668
669 fn default_channel(&self) -> u8 {
670 match self {
671 Command::ToServer(c) => c.default_channel(),
672 Command::ToClient(c) => c.default_channel(),
673 }
674 }
675
676 fn default_reliability(&self) -> bool {
677 match self {
678 Command::ToServer(c) => c.default_reliability(),
679 Command::ToClient(c) => c.default_reliability(),
680 }
681 }
682
683 fn command_name(&self) -> &'static str {
684 match self {
685 Command::ToServer(c) => c.command_name(),
686 Command::ToClient(c) => c.command_name(),
687 }
688 }
689}
690
691impl CommandRef for Command {
692 fn toserver_ref(&self) -> Option<&ToServerCommand> {
693 match self {
694 Command::ToServer(s) => Some(s),
695 Command::ToClient(_) => None,
696 }
697 }
698
699 fn toclient_ref(&self) -> Option<&ToClientCommand> {
700 match self {
701 Command::ToServer(_) => None,
702 Command::ToClient(c) => Some(c),
703 }
704 }
705}
706
707impl CommandRef for ToClientCommand {
708 fn toserver_ref(&self) -> Option<&ToServerCommand> {
709 None
710 }
711
712 fn toclient_ref(&self) -> Option<&ToClientCommand> {
713 Some(self)
714 }
715}
716
717impl CommandRef for ToServerCommand {
718 fn toserver_ref(&self) -> Option<&ToServerCommand> {
719 Some(self)
720 }
721
722 fn toclient_ref(&self) -> Option<&ToClientCommand> {
723 None
724 }
725}
726
727impl Serialize for Command {
728 type Input = Self;
729 fn serialize<S: Serializer>(value: &Self::Input, ser: &mut S) -> SerializeResult {
730 match value {
731 Command::ToServer(c) => ToServerCommand::serialize(c, ser),
732 Command::ToClient(c) => ToClientCommand::serialize(c, ser),
733 }
734 }
735}
736
737impl Deserialize for Command {
738 type Output = Self;
739 fn deserialize(deser: &mut Deserializer) -> DeserializeResult<Self> {
740 Ok(match deser.direction() {
741 CommandDirection::ToClient => Command::ToClient(ToClientCommand::deserialize(deser)?),
742 CommandDirection::ToServer => Command::ToServer(ToServerCommand::deserialize(deser)?),
743 })
744 }
745}