Skip to main content

kojacoord_protocol/versions/v1_7_10/play/
mod.rs

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, ClientboundJoinGame,
15    ClientboundKeepAlive, ClientboundLevelChunkWithLight, ClientboundLevelEvent,
16    ClientboundLevelParticles, ClientboundMapItemData, ClientboundMoveEntityPos,
17    ClientboundMoveEntityPosRot, ClientboundMoveEntityRot, ClientboundOpenScreen,
18    ClientboundOpenSignEditor, ClientboundPlayerAbilities, ClientboundPlayerCombatEnd,
19    ClientboundPlayerCombatEnter, ClientboundPlayerCombatKill, ClientboundPlayerInfoUpdate,
20    ClientboundPlayerPosition, ClientboundPluginMessage, ClientboundRemoveEntities,
21    ClientboundRemoveEntityEffect, ClientboundResetScore, ClientboundResourcePackPush,
22    ClientboundRespawn, ClientboundRotateHead, ClientboundSectionBlocksUpdate,
23    ClientboundSetEntityLink, ClientboundSetEntityMotion, ClientboundSetEquipment,
24    ClientboundSetExperience, ClientboundSetHealth, ClientboundSetHeldItem,
25    ClientboundSetScoreboardObjective, ClientboundSetScoreboardScore, ClientboundSetTime,
26    ClientboundSound, ClientboundSpawnEntity, ClientboundSpawnExperienceOrb, ClientboundSpawnMob,
27    ClientboundSpawnPlayer, ClientboundTabList, ClientboundTakeItemEntity,
28    ClientboundTeleportEntity, ClientboundUpdateAttributes, ClientboundUpdateEffects,
29    InteractAction, ServerboundAnimation, ServerboundChatMessage, ServerboundClickWindow,
30    ServerboundClickWindowButton, ServerboundClientSettings, ServerboundClientStatus,
31    ServerboundCloseWindow, ServerboundCommandSuggestion, ServerboundConfirmTransaction,
32    ServerboundCreativeInventoryAction, ServerboundEnchantItem, ServerboundEntityAction,
33    ServerboundHeldItemChange, ServerboundInteract, ServerboundKeepAlive, ServerboundMovePlayerPos,
34    ServerboundMovePlayerPosRot, ServerboundMovePlayerRot, ServerboundMovePlayerStatusOnly,
35    ServerboundPlayerAbilities, ServerboundPlayerAction, ServerboundPlayerBlockPlacement,
36    ServerboundPluginMessage, ServerboundResourcePackStatus, ServerboundSteerVehicle,
37    ServerboundUpdateSign,
38};
39
40mod packets {
41    use super::*;
42
43    fn encode_str(s: &str, dst: &mut BytesMut) -> Result<(), ProtocolError> {
44        let bytes = s.as_bytes();
45        VarInt(bytes.len() as i32).encode(dst)?;
46        dst.put_slice(bytes);
47        Ok(())
48    }
49
50    fn decode_str(src: &mut Bytes, ctx: &'static str) -> Result<String, ProtocolError> {
51        let len = VarInt::decode(src)?.0 as usize;
52        if src.remaining() < len {
53            return Err(ProtocolError::Io(std::io::Error::new(
54                std::io::ErrorKind::UnexpectedEof,
55                format!("Missing bytes for {ctx}"),
56            )));
57        }
58        let mut b = vec![0u8; len];
59        src.copy_to_slice(&mut b);
60        String::from_utf8(b).map_err(|_| {
61            ProtocolError::Io(std::io::Error::new(
62                std::io::ErrorKind::InvalidData,
63                format!("Invalid UTF-8 in {ctx}"),
64            ))
65        })
66    }
67
68    fn need(src: &Bytes, n: usize) -> Result<(), ProtocolError> {
69        if src.remaining() < n {
70            Err(ProtocolError::Io(std::io::Error::new(
71                std::io::ErrorKind::UnexpectedEof,
72                format!("Need {n} bytes, have {}", src.remaining()),
73            )))
74        } else {
75            Ok(())
76        }
77    }
78
79    // ── Raw opaque packet macro ───────────────────────────────────────────────
80
81    macro_rules! raw_packet {
82        ($name:ident, $id:expr) => {
83            #[derive(Debug, Clone, PartialEq)]
84            pub struct $name {
85                pub raw: Vec<u8>,
86            }
87            impl PacketId for $name {
88                fn packet_id(_ver: u32) -> u8 {
89                    $id
90                }
91            }
92            impl Encode for $name {
93                fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
94                    dst.put_slice(&self.raw);
95                    Ok(())
96                }
97            }
98            impl Decode for $name {
99                fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
100                    let len = src.remaining();
101                    let mut raw = vec![0u8; len];
102                    src.copy_to_slice(&mut raw);
103                    Ok(Self { raw })
104                }
105            }
106        };
107    }
108
109    // ── KeepAlive (0x00 / 0x00) ───────────────────────────────────────────────
110
111    #[derive(Debug, Clone, PartialEq)]
112    pub struct ClientboundKeepAlive {
113        pub keep_alive_id: VarInt,
114    }
115
116    impl PacketId for ClientboundKeepAlive {
117        fn packet_id(_ver: u32) -> u8 {
118            0x00
119        }
120    }
121
122    impl Encode for ClientboundKeepAlive {
123        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
124            self.keep_alive_id.encode(dst)
125        }
126    }
127
128    impl Decode for ClientboundKeepAlive {
129        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
130            Ok(Self {
131                keep_alive_id: VarInt::decode(src)?,
132            })
133        }
134    }
135
136    #[derive(Debug, Clone, PartialEq)]
137    pub struct ServerboundKeepAlive {
138        pub keep_alive_id: VarInt,
139    }
140
141    impl PacketId for ServerboundKeepAlive {
142        fn packet_id(_ver: u32) -> u8 {
143            0x00
144        }
145    }
146
147    impl Encode for ServerboundKeepAlive {
148        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
149            self.keep_alive_id.encode(dst)
150        }
151    }
152
153    impl Decode for ServerboundKeepAlive {
154        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
155            Ok(Self {
156                keep_alive_id: VarInt::decode(src)?,
157            })
158        }
159    }
160
161    // ── JoinGame (0x01) ───────────────────────────────────────────────────────
162
163    #[derive(Debug, Clone, PartialEq)]
164    pub struct ClientboundJoinGame {
165        pub entity_id: i32,
166        pub game_mode: u8,
167        pub dimension: i8,
168        pub difficulty: u8,
169        pub max_players: u8,
170        pub level_type: String,
171        pub reduced_debug_info: bool,
172    }
173
174    impl PacketId for ClientboundJoinGame {
175        fn packet_id(_ver: u32) -> u8 {
176            0x01
177        }
178    }
179
180    impl Encode for ClientboundJoinGame {
181        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
182            dst.put_i32(self.entity_id);
183            dst.put_u8(self.game_mode);
184            dst.put_i8(self.dimension);
185            dst.put_u8(self.difficulty);
186            dst.put_u8(self.max_players);
187            encode_str(&self.level_type, dst)?;
188            dst.put_u8(self.reduced_debug_info as u8);
189            Ok(())
190        }
191    }
192
193    impl Decode for ClientboundJoinGame {
194        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
195            need(src, 4 + 1 + 1 + 1 + 1)?;
196            let entity_id = src.get_i32();
197            let game_mode = src.get_u8();
198            let dimension = src.get_i8();
199            let difficulty = src.get_u8();
200            let max_players = src.get_u8();
201            let level_type = decode_str(src, "ClientboundJoinGame level_type")?;
202            need(src, 1)?;
203            let reduced_debug_info = src.get_u8() != 0;
204            Ok(Self {
205                entity_id,
206                game_mode,
207                dimension,
208                difficulty,
209                max_players,
210                level_type,
211                reduced_debug_info,
212            })
213        }
214    }
215
216    // ── ChatMessage (0x02 / 0x01) ─────────────────────────────────────────────
217
218    #[derive(Debug, Clone, PartialEq)]
219    pub struct ClientboundChatMessage {
220        pub json_message: String,
221        pub position: u8,
222    }
223
224    impl PacketId for ClientboundChatMessage {
225        fn packet_id(_ver: u32) -> u8 {
226            0x02
227        }
228    }
229
230    impl Encode for ClientboundChatMessage {
231        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
232            encode_str(&self.json_message, dst)?;
233            dst.put_u8(self.position);
234            Ok(())
235        }
236    }
237
238    impl Decode for ClientboundChatMessage {
239        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
240            let json_message = decode_str(src, "ClientboundChatMessage json_message")?;
241            need(src, 1)?;
242            Ok(Self {
243                json_message,
244                position: src.get_u8(),
245            })
246        }
247    }
248
249    #[derive(Debug, Clone, PartialEq)]
250    pub struct ServerboundChatMessage {
251        pub message: String,
252    }
253
254    impl PacketId for ServerboundChatMessage {
255        fn packet_id(_ver: u32) -> u8 {
256            0x01
257        }
258    }
259
260    impl Encode for ServerboundChatMessage {
261        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
262            encode_str(&self.message, dst)
263        }
264    }
265
266    impl Decode for ServerboundChatMessage {
267        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
268            Ok(Self {
269                message: decode_str(src, "ServerboundChatMessage message")?,
270            })
271        }
272    }
273
274    // ── Respawn (0x07) ────────────────────────────────────────────────────────
275
276    #[derive(Debug, Clone, PartialEq)]
277    pub struct ClientboundRespawn {
278        pub dimension: i32,
279        pub difficulty: u8,
280        pub game_mode: u8,
281        pub level_type: String,
282    }
283
284    impl PacketId for ClientboundRespawn {
285        fn packet_id(_ver: u32) -> u8 {
286            0x07
287        }
288    }
289
290    impl Encode for ClientboundRespawn {
291        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
292            dst.put_i32(self.dimension);
293            dst.put_u8(self.difficulty);
294            dst.put_u8(self.game_mode);
295            encode_str(&self.level_type, dst)
296        }
297    }
298
299    impl Decode for ClientboundRespawn {
300        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
301            need(src, 4 + 1 + 1)?;
302            let dimension = src.get_i32();
303            let difficulty = src.get_u8();
304            let game_mode = src.get_u8();
305            let level_type = decode_str(src, "ClientboundRespawn level_type")?;
306            Ok(Self {
307                dimension,
308                difficulty,
309                game_mode,
310                level_type,
311            })
312        }
313    }
314
315    // ── PlayerPosition (0x08) ─────────────────────────────────────────────────
316    // 1.7.10 uses head_y instead of feet_y and has no teleport_id.
317
318    #[derive(Debug, Clone, PartialEq)]
319    pub struct ClientboundPlayerPosition {
320        pub x: f64,
321        pub head_y: f64,
322        pub z: f64,
323        pub yaw: f32,
324        pub pitch: f32,
325        pub flags: u8,
326    }
327
328    impl PacketId for ClientboundPlayerPosition {
329        fn packet_id(_ver: u32) -> u8 {
330            0x08
331        }
332    }
333
334    impl Encode for ClientboundPlayerPosition {
335        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
336            dst.put_f64(self.x);
337            dst.put_f64(self.head_y);
338            dst.put_f64(self.z);
339            dst.put_f32(self.yaw);
340            dst.put_f32(self.pitch);
341            dst.put_u8(self.flags);
342            Ok(())
343        }
344    }
345
346    impl Decode for ClientboundPlayerPosition {
347        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
348            need(src, 8 + 8 + 8 + 4 + 4 + 1)?;
349            Ok(Self {
350                x: src.get_f64(),
351                head_y: src.get_f64(),
352                z: src.get_f64(),
353                yaw: src.get_f32(),
354                pitch: src.get_f32(),
355                flags: src.get_u8(),
356            })
357        }
358    }
359
360    // ── Disconnect (0x40) ─────────────────────────────────────────────────────
361
362    #[derive(Debug, Clone, PartialEq)]
363    pub struct ClientboundDisconnect {
364        pub reason: String,
365    }
366
367    impl PacketId for ClientboundDisconnect {
368        fn packet_id(_ver: u32) -> u8 {
369            0x40
370        }
371    }
372
373    impl Encode for ClientboundDisconnect {
374        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
375            encode_str(&self.reason, dst)
376        }
377    }
378
379    impl Decode for ClientboundDisconnect {
380        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
381            Ok(Self {
382                reason: decode_str(src, "ClientboundDisconnect reason")?,
383            })
384        }
385    }
386
387    // ── PluginMessage (0x3F / 0x17) ───────────────────────────────────────────
388    // 1.7.10 uses a signed i16 length prefix for the data payload, not VarInt.
389
390    #[derive(Debug, Clone, PartialEq)]
391    pub struct ClientboundPluginMessage {
392        pub channel: String,
393        pub data: Vec<u8>,
394    }
395
396    impl PacketId for ClientboundPluginMessage {
397        fn packet_id(_ver: u32) -> u8 {
398            0x3F
399        }
400    }
401
402    impl Encode for ClientboundPluginMessage {
403        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
404            encode_str(&self.channel, dst)?;
405            dst.put_i16(self.data.len() as i16);
406            dst.put_slice(&self.data);
407            Ok(())
408        }
409    }
410
411    impl Decode for ClientboundPluginMessage {
412        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
413            let channel = decode_str(src, "ClientboundPluginMessage channel")?;
414            need(src, 2)?;
415            let data_len = src.get_i16() as usize;
416            need(src, data_len)?;
417            let mut data = vec![0u8; data_len];
418            src.copy_to_slice(&mut data);
419            Ok(Self { channel, data })
420        }
421    }
422
423    #[derive(Debug, Clone, PartialEq)]
424    pub struct ServerboundPluginMessage {
425        pub channel: String,
426        pub data: Vec<u8>,
427    }
428
429    impl PacketId for ServerboundPluginMessage {
430        fn packet_id(_ver: u32) -> u8 {
431            0x17
432        }
433    }
434
435    impl Encode for ServerboundPluginMessage {
436        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
437            encode_str(&self.channel, dst)?;
438            dst.put_i16(self.data.len() as i16);
439            dst.put_slice(&self.data);
440            Ok(())
441        }
442    }
443
444    impl Decode for ServerboundPluginMessage {
445        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
446            let channel = decode_str(src, "ServerboundPluginMessage channel")?;
447            need(src, 2)?;
448            let data_len = src.get_i16() as usize;
449            need(src, data_len)?;
450            let mut data = vec![0u8; data_len];
451            src.copy_to_slice(&mut data);
452            Ok(Self { channel, data })
453        }
454    }
455
456    // ── Interact (0x02) ───────────────────────────────────────────────────────
457    // 1.7.10: no hand field in Interact / InteractAt variants.
458
459    #[derive(Debug, Clone, PartialEq)]
460    pub enum InteractAction {
461        Interact,
462        Attack,
463        InteractAt {
464            target_x: f32,
465            target_y: f32,
466            target_z: f32,
467        },
468    }
469
470    #[derive(Debug, Clone, PartialEq)]
471    pub struct ServerboundInteract {
472        pub entity_id: VarInt,
473        pub action: InteractAction,
474    }
475
476    impl PacketId for ServerboundInteract {
477        fn packet_id(_ver: u32) -> u8 {
478            0x02
479        }
480    }
481
482    impl Encode for ServerboundInteract {
483        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
484            self.entity_id.encode(dst)?;
485            match &self.action {
486                InteractAction::Interact => {
487                    VarInt(0).encode(dst)?;
488                },
489                InteractAction::Attack => {
490                    VarInt(1).encode(dst)?;
491                },
492                InteractAction::InteractAt {
493                    target_x,
494                    target_y,
495                    target_z,
496                } => {
497                    VarInt(2).encode(dst)?;
498                    dst.put_f32(*target_x);
499                    dst.put_f32(*target_y);
500                    dst.put_f32(*target_z);
501                },
502            }
503            Ok(())
504        }
505    }
506
507    impl Decode for ServerboundInteract {
508        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
509            let entity_id = VarInt::decode(src)?;
510            let action = match VarInt::decode(src)?.0 {
511                0 => InteractAction::Interact,
512                1 => InteractAction::Attack,
513                2 => {
514                    need(src, 4 + 4 + 4)?;
515                    InteractAction::InteractAt {
516                        target_x: src.get_f32(),
517                        target_y: src.get_f32(),
518                        target_z: src.get_f32(),
519                    }
520                },
521                _ => {
522                    return Err(ProtocolError::Io(std::io::Error::new(
523                        std::io::ErrorKind::InvalidData,
524                        "Unknown ServerboundInteract action type",
525                    )))
526                },
527            };
528            Ok(Self { entity_id, action })
529        }
530    }
531
532    // ── Movement (0x03–0x06) ──────────────────────────────────────────────────
533
534    #[derive(Debug, Clone, PartialEq)]
535    pub struct ServerboundMovePlayerPos {
536        pub x: f64,
537        pub feet_y: f64,
538        pub z: f64,
539        pub on_ground: bool,
540    }
541
542    impl PacketId for ServerboundMovePlayerPos {
543        fn packet_id(_ver: u32) -> u8 {
544            0x04
545        }
546    }
547
548    impl Encode for ServerboundMovePlayerPos {
549        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
550            dst.put_f64(self.x);
551            dst.put_f64(self.feet_y);
552            dst.put_f64(self.z);
553            dst.put_u8(self.on_ground as u8);
554            Ok(())
555        }
556    }
557
558    impl Decode for ServerboundMovePlayerPos {
559        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
560            need(src, 8 + 8 + 8 + 1)?;
561            Ok(Self {
562                x: src.get_f64(),
563                feet_y: src.get_f64(),
564                z: src.get_f64(),
565                on_ground: src.get_u8() != 0,
566            })
567        }
568    }
569
570    #[derive(Debug, Clone, PartialEq)]
571    pub struct ServerboundMovePlayerRot {
572        pub yaw: f32,
573        pub pitch: f32,
574        pub on_ground: bool,
575    }
576
577    impl PacketId for ServerboundMovePlayerRot {
578        fn packet_id(_ver: u32) -> u8 {
579            0x05
580        }
581    }
582
583    impl Encode for ServerboundMovePlayerRot {
584        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
585            dst.put_f32(self.yaw);
586            dst.put_f32(self.pitch);
587            dst.put_u8(self.on_ground as u8);
588            Ok(())
589        }
590    }
591
592    impl Decode for ServerboundMovePlayerRot {
593        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
594            need(src, 4 + 4 + 1)?;
595            Ok(Self {
596                yaw: src.get_f32(),
597                pitch: src.get_f32(),
598                on_ground: src.get_u8() != 0,
599            })
600        }
601    }
602
603    #[derive(Debug, Clone, PartialEq)]
604    pub struct ServerboundMovePlayerPosRot {
605        pub x: f64,
606        pub feet_y: f64,
607        pub z: f64,
608        pub yaw: f32,
609        pub pitch: f32,
610        pub on_ground: bool,
611    }
612
613    impl PacketId for ServerboundMovePlayerPosRot {
614        fn packet_id(_ver: u32) -> u8 {
615            0x06
616        }
617    }
618
619    impl Encode for ServerboundMovePlayerPosRot {
620        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
621            dst.put_f64(self.x);
622            dst.put_f64(self.feet_y);
623            dst.put_f64(self.z);
624            dst.put_f32(self.yaw);
625            dst.put_f32(self.pitch);
626            dst.put_u8(self.on_ground as u8);
627            Ok(())
628        }
629    }
630
631    impl Decode for ServerboundMovePlayerPosRot {
632        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
633            need(src, 8 + 8 + 8 + 4 + 4 + 1)?;
634            Ok(Self {
635                x: src.get_f64(),
636                feet_y: src.get_f64(),
637                z: src.get_f64(),
638                yaw: src.get_f32(),
639                pitch: src.get_f32(),
640                on_ground: src.get_u8() != 0,
641            })
642        }
643    }
644
645    #[derive(Debug, Clone, PartialEq)]
646    pub struct ClientboundSpawnMob {
647        pub entity_id: VarInt,
648        pub kind: u8, // Wither ID is strictly 64 in Minecraft 1.7.10
649        pub x: i32,   // (Absolute X Coordinate) * 32
650        pub y: i32,   // (Absolute Y Coordinate) * 32
651        pub z: i32,   // (Absolute Z Coordinate) * 32
652        pub pitch: u8,
653        pub yaw: u8,
654        pub head_pitch: u8,
655        pub velocity_x: i16,
656        pub velocity_y: i16,
657        pub velocity_z: i16,
658        pub metadata: Vec<u8>, // Raw 1.7.10 DataWatcher Metadata sequence terminating with 0x7F
659    }
660
661    impl PacketId for ClientboundSpawnMob {
662        fn packet_id(_ver: u32) -> u8 {
663            0x0F // ID for SpawnMob in Protocol 5 (1.7.10)
664        }
665    }
666
667    impl Encode for ClientboundSpawnMob {
668        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
669            self.entity_id.encode(dst)?;
670            dst.put_u8(self.kind);
671            dst.put_i32(self.x);
672            dst.put_i32(self.y);
673            dst.put_i32(self.z);
674            dst.put_u8(self.pitch);
675            dst.put_u8(self.yaw);
676            dst.put_u8(self.head_pitch);
677            dst.put_i16(self.velocity_x);
678            dst.put_i16(self.velocity_y);
679            dst.put_i16(self.velocity_z);
680
681            // Write trailing 1.7.10 style metadata fields directly
682            dst.put_slice(&self.metadata);
683            Ok(())
684        }
685    }
686
687    // ── Set Equipment (0x04) ─────────────────────────────────────────────────────
688
689    #[derive(Debug, Clone, PartialEq)]
690    pub struct ClientboundSetEquipment {
691        pub entity_id: i32,
692        pub slot: i16,
693        pub item: LegacySlot,
694    }
695
696    impl PacketId for ClientboundSetEquipment {
697        fn packet_id(_ver: u32) -> u8 {
698            0x04
699        }
700    }
701
702    impl Encode for ClientboundSetEquipment {
703        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
704            dst.put_i32(self.entity_id);
705            dst.put_i16(self.slot);
706            self.item.encode(dst)?;
707            Ok(())
708        }
709    }
710
711    impl Decode for ClientboundSetEquipment {
712        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
713            need(src, 4 + 2)?;
714            let entity_id = src.get_i32();
715            let slot = src.get_i16();
716            let item = LegacySlot::decode(src)?;
717            Ok(Self {
718                entity_id,
719                slot,
720                item,
721            })
722        }
723    }
724
725    // ── Entity Animation (0x0B) ───────────────────────────────────────────────
726
727    #[derive(Debug, Clone, PartialEq)]
728    pub struct ClientboundEntityAnimation {
729        pub entity_id: i32,
730        pub animation: i8,
731    }
732
733    impl PacketId for ClientboundEntityAnimation {
734        fn packet_id(_ver: u32) -> u8 {
735            0x0B
736        }
737    }
738
739    impl Encode for ClientboundEntityAnimation {
740        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
741            dst.put_i32(self.entity_id);
742            dst.put_i8(self.animation);
743            Ok(())
744        }
745    }
746
747    impl Decode for ClientboundEntityAnimation {
748        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
749            need(src, 4 + 1)?;
750            Ok(Self {
751                entity_id: src.get_i32(),
752                animation: src.get_i8(),
753            })
754        }
755    }
756
757    // ── Take Item Entity (0x0D) ───────────────────────────────────────────────
758
759    #[derive(Debug, Clone, PartialEq)]
760    pub struct ClientboundTakeItemEntity {
761        pub collected_entity_id: i32,
762        pub collector_entity_id: i32,
763    }
764
765    impl PacketId for ClientboundTakeItemEntity {
766        fn packet_id(_ver: u32) -> u8 {
767            0x0D
768        }
769    }
770
771    impl Encode for ClientboundTakeItemEntity {
772        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
773            dst.put_i32(self.collected_entity_id);
774            dst.put_i32(self.collector_entity_id);
775            Ok(())
776        }
777    }
778
779    impl Decode for ClientboundTakeItemEntity {
780        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
781            need(src, 4 + 4)?;
782            Ok(Self {
783                collected_entity_id: src.get_i32(),
784                collector_entity_id: src.get_i32(),
785            })
786        }
787    }
788
789    // ── Spawn Experience Orb (0x11) ────────────────────────────────────────────
790
791    #[derive(Debug, Clone, PartialEq)]
792    pub struct ClientboundSpawnExperienceOrb {
793        pub entity_id: i32,
794        pub x: i32,
795        pub y: i32,
796        pub z: i32,
797        pub count: i16,
798    }
799
800    impl PacketId for ClientboundSpawnExperienceOrb {
801        fn packet_id(_ver: u32) -> u8 {
802            0x11
803        }
804    }
805
806    impl Encode for ClientboundSpawnExperienceOrb {
807        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
808            dst.put_i32(self.entity_id);
809            dst.put_i32(self.x);
810            dst.put_i32(self.y);
811            dst.put_i32(self.z);
812            dst.put_i16(self.count);
813            Ok(())
814        }
815    }
816
817    impl Decode for ClientboundSpawnExperienceOrb {
818        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
819            need(src, 4 + 4 + 4 + 4 + 2)?;
820            Ok(Self {
821                entity_id: src.get_i32(),
822                x: src.get_i32(),
823                y: src.get_i32(),
824                z: src.get_i32(),
825                count: src.get_i16(),
826            })
827        }
828    }
829
830    // ── Set Entity Motion (0x12) ───────────────────────────────────────────────
831
832    #[derive(Debug, Clone, PartialEq)]
833    pub struct ClientboundSetEntityMotion {
834        pub entity_id: i32,
835        pub mot_x: i16,
836        pub mot_y: i16,
837        pub mot_z: i16,
838    }
839
840    impl PacketId for ClientboundSetEntityMotion {
841        fn packet_id(_ver: u32) -> u8 {
842            0x12
843        }
844    }
845
846    impl Encode for ClientboundSetEntityMotion {
847        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
848            dst.put_i32(self.entity_id);
849            dst.put_i16(self.mot_x);
850            dst.put_i16(self.mot_y);
851            dst.put_i16(self.mot_z);
852            Ok(())
853        }
854    }
855
856    impl Decode for ClientboundSetEntityMotion {
857        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
858            need(src, 4 + 2 + 2 + 2)?;
859            Ok(Self {
860                entity_id: src.get_i32(),
861                mot_x: src.get_i16(),
862                mot_y: src.get_i16(),
863                mot_z: src.get_i16(),
864            })
865        }
866    }
867
868    // ── Remove Entities (0x13) ─────────────────────────────────────────────────
869
870    #[derive(Debug, Clone, PartialEq)]
871    pub struct ClientboundRemoveEntities {
872        pub entity_ids: Vec<i32>,
873    }
874
875    impl PacketId for ClientboundRemoveEntities {
876        fn packet_id(_ver: u32) -> u8 {
877            0x13
878        }
879    }
880
881    impl Encode for ClientboundRemoveEntities {
882        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
883            VarInt(self.entity_ids.len() as i32).encode(dst)?;
884            for id in &self.entity_ids {
885                dst.put_i32(*id);
886            }
887            Ok(())
888        }
889    }
890
891    impl Decode for ClientboundRemoveEntities {
892        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
893            let count = VarInt::decode(src)?.0 as usize;
894            need(src, count * 4)?;
895            let mut entity_ids = Vec::with_capacity(count);
896            for _ in 0..count {
897                entity_ids.push(src.get_i32());
898            }
899            Ok(Self { entity_ids })
900        }
901    }
902
903    // ── Move Entity Pos (0x15) ───────────────────────────────────────────────
904
905    #[derive(Debug, Clone, PartialEq)]
906    pub struct ClientboundMoveEntityPos {
907        pub entity_id: i32,
908        pub dx: i8,
909        pub dy: i8,
910        pub dz: i8,
911    }
912
913    impl PacketId for ClientboundMoveEntityPos {
914        fn packet_id(_ver: u32) -> u8 {
915            0x15
916        }
917    }
918
919    impl Encode for ClientboundMoveEntityPos {
920        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
921            dst.put_i32(self.entity_id);
922            dst.put_i8(self.dx);
923            dst.put_i8(self.dy);
924            dst.put_i8(self.dz);
925            Ok(())
926        }
927    }
928
929    impl Decode for ClientboundMoveEntityPos {
930        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
931            need(src, 4 + 1 + 1 + 1)?;
932            Ok(Self {
933                entity_id: src.get_i32(),
934                dx: src.get_i8(),
935                dy: src.get_i8(),
936                dz: src.get_i8(),
937            })
938        }
939    }
940
941    // ── Move Entity Rot (0x16) ───────────────────────────────────────────────
942
943    #[derive(Debug, Clone, PartialEq)]
944    pub struct ClientboundMoveEntityRot {
945        pub entity_id: i32,
946        pub yaw: i8,
947        pub pitch: i8,
948    }
949
950    impl PacketId for ClientboundMoveEntityRot {
951        fn packet_id(_ver: u32) -> u8 {
952            0x16
953        }
954    }
955
956    impl Encode for ClientboundMoveEntityRot {
957        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
958            dst.put_i32(self.entity_id);
959            dst.put_i8(self.yaw);
960            dst.put_i8(self.pitch);
961            Ok(())
962        }
963    }
964
965    impl Decode for ClientboundMoveEntityRot {
966        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
967            need(src, 4 + 1 + 1)?;
968            Ok(Self {
969                entity_id: src.get_i32(),
970                yaw: src.get_i8(),
971                pitch: src.get_i8(),
972            })
973        }
974    }
975
976    // ── Move Entity Pos Rot (0x17) ───────────────────────────────────────────
977
978    #[derive(Debug, Clone, PartialEq)]
979    pub struct ClientboundMoveEntityPosRot {
980        pub entity_id: i32,
981        pub dx: i8,
982        pub dy: i8,
983        pub dz: i8,
984        pub yaw: i8,
985        pub pitch: i8,
986    }
987
988    impl PacketId for ClientboundMoveEntityPosRot {
989        fn packet_id(_ver: u32) -> u8 {
990            0x17
991        }
992    }
993
994    impl Encode for ClientboundMoveEntityPosRot {
995        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
996            dst.put_i32(self.entity_id);
997            dst.put_i8(self.dx);
998            dst.put_i8(self.dy);
999            dst.put_i8(self.dz);
1000            dst.put_i8(self.yaw);
1001            dst.put_i8(self.pitch);
1002            Ok(())
1003        }
1004    }
1005
1006    impl Decode for ClientboundMoveEntityPosRot {
1007        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1008            need(src, 4 + 1 + 1 + 1 + 1 + 1)?;
1009            Ok(Self {
1010                entity_id: src.get_i32(),
1011                dx: src.get_i8(),
1012                dy: src.get_i8(),
1013                dz: src.get_i8(),
1014                yaw: src.get_i8(),
1015                pitch: src.get_i8(),
1016            })
1017        }
1018    }
1019
1020    // ── Teleport Entity (0x18) ───────────────────────────────────────────────
1021
1022    #[derive(Debug, Clone, PartialEq)]
1023    pub struct ClientboundTeleportEntity {
1024        pub entity_id: i32,
1025        pub x: i32,
1026        pub y: i32,
1027        pub z: i32,
1028        pub yaw: i8,
1029        pub pitch: i8,
1030    }
1031
1032    impl PacketId for ClientboundTeleportEntity {
1033        fn packet_id(_ver: u32) -> u8 {
1034            0x18
1035        }
1036    }
1037
1038    impl Encode for ClientboundTeleportEntity {
1039        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1040            dst.put_i32(self.entity_id);
1041            dst.put_i32(self.x);
1042            dst.put_i32(self.y);
1043            dst.put_i32(self.z);
1044            dst.put_i8(self.yaw);
1045            dst.put_i8(self.pitch);
1046            Ok(())
1047        }
1048    }
1049
1050    impl Decode for ClientboundTeleportEntity {
1051        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1052            need(src, 4 + 4 + 4 + 4 + 1 + 1)?;
1053            Ok(Self {
1054                entity_id: src.get_i32(),
1055                x: src.get_i32(),
1056                y: src.get_i32(),
1057                z: src.get_i32(),
1058                yaw: src.get_i8(),
1059                pitch: src.get_i8(),
1060            })
1061        }
1062    }
1063
1064    // ── Rotate Head (0x19) ───────────────────────────────────────────────────
1065
1066    #[derive(Debug, Clone, PartialEq)]
1067    pub struct ClientboundRotateHead {
1068        pub entity_id: i32,
1069        pub head_yaw: i8,
1070    }
1071
1072    impl PacketId for ClientboundRotateHead {
1073        fn packet_id(_ver: u32) -> u8 {
1074            0x19
1075        }
1076    }
1077
1078    impl Encode for ClientboundRotateHead {
1079        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1080            dst.put_i32(self.entity_id);
1081            dst.put_i8(self.head_yaw);
1082            Ok(())
1083        }
1084    }
1085
1086    impl Decode for ClientboundRotateHead {
1087        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1088            need(src, 4 + 1)?;
1089            Ok(Self {
1090                entity_id: src.get_i32(),
1091                head_yaw: src.get_i8(),
1092            })
1093        }
1094    }
1095
1096    // ── Entity Event (0x1A) ─────────────────────────────────────────────────
1097
1098    #[derive(Debug, Clone, PartialEq)]
1099    pub struct ClientboundEntityEvent {
1100        pub entity_id: i32,
1101        pub event_id: i8,
1102    }
1103
1104    impl PacketId for ClientboundEntityEvent {
1105        fn packet_id(_ver: u32) -> u8 {
1106            0x1A
1107        }
1108    }
1109
1110    impl Encode for ClientboundEntityEvent {
1111        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1112            dst.put_i32(self.entity_id);
1113            dst.put_i8(self.event_id);
1114            Ok(())
1115        }
1116    }
1117
1118    impl Decode for ClientboundEntityEvent {
1119        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1120            need(src, 4 + 1)?;
1121            Ok(Self {
1122                entity_id: src.get_i32(),
1123                event_id: src.get_i8(),
1124            })
1125        }
1126    }
1127
1128    // ── Set Entity Link (0x1B) ───────────────────────────────────────────────
1129
1130    #[derive(Debug, Clone, PartialEq)]
1131    pub struct ClientboundSetEntityLink {
1132        pub entity_id: i32,
1133        pub vehicle_id: i32,
1134    }
1135
1136    impl PacketId for ClientboundSetEntityLink {
1137        fn packet_id(_ver: u32) -> u8 {
1138            0x1B
1139        }
1140    }
1141
1142    impl Encode for ClientboundSetEntityLink {
1143        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1144            dst.put_i32(self.entity_id);
1145            dst.put_i32(self.vehicle_id);
1146            Ok(())
1147        }
1148    }
1149
1150    impl Decode for ClientboundSetEntityLink {
1151        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1152            need(src, 4 + 4)?;
1153            Ok(Self {
1154                entity_id: src.get_i32(),
1155                vehicle_id: src.get_i32(),
1156            })
1157        }
1158    }
1159
1160    // ── Update Effects (0x1D) ───────────────────────────────────────────────
1161
1162    #[derive(Debug, Clone, PartialEq)]
1163    pub struct ClientboundUpdateEffects {
1164        pub entity_id: i32,
1165        pub effect_id: i8,
1166        pub amplifier: i8,
1167        pub duration: i16,
1168    }
1169
1170    impl PacketId for ClientboundUpdateEffects {
1171        fn packet_id(_ver: u32) -> u8 {
1172            0x1D
1173        }
1174    }
1175
1176    impl Encode for ClientboundUpdateEffects {
1177        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1178            dst.put_i32(self.entity_id);
1179            dst.put_i8(self.effect_id);
1180            dst.put_i8(self.amplifier);
1181            dst.put_i16(self.duration);
1182            Ok(())
1183        }
1184    }
1185
1186    impl Decode for ClientboundUpdateEffects {
1187        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1188            need(src, 4 + 1 + 1 + 2)?;
1189            Ok(Self {
1190                entity_id: src.get_i32(),
1191                effect_id: src.get_i8(),
1192                amplifier: src.get_i8(),
1193                duration: src.get_i16(),
1194            })
1195        }
1196    }
1197
1198    // ── Remove Entity Effect (0x1E) ───────────────────────────────────────────
1199
1200    #[derive(Debug, Clone, PartialEq)]
1201    pub struct ClientboundRemoveEntityEffect {
1202        pub entity_id: i32,
1203        pub effect_id: i8,
1204    }
1205
1206    impl PacketId for ClientboundRemoveEntityEffect {
1207        fn packet_id(_ver: u32) -> u8 {
1208            0x1E
1209        }
1210    }
1211
1212    impl Encode for ClientboundRemoveEntityEffect {
1213        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1214            dst.put_i32(self.entity_id);
1215            dst.put_i8(self.effect_id);
1216            Ok(())
1217        }
1218    }
1219
1220    impl Decode for ClientboundRemoveEntityEffect {
1221        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1222            need(src, 4 + 1)?;
1223            Ok(Self {
1224                entity_id: src.get_i32(),
1225                effect_id: src.get_i8(),
1226            })
1227        }
1228    }
1229
1230    // ── Update Attributes (0x20) ───────────────────────────────────────────────
1231
1232    #[derive(Debug, Clone, PartialEq)]
1233    pub struct ClientboundUpdateAttributes {
1234        pub entity_id: i32,
1235        pub data: Vec<u8>,
1236    }
1237
1238    impl PacketId for ClientboundUpdateAttributes {
1239        fn packet_id(_ver: u32) -> u8 {
1240            0x20
1241        }
1242    }
1243
1244    impl Encode for ClientboundUpdateAttributes {
1245        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1246            dst.put_i32(self.entity_id);
1247            dst.extend_from_slice(&self.data);
1248            Ok(())
1249        }
1250    }
1251
1252    impl Decode for ClientboundUpdateAttributes {
1253        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1254            need(src, 4)?;
1255            let entity_id = src.get_i32();
1256            let data = src.to_vec();
1257            Ok(Self { entity_id, data })
1258        }
1259    }
1260
1261    // ── Level Chunk With Light (0x21) ───────────────────────────────────────
1262
1263    #[derive(Debug, Clone, PartialEq)]
1264    pub struct ClientboundLevelChunkWithLight {
1265        pub chunk_x: i32,
1266        pub chunk_z: i32,
1267        pub continuous: bool,
1268        pub primary_bitmap: i16,
1269        pub add_bitmap: i16,
1270        pub compressed_data: Vec<u8>,
1271    }
1272
1273    impl PacketId for ClientboundLevelChunkWithLight {
1274        fn packet_id(_ver: u32) -> u8 {
1275            0x21
1276        }
1277    }
1278
1279    impl Encode for ClientboundLevelChunkWithLight {
1280        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1281            dst.put_i32(self.chunk_x);
1282            dst.put_i32(self.chunk_z);
1283            dst.put_u8(self.continuous as u8);
1284            dst.put_i16(self.primary_bitmap);
1285            dst.put_i16(self.add_bitmap);
1286            dst.put_i32(self.compressed_data.len() as i32);
1287            dst.extend_from_slice(&self.compressed_data);
1288            Ok(())
1289        }
1290    }
1291
1292    impl Decode for ClientboundLevelChunkWithLight {
1293        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1294            need(src, 4 + 4 + 1 + 2 + 2 + 4)?;
1295            let chunk_x = src.get_i32();
1296            let chunk_z = src.get_i32();
1297            let continuous = src.get_u8() != 0;
1298            let primary_bitmap = src.get_i16();
1299            let add_bitmap = src.get_i16();
1300            let data_len = src.get_i32() as usize;
1301            if src.remaining() < data_len {
1302                return Err(ProtocolError::UnexpectedEof);
1303            }
1304            let compressed_data = src.copy_to_bytes(data_len).to_vec();
1305            Ok(Self {
1306                chunk_x,
1307                chunk_z,
1308                continuous,
1309                primary_bitmap,
1310                add_bitmap,
1311                compressed_data,
1312            })
1313        }
1314    }
1315
1316    // ── Section Blocks Update (0x22) ─────────────────────────────────────────
1317
1318    #[derive(Debug, Clone, PartialEq)]
1319    pub struct ClientboundSectionBlocksUpdate {
1320        pub chunk_x: i32,
1321        pub chunk_z: i32,
1322        pub record_count: i16,
1323        pub data: Vec<u8>,
1324    }
1325
1326    impl PacketId for ClientboundSectionBlocksUpdate {
1327        fn packet_id(_ver: u32) -> u8 {
1328            0x22
1329        }
1330    }
1331
1332    impl Encode for ClientboundSectionBlocksUpdate {
1333        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1334            dst.put_i32(self.chunk_x);
1335            dst.put_i32(self.chunk_z);
1336            dst.put_i16(self.record_count);
1337            dst.extend_from_slice(&self.data);
1338            Ok(())
1339        }
1340    }
1341
1342    impl Decode for ClientboundSectionBlocksUpdate {
1343        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1344            need(src, 4 + 4 + 2)?;
1345            let chunk_x = src.get_i32();
1346            let chunk_z = src.get_i32();
1347            let record_count = src.get_i16();
1348            let data = src.to_vec();
1349            Ok(Self {
1350                chunk_x,
1351                chunk_z,
1352                record_count,
1353                data,
1354            })
1355        }
1356    }
1357
1358    // ── Block Action (0x24) ─────────────────────────────────────────────────
1359
1360    #[derive(Debug, Clone, PartialEq)]
1361    pub struct ClientboundBlockAction {
1362        pub x: i32,
1363        pub y: i16,
1364        pub z: i32,
1365        pub action_id: i8,
1366        pub action_param: i8,
1367        pub block_type: i32,
1368    }
1369
1370    impl PacketId for ClientboundBlockAction {
1371        fn packet_id(_ver: u32) -> u8 {
1372            0x24
1373        }
1374    }
1375
1376    impl Encode for ClientboundBlockAction {
1377        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1378            dst.put_i32(self.x);
1379            dst.put_i16(self.y);
1380            dst.put_i32(self.z);
1381            dst.put_i8(self.action_id);
1382            dst.put_i8(self.action_param);
1383            VarInt(self.block_type).encode(dst)?;
1384            Ok(())
1385        }
1386    }
1387
1388    impl Decode for ClientboundBlockAction {
1389        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1390            need(src, 4 + 2 + 4 + 1 + 1)?;
1391            Ok(Self {
1392                x: src.get_i32(),
1393                y: src.get_i16(),
1394                z: src.get_i32(),
1395                action_id: src.get_i8(),
1396                action_param: src.get_i8(),
1397                block_type: VarInt::decode(src)?.0,
1398            })
1399        }
1400    }
1401
1402    // ── Block Destroy Stage (0x25) ─────────────────────────────────────────
1403
1404    #[derive(Debug, Clone, PartialEq)]
1405    pub struct ClientboundBlockDestroyStage {
1406        pub x: i32,
1407        pub y: i8,
1408        pub z: i32,
1409        pub stage: i8,
1410    }
1411
1412    impl PacketId for ClientboundBlockDestroyStage {
1413        fn packet_id(_ver: u32) -> u8 {
1414            0x25
1415        }
1416    }
1417
1418    impl Encode for ClientboundBlockDestroyStage {
1419        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1420            dst.put_i32(self.x);
1421            dst.put_i8(self.y);
1422            dst.put_i32(self.z);
1423            dst.put_i8(self.stage);
1424            Ok(())
1425        }
1426    }
1427
1428    impl Decode for ClientboundBlockDestroyStage {
1429        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1430            need(src, 4 + 1 + 4 + 1)?;
1431            Ok(Self {
1432                x: src.get_i32(),
1433                y: src.get_i8(),
1434                z: src.get_i32(),
1435                stage: src.get_i8(),
1436            })
1437        }
1438    }
1439
1440    // ── Forget Level Chunk (0x26) ───────────────────────────────────────────
1441
1442    #[derive(Debug, Clone, PartialEq)]
1443    pub struct ClientboundForgetLevelChunk {
1444        pub chunk_x: i32,
1445        pub chunk_z: i32,
1446    }
1447
1448    impl PacketId for ClientboundForgetLevelChunk {
1449        fn packet_id(_ver: u32) -> u8 {
1450            0x26
1451        }
1452    }
1453
1454    impl Encode for ClientboundForgetLevelChunk {
1455        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1456            dst.put_i32(self.chunk_x);
1457            dst.put_i32(self.chunk_z);
1458            Ok(())
1459        }
1460    }
1461
1462    impl Decode for ClientboundForgetLevelChunk {
1463        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1464            need(src, 4 + 4)?;
1465            Ok(Self {
1466                chunk_x: src.get_i32(),
1467                chunk_z: src.get_i32(),
1468            })
1469        }
1470    }
1471
1472    // ── Explosion (0x27) ─────────────────────────────────────────────────────
1473
1474    #[derive(Debug, Clone, PartialEq)]
1475    pub struct ClientboundExplosion {
1476        pub x: f32,
1477        pub y: f32,
1478        pub z: f32,
1479        pub radius: f32,
1480        pub record_count: i32,
1481        pub records: Vec<u8>,
1482        pub player_motion_x: f32,
1483        pub player_motion_y: f32,
1484        pub player_motion_z: f32,
1485    }
1486
1487    impl PacketId for ClientboundExplosion {
1488        fn packet_id(_ver: u32) -> u8 {
1489            0x27
1490        }
1491    }
1492
1493    impl Encode for ClientboundExplosion {
1494        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1495            dst.put_f32(self.x);
1496            dst.put_f32(self.y);
1497            dst.put_f32(self.z);
1498            dst.put_f32(self.radius);
1499            dst.put_i32(self.record_count);
1500            dst.extend_from_slice(&self.records);
1501            dst.put_f32(self.player_motion_x);
1502            dst.put_f32(self.player_motion_y);
1503            dst.put_f32(self.player_motion_z);
1504            Ok(())
1505        }
1506    }
1507
1508    impl Decode for ClientboundExplosion {
1509        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1510            need(src, 4 + 4 + 4 + 4 + 4 + 4 + 4 + 4)?;
1511            let x = src.get_f32();
1512            let y = src.get_f32();
1513            let z = src.get_f32();
1514            let radius = src.get_f32();
1515            let record_count = src.get_i32();
1516            let records_len = record_count as usize * 3;
1517            if src.remaining() < records_len + 12 {
1518                return Err(ProtocolError::UnexpectedEof);
1519            }
1520            let mut records = vec![0u8; records_len];
1521            src.copy_to_slice(&mut records);
1522            let player_motion_x = src.get_f32();
1523            let player_motion_y = src.get_f32();
1524            let player_motion_z = src.get_f32();
1525            Ok(Self {
1526                x,
1527                y,
1528                z,
1529                radius,
1530                record_count,
1531                records,
1532                player_motion_x,
1533                player_motion_y,
1534                player_motion_z,
1535            })
1536        }
1537    }
1538
1539    // ── Level Event (0x28) ─────────────────────────────────────────────────
1540
1541    #[derive(Debug, Clone, PartialEq)]
1542    pub struct ClientboundLevelEvent {
1543        pub event_id: i32,
1544        pub x: i32,
1545        pub y: i32,
1546        pub z: i32,
1547        pub data: i32,
1548    }
1549
1550    impl PacketId for ClientboundLevelEvent {
1551        fn packet_id(_ver: u32) -> u8 {
1552            0x28
1553        }
1554    }
1555
1556    impl Encode for ClientboundLevelEvent {
1557        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1558            dst.put_i32(self.event_id);
1559            dst.put_i32(self.x);
1560            dst.put_i32(self.y);
1561            dst.put_i32(self.z);
1562            dst.put_i32(self.data);
1563            Ok(())
1564        }
1565    }
1566
1567    impl Decode for ClientboundLevelEvent {
1568        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1569            need(src, 4 + 4 + 4 + 4 + 4)?;
1570            Ok(Self {
1571                event_id: src.get_i32(),
1572                x: src.get_i32(),
1573                y: src.get_i32(),
1574                z: src.get_i32(),
1575                data: src.get_i32(),
1576            })
1577        }
1578    }
1579
1580    // ── Level Particles (0x2A) ───────────────────────────────────────────────
1581
1582    #[derive(Debug, Clone, PartialEq)]
1583    pub struct ClientboundLevelParticles {
1584        pub particle_id: i32,
1585        pub x: f32,
1586        pub y: f32,
1587        pub z: f32,
1588        pub offset_x: f32,
1589        pub offset_y: f32,
1590        pub offset_z: f32,
1591        pub particle_speed: f32,
1592        pub particle_count: i32,
1593        pub data: Vec<u8>,
1594    }
1595
1596    impl PacketId for ClientboundLevelParticles {
1597        fn packet_id(_ver: u32) -> u8 {
1598            0x2A
1599        }
1600    }
1601
1602    impl Encode for ClientboundLevelParticles {
1603        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1604            dst.put_i32(self.particle_id);
1605            dst.put_f32(self.x);
1606            dst.put_f32(self.y);
1607            dst.put_f32(self.z);
1608            dst.put_f32(self.offset_x);
1609            dst.put_f32(self.offset_y);
1610            dst.put_f32(self.offset_z);
1611            dst.put_f32(self.particle_speed);
1612            dst.put_i32(self.particle_count);
1613            dst.extend_from_slice(&self.data);
1614            Ok(())
1615        }
1616    }
1617
1618    impl Decode for ClientboundLevelParticles {
1619        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1620            need(src, 4 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + 4)?;
1621            let particle_id = src.get_i32();
1622            let x = src.get_f32();
1623            let y = src.get_f32();
1624            let z = src.get_f32();
1625            let offset_x = src.get_f32();
1626            let offset_y = src.get_f32();
1627            let offset_z = src.get_f32();
1628            let particle_speed = src.get_f32();
1629            let particle_count = src.get_i32();
1630            let data = src.to_vec();
1631            Ok(Self {
1632                particle_id,
1633                x,
1634                y,
1635                z,
1636                offset_x,
1637                offset_y,
1638                offset_z,
1639                particle_speed,
1640                particle_count,
1641                data,
1642            })
1643        }
1644    }
1645
1646    // ── Game Event (0x2B) ─────────────────────────────────────────────────
1647
1648    #[derive(Debug, Clone, PartialEq)]
1649    pub struct ClientboundGameEvent {
1650        pub event: u8,
1651        pub data: f32,
1652    }
1653
1654    impl PacketId for ClientboundGameEvent {
1655        fn packet_id(_ver: u32) -> u8 {
1656            0x2B
1657        }
1658    }
1659
1660    impl Encode for ClientboundGameEvent {
1661        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1662            dst.put_u8(self.event);
1663            dst.put_f32(self.data);
1664            Ok(())
1665        }
1666    }
1667
1668    impl Decode for ClientboundGameEvent {
1669        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1670            need(src, 1 + 4)?;
1671            Ok(Self {
1672                event: src.get_u8(),
1673                data: src.get_f32(),
1674            })
1675        }
1676    }
1677
1678    // ── Open Screen (0x2D) ─────────────────────────────────────────────────
1679
1680    #[derive(Debug, Clone, PartialEq)]
1681    pub struct ClientboundOpenScreen {
1682        pub window_id: i8,
1683        pub window_type: String,
1684        pub window_title: String,
1685        pub slots_count: i8,
1686    }
1687
1688    impl PacketId for ClientboundOpenScreen {
1689        fn packet_id(_ver: u32) -> u8 {
1690            0x2D
1691        }
1692    }
1693
1694    impl Encode for ClientboundOpenScreen {
1695        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1696            dst.put_i8(self.window_id);
1697            encode_str(&self.window_type, dst)?;
1698            encode_str(&self.window_title, dst)?;
1699            dst.put_i8(self.slots_count);
1700            Ok(())
1701        }
1702    }
1703
1704    impl Decode for ClientboundOpenScreen {
1705        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1706            let window_id = src.get_i8();
1707            let window_type = decode_str(src, "ClientboundOpenScreen window_type")?;
1708            let window_title = decode_str(src, "ClientboundOpenScreen window_title")?;
1709            need(src, 1)?;
1710            let slots_count = src.get_i8();
1711            Ok(Self {
1712                window_id,
1713                window_type,
1714                window_title,
1715                slots_count,
1716            })
1717        }
1718    }
1719
1720    // ── Container Close (0x2E) ─────────────────────────────────────────────
1721
1722    #[derive(Debug, Clone, PartialEq)]
1723    pub struct ClientboundContainerClose {
1724        pub window_id: i8,
1725    }
1726
1727    impl PacketId for ClientboundContainerClose {
1728        fn packet_id(_ver: u32) -> u8 {
1729            0x2E
1730        }
1731    }
1732
1733    impl Encode for ClientboundContainerClose {
1734        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1735            dst.put_i8(self.window_id);
1736            Ok(())
1737        }
1738    }
1739
1740    impl Decode for ClientboundContainerClose {
1741        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1742            need(src, 1)?;
1743            Ok(Self {
1744                window_id: src.get_i8(),
1745            })
1746        }
1747    }
1748
1749    // ── Container Set Property (0x31) ───────────────────────────────────────
1750
1751    #[derive(Debug, Clone, PartialEq)]
1752    pub struct ClientboundContainerSetProperty {
1753        pub window_id: i8,
1754        pub property: i16,
1755        pub value: i16,
1756    }
1757
1758    impl PacketId for ClientboundContainerSetProperty {
1759        fn packet_id(_ver: u32) -> u8 {
1760            0x31
1761        }
1762    }
1763
1764    impl Encode for ClientboundContainerSetProperty {
1765        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1766            dst.put_i8(self.window_id);
1767            dst.put_i16(self.property);
1768            dst.put_i16(self.value);
1769            Ok(())
1770        }
1771    }
1772
1773    impl Decode for ClientboundContainerSetProperty {
1774        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1775            need(src, 1 + 2 + 2)?;
1776            Ok(Self {
1777                window_id: src.get_i8(),
1778                property: src.get_i16(),
1779                value: src.get_i16(),
1780            })
1781        }
1782    }
1783
1784    // ── Set Time (0x03) ───────────────────────────────────────────────────────
1785
1786    #[derive(Debug, Clone, PartialEq)]
1787    pub struct ClientboundSetTime {
1788        pub age: i64,
1789        pub time: i64,
1790    }
1791
1792    impl PacketId for ClientboundSetTime {
1793        fn packet_id(_ver: u32) -> u8 {
1794            0x03
1795        }
1796    }
1797
1798    impl Encode for ClientboundSetTime {
1799        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1800            dst.put_i64(self.age);
1801            dst.put_i64(self.time);
1802            Ok(())
1803        }
1804    }
1805
1806    impl Decode for ClientboundSetTime {
1807        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1808            need(src, 8 + 8)?;
1809            Ok(Self {
1810                age: src.get_i64(),
1811                time: src.get_i64(),
1812            })
1813        }
1814    }
1815
1816    // ── Set Health (0x06) ───────────────────────────────────────────────────────
1817
1818    #[derive(Debug, Clone, PartialEq)]
1819    pub struct ClientboundSetHealth {
1820        pub health: f32,
1821        pub food: i16,
1822        pub food_saturation: f32,
1823    }
1824
1825    impl PacketId for ClientboundSetHealth {
1826        fn packet_id(_ver: u32) -> u8 {
1827            0x06
1828        }
1829    }
1830
1831    impl Encode for ClientboundSetHealth {
1832        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1833            dst.put_f32(self.health);
1834            dst.put_i16(self.food);
1835            dst.put_f32(self.food_saturation);
1836            Ok(())
1837        }
1838    }
1839
1840    impl Decode for ClientboundSetHealth {
1841        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1842            need(src, 4 + 2 + 4)?;
1843            Ok(Self {
1844                health: src.get_f32(),
1845                food: src.get_i16(),
1846                food_saturation: src.get_f32(),
1847            })
1848        }
1849    }
1850
1851    // ── Spawn Player (0x0C) ─────────────────────────────────────────────────────
1852
1853    #[derive(Debug, Clone, PartialEq)]
1854    pub struct ClientboundSpawnPlayer {
1855        pub entity_id: i32,
1856        pub player_uuid: uuid::Uuid,
1857        pub username: String,
1858        pub x: i32,
1859        pub y: i32,
1860        pub z: i32,
1861        pub yaw: i8,
1862        pub pitch: i8,
1863        pub current_item: i16,
1864    }
1865
1866    impl PacketId for ClientboundSpawnPlayer {
1867        fn packet_id(_ver: u32) -> u8 {
1868            0x0C
1869        }
1870    }
1871
1872    impl Encode for ClientboundSpawnPlayer {
1873        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1874            dst.put_i32(self.entity_id);
1875            let (hi, lo) = self.player_uuid.as_u64_pair();
1876            dst.put_i64(hi as i64);
1877            dst.put_i64(lo as i64);
1878            encode_str(&self.username, dst)?;
1879            dst.put_i32(self.x);
1880            dst.put_i32(self.y);
1881            dst.put_i32(self.z);
1882            dst.put_i8(self.yaw);
1883            dst.put_i8(self.pitch);
1884            dst.put_i16(self.current_item);
1885            Ok(())
1886        }
1887    }
1888
1889    impl Decode for ClientboundSpawnPlayer {
1890        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1891            need(src, 4 + 8 + 8)?;
1892            let entity_id = src.get_i32();
1893            let hi = src.get_i64() as u64;
1894            let lo = src.get_i64() as u64;
1895            let player_uuid = uuid::Uuid::from_u64_pair(hi, lo);
1896            let username = decode_str(src, "ClientboundSpawnPlayer username")?;
1897            need(src, 4 + 4 + 4 + 1 + 1 + 2)?;
1898            Ok(Self {
1899                entity_id,
1900                player_uuid,
1901                username,
1902                x: src.get_i32(),
1903                y: src.get_i32(),
1904                z: src.get_i32(),
1905                yaw: src.get_i8(),
1906                pitch: src.get_i8(),
1907                current_item: src.get_i16(),
1908            })
1909        }
1910    }
1911
1912    // ── Spawn Entity (0x0E) ─────────────────────────────────────────────────────
1913
1914    #[derive(Debug, Clone, PartialEq)]
1915    pub struct ClientboundSpawnEntity {
1916        pub entity_id: i32,
1917        pub entity_type: i8,
1918        pub x: i32,
1919        pub y: i32,
1920        pub z: i32,
1921        pub yaw: i8,
1922        pub pitch: i8,
1923        pub head_pitch: i8,
1924        pub velocity_x: i16,
1925        pub velocity_y: i16,
1926        pub velocity_z: i16,
1927    }
1928
1929    impl PacketId for ClientboundSpawnEntity {
1930        fn packet_id(_ver: u32) -> u8 {
1931            0x0E
1932        }
1933    }
1934
1935    impl Encode for ClientboundSpawnEntity {
1936        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1937            dst.put_i32(self.entity_id);
1938            dst.put_i8(self.entity_type);
1939            dst.put_i32(self.x);
1940            dst.put_i32(self.y);
1941            dst.put_i32(self.z);
1942            dst.put_i8(self.yaw);
1943            dst.put_i8(self.pitch);
1944            dst.put_i8(self.head_pitch);
1945            dst.put_i16(self.velocity_x);
1946            dst.put_i16(self.velocity_y);
1947            dst.put_i16(self.velocity_z);
1948            Ok(())
1949        }
1950    }
1951
1952    impl Decode for ClientboundSpawnEntity {
1953        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1954            need(src, 4 + 1 + 4 + 4 + 4 + 1 + 1 + 1 + 2 + 2 + 2)?;
1955            Ok(Self {
1956                entity_id: src.get_i32(),
1957                entity_type: src.get_i8(),
1958                x: src.get_i32(),
1959                y: src.get_i32(),
1960                z: src.get_i32(),
1961                yaw: src.get_i8(),
1962                pitch: src.get_i8(),
1963                head_pitch: src.get_i8(),
1964                velocity_x: src.get_i16(),
1965                velocity_y: src.get_i16(),
1966                velocity_z: src.get_i16(),
1967            })
1968        }
1969    }
1970
1971    // ── Set Experience (0x1F) ───────────────────────────────────────────────────
1972
1973    #[derive(Debug, Clone, PartialEq)]
1974    pub struct ClientboundSetExperience {
1975        pub experience_bar: f32,
1976        pub level: i16,
1977        pub total_experience: i16,
1978    }
1979
1980    impl PacketId for ClientboundSetExperience {
1981        fn packet_id(_ver: u32) -> u8 {
1982            0x1F
1983        }
1984    }
1985
1986    impl Encode for ClientboundSetExperience {
1987        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1988            dst.put_f32(self.experience_bar);
1989            dst.put_i16(self.level);
1990            dst.put_i16(self.total_experience);
1991            Ok(())
1992        }
1993    }
1994
1995    impl Decode for ClientboundSetExperience {
1996        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1997            need(src, 4 + 2 + 2)?;
1998            Ok(Self {
1999                experience_bar: src.get_f32(),
2000                level: src.get_i16(),
2001                total_experience: src.get_i16(),
2002            })
2003        }
2004    }
2005
2006    // ── Block Update (0x23) ───────────────────────────────────────────────────
2007
2008    #[derive(Debug, Clone, PartialEq)]
2009    pub struct ClientboundBlockUpdate {
2010        pub x: i32,
2011        pub y: i8,
2012        pub z: i32,
2013        pub block_id: i32,
2014        pub block_metadata: i8,
2015    }
2016
2017    impl PacketId for ClientboundBlockUpdate {
2018        fn packet_id(_ver: u32) -> u8 {
2019            0x23
2020        }
2021    }
2022
2023    impl Encode for ClientboundBlockUpdate {
2024        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2025            dst.put_i32(self.x);
2026            dst.put_i8(self.y);
2027            dst.put_i32(self.z);
2028            VarInt(self.block_id).encode(dst)?;
2029            dst.put_i8(self.block_metadata);
2030            Ok(())
2031        }
2032    }
2033
2034    impl Decode for ClientboundBlockUpdate {
2035        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2036            need(src, 4 + 1 + 4)?;
2037            Ok(Self {
2038                x: src.get_i32(),
2039                y: src.get_i8(),
2040                z: src.get_i32(),
2041                block_id: VarInt::decode(src)?.0,
2042                block_metadata: src.get_i8(),
2043            })
2044        }
2045    }
2046
2047    // ── Set Held Item (0x09) ─────────────────────────────────────────────────────
2048
2049    #[derive(Debug, Clone, PartialEq)]
2050    pub struct ClientboundSetHeldItem {
2051        pub slot: u8,
2052    }
2053
2054    impl PacketId for ClientboundSetHeldItem {
2055        fn packet_id(_ver: u32) -> u8 {
2056            0x09
2057        }
2058    }
2059
2060    impl Encode for ClientboundSetHeldItem {
2061        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2062            dst.put_u8(self.slot);
2063            Ok(())
2064        }
2065    }
2066
2067    impl Decode for ClientboundSetHeldItem {
2068        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2069            need(src, 1)?;
2070            Ok(Self { slot: src.get_u8() })
2071        }
2072    }
2073
2074    // ── Named Sound Effect (0x29) ────────────────────────────────────────────────
2075
2076    #[derive(Debug, Clone, PartialEq)]
2077    pub struct ClientboundSound {
2078        pub sound_name: String,
2079        pub x: i32,
2080        pub y: i32,
2081        pub z: i32,
2082        pub volume: f32,
2083        pub pitch: u8,
2084    }
2085
2086    impl PacketId for ClientboundSound {
2087        fn packet_id(_ver: u32) -> u8 {
2088            0x29
2089        }
2090    }
2091
2092    impl Encode for ClientboundSound {
2093        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2094            encode_str(&self.sound_name, dst)?;
2095            dst.put_i32(self.x);
2096            dst.put_i32(self.y);
2097            dst.put_i32(self.z);
2098            dst.put_f32(self.volume);
2099            dst.put_u8(self.pitch);
2100            Ok(())
2101        }
2102    }
2103
2104    impl Decode for ClientboundSound {
2105        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2106            let sound_name = decode_str(src, "ClientboundSound sound_name")?;
2107            need(src, 4 + 4 + 4 + 4 + 1)?;
2108            Ok(Self {
2109                sound_name,
2110                x: src.get_i32(),
2111                y: src.get_i32(),
2112                z: src.get_i32(),
2113                volume: src.get_f32(),
2114                pitch: src.get_u8(),
2115            })
2116        }
2117    }
2118
2119    // ── Player Abilities (0x39) ───────────────────────────────────────────────────
2120
2121    #[derive(Debug, Clone, PartialEq)]
2122    pub struct ClientboundPlayerAbilities {
2123        pub flags: u8,
2124        pub flying_speed: f32,
2125        pub field_of_view_modifier: f32,
2126    }
2127
2128    impl PacketId for ClientboundPlayerAbilities {
2129        fn packet_id(_ver: u32) -> u8 {
2130            0x39
2131        }
2132    }
2133
2134    impl Encode for ClientboundPlayerAbilities {
2135        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2136            dst.put_u8(self.flags);
2137            dst.put_f32(self.flying_speed);
2138            dst.put_f32(self.field_of_view_modifier);
2139            Ok(())
2140        }
2141    }
2142
2143    impl Decode for ClientboundPlayerAbilities {
2144        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2145            need(src, 1 + 4 + 4)?;
2146            Ok(Self {
2147                flags: src.get_u8(),
2148                flying_speed: src.get_f32(),
2149                field_of_view_modifier: src.get_f32(),
2150            })
2151        }
2152    }
2153
2154    // ── ContainerSetContent (0x30) ────────────────────────────────────────────
2155
2156    #[derive(Debug, Clone, PartialEq)]
2157    pub struct ClientboundContainerSetContent {
2158        pub window_id: u8,
2159        pub slots: Vec<LegacySlot>,
2160        pub carried_item: LegacySlot,
2161    }
2162
2163    impl PacketId for ClientboundContainerSetContent {
2164        fn packet_id(_ver: u32) -> u8 {
2165            0x30
2166        }
2167    }
2168
2169    impl Encode for ClientboundContainerSetContent {
2170        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2171            dst.put_u8(self.window_id);
2172            dst.put_i16(self.slots.len() as i16);
2173            for slot in &self.slots {
2174                slot.encode(dst)?;
2175            }
2176            self.carried_item.encode(dst)?;
2177            Ok(())
2178        }
2179    }
2180
2181    impl Decode for ClientboundContainerSetContent {
2182        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2183            need(src, 1 + 2)?;
2184            let window_id = src.get_u8();
2185            let count = src.get_i16() as usize;
2186            let mut slots = Vec::with_capacity(count);
2187            for _ in 0..count {
2188                slots.push(LegacySlot::decode(src)?);
2189            }
2190            let carried_item = LegacySlot::decode(src)?;
2191            Ok(Self {
2192                window_id,
2193                slots,
2194                carried_item,
2195            })
2196        }
2197    }
2198
2199    // ── ContainerSetSlot (0x2F) ───────────────────────────────────────────────
2200
2201    #[derive(Debug, Clone, PartialEq)]
2202    pub struct ClientboundContainerSetSlot {
2203        pub window_id: i8,
2204        pub slot: i16,
2205        pub slot_data: LegacySlot,
2206    }
2207
2208    impl PacketId for ClientboundContainerSetSlot {
2209        fn packet_id(_ver: u32) -> u8 {
2210            0x2F
2211        }
2212    }
2213
2214    impl Encode for ClientboundContainerSetSlot {
2215        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2216            dst.put_i8(self.window_id);
2217            dst.put_i16(self.slot);
2218            self.slot_data.encode(dst)?;
2219            Ok(())
2220        }
2221    }
2222
2223    impl Decode for ClientboundContainerSetSlot {
2224        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2225            need(src, 1 + 2)?;
2226            let window_id = src.get_i8();
2227            let slot = src.get_i16();
2228            let slot_data = LegacySlot::decode(src)?;
2229            Ok(Self {
2230                window_id,
2231                slot,
2232                slot_data,
2233            })
2234        }
2235    }
2236
2237    raw_packet!(ClientboundMapItemData, 0x34);
2238    raw_packet!(ClientboundBlockEntityData, 0x35);
2239    raw_packet!(ClientboundAwardStats, 0x37);
2240    raw_packet!(ClientboundCommandSuggestions, 0x3A);
2241    raw_packet!(ClientboundSetScoreboardObjective, 0x3B);
2242    raw_packet!(ClientboundResetScore, 0x3B);
2243    raw_packet!(ClientboundSetScoreboardScore, 0x3C);
2244    raw_packet!(ClientboundChangeDifficulty, 0x41);
2245    raw_packet!(ClientboundPlayerCombatEnd, 0x42);
2246    raw_packet!(ClientboundPlayerCombatEnter, 0x42);
2247    raw_packet!(ClientboundPlayerCombatKill, 0x42);
2248
2249    // ── Open Sign Editor (0x36) ─────────────────────────────────────────────
2250
2251    #[derive(Debug, Clone, PartialEq)]
2252    pub struct ClientboundOpenSignEditor {
2253        pub x: i32,
2254        pub y: i32,
2255        pub z: i32,
2256    }
2257
2258    impl PacketId for ClientboundOpenSignEditor {
2259        fn packet_id(_ver: u32) -> u8 {
2260            0x36
2261        }
2262    }
2263
2264    impl Encode for ClientboundOpenSignEditor {
2265        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2266            dst.put_i32(self.x);
2267            dst.put_i32(self.y);
2268            dst.put_i32(self.z);
2269            Ok(())
2270        }
2271    }
2272
2273    impl Decode for ClientboundOpenSignEditor {
2274        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2275            need(src, 4 + 4 + 4)?;
2276            Ok(Self {
2277                x: src.get_i32(),
2278                y: src.get_i32(),
2279                z: src.get_i32(),
2280            })
2281        }
2282    }
2283
2284    // ── Player Info Update (0x38) ─────────────────────────────────────────────
2285
2286    #[derive(Debug, Clone, PartialEq)]
2287    pub struct ClientboundPlayerInfoUpdate {
2288        pub action: i8,
2289        pub data: Vec<u8>,
2290    }
2291
2292    impl PacketId for ClientboundPlayerInfoUpdate {
2293        fn packet_id(_ver: u32) -> u8 {
2294            0x38
2295        }
2296    }
2297
2298    impl Encode for ClientboundPlayerInfoUpdate {
2299        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2300            dst.put_i8(self.action);
2301            dst.extend_from_slice(&self.data);
2302            Ok(())
2303        }
2304    }
2305
2306    impl Decode for ClientboundPlayerInfoUpdate {
2307        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2308            need(src, 1)?;
2309            let action = src.get_i8();
2310            let data = src.to_vec();
2311            Ok(Self { action, data })
2312        }
2313    }
2314
2315    // ── Tab List (0x47) ─────────────────────────────────────────────────────
2316
2317    #[derive(Debug, Clone, PartialEq)]
2318    pub struct ClientboundTabList {
2319        pub header: String,
2320        pub footer: String,
2321    }
2322
2323    impl PacketId for ClientboundTabList {
2324        fn packet_id(_ver: u32) -> u8 {
2325            0x47
2326        }
2327    }
2328
2329    impl Encode for ClientboundTabList {
2330        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2331            encode_str(&self.header, dst)?;
2332            encode_str(&self.footer, dst)?;
2333            Ok(())
2334        }
2335    }
2336
2337    impl Decode for ClientboundTabList {
2338        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2339            let header = decode_str(src, "ClientboundTabList header")?;
2340            let footer = decode_str(src, "ClientboundTabList footer")?;
2341            Ok(Self { header, footer })
2342        }
2343    }
2344
2345    // ── Resource Pack Push (0x48) ───────────────────────────────────────────
2346
2347    #[derive(Debug, Clone, PartialEq)]
2348    pub struct ClientboundResourcePackPush {
2349        pub url: String,
2350        pub hash: String,
2351    }
2352
2353    impl PacketId for ClientboundResourcePackPush {
2354        fn packet_id(_ver: u32) -> u8 {
2355            0x48
2356        }
2357    }
2358
2359    impl Encode for ClientboundResourcePackPush {
2360        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2361            encode_str(&self.url, dst)?;
2362            encode_str(&self.hash, dst)?;
2363            Ok(())
2364        }
2365    }
2366
2367    impl Decode for ClientboundResourcePackPush {
2368        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2369            let url = decode_str(src, "ClientboundResourcePackPush url")?;
2370            let hash = decode_str(src, "ClientboundResourcePackPush hash")?;
2371            Ok(Self { url, hash })
2372        }
2373    }
2374
2375    raw_packet!(ServerboundMovePlayerStatusOnly, 0x03);
2376    raw_packet!(ServerboundPlayerAction, 0x07);
2377    raw_packet!(ServerboundPlayerBlockPlacement, 0x08);
2378    raw_packet!(ServerboundHeldItemChange, 0x09);
2379    raw_packet!(ServerboundAnimation, 0x0A);
2380    raw_packet!(ServerboundEntityAction, 0x0B);
2381    raw_packet!(ServerboundSteerVehicle, 0x0C);
2382    raw_packet!(ServerboundCloseWindow, 0x0D);
2383    raw_packet!(ServerboundClickWindow, 0x0E);
2384    raw_packet!(ServerboundConfirmTransaction, 0x0F);
2385    raw_packet!(ServerboundCreativeInventoryAction, 0x10);
2386    raw_packet!(ServerboundEnchantItem, 0x11);
2387    raw_packet!(ServerboundClickWindowButton, 0x11);
2388    raw_packet!(ServerboundUpdateSign, 0x12);
2389    raw_packet!(ServerboundPlayerAbilities, 0x13);
2390    raw_packet!(ServerboundCommandSuggestion, 0x14);
2391    raw_packet!(ServerboundClientSettings, 0x15);
2392    raw_packet!(ServerboundClientStatus, 0x16);
2393    raw_packet!(ServerboundResourcePackStatus, 0x19);
2394}
2395
2396#[cfg(test)]
2397mod tests {
2398    use super::*;
2399
2400    macro_rules! roundtrip {
2401        ($T:ty, $val:expr) => {{
2402            let v = $val;
2403            let mut buf = BytesMut::new();
2404            v.encode(&mut buf).unwrap();
2405            let mut b = buf.freeze();
2406            assert_eq!(<$T>::decode(&mut b).unwrap(), v);
2407        }};
2408    }
2409
2410    #[test]
2411    fn keepalive_roundtrip() {
2412        roundtrip!(
2413            ClientboundKeepAlive,
2414            ClientboundKeepAlive {
2415                keep_alive_id: VarInt(42)
2416            }
2417        );
2418        roundtrip!(
2419            ServerboundKeepAlive,
2420            ServerboundKeepAlive {
2421                keep_alive_id: VarInt(42)
2422            }
2423        );
2424    }
2425
2426    #[test]
2427    fn join_game_roundtrip() {
2428        roundtrip!(
2429            ClientboundJoinGame,
2430            ClientboundJoinGame {
2431                entity_id: 1,
2432                game_mode: 0,
2433                dimension: 0,
2434                difficulty: 1,
2435                max_players: 20,
2436                level_type: "default".to_string(),
2437                reduced_debug_info: false,
2438            }
2439        );
2440    }
2441
2442    #[test]
2443    fn chat_roundtrip() {
2444        roundtrip!(
2445            ClientboundChatMessage,
2446            ClientboundChatMessage {
2447                json_message: r#"{"text":"hi"}"#.to_string(),
2448                position: 0,
2449            }
2450        );
2451        roundtrip!(
2452            ServerboundChatMessage,
2453            ServerboundChatMessage {
2454                message: "hello".to_string()
2455            }
2456        );
2457    }
2458
2459    #[test]
2460    fn respawn_roundtrip() {
2461        roundtrip!(
2462            ClientboundRespawn,
2463            ClientboundRespawn {
2464                dimension: -1,
2465                difficulty: 2,
2466                game_mode: 0,
2467                level_type: "default".to_string(),
2468            }
2469        );
2470    }
2471
2472    #[test]
2473    fn player_position_roundtrip() {
2474        roundtrip!(
2475            ClientboundPlayerPosition,
2476            ClientboundPlayerPosition {
2477                x: 10.0,
2478                head_y: 66.62,
2479                z: -5.0,
2480                yaw: 0.0,
2481                pitch: 0.0,
2482                flags: 0,
2483            }
2484        );
2485    }
2486
2487    #[test]
2488    fn disconnect_roundtrip() {
2489        roundtrip!(
2490            ClientboundDisconnect,
2491            ClientboundDisconnect {
2492                reason: r#"{"text":"bye"}"#.to_string(),
2493            }
2494        );
2495    }
2496
2497    #[test]
2498    fn movement_roundtrip() {
2499        roundtrip!(
2500            ServerboundMovePlayerPos,
2501            ServerboundMovePlayerPos {
2502                x: 5.0,
2503                feet_y: 64.0,
2504                z: -2.0,
2505                on_ground: true,
2506            }
2507        );
2508        roundtrip!(
2509            ServerboundMovePlayerRot,
2510            ServerboundMovePlayerRot {
2511                yaw: 90.0,
2512                pitch: -10.0,
2513                on_ground: true,
2514            }
2515        );
2516        roundtrip!(
2517            ServerboundMovePlayerPosRot,
2518            ServerboundMovePlayerPosRot {
2519                x: 1.0,
2520                feet_y: 65.0,
2521                z: 2.0,
2522                yaw: 180.0,
2523                pitch: 45.0,
2524                on_ground: false,
2525            }
2526        );
2527    }
2528
2529    #[test]
2530    fn interact_roundtrip() {
2531        roundtrip!(
2532            ServerboundInteract,
2533            ServerboundInteract {
2534                entity_id: VarInt(1),
2535                action: InteractAction::Attack,
2536            }
2537        );
2538        roundtrip!(
2539            ServerboundInteract,
2540            ServerboundInteract {
2541                entity_id: VarInt(2),
2542                action: InteractAction::Interact,
2543            }
2544        );
2545        roundtrip!(
2546            ServerboundInteract,
2547            ServerboundInteract {
2548                entity_id: VarInt(3),
2549                action: InteractAction::InteractAt {
2550                    target_x: 0.5,
2551                    target_y: 1.0,
2552                    target_z: 0.5
2553                },
2554            }
2555        );
2556    }
2557
2558    #[test]
2559    fn plugin_message_roundtrip() {
2560        roundtrip!(
2561            ClientboundPluginMessage,
2562            ClientboundPluginMessage {
2563                channel: "MC|Brand".to_string(),
2564                data: b"vanilla".to_vec(),
2565            }
2566        );
2567        roundtrip!(
2568            ServerboundPluginMessage,
2569            ServerboundPluginMessage {
2570                channel: "MC|Brand".to_string(),
2571                data: b"client".to_vec(),
2572            }
2573        );
2574    }
2575
2576    #[test]
2577    fn packet_ids() {
2578        assert_eq!(ClientboundKeepAlive::packet_id(5), 0x00);
2579        assert_eq!(ClientboundJoinGame::packet_id(5), 0x01);
2580        assert_eq!(ClientboundChatMessage::packet_id(5), 0x02);
2581        assert_eq!(ClientboundRespawn::packet_id(5), 0x07);
2582        assert_eq!(ClientboundPlayerPosition::packet_id(5), 0x08);
2583        assert_eq!(ClientboundDisconnect::packet_id(5), 0x40);
2584        assert_eq!(ClientboundPluginMessage::packet_id(5), 0x3F);
2585        assert_eq!(ServerboundKeepAlive::packet_id(5), 0x00);
2586        assert_eq!(ServerboundChatMessage::packet_id(5), 0x01);
2587        assert_eq!(ServerboundInteract::packet_id(5), 0x02);
2588        assert_eq!(ServerboundMovePlayerPos::packet_id(5), 0x04);
2589        assert_eq!(ServerboundMovePlayerRot::packet_id(5), 0x05);
2590        assert_eq!(ServerboundMovePlayerPosRot::packet_id(5), 0x06);
2591        assert_eq!(ServerboundPluginMessage::packet_id(5), 0x17);
2592    }
2593}