1use bytes::{Buf, BufMut, Bytes, BytesMut};
2
3use crate::codec::{Decode, Encode, PacketId};
4use crate::error::ProtocolError;
5use crate::types::slot::LegacySlot;
6use crate::types::VarInt;
7
8pub use packets::{
9 ClientboundAwardStats, ClientboundBlockAction, ClientboundBlockDestroyStage,
10 ClientboundBlockEntityData, ClientboundBlockUpdate, ClientboundChangeDifficulty,
11 ClientboundChatMessage, ClientboundCommandSuggestions, ClientboundContainerClose,
12 ClientboundContainerSetContent, ClientboundContainerSetProperty, ClientboundContainerSetSlot,
13 ClientboundDisconnect, ClientboundEntityAnimation, ClientboundEntityEvent,
14 ClientboundExplosion, ClientboundForgetLevelChunk, ClientboundGameEvent,
15 ClientboundInitializeBorder, ClientboundJoinGame, ClientboundKeepAlive,
16 ClientboundLevelChunkWithLight, ClientboundLevelEvent, ClientboundLevelParticles,
17 ClientboundLoginPlay, ClientboundMapItemData, ClientboundMoveEntityPos,
18 ClientboundMoveEntityPosRot, ClientboundMoveEntityRot, ClientboundOpenScreen,
19 ClientboundOpenSignEditor, ClientboundPlayerAbilities, ClientboundPlayerCombatEnd,
20 ClientboundPlayerCombatEnter, ClientboundPlayerCombatKill, ClientboundPlayerInfoUpdate,
21 ClientboundPlayerPosition, ClientboundPluginMessage, ClientboundRemoveEntities,
22 ClientboundRemoveEntityEffect, ClientboundResetScore, ClientboundResourcePackPush,
23 ClientboundRespawn, ClientboundRotateHead, ClientboundSectionBlocksUpdate,
24 ClientboundSetBorderCenter, ClientboundSetBorderLerpSize, ClientboundSetBorderSize,
25 ClientboundSetBorderWarningDelay, ClientboundSetBorderWarningDistance, ClientboundSetCamera,
26 ClientboundSetEntityLink, ClientboundSetEntityMotion, ClientboundSetEquipment,
27 ClientboundSetExperience, ClientboundSetHealth, ClientboundSetHeldItem,
28 ClientboundSetScoreboardObjective, ClientboundSetScoreboardScore, ClientboundSetTime,
29 ClientboundSound, ClientboundSpawnEntity, ClientboundSpawnExperienceOrb,
30 ClientboundSpawnPlayer, ClientboundTabList, ClientboundTakeItemEntity,
31 ClientboundTeleportEntity, ClientboundUpdateAttributes, ClientboundUpdateEffects,
32 InteractAction, ServerboundAnimation, ServerboundChatMessage, ServerboundClickWindow,
33 ServerboundClickWindowButton, ServerboundClientSettings, ServerboundClientStatus,
34 ServerboundCloseWindow, ServerboundCommandSuggestion, ServerboundConfirmTransaction,
35 ServerboundCreativeInventoryAction, ServerboundEnchantItem, ServerboundEntityAction,
36 ServerboundHeldItemChange, ServerboundInteract, ServerboundKeepAlive, ServerboundMovePlayerPos,
37 ServerboundMovePlayerPosRot, ServerboundMovePlayerRot, ServerboundMovePlayerStatusOnly,
38 ServerboundPlayerAbilities, ServerboundPlayerAction, ServerboundPlayerBlockPlacement,
39 ServerboundPluginMessage, ServerboundResourcePackStatus, ServerboundSpectate,
40 ServerboundSteerVehicle, ServerboundUpdateSign,
41};
42
43mod packets {
44 use super::*;
45
46 fn encode_str(s: &str, dst: &mut BytesMut) -> Result<(), ProtocolError> {
47 let bytes = s.as_bytes();
48 VarInt(bytes.len() as i32).encode(dst)?;
49 dst.put_slice(bytes);
50 Ok(())
51 }
52
53 fn decode_str(src: &mut Bytes, ctx: &'static str) -> Result<String, ProtocolError> {
54 let len = VarInt::decode(src)?.0 as usize;
55 if src.remaining() < len {
56 return Err(ProtocolError::Io(std::io::Error::new(
57 std::io::ErrorKind::UnexpectedEof,
58 format!("Missing bytes for {ctx}"),
59 )));
60 }
61 let mut b = vec![0u8; len];
62 src.copy_to_slice(&mut b);
63 String::from_utf8(b).map_err(|_| {
64 ProtocolError::Io(std::io::Error::new(
65 std::io::ErrorKind::InvalidData,
66 format!("Invalid UTF-8 in {ctx}"),
67 ))
68 })
69 }
70
71 fn need(src: &Bytes, n: usize) -> Result<(), ProtocolError> {
72 if src.remaining() < n {
73 Err(ProtocolError::Io(std::io::Error::new(
74 std::io::ErrorKind::UnexpectedEof,
75 format!("Need {n} bytes, have {}", src.remaining()),
76 )))
77 } else {
78 Ok(())
79 }
80 }
81
82 macro_rules! raw_packet {
85 ($name:ident, $id:expr) => {
86 #[derive(Debug, Clone, PartialEq)]
87 pub struct $name {
88 pub raw: Vec<u8>,
89 }
90 impl PacketId for $name {
91 fn packet_id(_ver: u32) -> u8 {
92 $id
93 }
94 }
95 impl Encode for $name {
96 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
97 dst.put_slice(&self.raw);
98 Ok(())
99 }
100 }
101 impl Decode for $name {
102 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
103 let len = src.remaining();
104 let mut raw = vec![0u8; len];
105 src.copy_to_slice(&mut raw);
106 Ok(Self { raw })
107 }
108 }
109 };
110 }
111
112 #[derive(Debug, Clone, PartialEq)]
115 pub struct ClientboundJoinGame {
116 pub entity_id: i32,
117 pub game_mode: u8,
118 pub dimension: i8,
119 pub difficulty: u8,
120 pub max_players: u8,
121 pub level_type: String,
122 pub reduced_debug_info: bool,
123 }
124
125 impl PacketId for ClientboundJoinGame {
126 fn packet_id(_ver: u32) -> u8 {
127 0x01
128 }
129 }
130
131 impl Encode for ClientboundJoinGame {
132 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
133 dst.put_i32(self.entity_id);
134 dst.put_u8(self.game_mode);
135 dst.put_i8(self.dimension);
136 dst.put_u8(self.difficulty);
137 dst.put_u8(self.max_players);
138 encode_str(&self.level_type, dst)?;
139 dst.put_u8(self.reduced_debug_info as u8);
140 Ok(())
141 }
142 }
143
144 impl Decode for ClientboundJoinGame {
145 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
146 need(src, 4 + 1 + 1 + 1 + 1)?;
147 let entity_id = src.get_i32();
148 let game_mode = src.get_u8();
149 let dimension = src.get_i8();
150 let difficulty = src.get_u8();
151 let max_players = src.get_u8();
152 let level_type = decode_str(src, "ClientboundJoinGame level_type")?;
153 need(src, 1)?;
154 let reduced_debug_info = src.get_u8() != 0;
155 Ok(Self {
156 entity_id,
157 game_mode,
158 dimension,
159 difficulty,
160 max_players,
161 level_type,
162 reduced_debug_info,
163 })
164 }
165 }
166
167 raw_packet!(ClientboundLoginPlay, 0x01);
169
170 #[derive(Debug, Clone, PartialEq)]
173 pub struct ClientboundRespawn {
174 pub dimension: i32,
175 pub difficulty: u8,
176 pub game_mode: u8,
177 pub level_type: String,
178 }
179
180 impl PacketId for ClientboundRespawn {
181 fn packet_id(_ver: u32) -> u8 {
182 0x07
183 }
184 }
185
186 impl Encode for ClientboundRespawn {
187 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
188 dst.put_i32(self.dimension);
189 dst.put_u8(self.difficulty);
190 dst.put_u8(self.game_mode);
191 encode_str(&self.level_type, dst)
192 }
193 }
194
195 impl Decode for ClientboundRespawn {
196 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
197 need(src, 4 + 1 + 1)?;
198 let dimension = src.get_i32();
199 let difficulty = src.get_u8();
200 let game_mode = src.get_u8();
201 let level_type = decode_str(src, "ClientboundRespawn level_type")?;
202 Ok(Self {
203 dimension,
204 difficulty,
205 game_mode,
206 level_type,
207 })
208 }
209 }
210
211 #[derive(Debug, Clone, PartialEq)]
214 pub struct ClientboundPlayerPosition {
215 pub x: f64,
216 pub head_y: f64,
217 pub z: f64,
218 pub yaw: f32,
219 pub pitch: f32,
220 pub flags: u8,
221 }
222
223 impl PacketId for ClientboundPlayerPosition {
224 fn packet_id(_ver: u32) -> u8 {
225 0x08
226 }
227 }
228
229 impl Encode for ClientboundPlayerPosition {
230 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
231 dst.put_f64(self.x);
232 dst.put_f64(self.head_y);
233 dst.put_f64(self.z);
234 dst.put_f32(self.yaw);
235 dst.put_f32(self.pitch);
236 dst.put_u8(self.flags);
237 Ok(())
238 }
239 }
240
241 impl Decode for ClientboundPlayerPosition {
242 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
243 need(src, 8 + 8 + 8 + 4 + 4 + 1)?;
244 Ok(Self {
245 x: src.get_f64(),
246 head_y: src.get_f64(),
247 z: src.get_f64(),
248 yaw: src.get_f32(),
249 pitch: src.get_f32(),
250 flags: src.get_u8(),
251 })
252 }
253 }
254
255 #[derive(Debug, Clone, PartialEq)]
258 pub struct ClientboundKeepAlive {
259 pub keep_alive_id: VarInt,
260 }
261
262 impl PacketId for ClientboundKeepAlive {
263 fn packet_id(_ver: u32) -> u8 {
264 0x00
265 }
266 }
267
268 impl Encode for ClientboundKeepAlive {
269 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
270 self.keep_alive_id.encode(dst)
271 }
272 }
273
274 impl Decode for ClientboundKeepAlive {
275 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
276 Ok(Self {
277 keep_alive_id: VarInt::decode(src)?,
278 })
279 }
280 }
281
282 #[derive(Debug, Clone, PartialEq)]
283 pub struct ServerboundKeepAlive {
284 pub keep_alive_id: VarInt,
285 }
286
287 impl PacketId for ServerboundKeepAlive {
288 fn packet_id(_ver: u32) -> u8 {
289 0x00
290 }
291 }
292
293 impl Encode for ServerboundKeepAlive {
294 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
295 self.keep_alive_id.encode(dst)
296 }
297 }
298
299 impl Decode for ServerboundKeepAlive {
300 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
301 Ok(Self {
302 keep_alive_id: VarInt::decode(src)?,
303 })
304 }
305 }
306
307 #[derive(Debug, Clone, PartialEq)]
310 pub struct ClientboundChatMessage {
311 pub json_message: String,
312 pub position: u8,
313 }
314
315 impl PacketId for ClientboundChatMessage {
316 fn packet_id(_ver: u32) -> u8 {
317 0x02
318 }
319 }
320
321 impl Encode for ClientboundChatMessage {
322 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
323 encode_str(&self.json_message, dst)?;
324 dst.put_u8(self.position);
325 Ok(())
326 }
327 }
328
329 impl Decode for ClientboundChatMessage {
330 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
331 let json_message = decode_str(src, "ClientboundChatMessage json_message")?;
332 need(src, 1)?;
333 Ok(Self {
334 json_message,
335 position: src.get_u8(),
336 })
337 }
338 }
339
340 #[derive(Debug, Clone, PartialEq)]
341 pub struct ServerboundChatMessage {
342 pub message: String,
343 }
344
345 impl PacketId for ServerboundChatMessage {
346 fn packet_id(_ver: u32) -> u8 {
347 0x01
348 }
349 }
350
351 impl Encode for ServerboundChatMessage {
352 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
353 encode_str(&self.message, dst)
354 }
355 }
356
357 impl Decode for ServerboundChatMessage {
358 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
359 Ok(Self {
360 message: decode_str(src, "ServerboundChatMessage message")?,
361 })
362 }
363 }
364
365 #[derive(Debug, Clone, PartialEq)]
368 pub struct ClientboundPluginMessage {
369 pub channel: String,
370 pub data: Vec<u8>,
371 }
372
373 impl PacketId for ClientboundPluginMessage {
374 fn packet_id(_ver: u32) -> u8 {
375 0x3F
376 }
377 }
378
379 impl Encode for ClientboundPluginMessage {
380 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
381 encode_str(&self.channel, dst)?;
382 dst.put_slice(&self.data);
383 Ok(())
384 }
385 }
386
387 impl Decode for ClientboundPluginMessage {
388 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
389 let channel = decode_str(src, "ClientboundPluginMessage channel")?;
390 let len = src.remaining();
391 let mut data = vec![0u8; len];
392 src.copy_to_slice(&mut data);
393 Ok(Self { channel, data })
394 }
395 }
396
397 #[derive(Debug, Clone, PartialEq)]
398 pub struct ServerboundPluginMessage {
399 pub channel: String,
400 pub data: Vec<u8>,
401 }
402
403 impl PacketId for ServerboundPluginMessage {
404 fn packet_id(_ver: u32) -> u8 {
405 0x17
406 }
407 }
408
409 impl Encode for ServerboundPluginMessage {
410 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
411 encode_str(&self.channel, dst)?;
412 dst.put_slice(&self.data);
413 Ok(())
414 }
415 }
416
417 impl Decode for ServerboundPluginMessage {
418 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
419 let channel = decode_str(src, "ServerboundPluginMessage channel")?;
420 let len = src.remaining();
421 let mut data = vec![0u8; len];
422 src.copy_to_slice(&mut data);
423 Ok(Self { channel, data })
424 }
425 }
426
427 #[derive(Debug, Clone, PartialEq)]
430 pub struct ClientboundDisconnect {
431 pub reason: String,
432 }
433
434 impl PacketId for ClientboundDisconnect {
435 fn packet_id(_ver: u32) -> u8 {
436 0x40
437 }
438 }
439
440 impl Encode for ClientboundDisconnect {
441 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
442 encode_str(&self.reason, dst)
443 }
444 }
445
446 impl Decode for ClientboundDisconnect {
447 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
448 Ok(Self {
449 reason: decode_str(src, "ClientboundDisconnect reason")?,
450 })
451 }
452 }
453
454 #[derive(Debug, Clone, PartialEq)]
457 pub struct ClientboundSetHeldItem {
458 pub slot: u8,
459 }
460
461 impl PacketId for ClientboundSetHeldItem {
462 fn packet_id(_ver: u32) -> u8 {
463 0x09
464 }
465 }
466
467 impl Encode for ClientboundSetHeldItem {
468 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
469 dst.put_u8(self.slot);
470 Ok(())
471 }
472 }
473
474 impl Decode for ClientboundSetHeldItem {
475 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
476 need(src, 1)?;
477 Ok(Self { slot: src.get_u8() })
478 }
479 }
480
481 #[derive(Debug, Clone, PartialEq)]
484 pub enum InteractAction {
485 Interact,
486 Attack,
487 InteractAt {
488 target_x: f32,
489 target_y: f32,
490 target_z: f32,
491 },
492 }
493
494 #[derive(Debug, Clone, PartialEq)]
495 pub struct ServerboundInteract {
496 pub entity_id: VarInt,
497 pub action: InteractAction,
498 }
499
500 impl PacketId for ServerboundInteract {
501 fn packet_id(_ver: u32) -> u8 {
502 0x02
503 }
504 }
505
506 impl Encode for ServerboundInteract {
507 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
508 self.entity_id.encode(dst)?;
509 match &self.action {
510 InteractAction::Interact => {
511 VarInt(0).encode(dst)?;
512 },
513 InteractAction::Attack => {
514 VarInt(1).encode(dst)?;
515 },
516 InteractAction::InteractAt {
517 target_x,
518 target_y,
519 target_z,
520 } => {
521 VarInt(2).encode(dst)?;
522 dst.put_f32(*target_x);
523 dst.put_f32(*target_y);
524 dst.put_f32(*target_z);
525 },
526 }
527 Ok(())
528 }
529 }
530
531 impl Decode for ServerboundInteract {
532 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
533 let entity_id = VarInt::decode(src)?;
534 let action = match VarInt::decode(src)?.0 {
535 0 => InteractAction::Interact,
536 1 => InteractAction::Attack,
537 2 => {
538 need(src, 4 + 4 + 4)?;
539 InteractAction::InteractAt {
540 target_x: src.get_f32(),
541 target_y: src.get_f32(),
542 target_z: src.get_f32(),
543 }
544 },
545 _ => {
546 return Err(ProtocolError::Io(std::io::Error::new(
547 std::io::ErrorKind::InvalidData,
548 "Unknown ServerboundInteract action type",
549 )))
550 },
551 };
552 Ok(Self { entity_id, action })
553 }
554 }
555
556 #[derive(Debug, Clone, PartialEq)]
559 pub struct ServerboundMovePlayerStatusOnly {
560 pub on_ground: bool,
561 }
562
563 impl PacketId for ServerboundMovePlayerStatusOnly {
564 fn packet_id(_ver: u32) -> u8 {
565 0x03
566 }
567 }
568
569 impl Encode for ServerboundMovePlayerStatusOnly {
570 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
571 dst.put_u8(self.on_ground as u8);
572 Ok(())
573 }
574 }
575
576 impl Decode for ServerboundMovePlayerStatusOnly {
577 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
578 need(src, 1)?;
579 Ok(Self {
580 on_ground: src.get_u8() != 0,
581 })
582 }
583 }
584
585 #[derive(Debug, Clone, PartialEq)]
586 pub struct ServerboundMovePlayerPos {
587 pub x: f64,
588 pub feet_y: f64,
589 pub z: f64,
590 pub on_ground: bool,
591 }
592
593 impl PacketId for ServerboundMovePlayerPos {
594 fn packet_id(_ver: u32) -> u8 {
595 0x04
596 }
597 }
598
599 impl Encode for ServerboundMovePlayerPos {
600 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
601 dst.put_f64(self.x);
602 dst.put_f64(self.feet_y);
603 dst.put_f64(self.z);
604 dst.put_u8(self.on_ground as u8);
605 Ok(())
606 }
607 }
608
609 impl Decode for ServerboundMovePlayerPos {
610 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
611 need(src, 8 + 8 + 8 + 1)?;
612 Ok(Self {
613 x: src.get_f64(),
614 feet_y: src.get_f64(),
615 z: src.get_f64(),
616 on_ground: src.get_u8() != 0,
617 })
618 }
619 }
620
621 #[derive(Debug, Clone, PartialEq)]
622 pub struct ServerboundMovePlayerRot {
623 pub yaw: f32,
624 pub pitch: f32,
625 pub on_ground: bool,
626 }
627
628 impl PacketId for ServerboundMovePlayerRot {
629 fn packet_id(_ver: u32) -> u8 {
630 0x05
631 }
632 }
633
634 impl Encode for ServerboundMovePlayerRot {
635 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
636 dst.put_f32(self.yaw);
637 dst.put_f32(self.pitch);
638 dst.put_u8(self.on_ground as u8);
639 Ok(())
640 }
641 }
642
643 impl Decode for ServerboundMovePlayerRot {
644 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
645 need(src, 4 + 4 + 1)?;
646 Ok(Self {
647 yaw: src.get_f32(),
648 pitch: src.get_f32(),
649 on_ground: src.get_u8() != 0,
650 })
651 }
652 }
653
654 #[derive(Debug, Clone, PartialEq)]
655 pub struct ServerboundMovePlayerPosRot {
656 pub x: f64,
657 pub feet_y: f64,
658 pub z: f64,
659 pub yaw: f32,
660 pub pitch: f32,
661 pub on_ground: bool,
662 }
663
664 impl PacketId for ServerboundMovePlayerPosRot {
665 fn packet_id(_ver: u32) -> u8 {
666 0x06
667 }
668 }
669
670 impl Encode for ServerboundMovePlayerPosRot {
671 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
672 dst.put_f64(self.x);
673 dst.put_f64(self.feet_y);
674 dst.put_f64(self.z);
675 dst.put_f32(self.yaw);
676 dst.put_f32(self.pitch);
677 dst.put_u8(self.on_ground as u8);
678 Ok(())
679 }
680 }
681
682 impl Decode for ServerboundMovePlayerPosRot {
683 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
684 need(src, 8 + 8 + 8 + 4 + 4 + 1)?;
685 Ok(Self {
686 x: src.get_f64(),
687 feet_y: src.get_f64(),
688 z: src.get_f64(),
689 yaw: src.get_f32(),
690 pitch: src.get_f32(),
691 on_ground: src.get_u8() != 0,
692 })
693 }
694 }
695
696 #[derive(Debug, Clone, PartialEq)]
697 pub struct ClientboundSound {
698 pub sound_name: String,
699 pub x: i32,
700 pub y: i32,
701 pub z: i32,
702 pub volume: f32,
703 pub pitch: u8,
704 }
705
706 impl PacketId for ClientboundSound {
707 fn packet_id(_ver: u32) -> u8 {
708 0x29
709 }
710 }
711
712 impl Encode for ClientboundSound {
713 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
714 encode_str(&self.sound_name, dst)?;
715 dst.put_i32(self.x);
716 dst.put_i32(self.y);
717 dst.put_i32(self.z);
718 dst.put_f32(self.volume);
719 dst.put_u8(self.pitch);
720 Ok(())
721 }
722 }
723
724 impl Decode for ClientboundSound {
725 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
726 let sound_name = decode_str(src, "MinecraftNamedSound context")?;
727 need(src, 4 + 4 + 4 + 4 + 1)?;
728 Ok(Self {
729 sound_name,
730 x: src.get_i32(),
731 y: src.get_i32(),
732 z: src.get_i32(),
733 volume: src.get_f32(),
734 pitch: src.get_u8(),
735 })
736 }
737 }
738
739 #[derive(Debug, Clone, PartialEq)]
742 pub struct ServerboundAnimation;
743
744 impl PacketId for ServerboundAnimation {
745 fn packet_id(_ver: u32) -> u8 {
746 0x0A
747 }
748 }
749
750 impl Encode for ServerboundAnimation {
751 fn encode(&self, _dst: &mut BytesMut) -> Result<(), ProtocolError> {
752 Ok(())
753 }
754 }
755
756 impl Decode for ServerboundAnimation {
757 fn decode(_src: &mut Bytes) -> Result<Self, ProtocolError> {
758 Ok(Self)
759 }
760 }
761
762 #[derive(Debug, Clone, PartialEq)]
765 pub struct ClientboundPlayerAbilities {
766 pub flags: u8,
767 pub flying_speed: f32,
768 pub field_of_view_modifier: f32,
769 }
770
771 impl PacketId for ClientboundPlayerAbilities {
772 fn packet_id(_ver: u32) -> u8 {
773 0x39
774 }
775 }
776
777 impl Encode for ClientboundPlayerAbilities {
778 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
779 dst.put_u8(self.flags);
780 dst.put_f32(self.flying_speed);
781 dst.put_f32(self.field_of_view_modifier);
782 Ok(())
783 }
784 }
785
786 impl Decode for ClientboundPlayerAbilities {
787 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
788 need(src, 1 + 4 + 4)?;
789 Ok(Self {
790 flags: src.get_u8(),
791 flying_speed: src.get_f32(),
792 field_of_view_modifier: src.get_f32(),
793 })
794 }
795 }
796
797 raw_packet!(ClientboundSpawnEntity, 0x0E);
800 raw_packet!(ClientboundSpawnExperienceOrb, 0x11);
801 raw_packet!(ClientboundSpawnPlayer, 0x0C);
802 raw_packet!(ClientboundEntityAnimation, 0x0B);
803 raw_packet!(ClientboundAwardStats, 0x37);
804 raw_packet!(ClientboundBlockDestroyStage, 0x25);
805 raw_packet!(ClientboundBlockEntityData, 0x35);
806 raw_packet!(ClientboundBlockAction, 0x24);
807 raw_packet!(ClientboundBlockUpdate, 0x23);
808 raw_packet!(ClientboundChangeDifficulty, 0x41);
809 raw_packet!(ClientboundCommandSuggestions, 0x3A);
810 raw_packet!(ClientboundContainerClose, 0x2E);
811 raw_packet!(ClientboundContainerSetProperty, 0x31);
812
813 #[derive(Debug, Clone, PartialEq)]
816 pub struct ClientboundContainerSetContent {
817 pub window_id: u8,
818 pub slots: Vec<LegacySlot>,
819 pub carried_item: LegacySlot,
820 }
821
822 impl PacketId for ClientboundContainerSetContent {
823 fn packet_id(_ver: u32) -> u8 {
824 0x30
825 }
826 }
827
828 impl Encode for ClientboundContainerSetContent {
829 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
830 dst.put_u8(self.window_id);
831 dst.put_i16(self.slots.len() as i16);
832 for slot in &self.slots {
833 slot.encode(dst)?;
834 }
835 self.carried_item.encode(dst)?;
836 Ok(())
837 }
838 }
839
840 impl Decode for ClientboundContainerSetContent {
841 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
842 need(src, 1 + 2)?;
843 let window_id = src.get_u8();
844 let count = src.get_i16() as usize;
845 let mut slots = Vec::with_capacity(count);
846 for _ in 0..count {
847 slots.push(LegacySlot::decode(src)?);
848 }
849 let carried_item = LegacySlot::decode(src)?;
850 Ok(Self {
851 window_id,
852 slots,
853 carried_item,
854 })
855 }
856 }
857
858 #[derive(Debug, Clone, PartialEq)]
861 pub struct ClientboundContainerSetSlot {
862 pub window_id: i8,
863 pub slot: i16,
864 pub slot_data: LegacySlot,
865 }
866
867 impl PacketId for ClientboundContainerSetSlot {
868 fn packet_id(_ver: u32) -> u8 {
869 0x2F
870 }
871 }
872
873 impl Encode for ClientboundContainerSetSlot {
874 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
875 dst.put_i8(self.window_id);
876 dst.put_i16(self.slot);
877 self.slot_data.encode(dst)?;
878 Ok(())
879 }
880 }
881
882 impl Decode for ClientboundContainerSetSlot {
883 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
884 need(src, 1 + 2)?;
885 let window_id = src.get_i8();
886 let slot = src.get_i16();
887 let slot_data = LegacySlot::decode(src)?;
888 Ok(Self {
889 window_id,
890 slot,
891 slot_data,
892 })
893 }
894 }
895
896 raw_packet!(ClientboundEntityEvent, 0x1A);
897 raw_packet!(ClientboundExplosion, 0x27);
898 raw_packet!(ClientboundForgetLevelChunk, 0x26);
899 raw_packet!(ClientboundGameEvent, 0x2B);
900 raw_packet!(ClientboundInitializeBorder, 0x44);
901 raw_packet!(ClientboundLevelChunkWithLight, 0x21);
902 raw_packet!(ClientboundLevelEvent, 0x28);
903 raw_packet!(ClientboundLevelParticles, 0x2A);
904 raw_packet!(ClientboundMapItemData, 0x34);
905 raw_packet!(ClientboundMoveEntityPos, 0x15);
906 raw_packet!(ClientboundMoveEntityPosRot, 0x17);
907 raw_packet!(ClientboundMoveEntityRot, 0x16);
908 raw_packet!(ClientboundOpenScreen, 0x2D);
909 raw_packet!(ClientboundOpenSignEditor, 0x36);
910 raw_packet!(ClientboundPlayerCombatEnd, 0x42);
911 raw_packet!(ClientboundPlayerCombatEnter, 0x42);
912 raw_packet!(ClientboundPlayerCombatKill, 0x42);
913 raw_packet!(ClientboundPlayerInfoUpdate, 0x38);
914 raw_packet!(ClientboundRemoveEntities, 0x13);
915 raw_packet!(ClientboundRemoveEntityEffect, 0x1E);
916 raw_packet!(ClientboundResetScore, 0x3B);
917 raw_packet!(ClientboundResourcePackPush, 0x48);
918 raw_packet!(ClientboundRotateHead, 0x19);
919 raw_packet!(ClientboundSectionBlocksUpdate, 0x22);
920 raw_packet!(ClientboundSetBorderCenter, 0x44);
921 raw_packet!(ClientboundSetBorderLerpSize, 0x44);
922 raw_packet!(ClientboundSetBorderSize, 0x44);
923 raw_packet!(ClientboundSetBorderWarningDelay, 0x44);
924 raw_packet!(ClientboundSetBorderWarningDistance, 0x44);
925 raw_packet!(ClientboundSetCamera, 0x43);
926 raw_packet!(ClientboundSetEntityLink, 0x1B);
927 raw_packet!(ClientboundSetEntityMotion, 0x12);
928 raw_packet!(ClientboundSetEquipment, 0x04);
929 raw_packet!(ClientboundSetExperience, 0x1F);
930 raw_packet!(ClientboundSetHealth, 0x06);
931 raw_packet!(ClientboundSetScoreboardObjective, 0x3B);
932 raw_packet!(ClientboundSetScoreboardScore, 0x3C);
933 raw_packet!(ClientboundSetTime, 0x03);
934 raw_packet!(ClientboundTabList, 0x47);
935 raw_packet!(ClientboundTakeItemEntity, 0x0D);
936 raw_packet!(ClientboundTeleportEntity, 0x18);
937 raw_packet!(ClientboundUpdateAttributes, 0x20);
938 raw_packet!(ClientboundUpdateEffects, 0x1D);
939
940 raw_packet!(ServerboundPlayerBlockPlacement, 0x08);
941 raw_packet!(ServerboundClientSettings, 0x15);
942 raw_packet!(ServerboundClickWindowButton, 0x11);
943 raw_packet!(ServerboundClickWindow, 0x0E);
944 raw_packet!(ServerboundCloseWindow, 0x0D);
945 raw_packet!(ServerboundCommandSuggestion, 0x14);
946 raw_packet!(ServerboundConfirmTransaction, 0x0F);
947 raw_packet!(ServerboundCreativeInventoryAction, 0x10);
948 raw_packet!(ServerboundEnchantItem, 0x11);
949 raw_packet!(ServerboundEntityAction, 0x0B);
950 raw_packet!(ServerboundHeldItemChange, 0x09);
951 raw_packet!(ServerboundPlayerAbilities, 0x13);
952 raw_packet!(ServerboundPlayerAction, 0x07);
953 raw_packet!(ServerboundResourcePackStatus, 0x19);
954 raw_packet!(ServerboundClientStatus, 0x16);
955 raw_packet!(ServerboundSpectate, 0x18);
956 raw_packet!(ServerboundSteerVehicle, 0x0C);
957 raw_packet!(ServerboundUpdateSign, 0x12);
958}
959
960#[cfg(test)]
961mod tests {
962 use super::*;
963
964 macro_rules! roundtrip {
965 ($T:ty, $val:expr) => {{
966 let v = $val;
967 let mut buf = BytesMut::new();
968 v.encode(&mut buf).unwrap();
969 let mut b = buf.freeze();
970 assert_eq!(<$T>::decode(&mut b).unwrap(), v);
971 }};
972 }
973
974 #[test]
975 fn join_game_roundtrip() {
976 roundtrip!(
977 ClientboundJoinGame,
978 ClientboundJoinGame {
979 entity_id: 1,
980 game_mode: 0,
981 dimension: 0,
982 difficulty: 1,
983 max_players: 20,
984 level_type: "default".to_string(),
985 reduced_debug_info: false,
986 }
987 );
988 }
989
990 #[test]
991 fn respawn_roundtrip() {
992 roundtrip!(
993 ClientboundRespawn,
994 ClientboundRespawn {
995 dimension: -1,
996 difficulty: 2,
997 game_mode: 0,
998 level_type: "default".to_string(),
999 }
1000 );
1001 }
1002
1003 #[test]
1004 fn player_position_roundtrip() {
1005 roundtrip!(
1006 ClientboundPlayerPosition,
1007 ClientboundPlayerPosition {
1008 x: 100.0,
1009 head_y: 65.62,
1010 z: -50.0,
1011 yaw: 0.0,
1012 pitch: 0.0,
1013 flags: 0,
1014 }
1015 );
1016 }
1017
1018 #[test]
1019 fn keepalive_roundtrip() {
1020 roundtrip!(
1021 ClientboundKeepAlive,
1022 ClientboundKeepAlive {
1023 keep_alive_id: VarInt(12345)
1024 }
1025 );
1026 roundtrip!(
1027 ServerboundKeepAlive,
1028 ServerboundKeepAlive {
1029 keep_alive_id: VarInt(12345)
1030 }
1031 );
1032 }
1033
1034 #[test]
1035 fn chat_roundtrip() {
1036 roundtrip!(
1037 ClientboundChatMessage,
1038 ClientboundChatMessage {
1039 json_message: r#"{"text":"hello"}"#.to_string(),
1040 position: 0,
1041 }
1042 );
1043 roundtrip!(
1044 ServerboundChatMessage,
1045 ServerboundChatMessage {
1046 message: "/help".to_string()
1047 }
1048 );
1049 }
1050
1051 #[test]
1052 fn move_pos_roundtrip() {
1053 roundtrip!(
1054 ServerboundMovePlayerPos,
1055 ServerboundMovePlayerPos {
1056 x: 1.0,
1057 feet_y: 64.0,
1058 z: -3.5,
1059 on_ground: true,
1060 }
1061 );
1062 }
1063
1064 #[test]
1065 fn move_rot_roundtrip() {
1066 roundtrip!(
1067 ServerboundMovePlayerRot,
1068 ServerboundMovePlayerRot {
1069 yaw: 45.0,
1070 pitch: 0.0,
1071 on_ground: true,
1072 }
1073 );
1074 }
1075
1076 #[test]
1077 fn move_pos_rot_roundtrip() {
1078 roundtrip!(
1079 ServerboundMovePlayerPosRot,
1080 ServerboundMovePlayerPosRot {
1081 x: 10.0,
1082 feet_y: 65.0,
1083 z: 20.0,
1084 yaw: 90.0,
1085 pitch: -30.0,
1086 on_ground: false,
1087 }
1088 );
1089 }
1090
1091 #[test]
1092 fn move_status_only_roundtrip() {
1093 roundtrip!(
1094 ServerboundMovePlayerStatusOnly,
1095 ServerboundMovePlayerStatusOnly { on_ground: true }
1096 );
1097 }
1098
1099 #[test]
1100 fn interact_roundtrip() {
1101 roundtrip!(
1102 ServerboundInteract,
1103 ServerboundInteract {
1104 entity_id: VarInt(42),
1105 action: InteractAction::Attack,
1106 }
1107 );
1108 roundtrip!(
1109 ServerboundInteract,
1110 ServerboundInteract {
1111 entity_id: VarInt(7),
1112 action: InteractAction::Interact,
1113 }
1114 );
1115 roundtrip!(
1116 ServerboundInteract,
1117 ServerboundInteract {
1118 entity_id: VarInt(5),
1119 action: InteractAction::InteractAt {
1120 target_x: 0.5,
1121 target_y: 1.0,
1122 target_z: 0.5
1123 },
1124 }
1125 );
1126 }
1127
1128 #[test]
1129 fn plugin_message_roundtrip() {
1130 roundtrip!(
1131 ClientboundPluginMessage,
1132 ClientboundPluginMessage {
1133 channel: "MC|Brand".to_string(),
1134 data: b"vanilla".to_vec(),
1135 }
1136 );
1137 roundtrip!(
1138 ServerboundPluginMessage,
1139 ServerboundPluginMessage {
1140 channel: "MC|Brand".to_string(),
1141 data: b"vanilla".to_vec(),
1142 }
1143 );
1144 }
1145
1146 #[test]
1147 fn disconnect_roundtrip() {
1148 roundtrip!(
1149 ClientboundDisconnect,
1150 ClientboundDisconnect {
1151 reason: r#"{"text":"You are banned"}"#.to_string(),
1152 }
1153 );
1154 }
1155
1156 #[test]
1157 fn set_held_item_roundtrip() {
1158 roundtrip!(ClientboundSetHeldItem, ClientboundSetHeldItem { slot: 3 });
1159 }
1160
1161 #[test]
1162 fn animation_roundtrip() {
1163 roundtrip!(ServerboundAnimation, ServerboundAnimation);
1164 }
1165
1166 #[test]
1167 fn packet_ids() {
1168 assert_eq!(ClientboundJoinGame::packet_id(47), 0x01);
1169 assert_eq!(ClientboundLoginPlay::packet_id(47), 0x01);
1170 assert_eq!(ClientboundRespawn::packet_id(47), 0x07);
1171 assert_eq!(ClientboundPlayerPosition::packet_id(47), 0x08);
1172 assert_eq!(ClientboundKeepAlive::packet_id(47), 0x00);
1173 assert_eq!(ServerboundKeepAlive::packet_id(47), 0x00);
1174 assert_eq!(ClientboundChatMessage::packet_id(47), 0x02);
1175 assert_eq!(ServerboundChatMessage::packet_id(47), 0x01);
1176 assert_eq!(ServerboundMovePlayerPos::packet_id(47), 0x04);
1177 assert_eq!(ServerboundMovePlayerRot::packet_id(47), 0x05);
1178 assert_eq!(ServerboundMovePlayerPosRot::packet_id(47), 0x06);
1179 assert_eq!(ServerboundMovePlayerStatusOnly::packet_id(47), 0x03);
1180 assert_eq!(ServerboundInteract::packet_id(47), 0x02);
1181 assert_eq!(ClientboundPluginMessage::packet_id(47), 0x3F);
1182 assert_eq!(ServerboundPluginMessage::packet_id(47), 0x17);
1183 assert_eq!(ClientboundDisconnect::packet_id(47), 0x40);
1184 assert_eq!(ClientboundSetHeldItem::packet_id(47), 0x09);
1185 assert_eq!(ServerboundAnimation::packet_id(47), 0x0A);
1186 assert_eq!(ServerboundPlayerBlockPlacement::packet_id(47), 0x08);
1187 assert_eq!(ServerboundClientSettings::packet_id(47), 0x15);
1188 }
1189}