Skip to main content

kojacoord_protocol/versions/v1_12_2/play/
mod.rs

1use crate::codec::{Decode, Encode, PacketId};
2use crate::error::ProtocolError;
3use crate::types::slot::LegacySlot;
4use crate::types::VarInt;
5use bytes::{Buf, BufMut, Bytes, BytesMut};
6
7pub use packets::{
8    ClientboundAwardStats, ClientboundBlockAction, ClientboundBlockDestroyStage,
9    ClientboundBlockEntityData, ClientboundBlockUpdate, ClientboundBossBar,
10    ClientboundChangeDifficulty, ClientboundChatMessage, ClientboundCommandSuggestions,
11    ClientboundContainerClose, ClientboundContainerSetContent, ClientboundContainerSetProperty,
12    ClientboundContainerSetSlot, ClientboundCooldown, ClientboundDisconnect,
13    ClientboundEntityAnimation, ClientboundEntityEvent, ClientboundExplosion,
14    ClientboundForgetLevelChunk, ClientboundGameEvent, ClientboundHorseScreenOpen,
15    ClientboundInitializeBorder, ClientboundJoinGame, ClientboundKeepAlive,
16    ClientboundLevelChunkWithLight, ClientboundLevelEvent, ClientboundLevelParticles,
17    ClientboundMapItemData, ClientboundMoveEntityPos, ClientboundMoveEntityPosRot,
18    ClientboundMoveEntityRot, ClientboundMoveVehicle, ClientboundOpenScreen,
19    ClientboundOpenSignEditor, ClientboundPlaceGhostRecipe, ClientboundPlayerAbilities,
20    ClientboundPlayerCombatEnd, ClientboundPlayerCombatEnter, ClientboundPlayerCombatKill,
21    ClientboundPlayerInfoUpdate, ClientboundPlayerPosition, ClientboundPluginMessage,
22    ClientboundRecipes, ClientboundRemoveEntities, ClientboundRemoveEntityEffect,
23    ClientboundResetScore, ClientboundResourcePackPush, ClientboundRespawn, ClientboundRotateHead,
24    ClientboundSectionBlocksUpdate, ClientboundSelectAdvancementsTab, ClientboundSetBorderCenter,
25    ClientboundSetBorderLerpSize, ClientboundSetBorderSize, ClientboundSetBorderWarningDelay,
26    ClientboundSetBorderWarningDistance, ClientboundSetCamera, ClientboundSetCarriedItem,
27    ClientboundSetEntityLink, ClientboundSetEntityMotion, ClientboundSetEquipment,
28    ClientboundSetExperience, ClientboundSetHealth, ClientboundSetHeldItem,
29    ClientboundSetScoreboardObjective, ClientboundSetScoreboardScore, ClientboundSetTime,
30    ClientboundSound, ClientboundSpawnEntity, ClientboundSpawnExperienceOrb,
31    ClientboundSpawnGlobalEntity, ClientboundSpawnMob, ClientboundSpawnPainting,
32    ClientboundSpawnPlayer, ClientboundStopSound, ClientboundTabList, ClientboundTakeItemEntity,
33    ClientboundTeleportEntity, ClientboundUpdateAdvancements, ClientboundUpdateAttributes,
34    ClientboundUpdateEffects, InteractAction, SPacketChunkData, ServerboundAnimation,
35    ServerboundChatMessage, ServerboundClickWindow, ServerboundClickWindowButton,
36    ServerboundClientSettings, ServerboundClientStatus, ServerboundCloseWindow,
37    ServerboundCommandSuggestion, ServerboundConfirmTransaction,
38    ServerboundCreativeInventoryAction, ServerboundCustomPayload, ServerboundEnchantItem,
39    ServerboundEntityAction, ServerboundHeldItemChange, ServerboundInteract, ServerboundKeepAlive,
40    ServerboundMovePlayerPos, ServerboundMovePlayerPosRot, ServerboundMovePlayerRot,
41    ServerboundMovePlayerStatusOnly, ServerboundPickItem, ServerboundPlaceRecipe,
42    ServerboundPlayerAbilities, ServerboundPlayerAction, ServerboundPlayerBlockPlacement,
43    ServerboundPluginMessage, ServerboundRecipeBookChangeSettings, ServerboundRecipeBookSeenRecipe,
44    ServerboundResourcePackStatus, ServerboundSelectTrade, ServerboundSetBeaconEffect,
45    ServerboundSetStructureBlock, ServerboundSpectate, ServerboundSteerBoat,
46    ServerboundSteerVehicle, ServerboundTeleportConfirm, ServerboundUpdateCommandBlock,
47    ServerboundUpdateCommandBlockMinecart, ServerboundUpdateSign, ServerboundUseItem,
48    ServerboundVehicleMove,
49};
50
51mod packets {
52    use crate::types::Nbt;
53    use uuid::Uuid;
54
55    use super::*;
56
57    fn encode_str(s: &str, dst: &mut BytesMut) -> Result<(), ProtocolError> {
58        let bytes = s.as_bytes();
59        VarInt(bytes.len() as i32).encode(dst)?;
60        dst.put_slice(bytes);
61        Ok(())
62    }
63
64    fn decode_str(src: &mut Bytes, ctx: &'static str) -> Result<String, ProtocolError> {
65        let len = VarInt::decode(src)?.0 as usize;
66        if src.remaining() < len {
67            return Err(ProtocolError::Io(std::io::Error::new(
68                std::io::ErrorKind::UnexpectedEof,
69                format!("Missing bytes for {ctx}"),
70            )));
71        }
72        let mut b = vec![0u8; len];
73        src.copy_to_slice(&mut b);
74        String::from_utf8(b).map_err(|_| {
75            ProtocolError::Io(std::io::Error::new(
76                std::io::ErrorKind::InvalidData,
77                format!("Invalid UTF-8 in {ctx}"),
78            ))
79        })
80    }
81
82    fn need(src: &Bytes, n: usize) -> Result<(), ProtocolError> {
83        if src.remaining() < n {
84            Err(ProtocolError::Io(std::io::Error::new(
85                std::io::ErrorKind::UnexpectedEof,
86                format!("Need {n} bytes, have {}", src.remaining()),
87            )))
88        } else {
89            Ok(())
90        }
91    }
92
93    // ── JoinGame (0x23) ───────────────────────────────────────────────────────
94
95    #[derive(Debug, Clone, PartialEq)]
96    pub struct ClientboundJoinGame {
97        pub entity_id: i32,
98        pub gamemode: u8,
99        pub dimension: i32, // 1.12.2 uses an i32 (-1: Nether, 0: Overworld, 1: End)
100        pub difficulty: u8,
101        pub max_players: u8,
102        pub level_type: String, // e.g., "default", "flat"
103        pub reduced_debug_info: bool,
104    }
105
106    impl PacketId for ClientboundJoinGame {
107        fn packet_id(_ver: u32) -> u8 {
108            0x23
109        }
110    }
111
112    impl Encode for ClientboundJoinGame {
113        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
114            // 1. Entity ID (4 bytes)
115            dst.put_i32(self.entity_id);
116
117            // 2. Gamemode (1 byte)
118            dst.put_u8(self.gamemode);
119
120            // 3. Dimension (4 bytes)
121            dst.put_i32(self.dimension);
122
123            // 4. Difficulty (1 byte)
124            dst.put_u8(self.difficulty);
125
126            // 5. Max Players (1 byte - ignored by modern clients but strictly read)
127            dst.put_u8(self.max_players);
128
129            // 6. Level Type (String -> VarInt Length + UTF-8 payload)
130            let level_bytes = self.level_type.as_bytes();
131            VarInt(level_bytes.len() as i32).encode(dst)?;
132            dst.put_slice(level_bytes);
133
134            // 7. Reduced Debug Info (1 byte boolean)
135            dst.put_u8(self.reduced_debug_info as u8);
136
137            Ok(())
138        }
139    }
140
141    impl Decode for ClientboundJoinGame {
142        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
143            // Base static size check: 4 (EID) + 1 (GM) + 4 (Dim) + 1 (Diff) + 1 (MaxP) = 11 bytes
144            if src.remaining() < 11 {
145                return Err(ProtocolError::Io(std::io::Error::new(
146                    std::io::ErrorKind::UnexpectedEof,
147                    "Missing initial static data for ClientboundJoinGame",
148                )));
149            }
150
151            let entity_id = src.get_i32();
152            let gamemode = src.get_u8();
153            let dimension = src.get_i32();
154            let difficulty = src.get_u8();
155            let max_players = src.get_u8();
156
157            // Decode Level Type String
158            let level_len = VarInt::decode(src)?.0 as usize;
159            if src.remaining() < level_len {
160                return Err(ProtocolError::Io(std::io::Error::new(
161                    std::io::ErrorKind::UnexpectedEof,
162                    "Missing level_type string payload inside ClientboundJoinGame",
163                )));
164            }
165            let mut level_bytes = vec![0u8; level_len];
166            src.copy_to_slice(&mut level_bytes);
167            let level_type = String::from_utf8(level_bytes).map_err(|_| {
168                ProtocolError::Io(std::io::Error::new(
169                    std::io::ErrorKind::InvalidData,
170                    "Invalid UTF-8 sequence for level_type string",
171                ))
172            })?;
173
174            // Decode trailing boolean
175            if src.remaining() < 1 {
176                return Err(ProtocolError::Io(std::io::Error::new(
177                    std::io::ErrorKind::UnexpectedEof,
178                    "Missing reduced_debug_info flag inside ClientboundJoinGame",
179                )));
180            }
181            let reduced_debug_info = src.get_u8() != 0;
182
183            Ok(Self {
184                entity_id,
185                gamemode,
186                dimension,
187                difficulty,
188                max_players,
189                level_type,
190                reduced_debug_info,
191            })
192        }
193    }
194
195    // ── Respawn (0x35) ────────────────────────────────────────────────────────
196
197    #[derive(Debug, Clone, PartialEq)]
198    pub struct ClientboundRespawn {
199        pub dimension: i32,
200        pub difficulty: u8,
201        pub game_mode: u8,
202        pub level_type: String,
203    }
204
205    impl PacketId for ClientboundRespawn {
206        fn packet_id(_ver: u32) -> u8 {
207            0x35
208        }
209    }
210
211    impl Encode for ClientboundRespawn {
212        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
213            dst.put_i32(self.dimension);
214            dst.put_u8(self.difficulty);
215            dst.put_u8(self.game_mode);
216            encode_str(&self.level_type, dst)
217        }
218    }
219
220    impl Decode for ClientboundRespawn {
221        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
222            need(src, 4 + 1 + 1)?;
223            let dimension = src.get_i32();
224            let difficulty = src.get_u8();
225            let game_mode = src.get_u8();
226            let level_type = decode_str(src, "ClientboundRespawn level_type")?;
227            Ok(Self {
228                dimension,
229                difficulty,
230                game_mode,
231                level_type,
232            })
233        }
234    }
235
236    // ── PlayerPosition (0x2F) ─────────────────────────────────────────────────
237
238    #[derive(Debug, Clone, PartialEq)]
239    pub struct ClientboundPlayerPosition {
240        pub x: f64,
241        pub y: f64,
242        pub z: f64,
243        pub yaw: f32,
244        pub pitch: f32,
245        pub flags: u8,
246        pub teleport_id: VarInt,
247    }
248
249    impl PacketId for ClientboundPlayerPosition {
250        fn packet_id(_ver: u32) -> u8 {
251            0x2F
252        }
253    }
254
255    impl Encode for ClientboundPlayerPosition {
256        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
257            dst.put_f64(self.x);
258            dst.put_f64(self.y);
259            dst.put_f64(self.z);
260            dst.put_f32(self.yaw);
261            dst.put_f32(self.pitch);
262            dst.put_u8(self.flags);
263            self.teleport_id.encode(dst)
264        }
265    }
266
267    impl Decode for ClientboundPlayerPosition {
268        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
269            need(src, 8 + 8 + 8 + 4 + 4 + 1)?;
270            let x = src.get_f64();
271            let y = src.get_f64();
272            let z = src.get_f64();
273            let yaw = src.get_f32();
274            let pitch = src.get_f32();
275            let flags = src.get_u8();
276            let teleport_id = VarInt::decode(src)?;
277            Ok(Self {
278                x,
279                y,
280                z,
281                yaw,
282                pitch,
283                flags,
284                teleport_id,
285            })
286        }
287    }
288
289    // ── SpawnEntity (0x00) ────────────────────────────────────────────────────
290
291    #[derive(Debug, Clone, PartialEq)]
292    pub struct ClientboundSpawnEntity {
293        pub entity_id: VarInt,
294        pub object_uuid: Uuid,
295        pub kind: u8,
296        pub x: f64,
297        pub y: f64,
298        pub z: f64,
299        pub yaw: u8,
300        pub pitch: u8,
301        pub data: i32,
302        pub velocity_x: i16,
303        pub velocity_y: i16,
304        pub velocity_z: i16,
305    }
306
307    impl PacketId for ClientboundSpawnEntity {
308        fn packet_id(_ver: u32) -> u8 {
309            0x00
310        }
311    }
312
313    impl Encode for ClientboundSpawnEntity {
314        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
315            self.entity_id.encode(dst)?;
316            dst.put_slice(self.object_uuid.as_bytes());
317            dst.put_u8(self.kind);
318            dst.put_f64(self.x);
319            dst.put_f64(self.y);
320            dst.put_f64(self.z);
321            dst.put_u8(self.yaw);
322            dst.put_u8(self.pitch);
323            dst.put_i32(self.data);
324            dst.put_i16(self.velocity_x);
325            dst.put_i16(self.velocity_y);
326            dst.put_i16(self.velocity_z);
327            Ok(())
328        }
329    }
330
331    impl Decode for ClientboundSpawnEntity {
332        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
333            let entity_id = VarInt::decode(src)?;
334            // 16B UUID + 1B kind + 24B xyz + 2B yaw+pitch + 4B data + 6B velocity = 53
335            need(src, 53)?;
336            let mut uuid_bytes = [0u8; 16];
337            src.copy_to_slice(&mut uuid_bytes);
338            let object_uuid = Uuid::from_bytes(uuid_bytes);
339            let kind = src.get_u8();
340            let x = src.get_f64();
341            let y = src.get_f64();
342            let z = src.get_f64();
343            let yaw = src.get_u8();
344            let pitch = src.get_u8();
345            let data = src.get_i32();
346            let velocity_x = src.get_i16();
347            let velocity_y = src.get_i16();
348            let velocity_z = src.get_i16();
349            Ok(Self {
350                entity_id,
351                object_uuid,
352                kind,
353                x,
354                y,
355                z,
356                yaw,
357                pitch,
358                data,
359                velocity_x,
360                velocity_y,
361                velocity_z,
362            })
363        }
364    }
365
366    // ── KeepAlive (0x1F / 0x0B) ──────────────────────────────────────────────
367
368    #[derive(Debug, Clone, PartialEq)]
369    pub struct ClientboundKeepAlive {
370        pub keep_alive_id: i64,
371    }
372
373    impl PacketId for ClientboundKeepAlive {
374        fn packet_id(_ver: u32) -> u8 {
375            0x1F
376        }
377    }
378
379    impl Encode for ClientboundKeepAlive {
380        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
381            dst.put_i64(self.keep_alive_id);
382            Ok(())
383        }
384    }
385
386    impl Decode for ClientboundKeepAlive {
387        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
388            need(src, 8)?;
389            Ok(Self {
390                keep_alive_id: src.get_i64(),
391            })
392        }
393    }
394
395    #[derive(Debug, Clone, PartialEq)]
396    pub struct ServerboundKeepAlive {
397        pub keep_alive_id: i64,
398    }
399
400    impl PacketId for ServerboundKeepAlive {
401        fn packet_id(_ver: u32) -> u8 {
402            0x0B
403        }
404    }
405
406    impl Encode for ServerboundKeepAlive {
407        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
408            dst.put_i64(self.keep_alive_id);
409            Ok(())
410        }
411    }
412
413    impl Decode for ServerboundKeepAlive {
414        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
415            need(src, 8)?;
416            Ok(Self {
417                keep_alive_id: src.get_i64(),
418            })
419        }
420    }
421
422    // ── Chat (0x0F / 0x02) ────────────────────────────────────────────────────
423
424    #[derive(Debug, Clone, PartialEq)]
425    pub struct ClientboundChatMessage {
426        pub json_message: String,
427        pub position: u8,
428    }
429
430    impl PacketId for ClientboundChatMessage {
431        fn packet_id(_ver: u32) -> u8 {
432            0x0F
433        }
434    }
435
436    impl Encode for ClientboundChatMessage {
437        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
438            encode_str(&self.json_message, dst)?;
439            dst.put_u8(self.position);
440            Ok(())
441        }
442    }
443
444    impl Decode for ClientboundChatMessage {
445        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
446            let json_message = decode_str(src, "ClientboundChatMessage json_message")?;
447            need(src, 1)?;
448            let position = src.get_u8();
449            Ok(Self {
450                json_message,
451                position,
452            })
453        }
454    }
455
456    #[derive(Debug, Clone, PartialEq)]
457    pub struct ServerboundChatMessage {
458        pub message: String,
459    }
460
461    impl PacketId for ServerboundChatMessage {
462        fn packet_id(_ver: u32) -> u8 {
463            0x02
464        }
465    }
466
467    impl Encode for ServerboundChatMessage {
468        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
469            encode_str(&self.message, dst)
470        }
471    }
472
473    impl Decode for ServerboundChatMessage {
474        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
475            let message = decode_str(src, "ServerboundChatMessage message")?;
476            Ok(Self { message })
477        }
478    }
479
480    // ── Movement ──────────────────────────────────────────────────────────────
481
482    #[derive(Debug, Clone, PartialEq)]
483    pub struct ServerboundMovePlayerPos {
484        pub x: f64,
485        pub feet_y: f64,
486        pub z: f64,
487        pub on_ground: bool,
488    }
489
490    impl PacketId for ServerboundMovePlayerPos {
491        fn packet_id(_ver: u32) -> u8 {
492            0x0C
493        }
494    }
495
496    impl Encode for ServerboundMovePlayerPos {
497        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
498            dst.put_f64(self.x);
499            dst.put_f64(self.feet_y);
500            dst.put_f64(self.z);
501            dst.put_u8(self.on_ground as u8);
502            Ok(())
503        }
504    }
505
506    impl Decode for ServerboundMovePlayerPos {
507        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
508            need(src, 8 + 8 + 8 + 1)?;
509            Ok(Self {
510                x: src.get_f64(),
511                feet_y: src.get_f64(),
512                z: src.get_f64(),
513                on_ground: src.get_u8() != 0,
514            })
515        }
516    }
517
518    #[derive(Debug, Clone, PartialEq)]
519    pub struct ServerboundMovePlayerRot {
520        pub yaw: f32,
521        pub pitch: f32,
522        pub on_ground: bool,
523    }
524
525    impl PacketId for ServerboundMovePlayerRot {
526        fn packet_id(_ver: u32) -> u8 {
527            0x0E
528        }
529    }
530
531    impl Encode for ServerboundMovePlayerRot {
532        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
533            dst.put_f32(self.yaw);
534            dst.put_f32(self.pitch);
535            dst.put_u8(self.on_ground as u8);
536            Ok(())
537        }
538    }
539
540    impl Decode for ServerboundMovePlayerRot {
541        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
542            need(src, 4 + 4 + 1)?;
543            Ok(Self {
544                yaw: src.get_f32(),
545                pitch: src.get_f32(),
546                on_ground: src.get_u8() != 0,
547            })
548        }
549    }
550
551    #[derive(Debug, Clone, PartialEq)]
552    pub struct ServerboundMovePlayerPosRot {
553        pub x: f64,
554        pub feet_y: f64,
555        pub z: f64,
556        pub yaw: f32,
557        pub pitch: f32,
558        pub on_ground: bool,
559    }
560
561    impl PacketId for ServerboundMovePlayerPosRot {
562        fn packet_id(_ver: u32) -> u8 {
563            0x0D
564        }
565    }
566
567    impl Encode for ServerboundMovePlayerPosRot {
568        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
569            dst.put_f64(self.x);
570            dst.put_f64(self.feet_y);
571            dst.put_f64(self.z);
572            dst.put_f32(self.yaw);
573            dst.put_f32(self.pitch);
574            dst.put_u8(self.on_ground as u8);
575            Ok(())
576        }
577    }
578
579    impl Decode for ServerboundMovePlayerPosRot {
580        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
581            need(src, 8 + 8 + 8 + 4 + 4 + 1)?;
582            Ok(Self {
583                x: src.get_f64(),
584                feet_y: src.get_f64(),
585                z: src.get_f64(),
586                yaw: src.get_f32(),
587                pitch: src.get_f32(),
588                on_ground: src.get_u8() != 0,
589            })
590        }
591    }
592
593    // ── Interact (0x0A) ───────────────────────────────────────────────────────
594
595    #[derive(Debug, Clone, PartialEq)]
596    pub enum InteractAction {
597        Interact {
598            hand: VarInt,
599        },
600        Attack,
601        InteractAt {
602            target_x: f32,
603            target_y: f32,
604            target_z: f32,
605            hand: VarInt,
606        },
607    }
608
609    #[derive(Debug, Clone, PartialEq)]
610    pub struct ServerboundInteract {
611        pub entity_id: VarInt,
612        pub action: InteractAction,
613    }
614
615    impl PacketId for ServerboundInteract {
616        fn packet_id(_ver: u32) -> u8 {
617            0x0A
618        }
619    }
620
621    impl Encode for ServerboundInteract {
622        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
623            self.entity_id.encode(dst)?;
624            match &self.action {
625                InteractAction::Interact { hand } => {
626                    VarInt(0).encode(dst)?;
627                    hand.encode(dst)?;
628                },
629                InteractAction::Attack => {
630                    VarInt(1).encode(dst)?;
631                },
632                InteractAction::InteractAt {
633                    target_x,
634                    target_y,
635                    target_z,
636                    hand,
637                } => {
638                    VarInt(2).encode(dst)?;
639                    dst.put_f32(*target_x);
640                    dst.put_f32(*target_y);
641                    dst.put_f32(*target_z);
642                    hand.encode(dst)?;
643                },
644            }
645            Ok(())
646        }
647    }
648
649    impl Decode for ServerboundInteract {
650        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
651            let entity_id = VarInt::decode(src)?;
652            let action = match VarInt::decode(src)?.0 {
653                0 => InteractAction::Interact {
654                    hand: VarInt::decode(src)?,
655                },
656                1 => InteractAction::Attack,
657                2 => {
658                    need(src, 4 + 4 + 4)?;
659                    InteractAction::InteractAt {
660                        target_x: src.get_f32(),
661                        target_y: src.get_f32(),
662                        target_z: src.get_f32(),
663                        hand: VarInt::decode(src)?,
664                    }
665                },
666                _ => {
667                    return Err(ProtocolError::Io(std::io::Error::new(
668                        std::io::ErrorKind::InvalidData,
669                        "Unknown ServerboundInteract action type",
670                    )))
671                },
672            };
673            Ok(Self { entity_id, action })
674        }
675    }
676
677    // ── PluginMessage (0x18 / 0x09) ───────────────────────────────────────────
678
679    #[derive(Debug, Clone, PartialEq)]
680    pub struct ClientboundPluginMessage {
681        pub channel: String,
682        pub data: Vec<u8>,
683    }
684
685    impl PacketId for ClientboundPluginMessage {
686        fn packet_id(_ver: u32) -> u8 {
687            0x18
688        }
689    }
690
691    impl Encode for ClientboundPluginMessage {
692        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
693            encode_str(&self.channel, dst)?;
694            dst.put_slice(&self.data);
695            Ok(())
696        }
697    }
698
699    impl Decode for ClientboundPluginMessage {
700        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
701            let channel = decode_str(src, "ClientboundPluginMessage channel")?;
702            let len = src.remaining();
703            let mut data = vec![0u8; len];
704            src.copy_to_slice(&mut data);
705            Ok(Self { channel, data })
706        }
707    }
708
709    #[derive(Debug, Clone, PartialEq)]
710    pub struct ServerboundPluginMessage {
711        pub channel: String,
712        pub data: Vec<u8>,
713    }
714
715    impl PacketId for ServerboundPluginMessage {
716        fn packet_id(_ver: u32) -> u8 {
717            0x09
718        }
719    }
720
721    impl Encode for ServerboundPluginMessage {
722        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
723            encode_str(&self.channel, dst)?;
724            dst.put_slice(&self.data);
725            Ok(())
726        }
727    }
728
729    impl Decode for ServerboundPluginMessage {
730        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
731            let channel = decode_str(src, "ServerboundPluginMessage channel")?;
732            let len = src.remaining();
733            let mut data = vec![0u8; len];
734            src.copy_to_slice(&mut data);
735            Ok(Self { channel, data })
736        }
737    }
738
739    // ── Disconnect (0x1A) ─────────────────────────────────────────────────────
740
741    #[derive(Debug, Clone, PartialEq)]
742    pub struct ClientboundDisconnect {
743        pub reason: String,
744    }
745
746    impl PacketId for ClientboundDisconnect {
747        fn packet_id(_ver: u32) -> u8 {
748            0x1A
749        }
750    }
751
752    impl Encode for ClientboundDisconnect {
753        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
754            encode_str(&self.reason, dst)
755        }
756    }
757
758    impl Decode for ClientboundDisconnect {
759        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
760            let reason = decode_str(src, "ClientboundDisconnect reason")?;
761            Ok(Self { reason })
762        }
763    }
764
765    // ── PlayerAbilities (0x2C / 0x12) ─────────────────────────────────────────
766
767    #[derive(Debug, Clone, PartialEq)]
768    pub struct ClientboundPlayerAbilities {
769        pub flags: u8,
770        pub flying_speed: f32,
771        pub walking_speed: f32,
772    }
773
774    impl PacketId for ClientboundPlayerAbilities {
775        fn packet_id(_ver: u32) -> u8 {
776            0x2C
777        }
778    }
779
780    impl Encode for ClientboundPlayerAbilities {
781        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
782            dst.put_u8(self.flags);
783            dst.put_f32(self.flying_speed);
784            dst.put_f32(self.walking_speed);
785            Ok(())
786        }
787    }
788
789    impl Decode for ClientboundPlayerAbilities {
790        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
791            need(src, 1 + 4 + 4)?;
792            Ok(Self {
793                flags: src.get_u8(),
794                flying_speed: src.get_f32(),
795                walking_speed: src.get_f32(),
796            })
797        }
798    }
799
800    #[derive(Debug, Clone, PartialEq)]
801    pub struct ServerboundPlayerAbilities {
802        pub flags: u8,
803        pub flying_speed: f32,
804        pub walking_speed: f32,
805    }
806
807    impl PacketId for ServerboundPlayerAbilities {
808        fn packet_id(_ver: u32) -> u8 {
809            0x12
810        }
811    }
812
813    impl Encode for ServerboundPlayerAbilities {
814        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
815            dst.put_u8(self.flags);
816            dst.put_f32(self.flying_speed);
817            dst.put_f32(self.walking_speed);
818            Ok(())
819        }
820    }
821
822    impl Decode for ServerboundPlayerAbilities {
823        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
824            need(src, 1 + 4 + 4)?;
825            Ok(Self {
826                flags: src.get_u8(),
827                flying_speed: src.get_f32(),
828                walking_speed: src.get_f32(),
829            })
830        }
831    }
832
833    // ── SetCarriedItem (0x3A) ─────────────────────────────────────────────────
834
835    #[derive(Debug, Clone, PartialEq)]
836    pub struct ClientboundSetCarriedItem {
837        pub slot: i8,
838    }
839
840    impl PacketId for ClientboundSetCarriedItem {
841        fn packet_id(_ver: u32) -> u8 {
842            0x3A
843        }
844    }
845
846    impl Encode for ClientboundSetCarriedItem {
847        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
848            dst.put_i8(self.slot);
849            Ok(())
850        }
851    }
852
853    impl Decode for ClientboundSetCarriedItem {
854        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
855            need(src, 1)?;
856            Ok(Self { slot: src.get_i8() })
857        }
858    }
859
860    // ── BossBar (0x0C) ────────────────────────────────────────────────────────
861
862    #[derive(Debug, Clone, PartialEq)]
863    pub enum BossBarAction {
864        Add {
865            title: String,
866            health: f32,
867            color: VarInt,
868            division: VarInt,
869            flags: u8,
870        },
871        Remove,
872        UpdateHealth {
873            health: f32,
874        },
875        UpdateTitle {
876            title: String,
877        },
878        UpdateStyle {
879            color: VarInt,
880            division: VarInt,
881        },
882        UpdateFlags {
883            flags: u8,
884        },
885    }
886
887    #[derive(Debug, Clone, PartialEq)]
888    pub struct ClientboundBossBar {
889        pub uuid: Uuid,
890        pub action: BossBarAction,
891    }
892
893    impl PacketId for ClientboundBossBar {
894        fn packet_id(_ver: u32) -> u8 {
895            0x0C
896        }
897    }
898
899    impl Encode for ClientboundBossBar {
900        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
901            dst.put_slice(self.uuid.as_bytes());
902            let action_id: i32 = match &self.action {
903                BossBarAction::Add { .. } => 0,
904                BossBarAction::Remove => 1,
905                BossBarAction::UpdateHealth { .. } => 2,
906                BossBarAction::UpdateTitle { .. } => 3,
907                BossBarAction::UpdateStyle { .. } => 4,
908                BossBarAction::UpdateFlags { .. } => 5,
909            };
910            VarInt(action_id).encode(dst)?;
911            match &self.action {
912                BossBarAction::Add {
913                    title,
914                    health,
915                    color,
916                    division,
917                    flags,
918                } => {
919                    encode_str(title, dst)?;
920                    dst.put_f32(*health);
921                    color.encode(dst)?;
922                    division.encode(dst)?;
923                    dst.put_u8(*flags);
924                },
925                BossBarAction::Remove => {},
926                BossBarAction::UpdateHealth { health } => {
927                    dst.put_f32(*health);
928                },
929                BossBarAction::UpdateTitle { title } => {
930                    encode_str(title, dst)?;
931                },
932                BossBarAction::UpdateStyle { color, division } => {
933                    color.encode(dst)?;
934                    division.encode(dst)?;
935                },
936                BossBarAction::UpdateFlags { flags } => {
937                    dst.put_u8(*flags);
938                },
939            }
940            Ok(())
941        }
942    }
943
944    impl Decode for ClientboundBossBar {
945        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
946            need(src, 16)?;
947            let mut b = [0u8; 16];
948            src.copy_to_slice(&mut b);
949            let uuid = Uuid::from_bytes(b);
950            let action = match VarInt::decode(src)?.0 {
951                0 => {
952                    let title = decode_str(src, "BossBar Add title")?;
953                    need(src, 4)?;
954                    let health = src.get_f32();
955                    let color = VarInt::decode(src)?;
956                    let division = VarInt::decode(src)?;
957                    need(src, 1)?;
958                    let flags = src.get_u8();
959                    BossBarAction::Add {
960                        title,
961                        health,
962                        color,
963                        division,
964                        flags,
965                    }
966                },
967                1 => BossBarAction::Remove,
968                2 => {
969                    need(src, 4)?;
970                    BossBarAction::UpdateHealth {
971                        health: src.get_f32(),
972                    }
973                },
974                3 => BossBarAction::UpdateTitle {
975                    title: decode_str(src, "BossBar UpdateTitle")?,
976                },
977                4 => BossBarAction::UpdateStyle {
978                    color: VarInt::decode(src)?,
979                    division: VarInt::decode(src)?,
980                },
981                5 => {
982                    need(src, 1)?;
983                    BossBarAction::UpdateFlags {
984                        flags: src.get_u8(),
985                    }
986                },
987                _ => {
988                    return Err(ProtocolError::Io(std::io::Error::new(
989                        std::io::ErrorKind::InvalidData,
990                        "Unknown BossBar action id",
991                    )))
992                },
993            };
994            Ok(Self { uuid, action })
995        }
996    }
997
998    // ── SpawnExperienceOrb (0x01) ─────────────────────────────────────────────
999
1000    #[derive(Debug, Clone, PartialEq)]
1001    pub struct ClientboundSpawnExperienceOrb {
1002        pub entity_id: VarInt,
1003        pub x: f64,
1004        pub y: f64,
1005        pub z: f64,
1006        pub count: i16,
1007    }
1008
1009    impl PacketId for ClientboundSpawnExperienceOrb {
1010        fn packet_id(_ver: u32) -> u8 {
1011            0x01
1012        }
1013    }
1014
1015    impl Encode for ClientboundSpawnExperienceOrb {
1016        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1017            self.entity_id.encode(dst)?;
1018            dst.put_f64(self.x);
1019            dst.put_f64(self.y);
1020            dst.put_f64(self.z);
1021            dst.put_i16(self.count);
1022            Ok(())
1023        }
1024    }
1025
1026    impl Decode for ClientboundSpawnExperienceOrb {
1027        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1028            let entity_id = VarInt::decode(src)?;
1029            need(src, 8 + 8 + 8 + 2)?;
1030            Ok(Self {
1031                entity_id,
1032                x: src.get_f64(),
1033                y: src.get_f64(),
1034                z: src.get_f64(),
1035                count: src.get_i16(),
1036            })
1037        }
1038    }
1039
1040    #[derive(Debug, Clone, PartialEq)]
1041    pub struct ClientboundSpawnPainting {
1042        pub entity_id: VarInt,
1043        pub uuid: Uuid,
1044        pub title: String, // Název obrazu (např. "Alban", "Aztec") max 13 znaků
1045        pub position: u64, // Minecraft pozice x/y/z zabalená do u64
1046        pub direction: u8, // 0: jih, 1: západ, 2: sever, 3: východ
1047    }
1048
1049    impl PacketId for ClientboundSpawnPainting {
1050        fn packet_id(_ver: u32) -> u8 {
1051            0x04
1052        }
1053    }
1054
1055    impl Encode for ClientboundSpawnPainting {
1056        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1057            self.entity_id.encode(dst)?;
1058            dst.put_slice(self.uuid.as_bytes());
1059
1060            let title_bytes = self.title.as_bytes();
1061            VarInt(title_bytes.len() as i32).encode(dst)?;
1062            dst.put_slice(title_bytes);
1063
1064            dst.put_u64(self.position); // Pozice (Big Endian)
1065            dst.put_u8(self.direction);
1066            Ok(())
1067        }
1068    }
1069
1070    impl Decode for ClientboundSpawnPainting {
1071        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1072            let entity_id = VarInt::decode(src)?;
1073
1074            if src.remaining() < 16 {
1075                return Err(ProtocolError::UnexpectedEof);
1076            }
1077            let mut uuid_bytes = [0u8; 16];
1078            src.copy_to_slice(&mut uuid_bytes);
1079            let uuid = Uuid::from_bytes(uuid_bytes);
1080
1081            let title_len = VarInt::decode(src)?.0 as usize;
1082            if src.remaining() < title_len {
1083                return Err(ProtocolError::UnexpectedEof);
1084            }
1085            let mut title_bytes = vec![0u8; title_len];
1086            src.copy_to_slice(&mut title_bytes);
1087            let title = String::from_utf8(title_bytes).map_err(|_| ProtocolError::UnexpectedEof)?;
1088
1089            if src.remaining() < 9 {
1090                return Err(ProtocolError::UnexpectedEof);
1091            } // 8B pozice + 1B směr
1092            let position = src.get_u64();
1093            let direction = src.get_u8();
1094
1095            Ok(Self {
1096                entity_id,
1097                uuid,
1098                title,
1099                position,
1100                direction,
1101            })
1102        }
1103    }
1104
1105    #[derive(Debug, Clone, PartialEq)]
1106    pub struct ClientboundSpawnMob {
1107        pub entity_id: VarInt,
1108        pub uuid: Uuid,
1109        pub kind: VarInt, // ID typu moba (v 1.12.2 vyjádřeno jako VarInt)
1110        pub x: f64,
1111        pub y: f64,
1112        pub z: f64,
1113        pub yaw: u8,
1114        pub pitch: u8,
1115        pub head_pitch: u8,
1116        pub velocity_x: i16,
1117        pub velocity_y: i16,
1118        pub velocity_z: i16,
1119    }
1120
1121    impl PacketId for ClientboundSpawnMob {
1122        fn packet_id(_ver: u32) -> u8 {
1123            0x03
1124        }
1125    }
1126
1127    impl Encode for ClientboundSpawnMob {
1128        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1129            self.entity_id.encode(dst)?;
1130            dst.put_slice(self.uuid.as_bytes());
1131            self.kind.encode(dst)?;
1132            dst.put_f64(self.x);
1133            dst.put_f64(self.y);
1134            dst.put_f64(self.z);
1135            dst.put_u8(self.yaw);
1136            dst.put_u8(self.pitch);
1137            dst.put_u8(self.head_pitch);
1138            dst.put_i16(self.velocity_x);
1139            dst.put_i16(self.velocity_y);
1140            dst.put_i16(self.velocity_z);
1141
1142            // Konec paketu v 1.12.2 tvoří pole metadat (Entity Metadata) ukončené bajtem 0xFF.
1143            // Zapisujeme prázdný DataWatcher terminátor, abychom nekorumpovali stream.
1144            dst.put_u8(0xFF);
1145            Ok(())
1146        }
1147    }
1148
1149    impl Decode for ClientboundSpawnMob {
1150        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1151            let entity_id = VarInt::decode(src)?;
1152
1153            // Pevná velikost bloku dat: 16B (UUID) + dynamický VarInt pro typ (provádíme check po přečtení)
1154            if src.remaining() < 16 {
1155                return Err(ProtocolError::UnexpectedEof);
1156            }
1157            let mut uuid_bytes = [0u8; 16];
1158            src.copy_to_slice(&mut uuid_bytes);
1159            let uuid = Uuid::from_bytes(uuid_bytes);
1160
1161            let kind = VarInt::decode(src)?;
1162
1163            // Zbytek fixních polí: 3x f64 (24B) + 3x u8 (3B) + 3x i16 (6B) = 33 bajtů
1164            if src.remaining() < 33 {
1165                return Err(ProtocolError::UnexpectedEof);
1166            }
1167            let x = src.get_f64();
1168            let y = src.get_f64();
1169            let z = src.get_f64();
1170            let yaw = src.get_u8();
1171            let pitch = src.get_u8();
1172            let head_pitch = src.get_u8();
1173            let velocity_x = src.get_i16();
1174            let velocity_y = src.get_i16();
1175            let velocity_z = src.get_i16();
1176
1177            // Odr things: Zbývající bajty jsou DataWatcher metadata, která končí hodnotou 0xFF.
1178            // Proxy je bezpečně přeskočí, aby vyčistila buffer pro Netty.
1179            let remaining = src.remaining();
1180            src.advance(remaining);
1181
1182            Ok(Self {
1183                entity_id,
1184                uuid,
1185                kind,
1186                x,
1187                y,
1188                z,
1189                yaw,
1190                pitch,
1191                head_pitch,
1192                velocity_x,
1193                velocity_y,
1194                velocity_z,
1195            })
1196        }
1197    }
1198
1199    // ── SpawnPlayer (0x05) ────────────────────────────────────────────────────
1200
1201    #[derive(Debug, Clone, PartialEq)]
1202    pub struct ClientboundSpawnPlayer {
1203        pub entity_id: VarInt,
1204        pub player_uuid: Uuid,
1205        pub x: f64,
1206        pub y: f64,
1207        pub z: f64,
1208        pub yaw: u8,
1209        pub pitch: u8,
1210    }
1211
1212    impl PacketId for ClientboundSpawnPlayer {
1213        fn packet_id(_ver: u32) -> u8 {
1214            0x05
1215        }
1216    }
1217
1218    impl Encode for ClientboundSpawnPlayer {
1219        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1220            self.entity_id.encode(dst)?;
1221            dst.put_slice(self.player_uuid.as_bytes());
1222            dst.put_f64(self.x);
1223            dst.put_f64(self.y);
1224            dst.put_f64(self.z);
1225            dst.put_u8(self.yaw);
1226            dst.put_u8(self.pitch);
1227            Ok(())
1228        }
1229    }
1230
1231    impl Decode for ClientboundSpawnPlayer {
1232        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1233            let entity_id = VarInt::decode(src)?;
1234            // 16B UUID + 24B xyz + 2B yaw+pitch = 42
1235            need(src, 42)?;
1236            let mut b = [0u8; 16];
1237            src.copy_to_slice(&mut b);
1238            let player_uuid = Uuid::from_bytes(b);
1239            Ok(Self {
1240                entity_id,
1241                player_uuid,
1242                x: src.get_f64(),
1243                y: src.get_f64(),
1244                z: src.get_f64(),
1245                yaw: src.get_u8(),
1246                pitch: src.get_u8(),
1247            })
1248        }
1249    }
1250
1251    // ── EntityAnimation (0x06) ────────────────────────────────────────────────
1252
1253    #[derive(Debug, Clone, PartialEq)]
1254    pub struct ClientboundEntityAnimation {
1255        pub entity_id: VarInt,
1256        pub animation: u8,
1257    }
1258
1259    impl PacketId for ClientboundEntityAnimation {
1260        fn packet_id(_ver: u32) -> u8 {
1261            0x06
1262        }
1263    }
1264
1265    impl Encode for ClientboundEntityAnimation {
1266        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1267            self.entity_id.encode(dst)?;
1268            dst.put_u8(self.animation);
1269            Ok(())
1270        }
1271    }
1272
1273    impl Decode for ClientboundEntityAnimation {
1274        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1275            let entity_id = VarInt::decode(src)?;
1276            need(src, 1)?;
1277            Ok(Self {
1278                entity_id,
1279                animation: src.get_u8(),
1280            })
1281        }
1282    }
1283
1284    // ── AwardStats (0x07) ─────────────────────────────────────────────────────
1285
1286    #[derive(Debug, Clone, PartialEq)]
1287    pub struct ClientboundAwardStats {
1288        pub entries: Vec<(String, VarInt)>,
1289    }
1290
1291    impl PacketId for ClientboundAwardStats {
1292        fn packet_id(_ver: u32) -> u8 {
1293            0x07
1294        }
1295    }
1296
1297    impl Encode for ClientboundAwardStats {
1298        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1299            VarInt(self.entries.len() as i32).encode(dst)?;
1300            for (name, value) in &self.entries {
1301                encode_str(name, dst)?;
1302                value.encode(dst)?;
1303            }
1304            Ok(())
1305        }
1306    }
1307
1308    impl Decode for ClientboundAwardStats {
1309        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1310            let count = VarInt::decode(src)?.0 as usize;
1311            let mut entries = Vec::with_capacity(count);
1312            for _ in 0..count {
1313                let name = decode_str(src, "ClientboundAwardStats entry name")?;
1314                let value = VarInt::decode(src)?;
1315                entries.push((name, value));
1316            }
1317            Ok(Self { entries })
1318        }
1319    }
1320
1321    // ── BlockDestroyStage (0x08) ──────────────────────────────────────────────
1322
1323    #[derive(Debug, Clone, PartialEq)]
1324    pub struct ClientboundBlockDestroyStage {
1325        pub entity_id: VarInt,
1326        pub location: u64,
1327        pub destroy_stage: u8,
1328    }
1329
1330    impl PacketId for ClientboundBlockDestroyStage {
1331        fn packet_id(_ver: u32) -> u8 {
1332            0x08
1333        }
1334    }
1335
1336    impl Encode for ClientboundBlockDestroyStage {
1337        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1338            self.entity_id.encode(dst)?;
1339            dst.put_u64(self.location);
1340            dst.put_u8(self.destroy_stage);
1341            Ok(())
1342        }
1343    }
1344
1345    impl Decode for ClientboundBlockDestroyStage {
1346        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1347            let entity_id = VarInt::decode(src)?;
1348            need(src, 8 + 1)?;
1349            Ok(Self {
1350                entity_id,
1351                location: src.get_u64(),
1352                destroy_stage: src.get_u8(),
1353            })
1354        }
1355    }
1356
1357    // ── BlockEntityData (0x09) ────────────────────────────────────────────────
1358
1359    #[derive(Debug, Clone, PartialEq)]
1360    pub struct ClientboundBlockEntityData {
1361        pub location: u64,
1362        pub action: u8,
1363        pub nbt: Vec<u8>,
1364    }
1365
1366    impl PacketId for ClientboundBlockEntityData {
1367        fn packet_id(_ver: u32) -> u8 {
1368            0x09
1369        }
1370    }
1371
1372    impl Encode for ClientboundBlockEntityData {
1373        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1374            dst.put_u64(self.location);
1375            dst.put_u8(self.action);
1376            dst.put_slice(&self.nbt);
1377            Ok(())
1378        }
1379    }
1380
1381    impl Decode for ClientboundBlockEntityData {
1382        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1383            need(src, 8 + 1)?;
1384            let location = src.get_u64();
1385            let action = src.get_u8();
1386            let len = src.remaining();
1387            let mut nbt = vec![0u8; len];
1388            src.copy_to_slice(&mut nbt);
1389            Ok(Self {
1390                location,
1391                action,
1392                nbt,
1393            })
1394        }
1395    }
1396
1397    // ── BlockAction (0x0A) ────────────────────────────────────────────────────
1398
1399    #[derive(Debug, Clone, PartialEq)]
1400    pub struct ClientboundBlockAction {
1401        pub location: u64,
1402        pub action_id: u8,
1403        pub action_param: u8,
1404        pub block_type: VarInt,
1405    }
1406
1407    impl PacketId for ClientboundBlockAction {
1408        fn packet_id(_ver: u32) -> u8 {
1409            0x0A
1410        }
1411    }
1412
1413    impl Encode for ClientboundBlockAction {
1414        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1415            dst.put_u64(self.location);
1416            dst.put_u8(self.action_id);
1417            dst.put_u8(self.action_param);
1418            self.block_type.encode(dst)
1419        }
1420    }
1421
1422    impl Decode for ClientboundBlockAction {
1423        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1424            need(src, 8 + 1 + 1)?;
1425            let location = src.get_u64();
1426            let action_id = src.get_u8();
1427            let action_param = src.get_u8();
1428            let block_type = VarInt::decode(src)?;
1429            Ok(Self {
1430                location,
1431                action_id,
1432                action_param,
1433                block_type,
1434            })
1435        }
1436    }
1437
1438    // ── BlockUpdate (0x0B) ────────────────────────────────────────────────────
1439
1440    #[derive(Debug, Clone, PartialEq)]
1441    pub struct ClientboundBlockUpdate {
1442        pub location: u64,
1443        pub block_id: VarInt,
1444    }
1445
1446    impl PacketId for ClientboundBlockUpdate {
1447        fn packet_id(_ver: u32) -> u8 {
1448            0x0B
1449        }
1450    }
1451
1452    impl Encode for ClientboundBlockUpdate {
1453        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1454            dst.put_u64(self.location);
1455            self.block_id.encode(dst)
1456        }
1457    }
1458
1459    impl Decode for ClientboundBlockUpdate {
1460        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1461            need(src, 8)?;
1462            let location = src.get_u64();
1463            let block_id = VarInt::decode(src)?;
1464            Ok(Self { location, block_id })
1465        }
1466    }
1467
1468    // ── ChangeDifficulty (0x0D) ───────────────────────────────────────────────
1469
1470    #[derive(Debug, Clone, PartialEq)]
1471    pub struct ClientboundChangeDifficulty {
1472        pub difficulty: u8,
1473    }
1474
1475    impl PacketId for ClientboundChangeDifficulty {
1476        fn packet_id(_ver: u32) -> u8 {
1477            0x0D
1478        }
1479    }
1480
1481    impl Encode for ClientboundChangeDifficulty {
1482        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1483            dst.put_u8(self.difficulty);
1484            Ok(())
1485        }
1486    }
1487
1488    impl Decode for ClientboundChangeDifficulty {
1489        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1490            need(src, 1)?;
1491            Ok(Self {
1492                difficulty: src.get_u8(),
1493            })
1494        }
1495    }
1496
1497    // ── CommandSuggestions (0x0E) ─────────────────────────────────────────────
1498
1499    #[derive(Debug, Clone, PartialEq)]
1500    pub struct ClientboundCommandSuggestions {
1501        pub matches: Vec<String>,
1502    }
1503
1504    impl PacketId for ClientboundCommandSuggestions {
1505        fn packet_id(_ver: u32) -> u8 {
1506            0x0E
1507        }
1508    }
1509
1510    impl Encode for ClientboundCommandSuggestions {
1511        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1512            VarInt(self.matches.len() as i32).encode(dst)?;
1513            for m in &self.matches {
1514                encode_str(m, dst)?;
1515            }
1516            Ok(())
1517        }
1518    }
1519
1520    impl Decode for ClientboundCommandSuggestions {
1521        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1522            let count = VarInt::decode(src)?.0 as usize;
1523            let mut matches = Vec::with_capacity(count);
1524            for _ in 0..count {
1525                matches.push(decode_str(src, "ClientboundCommandSuggestions match")?);
1526            }
1527            Ok(Self { matches })
1528        }
1529    }
1530
1531    // ── SectionBlocksUpdate (0x10) ────────────────────────────────────────────
1532
1533    #[derive(Debug, Clone, PartialEq)]
1534    pub struct ClientboundSectionBlocksUpdate {
1535        pub chunk_x: i32,
1536        pub chunk_z: i32,
1537        pub records: Vec<(u8, u8, VarInt)>,
1538    }
1539
1540    impl PacketId for ClientboundSectionBlocksUpdate {
1541        fn packet_id(_ver: u32) -> u8 {
1542            0x10
1543        }
1544    }
1545
1546    impl Encode for ClientboundSectionBlocksUpdate {
1547        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1548            dst.put_i32(self.chunk_x);
1549            dst.put_i32(self.chunk_z);
1550            VarInt(self.records.len() as i32).encode(dst)?;
1551            for (h, y, id) in &self.records {
1552                dst.put_u8(*h);
1553                dst.put_u8(*y);
1554                id.encode(dst)?;
1555            }
1556            Ok(())
1557        }
1558    }
1559
1560    impl Decode for ClientboundSectionBlocksUpdate {
1561        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1562            need(src, 4 + 4)?;
1563            let chunk_x = src.get_i32();
1564            let chunk_z = src.get_i32();
1565            let count = VarInt::decode(src)?.0 as usize;
1566            let mut records = Vec::with_capacity(count);
1567            for _ in 0..count {
1568                need(src, 2)?;
1569                let h = src.get_u8();
1570                let y = src.get_u8();
1571                let id = VarInt::decode(src)?;
1572                records.push((h, y, id));
1573            }
1574            Ok(Self {
1575                chunk_x,
1576                chunk_z,
1577                records,
1578            })
1579        }
1580    }
1581
1582    // ── ContainerClose (0x12) ─────────────────────────────────────────────────
1583
1584    #[derive(Debug, Clone, PartialEq)]
1585    pub struct ClientboundContainerClose {
1586        pub window_id: u8,
1587    }
1588
1589    impl PacketId for ClientboundContainerClose {
1590        fn packet_id(_ver: u32) -> u8 {
1591            0x12
1592        }
1593    }
1594
1595    impl Encode for ClientboundContainerClose {
1596        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1597            dst.put_u8(self.window_id);
1598            Ok(())
1599        }
1600    }
1601
1602    impl Decode for ClientboundContainerClose {
1603        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1604            need(src, 1)?;
1605            Ok(Self {
1606                window_id: src.get_u8(),
1607            })
1608        }
1609    }
1610
1611    // ── OpenScreen (0x13) ─────────────────────────────────────────────────────
1612
1613    #[derive(Debug, Clone, PartialEq)]
1614    pub struct ClientboundOpenScreen {
1615        pub window_id: u8,
1616        pub window_type: String,
1617        pub window_title: String,
1618        pub slot_count: u8,
1619    }
1620
1621    impl PacketId for ClientboundOpenScreen {
1622        fn packet_id(_ver: u32) -> u8 {
1623            0x13
1624        }
1625    }
1626
1627    impl Encode for ClientboundOpenScreen {
1628        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1629            dst.put_u8(self.window_id);
1630            encode_str(&self.window_type, dst)?;
1631            encode_str(&self.window_title, dst)?;
1632            dst.put_u8(self.slot_count);
1633            Ok(())
1634        }
1635    }
1636
1637    impl Decode for ClientboundOpenScreen {
1638        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1639            need(src, 1)?;
1640            let window_id = src.get_u8();
1641            let window_type = decode_str(src, "ClientboundOpenScreen window_type")?;
1642            let window_title = decode_str(src, "ClientboundOpenScreen window_title")?;
1643            need(src, 1)?;
1644            let slot_count = src.get_u8();
1645            Ok(Self {
1646                window_id,
1647                window_type,
1648                window_title,
1649                slot_count,
1650            })
1651        }
1652    }
1653
1654    // ── ContainerSetContent (0x14) ────────────────────────────────────────────
1655
1656    #[derive(Debug, Clone, PartialEq)]
1657    pub struct ClientboundContainerSetContent {
1658        pub window_id: u8,
1659        pub slots: Vec<LegacySlot>,
1660        pub carried_item: LegacySlot,
1661    }
1662
1663    impl PacketId for ClientboundContainerSetContent {
1664        fn packet_id(_ver: u32) -> u8 {
1665            0x14
1666        }
1667    }
1668
1669    impl Encode for ClientboundContainerSetContent {
1670        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1671            dst.put_u8(self.window_id);
1672            VarInt(self.slots.len() as i32).encode(dst)?;
1673            for slot in &self.slots {
1674                slot.encode(dst)?;
1675            }
1676            self.carried_item.encode(dst)?;
1677            Ok(())
1678        }
1679    }
1680
1681    impl Decode for ClientboundContainerSetContent {
1682        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1683            need(src, 1)?;
1684            let window_id = src.get_u8();
1685            let count = VarInt::decode(src)?.0 as usize;
1686            let mut slots = Vec::with_capacity(count);
1687            for _ in 0..count {
1688                slots.push(LegacySlot::decode(src)?);
1689            }
1690            let carried_item = LegacySlot::decode(src)?;
1691            Ok(Self {
1692                window_id,
1693                slots,
1694                carried_item,
1695            })
1696        }
1697    }
1698
1699    // ── ContainerSetProperty (0x15) ───────────────────────────────────────────
1700
1701    #[derive(Debug, Clone, PartialEq)]
1702    pub struct ClientboundContainerSetProperty {
1703        pub window_id: u8,
1704        pub property: i16,
1705        pub value: i16,
1706    }
1707
1708    impl PacketId for ClientboundContainerSetProperty {
1709        fn packet_id(_ver: u32) -> u8 {
1710            0x15
1711        }
1712    }
1713
1714    impl Encode for ClientboundContainerSetProperty {
1715        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1716            dst.put_u8(self.window_id);
1717            dst.put_i16(self.property);
1718            dst.put_i16(self.value);
1719            Ok(())
1720        }
1721    }
1722
1723    impl Decode for ClientboundContainerSetProperty {
1724        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1725            need(src, 1 + 2 + 2)?;
1726            Ok(Self {
1727                window_id: src.get_u8(),
1728                property: src.get_i16(),
1729                value: src.get_i16(),
1730            })
1731        }
1732    }
1733
1734    // ── ContainerSetSlot (0x16) ───────────────────────────────────────────────
1735
1736    #[derive(Debug, Clone, PartialEq)]
1737    pub struct ClientboundContainerSetSlot {
1738        pub window_id: i8,
1739        pub slot: i16,
1740        pub slot_data: LegacySlot,
1741    }
1742
1743    impl PacketId for ClientboundContainerSetSlot {
1744        fn packet_id(_ver: u32) -> u8 {
1745            0x16
1746        }
1747    }
1748
1749    impl Encode for ClientboundContainerSetSlot {
1750        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1751            dst.put_i8(self.window_id);
1752            dst.put_i16(self.slot);
1753            self.slot_data.encode(dst)?;
1754            Ok(())
1755        }
1756    }
1757
1758    impl Decode for ClientboundContainerSetSlot {
1759        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1760            need(src, 1 + 2)?;
1761            let window_id = src.get_i8();
1762            let slot = src.get_i16();
1763            let slot_data = LegacySlot::decode(src)?;
1764            Ok(Self {
1765                window_id,
1766                slot,
1767                slot_data,
1768            })
1769        }
1770    }
1771
1772    // ── Cooldown (0x17) ───────────────────────────────────────────────────────
1773
1774    #[derive(Debug, Clone, PartialEq)]
1775    pub struct ClientboundCooldown {
1776        pub item_id: VarInt,
1777        pub cooldown_ticks: VarInt,
1778    }
1779
1780    impl PacketId for ClientboundCooldown {
1781        fn packet_id(_ver: u32) -> u8 {
1782            0x17
1783        }
1784    }
1785
1786    impl Encode for ClientboundCooldown {
1787        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1788            self.item_id.encode(dst)?;
1789            self.cooldown_ticks.encode(dst)
1790        }
1791    }
1792
1793    impl Decode for ClientboundCooldown {
1794        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1795            Ok(Self {
1796                item_id: VarInt::decode(src)?,
1797                cooldown_ticks: VarInt::decode(src)?,
1798            })
1799        }
1800    }
1801
1802    // ── EntityEvent (0x1B) ────────────────────────────────────────────────────
1803
1804    #[derive(Debug, Clone, PartialEq)]
1805    pub struct ClientboundEntityEvent {
1806        pub entity_id: i32,
1807        pub entity_status: u8,
1808    }
1809
1810    impl PacketId for ClientboundEntityEvent {
1811        fn packet_id(_ver: u32) -> u8 {
1812            0x1B
1813        }
1814    }
1815
1816    impl Encode for ClientboundEntityEvent {
1817        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1818            dst.put_i32(self.entity_id);
1819            dst.put_u8(self.entity_status);
1820            Ok(())
1821        }
1822    }
1823
1824    impl Decode for ClientboundEntityEvent {
1825        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1826            need(src, 4 + 1)?;
1827            Ok(Self {
1828                entity_id: src.get_i32(),
1829                entity_status: src.get_u8(),
1830            })
1831        }
1832    }
1833
1834    // ── Explosion (0x1C) ──────────────────────────────────────────────────────
1835
1836    #[derive(Debug, Clone, PartialEq)]
1837    pub struct ClientboundExplosion {
1838        pub x: f32,
1839        pub y: f32,
1840        pub z: f32,
1841        pub radius: f32,
1842        pub records: Vec<(i8, i8, i8)>,
1843        pub player_motion_x: f32,
1844        pub player_motion_y: f32,
1845        pub player_motion_z: f32,
1846    }
1847
1848    impl PacketId for ClientboundExplosion {
1849        fn packet_id(_ver: u32) -> u8 {
1850            0x1C
1851        }
1852    }
1853
1854    impl Encode for ClientboundExplosion {
1855        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1856            dst.put_f32(self.x);
1857            dst.put_f32(self.y);
1858            dst.put_f32(self.z);
1859            dst.put_f32(self.radius);
1860            dst.put_i32(self.records.len() as i32);
1861            for (rx, ry, rz) in &self.records {
1862                dst.put_i8(*rx);
1863                dst.put_i8(*ry);
1864                dst.put_i8(*rz);
1865            }
1866            dst.put_f32(self.player_motion_x);
1867            dst.put_f32(self.player_motion_y);
1868            dst.put_f32(self.player_motion_z);
1869            Ok(())
1870        }
1871    }
1872
1873    impl Decode for ClientboundExplosion {
1874        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1875            need(src, 4 + 4 + 4 + 4 + 4)?;
1876            let x = src.get_f32();
1877            let y = src.get_f32();
1878            let z = src.get_f32();
1879            let radius = src.get_f32();
1880            let count = src.get_i32() as usize;
1881            need(src, count * 3)?;
1882            let mut records = Vec::with_capacity(count);
1883            for _ in 0..count {
1884                records.push((src.get_i8(), src.get_i8(), src.get_i8()));
1885            }
1886            need(src, 4 + 4 + 4)?;
1887            Ok(Self {
1888                x,
1889                y,
1890                z,
1891                radius,
1892                records,
1893                player_motion_x: src.get_f32(),
1894                player_motion_y: src.get_f32(),
1895                player_motion_z: src.get_f32(),
1896            })
1897        }
1898    }
1899
1900    // ── ForgetLevelChunk (0x1D) ───────────────────────────────────────────────
1901
1902    #[derive(Debug, Clone, PartialEq)]
1903    pub struct ClientboundForgetLevelChunk {
1904        pub chunk_x: i32,
1905        pub chunk_z: i32,
1906    }
1907
1908    impl PacketId for ClientboundForgetLevelChunk {
1909        fn packet_id(_ver: u32) -> u8 {
1910            0x1D
1911        }
1912    }
1913
1914    impl Encode for ClientboundForgetLevelChunk {
1915        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1916            dst.put_i32(self.chunk_x);
1917            dst.put_i32(self.chunk_z);
1918            Ok(())
1919        }
1920    }
1921
1922    impl Decode for ClientboundForgetLevelChunk {
1923        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1924            need(src, 4 + 4)?;
1925            Ok(Self {
1926                chunk_x: src.get_i32(),
1927                chunk_z: src.get_i32(),
1928            })
1929        }
1930    }
1931
1932    // ── GameEvent (0x1E) ──────────────────────────────────────────────────────
1933
1934    #[derive(Debug, Clone, PartialEq)]
1935    pub struct ClientboundGameEvent {
1936        pub reason: u8,
1937        pub value: f32,
1938    }
1939
1940    impl PacketId for ClientboundGameEvent {
1941        fn packet_id(_ver: u32) -> u8 {
1942            0x1E
1943        }
1944    }
1945
1946    impl Encode for ClientboundGameEvent {
1947        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1948            dst.put_u8(self.reason);
1949            dst.put_f32(self.value);
1950            Ok(())
1951        }
1952    }
1953
1954    impl Decode for ClientboundGameEvent {
1955        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1956            need(src, 1 + 4)?;
1957            Ok(Self {
1958                reason: src.get_u8(),
1959                value: src.get_f32(),
1960            })
1961        }
1962    }
1963
1964    // ── LevelChunkWithLight (0x20) - SPacketChunkData for 1.12.2 ──────────────
1965
1966    #[derive(Debug, Clone, PartialEq)]
1967    pub struct ClientboundLevelChunkWithLight {
1968        pub chunk_x: i32,
1969        pub chunk_z: i32,
1970        pub full_chunk: bool,
1971        pub primary_bit_mask: VarInt,
1972        pub data: Vec<u8>,
1973        pub tile_entities: Vec<Nbt>, // NBT compound tags for tile entities
1974    }
1975
1976    // Type alias for 1.12.2 compatibility
1977    pub type SPacketChunkData = ClientboundLevelChunkWithLight;
1978
1979    impl PacketId for ClientboundLevelChunkWithLight {
1980        fn packet_id(_ver: u32) -> u8 {
1981            0x20
1982        }
1983    }
1984
1985    impl Encode for ClientboundLevelChunkWithLight {
1986        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1987            dst.put_i32(self.chunk_x);
1988            dst.put_i32(self.chunk_z);
1989            dst.put_u8(self.full_chunk as u8);
1990            self.primary_bit_mask.encode(dst)?;
1991            VarInt(self.data.len() as i32).encode(dst)?;
1992            dst.put_slice(&self.data);
1993            // Tile entity count (varint) followed by NBT compound tags
1994            VarInt(self.tile_entities.len() as i32).encode(dst)?;
1995            for tile_entity in &self.tile_entities {
1996                tile_entity.encode(dst)?;
1997            }
1998            Ok(())
1999        }
2000    }
2001
2002    impl Decode for ClientboundLevelChunkWithLight {
2003        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2004            need(src, 4 + 4 + 1)?;
2005            let chunk_x = src.get_i32();
2006            let chunk_z = src.get_i32();
2007            let full_chunk = src.get_u8() != 0;
2008            let primary_bit_mask = VarInt::decode(src)?;
2009            let len = VarInt::decode(src)?.0 as usize;
2010            need(src, len)?;
2011            let mut data = vec![0u8; len];
2012            src.copy_to_slice(&mut data);
2013            // Read tile entity count (varint) followed by NBT compound tags
2014            let tile_count = VarInt::decode(src)?.0 as usize;
2015            let mut tile_entities = Vec::with_capacity(tile_count);
2016            for _ in 0..tile_count {
2017                tile_entities.push(Nbt::decode(src)?);
2018            }
2019            Ok(Self {
2020                chunk_x,
2021                chunk_z,
2022                full_chunk,
2023                primary_bit_mask,
2024                data,
2025                tile_entities,
2026            })
2027        }
2028    }
2029
2030    // ── LevelEvent (0x21) ─────────────────────────────────────────────────────
2031
2032    #[derive(Debug, Clone, PartialEq)]
2033    pub struct ClientboundLevelEvent {
2034        pub effect_id: i32,
2035        pub location: u64,
2036        pub data: i32,
2037        pub disable_relative_volume: bool,
2038    }
2039
2040    impl PacketId for ClientboundLevelEvent {
2041        fn packet_id(_ver: u32) -> u8 {
2042            0x21
2043        }
2044    }
2045
2046    impl Encode for ClientboundLevelEvent {
2047        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2048            dst.put_i32(self.effect_id);
2049            dst.put_u64(self.location);
2050            dst.put_i32(self.data);
2051            dst.put_u8(self.disable_relative_volume as u8);
2052            Ok(())
2053        }
2054    }
2055
2056    impl Decode for ClientboundLevelEvent {
2057        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2058            need(src, 4 + 8 + 4 + 1)?;
2059            Ok(Self {
2060                effect_id: src.get_i32(),
2061                location: src.get_u64(),
2062                data: src.get_i32(),
2063                disable_relative_volume: src.get_u8() != 0,
2064            })
2065        }
2066    }
2067
2068    // ── LevelParticles (0x22) ─────────────────────────────────────────────────
2069
2070    #[derive(Debug, Clone, PartialEq)]
2071    pub struct ClientboundLevelParticles {
2072        pub particle_id: i32,
2073        pub long_distance: bool,
2074        pub x: f32,
2075        pub y: f32,
2076        pub z: f32,
2077        pub offset_x: f32,
2078        pub offset_y: f32,
2079        pub offset_z: f32,
2080        pub particle_data: f32,
2081        pub particle_count: i32,
2082        pub data: Vec<VarInt>,
2083    }
2084
2085    impl PacketId for ClientboundLevelParticles {
2086        fn packet_id(_ver: u32) -> u8 {
2087            0x22
2088        }
2089    }
2090
2091    impl Encode for ClientboundLevelParticles {
2092        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2093            // Write the base static payload
2094            dst.put_i32(self.particle_id);
2095            dst.put_u8(self.long_distance as u8);
2096            dst.put_f32(self.x);
2097            dst.put_f32(self.y);
2098            dst.put_f32(self.z);
2099            dst.put_f32(self.offset_x);
2100            dst.put_f32(self.offset_y);
2101            dst.put_f32(self.offset_z);
2102            dst.put_f32(self.particle_data);
2103            dst.put_i32(self.particle_count);
2104
2105            // ENFORCE EXACT SPECIFICATION LENGTHS FOR 1.12.2 WIRE TRAFFIC
2106            match self.particle_id {
2107                36 => {
2108                    // iconcrack requires EXACTLY 2 VarInts (Item ID, Metadata)
2109                    for d in self.data.iter().take(2) {
2110                        d.encode(dst)?;
2111                    }
2112                },
2113                37 | 38 => {
2114                    // blockcrack / blockdust requires EXACTLY 1 VarInt (Combined Block State)
2115                    if let Some(d) = self.data.first() {
2116                        d.encode(dst)?;
2117                    }
2118                },
2119                _ => {
2120                    // ANY OTHER PARTICLE MUST NOT WRITE TRAILING DATA
2121                    // If self.data contains residual values from a modern backend server, ignore them!
2122                },
2123            }
2124            Ok(())
2125        }
2126    }
2127
2128    impl Decode for ClientboundLevelParticles {
2129        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2130            // 4 + 1 + 3*4 + 3*4 + 4 + 4 = 37
2131            need(src, 37)?;
2132            let particle_id = src.get_i32();
2133            let long_distance = src.get_u8() != 0;
2134            let x = src.get_f32();
2135            let y = src.get_f32();
2136            let z = src.get_f32();
2137            let offset_x = src.get_f32();
2138            let offset_y = src.get_f32();
2139            let offset_z = src.get_f32();
2140            let particle_data = src.get_f32();
2141            let particle_count = src.get_i32();
2142            let expected = match particle_id {
2143                36 => 2,
2144                37 | 38 => 1,
2145                _ => 0,
2146            };
2147            let mut data = Vec::with_capacity(expected);
2148            for _ in 0..expected {
2149                data.push(VarInt::decode(src)?);
2150            }
2151            Ok(Self {
2152                particle_id,
2153                long_distance,
2154                x,
2155                y,
2156                z,
2157                offset_x,
2158                offset_y,
2159                offset_z,
2160                particle_data,
2161                particle_count,
2162                data,
2163            })
2164        }
2165    }
2166
2167    // ── MapItemData (0x24) ────────────────────────────────────────────────────
2168
2169    #[derive(Debug, Clone, PartialEq)]
2170    pub struct ClientboundMapItemData {
2171        pub map_id: VarInt,
2172        pub scale: u8,
2173        pub tracking_position: bool,
2174        pub icons: Vec<(u8, i8, i8)>,
2175        pub columns: u8,
2176        pub data: Vec<u8>,
2177    }
2178
2179    impl PacketId for ClientboundMapItemData {
2180        fn packet_id(_ver: u32) -> u8 {
2181            0x24
2182        }
2183    }
2184
2185    impl Encode for ClientboundMapItemData {
2186        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2187            self.map_id.encode(dst)?;
2188            dst.put_u8(self.scale);
2189            dst.put_u8(self.tracking_position as u8);
2190            VarInt(self.icons.len() as i32).encode(dst)?;
2191            for (dir_type, x, z) in &self.icons {
2192                dst.put_u8(*dir_type);
2193                dst.put_i8(*x);
2194                dst.put_i8(*z);
2195            }
2196            dst.put_u8(self.columns);
2197            if self.columns > 0 {
2198                dst.put_u8(self.data.len() as u8); // rows
2199                dst.put_i8(0);
2200                dst.put_i8(0); // x, z offset
2201                VarInt(self.data.len() as i32).encode(dst)?;
2202                dst.put_slice(&self.data);
2203            }
2204            Ok(())
2205        }
2206    }
2207
2208    impl Decode for ClientboundMapItemData {
2209        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2210            let map_id = VarInt::decode(src)?;
2211            need(src, 1 + 1)?;
2212            let scale = src.get_u8();
2213            let tracking_position = src.get_u8() != 0;
2214            let icon_count = VarInt::decode(src)?.0 as usize;
2215            let mut icons = Vec::with_capacity(icon_count);
2216            for _ in 0..icon_count {
2217                need(src, 3)?;
2218                icons.push((src.get_u8(), src.get_i8(), src.get_i8()));
2219            }
2220            need(src, 1)?;
2221            let columns = src.get_u8();
2222            let data = if columns > 0 {
2223                need(src, 1 + 1 + 1)?;
2224                let _rows = src.get_u8();
2225                let _x = src.get_i8();
2226                let _z = src.get_i8();
2227                let len = VarInt::decode(src)?.0 as usize;
2228                need(src, len)?;
2229                let mut d = vec![0u8; len];
2230                src.copy_to_slice(&mut d);
2231                d
2232            } else {
2233                vec![]
2234            };
2235            Ok(Self {
2236                map_id,
2237                scale,
2238                tracking_position,
2239                icons,
2240                columns,
2241                data,
2242            })
2243        }
2244    }
2245
2246    // ── MoveEntityPos (0x25) ──────────────────────────────────────────────────
2247
2248    #[derive(Debug, Clone, PartialEq)]
2249    pub struct ClientboundMoveEntityPos {
2250        pub entity_id: VarInt,
2251        pub delta_x: i16,
2252        pub delta_y: i16,
2253        pub delta_z: i16,
2254        pub on_ground: bool,
2255    }
2256
2257    impl PacketId for ClientboundMoveEntityPos {
2258        fn packet_id(_ver: u32) -> u8 {
2259            0x25
2260        }
2261    }
2262
2263    impl Encode for ClientboundMoveEntityPos {
2264        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2265            self.entity_id.encode(dst)?;
2266            dst.put_i16(self.delta_x);
2267            dst.put_i16(self.delta_y);
2268            dst.put_i16(self.delta_z);
2269            dst.put_u8(self.on_ground as u8);
2270            Ok(())
2271        }
2272    }
2273
2274    impl Decode for ClientboundMoveEntityPos {
2275        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2276            let entity_id = VarInt::decode(src)?;
2277            need(src, 2 + 2 + 2 + 1)?;
2278            Ok(Self {
2279                entity_id,
2280                delta_x: src.get_i16(),
2281                delta_y: src.get_i16(),
2282                delta_z: src.get_i16(),
2283                on_ground: src.get_u8() != 0,
2284            })
2285        }
2286    }
2287
2288    // ── MoveEntityPosRot (0x26) ───────────────────────────────────────────────
2289
2290    #[derive(Debug, Clone, PartialEq)]
2291    pub struct ClientboundMoveEntityPosRot {
2292        pub entity_id: VarInt,
2293        pub delta_x: i16,
2294        pub delta_y: i16,
2295        pub delta_z: i16,
2296        pub yaw: u8,
2297        pub pitch: u8,
2298        pub on_ground: bool,
2299    }
2300
2301    impl PacketId for ClientboundMoveEntityPosRot {
2302        fn packet_id(_ver: u32) -> u8 {
2303            0x26
2304        }
2305    }
2306
2307    impl Encode for ClientboundMoveEntityPosRot {
2308        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2309            self.entity_id.encode(dst)?;
2310            dst.put_i16(self.delta_x);
2311            dst.put_i16(self.delta_y);
2312            dst.put_i16(self.delta_z);
2313            dst.put_u8(self.yaw);
2314            dst.put_u8(self.pitch);
2315            dst.put_u8(self.on_ground as u8);
2316            Ok(())
2317        }
2318    }
2319
2320    impl Decode for ClientboundMoveEntityPosRot {
2321        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2322            let entity_id = VarInt::decode(src)?;
2323            need(src, 2 + 2 + 2 + 1 + 1 + 1)?;
2324            Ok(Self {
2325                entity_id,
2326                delta_x: src.get_i16(),
2327                delta_y: src.get_i16(),
2328                delta_z: src.get_i16(),
2329                yaw: src.get_u8(),
2330                pitch: src.get_u8(),
2331                on_ground: src.get_u8() != 0,
2332            })
2333        }
2334    }
2335
2336    // ── MoveEntityRot (0x27) ──────────────────────────────────────────────────
2337
2338    #[derive(Debug, Clone, PartialEq)]
2339    pub struct ClientboundMoveEntityRot {
2340        pub entity_id: VarInt,
2341        pub yaw: u8,
2342        pub pitch: u8,
2343        pub on_ground: bool,
2344    }
2345
2346    impl PacketId for ClientboundMoveEntityRot {
2347        fn packet_id(_ver: u32) -> u8 {
2348            0x27
2349        }
2350    }
2351
2352    impl Encode for ClientboundMoveEntityRot {
2353        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2354            self.entity_id.encode(dst)?;
2355            dst.put_u8(self.yaw);
2356            dst.put_u8(self.pitch);
2357            dst.put_u8(self.on_ground as u8);
2358            Ok(())
2359        }
2360    }
2361
2362    impl Decode for ClientboundMoveEntityRot {
2363        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2364            let entity_id = VarInt::decode(src)?;
2365            need(src, 1 + 1 + 1)?;
2366            Ok(Self {
2367                entity_id,
2368                yaw: src.get_u8(),
2369                pitch: src.get_u8(),
2370                on_ground: src.get_u8() != 0,
2371            })
2372        }
2373    }
2374
2375    // ── MoveVehicle (0x29) ────────────────────────────────────────────────────
2376
2377    #[derive(Debug, Clone, PartialEq)]
2378    pub struct ClientboundMoveVehicle {
2379        pub x: f64,
2380        pub y: f64,
2381        pub z: f64,
2382        pub yaw: f32,
2383        pub pitch: f32,
2384    }
2385
2386    impl PacketId for ClientboundMoveVehicle {
2387        fn packet_id(_ver: u32) -> u8 {
2388            0x29
2389        }
2390    }
2391
2392    impl Encode for ClientboundMoveVehicle {
2393        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2394            dst.put_f64(self.x);
2395            dst.put_f64(self.y);
2396            dst.put_f64(self.z);
2397            dst.put_f32(self.yaw);
2398            dst.put_f32(self.pitch);
2399            Ok(())
2400        }
2401    }
2402
2403    impl Decode for ClientboundMoveVehicle {
2404        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2405            need(src, 8 + 8 + 8 + 4 + 4)?;
2406            Ok(Self {
2407                x: src.get_f64(),
2408                y: src.get_f64(),
2409                z: src.get_f64(),
2410                yaw: src.get_f32(),
2411                pitch: src.get_f32(),
2412            })
2413        }
2414    }
2415
2416    // ── OpenSignEditor (0x2A) ─────────────────────────────────────────────────
2417
2418    #[derive(Debug, Clone, PartialEq)]
2419    pub struct ClientboundOpenSignEditor {
2420        pub location: u64,
2421    }
2422
2423    impl PacketId for ClientboundOpenSignEditor {
2424        fn packet_id(_ver: u32) -> u8 {
2425            0x2A
2426        }
2427    }
2428
2429    impl Encode for ClientboundOpenSignEditor {
2430        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2431            dst.put_u64(self.location);
2432            Ok(())
2433        }
2434    }
2435
2436    impl Decode for ClientboundOpenSignEditor {
2437        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2438            need(src, 8)?;
2439            Ok(Self {
2440                location: src.get_u64(),
2441            })
2442        }
2443    }
2444
2445    // ── PlayerInfoUpdate (0x2E) — opaque raw ─────────────────────────────────
2446
2447    #[derive(Debug, Clone, PartialEq)]
2448    pub struct ClientboundPlayerInfoUpdate {
2449        pub raw: Vec<u8>,
2450    }
2451
2452    impl PacketId for ClientboundPlayerInfoUpdate {
2453        fn packet_id(_ver: u32) -> u8 {
2454            0x2E
2455        }
2456    }
2457
2458    impl Encode for ClientboundPlayerInfoUpdate {
2459        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2460            dst.put_slice(&self.raw);
2461            Ok(())
2462        }
2463    }
2464
2465    impl Decode for ClientboundPlayerInfoUpdate {
2466        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2467            let len = src.remaining();
2468            let mut raw = vec![0u8; len];
2469            src.copy_to_slice(&mut raw);
2470            Ok(Self { raw })
2471        }
2472    }
2473
2474    // ── Recipes (0x31) — opaque raw ───────────────────────────────────────────
2475
2476    #[derive(Debug, Clone, PartialEq)]
2477    pub struct ClientboundRecipes {
2478        pub raw: Vec<u8>,
2479    }
2480
2481    impl PacketId for ClientboundRecipes {
2482        fn packet_id(_ver: u32) -> u8 {
2483            0x31
2484        }
2485    }
2486
2487    impl Encode for ClientboundRecipes {
2488        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2489            dst.put_slice(&self.raw);
2490            Ok(())
2491        }
2492    }
2493
2494    impl Decode for ClientboundRecipes {
2495        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2496            let len = src.remaining();
2497            let mut raw = vec![0u8; len];
2498            src.copy_to_slice(&mut raw);
2499            Ok(Self { raw })
2500        }
2501    }
2502
2503    // ── RemoveEntities (0x32) ─────────────────────────────────────────────────
2504
2505    #[derive(Debug, Clone, PartialEq)]
2506    pub struct ClientboundRemoveEntities {
2507        pub entity_ids: Vec<VarInt>,
2508    }
2509
2510    impl PacketId for ClientboundRemoveEntities {
2511        fn packet_id(_ver: u32) -> u8 {
2512            0x32
2513        }
2514    }
2515
2516    impl Encode for ClientboundRemoveEntities {
2517        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2518            VarInt(self.entity_ids.len() as i32).encode(dst)?;
2519            for id in &self.entity_ids {
2520                id.encode(dst)?;
2521            }
2522            Ok(())
2523        }
2524    }
2525
2526    impl Decode for ClientboundRemoveEntities {
2527        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2528            let count = VarInt::decode(src)?.0 as usize;
2529            let mut entity_ids = Vec::with_capacity(count);
2530            for _ in 0..count {
2531                entity_ids.push(VarInt::decode(src)?);
2532            }
2533            Ok(Self { entity_ids })
2534        }
2535    }
2536
2537    // ── RemoveEntityEffect (0x33) ─────────────────────────────────────────────
2538
2539    #[derive(Debug, Clone, PartialEq)]
2540    pub struct ClientboundRemoveEntityEffect {
2541        pub entity_id: VarInt,
2542        pub effect_id: u8,
2543    }
2544
2545    impl PacketId for ClientboundRemoveEntityEffect {
2546        fn packet_id(_ver: u32) -> u8 {
2547            0x33
2548        }
2549    }
2550
2551    impl Encode for ClientboundRemoveEntityEffect {
2552        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2553            self.entity_id.encode(dst)?;
2554            dst.put_u8(self.effect_id);
2555            Ok(())
2556        }
2557    }
2558
2559    impl Decode for ClientboundRemoveEntityEffect {
2560        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2561            let entity_id = VarInt::decode(src)?;
2562            need(src, 1)?;
2563            Ok(Self {
2564                entity_id,
2565                effect_id: src.get_u8(),
2566            })
2567        }
2568    }
2569
2570    // ── ResourcePackPush (0x34) ───────────────────────────────────────────────
2571
2572    #[derive(Debug, Clone, PartialEq)]
2573    pub struct ClientboundResourcePackPush {
2574        pub url: String,
2575        pub hash: String,
2576    }
2577
2578    impl PacketId for ClientboundResourcePackPush {
2579        fn packet_id(_ver: u32) -> u8 {
2580            0x34
2581        }
2582    }
2583
2584    impl Encode for ClientboundResourcePackPush {
2585        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2586            encode_str(&self.url, dst)?;
2587            encode_str(&self.hash, dst)
2588        }
2589    }
2590
2591    impl Decode for ClientboundResourcePackPush {
2592        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2593            let url = decode_str(src, "ClientboundResourcePackPush url")?;
2594            let hash = decode_str(src, "ClientboundResourcePackPush hash")?;
2595            Ok(Self { url, hash })
2596        }
2597    }
2598
2599    // ── RotateHead (0x36) ─────────────────────────────────────────────────────
2600
2601    #[derive(Debug, Clone, PartialEq)]
2602    pub struct ClientboundRotateHead {
2603        pub entity_id: VarInt,
2604        pub head_yaw: u8,
2605    }
2606
2607    impl PacketId for ClientboundRotateHead {
2608        fn packet_id(_ver: u32) -> u8 {
2609            0x36
2610        }
2611    }
2612
2613    impl Encode for ClientboundRotateHead {
2614        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2615            self.entity_id.encode(dst)?;
2616            dst.put_u8(self.head_yaw);
2617            Ok(())
2618        }
2619    }
2620
2621    impl Decode for ClientboundRotateHead {
2622        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2623            let entity_id = VarInt::decode(src)?;
2624            need(src, 1)?;
2625            Ok(Self {
2626                entity_id,
2627                head_yaw: src.get_u8(),
2628            })
2629        }
2630    }
2631
2632    // ── SelectAdvancementsTab (0x37) ──────────────────────────────────────────
2633
2634    #[derive(Debug, Clone, PartialEq)]
2635    pub struct ClientboundSelectAdvancementsTab {
2636        pub has_id: bool,
2637        pub tab_id: Option<String>,
2638    }
2639
2640    impl PacketId for ClientboundSelectAdvancementsTab {
2641        fn packet_id(_ver: u32) -> u8 {
2642            0x37
2643        }
2644    }
2645
2646    impl Encode for ClientboundSelectAdvancementsTab {
2647        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2648            dst.put_u8(self.has_id as u8);
2649            if let Some(id) = &self.tab_id {
2650                encode_str(id, dst)?;
2651            }
2652            Ok(())
2653        }
2654    }
2655
2656    impl Decode for ClientboundSelectAdvancementsTab {
2657        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2658            need(src, 1)?;
2659            let has_id = src.get_u8() != 0;
2660            let tab_id = if has_id {
2661                Some(decode_str(src, "ClientboundSelectAdvancementsTab tab_id")?)
2662            } else {
2663                None
2664            };
2665            Ok(Self { has_id, tab_id })
2666        }
2667    }
2668
2669    // ── SetCamera (0x39) ──────────────────────────────────────────────────────
2670
2671    #[derive(Debug, Clone, PartialEq)]
2672    pub struct ClientboundSetCamera {
2673        pub camera_id: VarInt,
2674    }
2675
2676    impl PacketId for ClientboundSetCamera {
2677        fn packet_id(_ver: u32) -> u8 {
2678            0x39
2679        }
2680    }
2681
2682    impl Encode for ClientboundSetCamera {
2683        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2684            self.camera_id.encode(dst)
2685        }
2686    }
2687
2688    impl Decode for ClientboundSetCamera {
2689        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2690            Ok(Self {
2691                camera_id: VarInt::decode(src)?,
2692            })
2693        }
2694    }
2695
2696    // ── SetHeldItem (0x3A) ────────────────────────────────────────────────────
2697
2698    #[derive(Debug, Clone, PartialEq)]
2699    pub struct ClientboundSetHeldItem {
2700        pub slot: u8,
2701    }
2702
2703    impl PacketId for ClientboundSetHeldItem {
2704        fn packet_id(_ver: u32) -> u8 {
2705            0x3A
2706        }
2707    }
2708
2709    impl Encode for ClientboundSetHeldItem {
2710        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2711            dst.put_u8(self.slot);
2712            Ok(())
2713        }
2714    }
2715
2716    impl Decode for ClientboundSetHeldItem {
2717        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2718            need(src, 1)?;
2719            Ok(Self { slot: src.get_u8() })
2720        }
2721    }
2722
2723    // ── SetEntityLink (0x3D) ──────────────────────────────────────────────────
2724
2725    #[derive(Debug, Clone, PartialEq)]
2726    pub struct ClientboundSetEntityLink {
2727        pub attached_entity_id: i32,
2728        pub holding_entity_id: i32,
2729    }
2730
2731    impl PacketId for ClientboundSetEntityLink {
2732        fn packet_id(_ver: u32) -> u8 {
2733            0x3D
2734        }
2735    }
2736
2737    impl Encode for ClientboundSetEntityLink {
2738        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2739            dst.put_i32(self.attached_entity_id);
2740            dst.put_i32(self.holding_entity_id);
2741            Ok(())
2742        }
2743    }
2744
2745    impl Decode for ClientboundSetEntityLink {
2746        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2747            need(src, 4 + 4)?;
2748            Ok(Self {
2749                attached_entity_id: src.get_i32(),
2750                holding_entity_id: src.get_i32(),
2751            })
2752        }
2753    }
2754
2755    // ── SetEntityMotion (0x3E) ────────────────────────────────────────────────
2756
2757    #[derive(Debug, Clone, PartialEq)]
2758    pub struct ClientboundSetEntityMotion {
2759        pub entity_id: VarInt,
2760        pub velocity_x: i16,
2761        pub velocity_y: i16,
2762        pub velocity_z: i16,
2763    }
2764
2765    impl PacketId for ClientboundSetEntityMotion {
2766        fn packet_id(_ver: u32) -> u8 {
2767            0x3E
2768        }
2769    }
2770
2771    impl Encode for ClientboundSetEntityMotion {
2772        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2773            self.entity_id.encode(dst)?;
2774            dst.put_i16(self.velocity_x);
2775            dst.put_i16(self.velocity_y);
2776            dst.put_i16(self.velocity_z);
2777            Ok(())
2778        }
2779    }
2780
2781    impl Decode for ClientboundSetEntityMotion {
2782        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2783            let entity_id = VarInt::decode(src)?;
2784            need(src, 2 + 2 + 2)?;
2785            Ok(Self {
2786                entity_id,
2787                velocity_x: src.get_i16(),
2788                velocity_y: src.get_i16(),
2789                velocity_z: src.get_i16(),
2790            })
2791        }
2792    }
2793
2794    // ── SetEquipment (0x3F) ───────────────────────────────────────────────────
2795
2796    #[derive(Debug, Clone, PartialEq)]
2797    pub struct ClientboundSetEquipment {
2798        pub entity_id: VarInt,
2799        pub slot: VarInt,
2800        pub item: Vec<u8>,
2801    }
2802
2803    impl PacketId for ClientboundSetEquipment {
2804        fn packet_id(_ver: u32) -> u8 {
2805            0x3F
2806        }
2807    }
2808
2809    impl Encode for ClientboundSetEquipment {
2810        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2811            self.entity_id.encode(dst)?;
2812            self.slot.encode(dst)?;
2813            dst.put_slice(&self.item);
2814            Ok(())
2815        }
2816    }
2817
2818    impl Decode for ClientboundSetEquipment {
2819        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2820            let entity_id = VarInt::decode(src)?;
2821            let slot = VarInt::decode(src)?;
2822            let len = src.remaining();
2823            let mut item = vec![0u8; len];
2824            src.copy_to_slice(&mut item);
2825            Ok(Self {
2826                entity_id,
2827                slot,
2828                item,
2829            })
2830        }
2831    }
2832
2833    // ── SetExperience (0x40) ──────────────────────────────────────────────────
2834
2835    #[derive(Debug, Clone, PartialEq)]
2836    pub struct ClientboundSetExperience {
2837        pub experience_bar: f32,
2838        pub level: VarInt,
2839        pub total_experience: VarInt,
2840    }
2841
2842    impl PacketId for ClientboundSetExperience {
2843        fn packet_id(_ver: u32) -> u8 {
2844            0x40
2845        }
2846    }
2847
2848    impl Encode for ClientboundSetExperience {
2849        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2850            dst.put_f32(self.experience_bar);
2851            self.level.encode(dst)?;
2852            self.total_experience.encode(dst)
2853        }
2854    }
2855
2856    impl Decode for ClientboundSetExperience {
2857        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2858            need(src, 4)?;
2859            let experience_bar = src.get_f32();
2860            let level = VarInt::decode(src)?;
2861            let total_experience = VarInt::decode(src)?;
2862            Ok(Self {
2863                experience_bar,
2864                level,
2865                total_experience,
2866            })
2867        }
2868    }
2869
2870    // ── SetHealth (0x41) ──────────────────────────────────────────────────────
2871
2872    #[derive(Debug, Clone, PartialEq)]
2873    pub struct ClientboundSetHealth {
2874        pub health: f32,
2875        pub food: VarInt,
2876        pub food_saturation: f32,
2877    }
2878
2879    impl PacketId for ClientboundSetHealth {
2880        fn packet_id(_ver: u32) -> u8 {
2881            0x41
2882        }
2883    }
2884
2885    impl Encode for ClientboundSetHealth {
2886        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2887            dst.put_f32(self.health);
2888            self.food.encode(dst)?;
2889            dst.put_f32(self.food_saturation);
2890            Ok(())
2891        }
2892    }
2893
2894    impl Decode for ClientboundSetHealth {
2895        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2896            need(src, 4)?;
2897            let health = src.get_f32();
2898            let food = VarInt::decode(src)?;
2899            need(src, 4)?;
2900            let food_saturation = src.get_f32();
2901            Ok(Self {
2902                health,
2903                food,
2904                food_saturation,
2905            })
2906        }
2907    }
2908
2909    // ── SetScoreboardObjective (0x42) ─────────────────────────────────────────
2910
2911    #[derive(Debug, Clone, PartialEq)]
2912    pub struct ClientboundSetScoreboardObjective {
2913        pub objective_name: String,
2914        pub mode: u8,
2915        pub objective_value: Option<String>,
2916        pub kind: Option<String>,
2917    }
2918
2919    impl PacketId for ClientboundSetScoreboardObjective {
2920        fn packet_id(_ver: u32) -> u8 {
2921            0x42
2922        }
2923    }
2924
2925    impl Encode for ClientboundSetScoreboardObjective {
2926        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2927            encode_str(&self.objective_name, dst)?;
2928            dst.put_u8(self.mode);
2929            if self.mode == 0 || self.mode == 2 {
2930                if let Some(v) = &self.objective_value {
2931                    encode_str(v, dst)?;
2932                }
2933                if let Some(k) = &self.kind {
2934                    encode_str(k, dst)?;
2935                }
2936            }
2937            Ok(())
2938        }
2939    }
2940
2941    impl Decode for ClientboundSetScoreboardObjective {
2942        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2943            let objective_name = decode_str(src, "ClientboundSetScoreboardObjective name")?;
2944            need(src, 1)?;
2945            let mode = src.get_u8();
2946            let (objective_value, kind) = if mode == 0 || mode == 2 {
2947                let v = decode_str(src, "ClientboundSetScoreboardObjective value")?;
2948                let k = decode_str(src, "ClientboundSetScoreboardObjective kind")?;
2949                (Some(v), Some(k))
2950            } else {
2951                (None, None)
2952            };
2953            Ok(Self {
2954                objective_name,
2955                mode,
2956                objective_value,
2957                kind,
2958            })
2959        }
2960    }
2961
2962    // ── SetScoreboardScore (0x45) ─────────────────────────────────────────────
2963
2964    #[derive(Debug, Clone, PartialEq)]
2965    pub struct ClientboundSetScoreboardScore {
2966        pub entity_name: String,
2967        pub action: u8,
2968        pub objective_name: String,
2969        pub value: Option<VarInt>,
2970    }
2971
2972    impl PacketId for ClientboundSetScoreboardScore {
2973        fn packet_id(_ver: u32) -> u8 {
2974            0x45
2975        }
2976    }
2977
2978    impl Encode for ClientboundSetScoreboardScore {
2979        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
2980            encode_str(&self.entity_name, dst)?;
2981            dst.put_u8(self.action);
2982            encode_str(&self.objective_name, dst)?;
2983            if self.action != 1 {
2984                if let Some(v) = &self.value {
2985                    v.encode(dst)?;
2986                }
2987            }
2988            Ok(())
2989        }
2990    }
2991
2992    impl Decode for ClientboundSetScoreboardScore {
2993        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
2994            let entity_name = decode_str(src, "ClientboundSetScoreboardScore entity_name")?;
2995            need(src, 1)?;
2996            let action = src.get_u8();
2997            let objective_name = decode_str(src, "ClientboundSetScoreboardScore objective_name")?;
2998            let value = if action != 1 {
2999                Some(VarInt::decode(src)?)
3000            } else {
3001                None
3002            };
3003            Ok(Self {
3004                entity_name,
3005                action,
3006                objective_name,
3007                value,
3008            })
3009        }
3010    }
3011
3012    // ── SetTime (0x47) ────────────────────────────────────────────────────────
3013
3014    #[derive(Debug, Clone, PartialEq)]
3015    pub struct ClientboundSetTime {
3016        pub world_age: i64,
3017        pub time_of_day: i64,
3018    }
3019
3020    impl PacketId for ClientboundSetTime {
3021        fn packet_id(_ver: u32) -> u8 {
3022            0x47
3023        }
3024    }
3025
3026    impl Encode for ClientboundSetTime {
3027        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3028            dst.put_i64(self.world_age);
3029            dst.put_i64(self.time_of_day);
3030            Ok(())
3031        }
3032    }
3033
3034    impl Decode for ClientboundSetTime {
3035        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3036            need(src, 8 + 8)?;
3037            Ok(Self {
3038                world_age: src.get_i64(),
3039                time_of_day: src.get_i64(),
3040            })
3041        }
3042    }
3043
3044    // ── Sound (0x48) ──────────────────────────────────────────────────────────
3045
3046    #[derive(Debug, Clone, PartialEq)]
3047    pub struct ClientboundSound {
3048        pub sound_id: VarInt,
3049        pub sound_category: VarInt,
3050        pub effect_pos_x: i32,
3051        pub effect_pos_y: i32,
3052        pub effect_pos_z: i32,
3053        pub volume: f32,
3054        pub pitch: f32,
3055    }
3056
3057    impl PacketId for ClientboundSound {
3058        fn packet_id(_ver: u32) -> u8 {
3059            0x48
3060        }
3061    }
3062
3063    impl Encode for ClientboundSound {
3064        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3065            self.sound_id.encode(dst)?;
3066            self.sound_category.encode(dst)?;
3067            dst.put_i32(self.effect_pos_x);
3068            dst.put_i32(self.effect_pos_y);
3069            dst.put_i32(self.effect_pos_z);
3070            dst.put_f32(self.volume);
3071            dst.put_f32(self.pitch);
3072            Ok(())
3073        }
3074    }
3075
3076    impl Decode for ClientboundSound {
3077        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3078            let sound_id = VarInt::decode(src)?;
3079            let sound_category = VarInt::decode(src)?;
3080            need(src, 4 + 4 + 4 + 4 + 4)?;
3081            Ok(Self {
3082                sound_id,
3083                sound_category,
3084                effect_pos_x: src.get_i32(),
3085                effect_pos_y: src.get_i32(),
3086                effect_pos_z: src.get_i32(),
3087                volume: src.get_f32(),
3088                pitch: src.get_f32(),
3089            })
3090        }
3091    }
3092
3093    // ── TabList (0x48 tab-list) ───────────────────────────────────────────────
3094
3095    #[derive(Debug, Clone, PartialEq)]
3096    pub struct ClientboundTabList {
3097        pub header: String,
3098        pub footer: String,
3099    }
3100
3101    impl PacketId for ClientboundTabList {
3102        fn packet_id(_ver: u32) -> u8 {
3103            0x48
3104        }
3105    }
3106
3107    impl Encode for ClientboundTabList {
3108        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3109            encode_str(&self.header, dst)?;
3110            encode_str(&self.footer, dst)
3111        }
3112    }
3113
3114    impl Decode for ClientboundTabList {
3115        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3116            let header = decode_str(src, "ClientboundTabList header")?;
3117            let footer = decode_str(src, "ClientboundTabList footer")?;
3118            Ok(Self { header, footer })
3119        }
3120    }
3121
3122    // ── TakeItemEntity (0x4B) ─────────────────────────────────────────────────
3123
3124    #[derive(Debug, Clone, PartialEq)]
3125    pub struct ClientboundTakeItemEntity {
3126        pub collected_entity_id: VarInt,
3127        pub collector_entity_id: VarInt,
3128        pub pickup_item_count: VarInt,
3129    }
3130
3131    impl PacketId for ClientboundTakeItemEntity {
3132        fn packet_id(_ver: u32) -> u8 {
3133            0x4B
3134        }
3135    }
3136
3137    impl Encode for ClientboundTakeItemEntity {
3138        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3139            self.collected_entity_id.encode(dst)?;
3140            self.collector_entity_id.encode(dst)?;
3141            self.pickup_item_count.encode(dst)
3142        }
3143    }
3144
3145    impl Decode for ClientboundTakeItemEntity {
3146        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3147            Ok(Self {
3148                collected_entity_id: VarInt::decode(src)?,
3149                collector_entity_id: VarInt::decode(src)?,
3150                pickup_item_count: VarInt::decode(src)?,
3151            })
3152        }
3153    }
3154
3155    // ── TeleportEntity (0x4C) ─────────────────────────────────────────────────
3156
3157    #[derive(Debug, Clone, PartialEq)]
3158    pub struct ClientboundTeleportEntity {
3159        pub entity_id: VarInt,
3160        pub x: f64,
3161        pub y: f64,
3162        pub z: f64,
3163        pub yaw: u8,
3164        pub pitch: u8,
3165        pub on_ground: bool,
3166    }
3167
3168    impl PacketId for ClientboundTeleportEntity {
3169        fn packet_id(_ver: u32) -> u8 {
3170            0x4C
3171        }
3172    }
3173
3174    impl Encode for ClientboundTeleportEntity {
3175        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3176            self.entity_id.encode(dst)?;
3177            dst.put_f64(self.x);
3178            dst.put_f64(self.y);
3179            dst.put_f64(self.z);
3180            dst.put_u8(self.yaw);
3181            dst.put_u8(self.pitch);
3182            dst.put_u8(self.on_ground as u8);
3183            Ok(())
3184        }
3185    }
3186
3187    impl Decode for ClientboundTeleportEntity {
3188        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3189            let entity_id = VarInt::decode(src)?;
3190            need(src, 8 + 8 + 8 + 1 + 1 + 1)?;
3191            Ok(Self {
3192                entity_id,
3193                x: src.get_f64(),
3194                y: src.get_f64(),
3195                z: src.get_f64(),
3196                yaw: src.get_u8(),
3197                pitch: src.get_u8(),
3198                on_ground: src.get_u8() != 0,
3199            })
3200        }
3201    }
3202
3203    // ── UpdateEffects (0x4F) ──────────────────────────────────────────────────
3204
3205    #[derive(Debug, Clone, PartialEq)]
3206    pub struct ClientboundUpdateEffects {
3207        pub entity_id: VarInt,
3208        pub effect_id: u8,
3209        pub amplifier: u8,
3210        pub duration: VarInt,
3211        pub flags: u8,
3212    }
3213
3214    impl PacketId for ClientboundUpdateEffects {
3215        fn packet_id(_ver: u32) -> u8 {
3216            0x4F
3217        }
3218    }
3219
3220    impl Encode for ClientboundUpdateEffects {
3221        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3222            self.entity_id.encode(dst)?;
3223            dst.put_u8(self.effect_id);
3224            dst.put_u8(self.amplifier);
3225            self.duration.encode(dst)?;
3226            dst.put_u8(self.flags);
3227            Ok(())
3228        }
3229    }
3230
3231    impl Decode for ClientboundUpdateEffects {
3232        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3233            let entity_id = VarInt::decode(src)?;
3234            need(src, 1 + 1)?;
3235            let effect_id = src.get_u8();
3236            let amplifier = src.get_u8();
3237            let duration = VarInt::decode(src)?;
3238            need(src, 1)?;
3239            let flags = src.get_u8();
3240            Ok(Self {
3241                entity_id,
3242                effect_id,
3243                amplifier,
3244                duration,
3245                flags,
3246            })
3247        }
3248    }
3249
3250    // ── SpawnGlobalEntity (0x02) ──────────────────────────────────────────────
3251
3252    #[derive(Debug, Clone, PartialEq)]
3253    pub struct ClientboundSpawnGlobalEntity {
3254        pub entity_id: i32, // FIX: V 1.12.2 je toto natvrdo i32, nikoliv VarInt!
3255        pub kind: u8,       // Vždy 1 pro blesk
3256        pub x: f64,
3257        pub y: f64,
3258        pub z: f64,
3259    }
3260
3261    impl PacketId for ClientboundSpawnGlobalEntity {
3262        fn packet_id(_ver: u32) -> u8 {
3263            0x02
3264        }
3265    }
3266
3267    impl Encode for ClientboundSpawnGlobalEntity {
3268        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3269            dst.clear();
3270            // 4 bajty (i32 Big Endian)
3271            dst.put_i32(self.entity_id);
3272
3273            // 1 bajt (u8)
3274            dst.put_u8(self.kind);
3275
3276            // 3x 8 bajtů (f64 Big Endian)
3277            dst.put_f64(self.x);
3278            dst.put_f64(self.y);
3279            dst.put_f64(self.z);
3280            Ok(())
3281        }
3282    }
3283
3284    impl Decode for ClientboundSpawnGlobalEntity {
3285        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3286            // Celková pevná délka v 1.12.2: 4 (i32) + 1 (u8) + 24 (3x f64) = 29 bajtů
3287            if src.remaining() < 29 {
3288                return Err(ProtocolError::Io(std::io::Error::new(
3289                    std::io::ErrorKind::UnexpectedEof,
3290                    "Nedostatek dat pro ClientboundSpawnGlobalEntity (vyzadovano 29B)",
3291                )));
3292            }
3293
3294            let entity_id = src.get_i32();
3295            let kind = src.get_u8();
3296            let x = src.get_f64();
3297            let y = src.get_f64();
3298            let z = src.get_f64();
3299
3300            Ok(Self {
3301                entity_id,
3302                kind,
3303                x,
3304                y,
3305                z,
3306            })
3307        }
3308    }
3309
3310    // ── ServerboundCustomPayload (0x0A) ───────────────────────────────────────
3311
3312    #[derive(Debug, Clone, PartialEq)]
3313    pub struct ServerboundCustomPayload {
3314        pub channel: String,
3315        pub data: Vec<u8>,
3316    }
3317
3318    impl PacketId for ServerboundCustomPayload {
3319        fn packet_id(_ver: u32) -> u8 {
3320            0x0A
3321        }
3322    }
3323
3324    impl Encode for ServerboundCustomPayload {
3325        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3326            encode_str(&self.channel, dst)?;
3327            dst.put_slice(&self.data);
3328            Ok(())
3329        }
3330    }
3331
3332    impl Decode for ServerboundCustomPayload {
3333        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3334            let channel = decode_str(src, "ServerboundCustomPayload channel")?;
3335            let len = src.remaining();
3336            let mut data = vec![0u8; len];
3337            src.copy_to_slice(&mut data);
3338            Ok(Self { channel, data })
3339        }
3340    }
3341
3342    // ── Serverbound packets ───────────────────────────────────────────────────
3343
3344    #[derive(Debug, Clone, PartialEq)]
3345    pub struct ServerboundTeleportConfirm {
3346        pub teleport_id: VarInt,
3347    }
3348    impl PacketId for ServerboundTeleportConfirm {
3349        fn packet_id(_ver: u32) -> u8 {
3350            0x00
3351        }
3352    }
3353    impl Encode for ServerboundTeleportConfirm {
3354        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3355            self.teleport_id.encode(dst)
3356        }
3357    }
3358    impl Decode for ServerboundTeleportConfirm {
3359        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3360            Ok(Self {
3361                teleport_id: VarInt::decode(src)?,
3362            })
3363        }
3364    }
3365
3366    #[derive(Debug, Clone, PartialEq)]
3367    pub struct ServerboundCommandSuggestion {
3368        pub text: String,
3369        pub block: Option<u64>,
3370    }
3371    impl PacketId for ServerboundCommandSuggestion {
3372        fn packet_id(_ver: u32) -> u8 {
3373            0x01
3374        }
3375    }
3376    impl Encode for ServerboundCommandSuggestion {
3377        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3378            encode_str(&self.text, dst)?;
3379            dst.put_u8(self.block.is_some() as u8);
3380            if let Some(b) = self.block {
3381                dst.put_u64(b);
3382            }
3383            Ok(())
3384        }
3385    }
3386    impl Decode for ServerboundCommandSuggestion {
3387        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3388            let text = decode_str(src, "ServerboundCommandSuggestion text")?;
3389            need(src, 1)?;
3390            let has_block = src.get_u8() != 0;
3391            let block = if has_block {
3392                need(src, 8)?;
3393                Some(src.get_u64())
3394            } else {
3395                None
3396            };
3397            Ok(Self { text, block })
3398        }
3399    }
3400
3401    #[derive(Debug, Clone, PartialEq)]
3402    pub struct ServerboundClientStatus {
3403        pub action_id: VarInt,
3404    }
3405    impl PacketId for ServerboundClientStatus {
3406        fn packet_id(_ver: u32) -> u8 {
3407            0x03
3408        }
3409    }
3410    impl Encode for ServerboundClientStatus {
3411        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3412            self.action_id.encode(dst)
3413        }
3414    }
3415    impl Decode for ServerboundClientStatus {
3416        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3417            Ok(Self {
3418                action_id: VarInt::decode(src)?,
3419            })
3420        }
3421    }
3422
3423    #[derive(Debug, Clone, PartialEq)]
3424    pub struct ServerboundClientSettings {
3425        pub locale: String,
3426        pub view_distance: u8,
3427        pub chat_mode: VarInt,
3428        pub chat_colors: bool,
3429        pub displayed_skin_parts: u8,
3430        pub main_hand: VarInt,
3431    }
3432    impl PacketId for ServerboundClientSettings {
3433        fn packet_id(_ver: u32) -> u8 {
3434            0x04
3435        }
3436    }
3437    impl Encode for ServerboundClientSettings {
3438        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3439            encode_str(&self.locale, dst)?;
3440            dst.put_u8(self.view_distance);
3441            self.chat_mode.encode(dst)?;
3442            dst.put_u8(self.chat_colors as u8);
3443            dst.put_u8(self.displayed_skin_parts);
3444            self.main_hand.encode(dst)
3445        }
3446    }
3447    impl Decode for ServerboundClientSettings {
3448        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3449            let locale = decode_str(src, "ServerboundClientSettings locale")?;
3450            need(src, 1)?;
3451            let view_distance = src.get_u8();
3452            let chat_mode = VarInt::decode(src)?;
3453            need(src, 1 + 1)?;
3454            let chat_colors = src.get_u8() != 0;
3455            let displayed_skin_parts = src.get_u8();
3456            let main_hand = VarInt::decode(src)?;
3457            Ok(Self {
3458                locale,
3459                view_distance,
3460                chat_mode,
3461                chat_colors,
3462                displayed_skin_parts,
3463                main_hand,
3464            })
3465        }
3466    }
3467
3468    #[derive(Debug, Clone, PartialEq)]
3469    pub struct ServerboundConfirmTransaction {
3470        pub window_id: i8,
3471        pub action_number: i16,
3472        pub accepted: bool,
3473    }
3474    impl PacketId for ServerboundConfirmTransaction {
3475        fn packet_id(_ver: u32) -> u8 {
3476            0x05
3477        }
3478    }
3479    impl Encode for ServerboundConfirmTransaction {
3480        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3481            dst.put_i8(self.window_id);
3482            dst.put_i16(self.action_number);
3483            dst.put_u8(self.accepted as u8);
3484            Ok(())
3485        }
3486    }
3487    impl Decode for ServerboundConfirmTransaction {
3488        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3489            need(src, 1 + 2 + 1)?;
3490            Ok(Self {
3491                window_id: src.get_i8(),
3492                action_number: src.get_i16(),
3493                accepted: src.get_u8() != 0,
3494            })
3495        }
3496    }
3497
3498    #[derive(Debug, Clone, PartialEq)]
3499    pub struct ServerboundEnchantItem {
3500        pub window_id: i8,
3501        pub enchantment: i8,
3502    }
3503    impl PacketId for ServerboundEnchantItem {
3504        fn packet_id(_ver: u32) -> u8 {
3505            0x06
3506        }
3507    }
3508    impl Encode for ServerboundEnchantItem {
3509        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3510            dst.put_i8(self.window_id);
3511            dst.put_i8(self.enchantment);
3512            Ok(())
3513        }
3514    }
3515    impl Decode for ServerboundEnchantItem {
3516        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3517            need(src, 1 + 1)?;
3518            Ok(Self {
3519                window_id: src.get_i8(),
3520                enchantment: src.get_i8(),
3521            })
3522        }
3523    }
3524
3525    #[derive(Debug, Clone, PartialEq)]
3526    pub struct ServerboundClickWindowButton {
3527        pub window_id: i8,
3528        pub button_id: i8,
3529    }
3530    impl PacketId for ServerboundClickWindowButton {
3531        fn packet_id(_ver: u32) -> u8 {
3532            0x06
3533        }
3534    }
3535    impl Encode for ServerboundClickWindowButton {
3536        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3537            dst.put_i8(self.window_id);
3538            dst.put_i8(self.button_id);
3539            Ok(())
3540        }
3541    }
3542    impl Decode for ServerboundClickWindowButton {
3543        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3544            need(src, 1 + 1)?;
3545            Ok(Self {
3546                window_id: src.get_i8(),
3547                button_id: src.get_i8(),
3548            })
3549        }
3550    }
3551
3552    #[derive(Debug, Clone, PartialEq)]
3553    pub struct ServerboundCloseWindow {
3554        pub window_id: u8,
3555    }
3556    impl PacketId for ServerboundCloseWindow {
3557        fn packet_id(_ver: u32) -> u8 {
3558            0x08
3559        }
3560    }
3561    impl Encode for ServerboundCloseWindow {
3562        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3563            dst.put_u8(self.window_id);
3564            Ok(())
3565        }
3566    }
3567    impl Decode for ServerboundCloseWindow {
3568        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3569            need(src, 1)?;
3570            Ok(Self {
3571                window_id: src.get_u8(),
3572            })
3573        }
3574    }
3575
3576    #[derive(Debug, Clone, PartialEq)]
3577    pub struct ServerboundMovePlayerStatusOnly {
3578        pub on_ground: bool,
3579    }
3580    impl PacketId for ServerboundMovePlayerStatusOnly {
3581        fn packet_id(_ver: u32) -> u8 {
3582            0x0F
3583        }
3584    }
3585    impl Encode for ServerboundMovePlayerStatusOnly {
3586        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3587            dst.put_u8(self.on_ground as u8);
3588            Ok(())
3589        }
3590    }
3591    impl Decode for ServerboundMovePlayerStatusOnly {
3592        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3593            need(src, 1)?;
3594            Ok(Self {
3595                on_ground: src.get_u8() != 0,
3596            })
3597        }
3598    }
3599
3600    #[derive(Debug, Clone, PartialEq)]
3601    pub struct ServerboundVehicleMove {
3602        pub x: f64,
3603        pub y: f64,
3604        pub z: f64,
3605        pub yaw: f32,
3606        pub pitch: f32,
3607    }
3608    impl PacketId for ServerboundVehicleMove {
3609        fn packet_id(_ver: u32) -> u8 {
3610            0x10
3611        }
3612    }
3613    impl Encode for ServerboundVehicleMove {
3614        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3615            dst.put_f64(self.x);
3616            dst.put_f64(self.y);
3617            dst.put_f64(self.z);
3618            dst.put_f32(self.yaw);
3619            dst.put_f32(self.pitch);
3620            Ok(())
3621        }
3622    }
3623    impl Decode for ServerboundVehicleMove {
3624        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3625            need(src, 8 + 8 + 8 + 4 + 4)?;
3626            Ok(Self {
3627                x: src.get_f64(),
3628                y: src.get_f64(),
3629                z: src.get_f64(),
3630                yaw: src.get_f32(),
3631                pitch: src.get_f32(),
3632            })
3633        }
3634    }
3635
3636    #[derive(Debug, Clone, PartialEq)]
3637    pub struct ServerboundSteerBoat {
3638        pub left_paddle: bool,
3639        pub right_paddle: bool,
3640    }
3641    impl PacketId for ServerboundSteerBoat {
3642        fn packet_id(_ver: u32) -> u8 {
3643            0x11
3644        }
3645    }
3646    impl Encode for ServerboundSteerBoat {
3647        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3648            dst.put_u8(self.left_paddle as u8);
3649            dst.put_u8(self.right_paddle as u8);
3650            Ok(())
3651        }
3652    }
3653    impl Decode for ServerboundSteerBoat {
3654        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3655            need(src, 1 + 1)?;
3656            Ok(Self {
3657                left_paddle: src.get_u8() != 0,
3658                right_paddle: src.get_u8() != 0,
3659            })
3660        }
3661    }
3662
3663    #[derive(Debug, Clone, PartialEq)]
3664    pub struct ServerboundPlayerAction {
3665        pub status: VarInt,
3666        pub location: u64,
3667        pub face: u8,
3668    }
3669    impl PacketId for ServerboundPlayerAction {
3670        fn packet_id(_ver: u32) -> u8 {
3671            0x13
3672        }
3673    }
3674    impl Encode for ServerboundPlayerAction {
3675        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3676            self.status.encode(dst)?;
3677            dst.put_u64(self.location);
3678            dst.put_u8(self.face);
3679            Ok(())
3680        }
3681    }
3682    impl Decode for ServerboundPlayerAction {
3683        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3684            let status = VarInt::decode(src)?;
3685            need(src, 8 + 1)?;
3686            Ok(Self {
3687                status,
3688                location: src.get_u64(),
3689                face: src.get_u8(),
3690            })
3691        }
3692    }
3693
3694    #[derive(Debug, Clone, PartialEq)]
3695    pub struct ServerboundEntityAction {
3696        pub entity_id: VarInt,
3697        pub action_id: VarInt,
3698        pub jump_boost: VarInt,
3699    }
3700    impl PacketId for ServerboundEntityAction {
3701        fn packet_id(_ver: u32) -> u8 {
3702            0x14
3703        }
3704    }
3705    impl Encode for ServerboundEntityAction {
3706        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3707            self.entity_id.encode(dst)?;
3708            self.action_id.encode(dst)?;
3709            self.jump_boost.encode(dst)
3710        }
3711    }
3712    impl Decode for ServerboundEntityAction {
3713        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3714            Ok(Self {
3715                entity_id: VarInt::decode(src)?,
3716                action_id: VarInt::decode(src)?,
3717                jump_boost: VarInt::decode(src)?,
3718            })
3719        }
3720    }
3721
3722    #[derive(Debug, Clone, PartialEq)]
3723    pub struct ServerboundSteerVehicle {
3724        pub sideways: f32,
3725        pub forward: f32,
3726        pub flags: u8,
3727    }
3728    impl PacketId for ServerboundSteerVehicle {
3729        fn packet_id(_ver: u32) -> u8 {
3730            0x15
3731        }
3732    }
3733    impl Encode for ServerboundSteerVehicle {
3734        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3735            dst.put_f32(self.sideways);
3736            dst.put_f32(self.forward);
3737            dst.put_u8(self.flags);
3738            Ok(())
3739        }
3740    }
3741    impl Decode for ServerboundSteerVehicle {
3742        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3743            need(src, 4 + 4 + 1)?;
3744            Ok(Self {
3745                sideways: src.get_f32(),
3746                forward: src.get_f32(),
3747                flags: src.get_u8(),
3748            })
3749        }
3750    }
3751
3752    #[derive(Debug, Clone, PartialEq)]
3753    pub struct ServerboundResourcePackStatus {
3754        pub result: VarInt,
3755    }
3756    impl PacketId for ServerboundResourcePackStatus {
3757        fn packet_id(_ver: u32) -> u8 {
3758            0x17
3759        }
3760    }
3761    impl Encode for ServerboundResourcePackStatus {
3762        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3763            self.result.encode(dst)
3764        }
3765    }
3766    impl Decode for ServerboundResourcePackStatus {
3767        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3768            Ok(Self {
3769                result: VarInt::decode(src)?,
3770            })
3771        }
3772    }
3773
3774    #[derive(Debug, Clone, PartialEq)]
3775    pub struct ServerboundRecipeBookSeenRecipe {
3776        pub recipe_id: VarInt,
3777    }
3778    impl PacketId for ServerboundRecipeBookSeenRecipe {
3779        fn packet_id(_ver: u32) -> u8 {
3780            0x18
3781        }
3782    }
3783    impl Encode for ServerboundRecipeBookSeenRecipe {
3784        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3785            self.recipe_id.encode(dst)
3786        }
3787    }
3788    impl Decode for ServerboundRecipeBookSeenRecipe {
3789        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3790            Ok(Self {
3791                recipe_id: VarInt::decode(src)?,
3792            })
3793        }
3794    }
3795
3796    #[derive(Debug, Clone, PartialEq)]
3797    pub struct ServerboundHeldItemChange {
3798        pub slot: i16,
3799    }
3800    impl PacketId for ServerboundHeldItemChange {
3801        fn packet_id(_ver: u32) -> u8 {
3802            0x1A
3803        }
3804    }
3805    impl Encode for ServerboundHeldItemChange {
3806        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3807            dst.put_i16(self.slot);
3808            Ok(())
3809        }
3810    }
3811    impl Decode for ServerboundHeldItemChange {
3812        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3813            need(src, 2)?;
3814            Ok(Self {
3815                slot: src.get_i16(),
3816            })
3817        }
3818    }
3819
3820    #[derive(Debug, Clone, PartialEq)]
3821    pub struct ServerboundCreativeInventoryAction {
3822        pub slot: i16,
3823        pub clicked_item: Vec<u8>,
3824    }
3825    impl PacketId for ServerboundCreativeInventoryAction {
3826        fn packet_id(_ver: u32) -> u8 {
3827            0x1B
3828        }
3829    }
3830    impl Encode for ServerboundCreativeInventoryAction {
3831        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3832            dst.put_i16(self.slot);
3833            dst.put_slice(&self.clicked_item);
3834            Ok(())
3835        }
3836    }
3837    impl Decode for ServerboundCreativeInventoryAction {
3838        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3839            need(src, 2)?;
3840            let slot = src.get_i16();
3841            let len = src.remaining();
3842            let mut clicked_item = vec![0u8; len];
3843            src.copy_to_slice(&mut clicked_item);
3844            Ok(Self { slot, clicked_item })
3845        }
3846    }
3847
3848    #[derive(Debug, Clone, PartialEq)]
3849    pub struct ServerboundUpdateSign {
3850        pub location: u64,
3851        pub lines: [String; 4],
3852    }
3853    impl PacketId for ServerboundUpdateSign {
3854        fn packet_id(_ver: u32) -> u8 {
3855            0x1C
3856        }
3857    }
3858    impl Encode for ServerboundUpdateSign {
3859        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3860            dst.put_u64(self.location);
3861            for line in &self.lines {
3862                encode_str(line, dst)?;
3863            }
3864            Ok(())
3865        }
3866    }
3867    impl Decode for ServerboundUpdateSign {
3868        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3869            need(src, 8)?;
3870            let location = src.get_u64();
3871            let lines = [
3872                decode_str(src, "ServerboundUpdateSign line1")?,
3873                decode_str(src, "ServerboundUpdateSign line2")?,
3874                decode_str(src, "ServerboundUpdateSign line3")?,
3875                decode_str(src, "ServerboundUpdateSign line4")?,
3876            ];
3877            Ok(Self { location, lines })
3878        }
3879    }
3880
3881    #[derive(Debug, Clone, PartialEq)]
3882    pub struct ServerboundAnimation {
3883        pub hand: VarInt,
3884    }
3885    impl PacketId for ServerboundAnimation {
3886        fn packet_id(_ver: u32) -> u8 {
3887            0x1D
3888        }
3889    }
3890    impl Encode for ServerboundAnimation {
3891        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3892            self.hand.encode(dst)
3893        }
3894    }
3895    impl Decode for ServerboundAnimation {
3896        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3897            Ok(Self {
3898                hand: VarInt::decode(src)?,
3899            })
3900        }
3901    }
3902
3903    #[derive(Debug, Clone, PartialEq)]
3904    pub struct ServerboundSpectate {
3905        pub target_player: Uuid,
3906    }
3907    impl PacketId for ServerboundSpectate {
3908        fn packet_id(_ver: u32) -> u8 {
3909            0x1E
3910        }
3911    }
3912    impl Encode for ServerboundSpectate {
3913        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3914            dst.put_slice(self.target_player.as_bytes());
3915            Ok(())
3916        }
3917    }
3918    impl Decode for ServerboundSpectate {
3919        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3920            need(src, 16)?;
3921            let mut b = [0u8; 16];
3922            src.copy_to_slice(&mut b);
3923            Ok(Self {
3924                target_player: Uuid::from_bytes(b),
3925            })
3926        }
3927    }
3928
3929    #[derive(Debug, Clone, PartialEq)]
3930    pub struct ServerboundPlayerBlockPlacement {
3931        pub location: u64,
3932        pub face: VarInt,
3933        pub hand: VarInt,
3934        pub cursor_x: f32,
3935        pub cursor_y: f32,
3936        pub cursor_z: f32,
3937    }
3938    impl PacketId for ServerboundPlayerBlockPlacement {
3939        fn packet_id(_ver: u32) -> u8 {
3940            0x1F
3941        }
3942    }
3943    impl Encode for ServerboundPlayerBlockPlacement {
3944        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3945            dst.put_u64(self.location);
3946            self.face.encode(dst)?;
3947            self.hand.encode(dst)?;
3948            dst.put_f32(self.cursor_x);
3949            dst.put_f32(self.cursor_y);
3950            dst.put_f32(self.cursor_z);
3951            Ok(())
3952        }
3953    }
3954    impl Decode for ServerboundPlayerBlockPlacement {
3955        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3956            need(src, 8)?;
3957            let location = src.get_u64();
3958            let face = VarInt::decode(src)?;
3959            let hand = VarInt::decode(src)?;
3960            need(src, 4 + 4 + 4)?;
3961            Ok(Self {
3962                location,
3963                face,
3964                hand,
3965                cursor_x: src.get_f32(),
3966                cursor_y: src.get_f32(),
3967                cursor_z: src.get_f32(),
3968            })
3969        }
3970    }
3971
3972    #[derive(Debug, Clone, PartialEq)]
3973    pub struct ServerboundUseItem {
3974        pub hand: VarInt,
3975    }
3976    impl PacketId for ServerboundUseItem {
3977        fn packet_id(_ver: u32) -> u8 {
3978            0x20
3979        }
3980    }
3981    impl Encode for ServerboundUseItem {
3982        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
3983            self.hand.encode(dst)
3984        }
3985    }
3986    impl Decode for ServerboundUseItem {
3987        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
3988            Ok(Self {
3989                hand: VarInt::decode(src)?,
3990            })
3991        }
3992    }
3993
3994    // ── Opaque raw stubs ──────────────────────────────────────────────────────
3995
3996    #[derive(Debug, Clone, PartialEq)]
3997    pub struct AttributeModifier {
3998        pub uuid: Uuid,
3999        pub amount: f64,
4000        pub operation: u8,
4001    }
4002
4003    #[derive(Debug, Clone, PartialEq)]
4004    pub struct Attribute {
4005        pub key: String,
4006        pub value: f64,
4007        pub modifiers: Vec<AttributeModifier>,
4008    }
4009
4010    #[derive(Debug, Clone, PartialEq)]
4011    pub struct ClientboundUpdateAttributes {
4012        pub entity_id: VarInt,
4013        pub attributes: Vec<Attribute>,
4014    }
4015
4016    impl PacketId for ClientboundUpdateAttributes {
4017        fn packet_id(_ver: u32) -> u8 {
4018            0x4E
4019        }
4020    }
4021
4022    impl Encode for ClientboundUpdateAttributes {
4023        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4024            self.entity_id.encode(dst)?;
4025            dst.put_i32(self.attributes.len() as i32); // NOTE: i32 not VarInt in 1.12.2
4026            for attr in &self.attributes {
4027                encode_str(&attr.key, dst)?;
4028                dst.put_f64(attr.value);
4029                VarInt(attr.modifiers.len() as i32).encode(dst)?;
4030                for m in &attr.modifiers {
4031                    dst.put_slice(m.uuid.as_bytes());
4032                    dst.put_f64(m.amount);
4033                    dst.put_u8(m.operation);
4034                }
4035            }
4036            Ok(())
4037        }
4038    }
4039
4040    impl Decode for ClientboundUpdateAttributes {
4041        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4042            let entity_id = VarInt::decode(src)?;
4043            need(src, 4)?;
4044            let count = src.get_i32() as usize; // NOTE: i32 not VarInt in 1.12.2
4045            let mut attributes = Vec::with_capacity(count);
4046            for _ in 0..count {
4047                let key = decode_str(src, "attribute key")?;
4048                need(src, 8)?;
4049                let value = src.get_f64();
4050                let mod_count = VarInt::decode(src)?.0 as usize;
4051                let mut modifiers = Vec::with_capacity(mod_count);
4052                for _ in 0..mod_count {
4053                    need(src, 16 + 8 + 1)?;
4054                    let mut b = [0u8; 16];
4055                    src.copy_to_slice(&mut b);
4056                    let uuid = Uuid::from_bytes(b);
4057                    let amount = src.get_f64();
4058                    let operation = src.get_u8();
4059                    modifiers.push(AttributeModifier {
4060                        uuid,
4061                        amount,
4062                        operation,
4063                    });
4064                }
4065                attributes.push(Attribute {
4066                    key,
4067                    value,
4068                    modifiers,
4069                });
4070            }
4071            Ok(Self {
4072                entity_id,
4073                attributes,
4074            })
4075        }
4076    }
4077
4078    #[derive(Debug, Clone, PartialEq)]
4079    pub struct AdvancementDisplay {
4080        pub title: String,
4081        pub description: String,
4082        pub icon_item_id: i16, // -1 = no icon
4083        pub icon_count: i8,
4084        pub icon_damage: i16,
4085        pub icon_nbt: Nbt,
4086        pub frame_type: VarInt,         // 0=task, 1=challenge, 2=goal
4087        pub flags: i32,                 // bit 0=has bg, bit 1=show toast, bit 2=hidden
4088        pub background: Option<String>, // only present if flags & 1
4089        pub x: f32,
4090        pub y: f32,
4091    }
4092
4093    #[derive(Debug, Clone, PartialEq)]
4094    pub struct Advancement {
4095        pub id: String,
4096        pub parent_id: Option<String>,
4097        pub display: Option<AdvancementDisplay>,
4098        pub criteria: Vec<String>,
4099        pub requirements: Vec<Vec<String>>,
4100    }
4101
4102    #[derive(Debug, Clone, PartialEq)]
4103    pub struct AdvancementProgress {
4104        pub id: String,
4105        pub criteria: Vec<(String, Option<i64>)>, // criterion id + completion timestamp if done
4106    }
4107
4108    #[derive(Debug, Clone, PartialEq)]
4109    pub struct ClientboundUpdateAdvancements {
4110        pub reset: bool,
4111        pub added: Vec<Advancement>,
4112        pub removed: Vec<String>,
4113        pub progress: Vec<AdvancementProgress>,
4114    }
4115
4116    impl PacketId for ClientboundUpdateAdvancements {
4117        fn packet_id(_ver: u32) -> u8 {
4118            0x4D
4119        }
4120    }
4121
4122    impl Encode for ClientboundUpdateAdvancements {
4123        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4124            dst.put_u8(self.reset as u8);
4125
4126            VarInt(self.added.len() as i32).encode(dst)?;
4127            for adv in &self.added {
4128                encode_str(&adv.id, dst)?;
4129                dst.put_u8(adv.parent_id.is_some() as u8);
4130                if let Some(p) = &adv.parent_id {
4131                    encode_str(p, dst)?;
4132                }
4133                dst.put_u8(adv.display.is_some() as u8);
4134                if let Some(d) = &adv.display {
4135                    encode_str(&d.title, dst)?;
4136                    encode_str(&d.description, dst)?;
4137                    // icon slot: item_id i16, if not -1: count i8 + damage i16 + nbt
4138                    dst.put_i16(d.icon_item_id);
4139                    if d.icon_item_id != -1 {
4140                        dst.put_i8(d.icon_count);
4141                        dst.put_i16(d.icon_damage);
4142                        d.icon_nbt.encode(dst)?;
4143                    }
4144                    d.frame_type.encode(dst)?;
4145                    dst.put_i32(d.flags);
4146                    if d.flags & 1 != 0 {
4147                        if let Some(bg) = &d.background {
4148                            encode_str(bg, dst)?;
4149                        }
4150                    }
4151                    dst.put_f32(d.x);
4152                    dst.put_f32(d.y);
4153                }
4154                VarInt(adv.criteria.len() as i32).encode(dst)?;
4155                for c in &adv.criteria {
4156                    encode_str(c, dst)?;
4157                }
4158                VarInt(adv.requirements.len() as i32).encode(dst)?;
4159                for req in &adv.requirements {
4160                    VarInt(req.len() as i32).encode(dst)?;
4161                    for r in req {
4162                        encode_str(r, dst)?;
4163                    }
4164                }
4165            }
4166
4167            VarInt(self.removed.len() as i32).encode(dst)?;
4168            for id in &self.removed {
4169                encode_str(id, dst)?;
4170            }
4171
4172            VarInt(self.progress.len() as i32).encode(dst)?;
4173            for prog in &self.progress {
4174                encode_str(&prog.id, dst)?;
4175                VarInt(prog.criteria.len() as i32).encode(dst)?;
4176                for (crit_id, timestamp) in &prog.criteria {
4177                    encode_str(crit_id, dst)?;
4178                    dst.put_u8(timestamp.is_some() as u8);
4179                    if let Some(ts) = timestamp {
4180                        dst.put_i64(*ts);
4181                    }
4182                }
4183            }
4184            Ok(())
4185        }
4186    }
4187
4188    impl Decode for ClientboundUpdateAdvancements {
4189        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4190            need(src, 1)?;
4191            let reset = src.get_u8() != 0;
4192
4193            let added_count = VarInt::decode(src)?.0 as usize;
4194            let mut added = Vec::with_capacity(added_count);
4195            for _ in 0..added_count {
4196                let id = decode_str(src, "advancement id")?;
4197                need(src, 1)?;
4198                let has_parent = src.get_u8() != 0;
4199                let parent_id = if has_parent {
4200                    Some(decode_str(src, "advancement parent")?)
4201                } else {
4202                    None
4203                };
4204                need(src, 1)?;
4205                let has_display = src.get_u8() != 0;
4206                let display = if has_display {
4207                    let title = decode_str(src, "advancement title")?;
4208                    let description = decode_str(src, "advancement description")?;
4209                    need(src, 2)?;
4210                    let icon_item_id = src.get_i16();
4211                    let (icon_count, icon_damage, icon_nbt) = if icon_item_id != -1 {
4212                        need(src, 1 + 2)?;
4213                        let c = src.get_i8();
4214                        let d = src.get_i16();
4215                        let n = Nbt::decode(src)?;
4216                        (c, d, n)
4217                    } else {
4218                        (0, 0, Nbt::empty(""))
4219                    };
4220                    let frame_type = VarInt::decode(src)?;
4221                    need(src, 4)?;
4222                    let flags = src.get_i32();
4223                    let background = if flags & 1 != 0 {
4224                        Some(decode_str(src, "advancement background")?)
4225                    } else {
4226                        None
4227                    };
4228                    need(src, 4 + 4)?;
4229                    let x = src.get_f32();
4230                    let y = src.get_f32();
4231                    Some(AdvancementDisplay {
4232                        title,
4233                        description,
4234                        icon_item_id,
4235                        icon_count,
4236                        icon_damage,
4237                        icon_nbt,
4238                        frame_type,
4239                        flags,
4240                        background,
4241                        x,
4242                        y,
4243                    })
4244                } else {
4245                    None
4246                };
4247                let crit_count = VarInt::decode(src)?.0 as usize;
4248                let mut criteria = Vec::with_capacity(crit_count);
4249                for _ in 0..crit_count {
4250                    criteria.push(decode_str(src, "criterion id")?);
4251                }
4252                let req_count = VarInt::decode(src)?.0 as usize;
4253                let mut requirements = Vec::with_capacity(req_count);
4254                for _ in 0..req_count {
4255                    let len = VarInt::decode(src)?.0 as usize;
4256                    let mut req = Vec::with_capacity(len);
4257                    for _ in 0..len {
4258                        req.push(decode_str(src, "requirement")?);
4259                    }
4260                    requirements.push(req);
4261                }
4262                added.push(Advancement {
4263                    id,
4264                    parent_id,
4265                    display,
4266                    criteria,
4267                    requirements,
4268                });
4269            }
4270
4271            let removed_count = VarInt::decode(src)?.0 as usize;
4272            let mut removed = Vec::with_capacity(removed_count);
4273            for _ in 0..removed_count {
4274                removed.push(decode_str(src, "removed advancement id")?);
4275            }
4276
4277            let progress_count = VarInt::decode(src)?.0 as usize;
4278            let mut progress = Vec::with_capacity(progress_count);
4279            for _ in 0..progress_count {
4280                let id = decode_str(src, "progress id")?;
4281                let crit_count = VarInt::decode(src)?.0 as usize;
4282                let mut criteria = Vec::with_capacity(crit_count);
4283                for _ in 0..crit_count {
4284                    let crit_id = decode_str(src, "criterion id")?;
4285                    need(src, 1)?;
4286                    let achieved = src.get_u8() != 0;
4287                    let timestamp = if achieved {
4288                        need(src, 8)?;
4289                        Some(src.get_i64())
4290                    } else {
4291                        None
4292                    };
4293                    criteria.push((crit_id, timestamp));
4294                }
4295                progress.push(AdvancementProgress { id, criteria });
4296            }
4297
4298            Ok(Self {
4299                reset,
4300                added,
4301                removed,
4302                progress,
4303            })
4304        }
4305    }
4306
4307    #[derive(Debug, Clone, PartialEq)]
4308    pub struct ClientboundPlayerCombatEnter;
4309
4310    impl PacketId for ClientboundPlayerCombatEnter {
4311        fn packet_id(_ver: u32) -> u8 {
4312            0x2D
4313        }
4314    }
4315
4316    impl Encode for ClientboundPlayerCombatEnter {
4317        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4318            VarInt(0).encode(dst) // action = 0: enter combat
4319        }
4320    }
4321
4322    impl Decode for ClientboundPlayerCombatEnter {
4323        fn decode(_src: &mut Bytes) -> Result<Self, ProtocolError> {
4324            // action VarInt already consumed by dispatcher
4325            Ok(Self)
4326        }
4327    }
4328
4329    #[derive(Debug, Clone, PartialEq)]
4330    pub struct ClientboundPlayerCombatEnd {
4331        pub duration: VarInt, // ticks in combat
4332        pub entity_id: i32,   // opponent entity id
4333    }
4334
4335    impl PacketId for ClientboundPlayerCombatEnd {
4336        fn packet_id(_ver: u32) -> u8 {
4337            0x2D
4338        }
4339    }
4340
4341    impl Encode for ClientboundPlayerCombatEnd {
4342        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4343            VarInt(1).encode(dst)?; // action = 1: end combat
4344            self.duration.encode(dst)?;
4345            dst.put_i32(self.entity_id);
4346            Ok(())
4347        }
4348    }
4349
4350    impl Decode for ClientboundPlayerCombatEnd {
4351        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4352            let duration = VarInt::decode(src)?;
4353            need(src, 4)?;
4354            let entity_id = src.get_i32();
4355            Ok(Self {
4356                duration,
4357                entity_id,
4358            })
4359        }
4360    }
4361
4362    #[derive(Debug, Clone, PartialEq)]
4363    pub struct ClientboundPlayerCombatKill {
4364        pub player_id: VarInt, // entity id of the player that died
4365        pub entity_id: i32,    // killer entity id (-1 if none)
4366        pub message: String,   // death message JSON
4367    }
4368
4369    impl PacketId for ClientboundPlayerCombatKill {
4370        fn packet_id(_ver: u32) -> u8 {
4371            0x2D
4372        }
4373    }
4374
4375    impl Encode for ClientboundPlayerCombatKill {
4376        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4377            VarInt(2).encode(dst)?; // action = 2: entity dead
4378            self.player_id.encode(dst)?;
4379            dst.put_i32(self.entity_id);
4380            encode_str(&self.message, dst)?;
4381            Ok(())
4382        }
4383    }
4384
4385    impl Decode for ClientboundPlayerCombatKill {
4386        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4387            let player_id = VarInt::decode(src)?;
4388            need(src, 4)?;
4389            let entity_id = src.get_i32();
4390            let message = decode_str(src, "ClientboundPlayerCombatKill message")?;
4391            Ok(Self {
4392                player_id,
4393                entity_id,
4394                message,
4395            })
4396        }
4397    }
4398
4399    // ── World border packets (0x38) ───────────────────────────────────────────────
4400    // In 1.12.2 all border variants share packet id 0x38,
4401    // distinguished by a leading VarInt action field.
4402
4403    #[derive(Debug, Clone, PartialEq)]
4404    pub struct ClientboundSetBorderSize {
4405        pub diameter: f64,
4406    }
4407
4408    impl PacketId for ClientboundSetBorderSize {
4409        fn packet_id(_ver: u32) -> u8 {
4410            0x38
4411        }
4412    }
4413
4414    impl Encode for ClientboundSetBorderSize {
4415        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4416            VarInt(0).encode(dst)?; // action = 0: set size
4417            dst.put_f64(self.diameter);
4418            Ok(())
4419        }
4420    }
4421
4422    impl Decode for ClientboundSetBorderSize {
4423        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4424            need(src, 8)?;
4425            Ok(Self {
4426                diameter: src.get_f64(),
4427            })
4428        }
4429    }
4430
4431    #[derive(Debug, Clone, PartialEq)]
4432    pub struct ClientboundSetBorderLerpSize {
4433        pub old_diameter: f64,
4434        pub new_diameter: f64,
4435        pub speed: VarInt, // ticks until new diameter is reached
4436    }
4437
4438    impl PacketId for ClientboundSetBorderLerpSize {
4439        fn packet_id(_ver: u32) -> u8 {
4440            0x38
4441        }
4442    }
4443
4444    impl Encode for ClientboundSetBorderLerpSize {
4445        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4446            VarInt(1).encode(dst)?; // action = 1: lerp size
4447            dst.put_f64(self.old_diameter);
4448            dst.put_f64(self.new_diameter);
4449            self.speed.encode(dst)?;
4450            Ok(())
4451        }
4452    }
4453
4454    impl Decode for ClientboundSetBorderLerpSize {
4455        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4456            need(src, 8 + 8)?;
4457            let old_diameter = src.get_f64();
4458            let new_diameter = src.get_f64();
4459            let speed = VarInt::decode(src)?;
4460            Ok(Self {
4461                old_diameter,
4462                new_diameter,
4463                speed,
4464            })
4465        }
4466    }
4467
4468    #[derive(Debug, Clone, PartialEq)]
4469    pub struct ClientboundSetBorderCenter {
4470        pub new_center_x: f64,
4471        pub new_center_z: f64,
4472    }
4473
4474    impl PacketId for ClientboundSetBorderCenter {
4475        fn packet_id(_ver: u32) -> u8 {
4476            0x38
4477        }
4478    }
4479
4480    impl Encode for ClientboundSetBorderCenter {
4481        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4482            VarInt(2).encode(dst)?; // action = 2: set center
4483            dst.put_f64(self.new_center_x);
4484            dst.put_f64(self.new_center_z);
4485            Ok(())
4486        }
4487    }
4488
4489    impl Decode for ClientboundSetBorderCenter {
4490        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4491            need(src, 8 + 8)?;
4492            Ok(Self {
4493                new_center_x: src.get_f64(),
4494                new_center_z: src.get_f64(),
4495            })
4496        }
4497    }
4498
4499    #[derive(Debug, Clone, PartialEq)]
4500    pub struct ClientboundInitializeBorder {
4501        pub x: f64,
4502        pub z: f64,
4503        pub old_diameter: f64,
4504        pub new_diameter: f64,
4505        pub speed: VarInt,
4506        pub portal_teleport_boundary: VarInt,
4507        pub warning_blocks: VarInt,
4508        pub warning_time: VarInt,
4509    }
4510
4511    impl PacketId for ClientboundInitializeBorder {
4512        fn packet_id(_ver: u32) -> u8 {
4513            0x38
4514        }
4515    }
4516
4517    impl Encode for ClientboundInitializeBorder {
4518        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4519            VarInt(3).encode(dst)?; // action = 3: initialize
4520            dst.put_f64(self.x);
4521            dst.put_f64(self.z);
4522            dst.put_f64(self.old_diameter);
4523            dst.put_f64(self.new_diameter);
4524            self.speed.encode(dst)?;
4525            self.portal_teleport_boundary.encode(dst)?;
4526            self.warning_blocks.encode(dst)?;
4527            self.warning_time.encode(dst)?;
4528            Ok(())
4529        }
4530    }
4531
4532    impl Decode for ClientboundInitializeBorder {
4533        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4534            need(src, 8 + 8 + 8 + 8)?;
4535            let x = src.get_f64();
4536            let z = src.get_f64();
4537            let old_diameter = src.get_f64();
4538            let new_diameter = src.get_f64();
4539            let speed = VarInt::decode(src)?;
4540            let portal_teleport_boundary = VarInt::decode(src)?;
4541            let warning_blocks = VarInt::decode(src)?;
4542            let warning_time = VarInt::decode(src)?;
4543            Ok(Self {
4544                x,
4545                z,
4546                old_diameter,
4547                new_diameter,
4548                speed,
4549                portal_teleport_boundary,
4550                warning_blocks,
4551                warning_time,
4552            })
4553        }
4554    }
4555
4556    #[derive(Debug, Clone, PartialEq)]
4557    pub struct ClientboundSetBorderWarningTime {
4558        pub warning_time: VarInt, // seconds
4559    }
4560
4561    impl PacketId for ClientboundSetBorderWarningTime {
4562        fn packet_id(_ver: u32) -> u8 {
4563            0x38
4564        }
4565    }
4566
4567    impl Encode for ClientboundSetBorderWarningTime {
4568        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4569            VarInt(4).encode(dst)?; // action = 4: set warning time
4570            self.warning_time.encode(dst)
4571        }
4572    }
4573
4574    impl Decode for ClientboundSetBorderWarningTime {
4575        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4576            Ok(Self {
4577                warning_time: VarInt::decode(src)?,
4578            })
4579        }
4580    }
4581
4582    #[derive(Debug, Clone, PartialEq)]
4583    pub struct ClientboundSetBorderWarningDistance {
4584        pub warning_blocks: VarInt,
4585    }
4586
4587    impl PacketId for ClientboundSetBorderWarningDistance {
4588        fn packet_id(_ver: u32) -> u8 {
4589            0x38
4590        }
4591    }
4592
4593    impl Encode for ClientboundSetBorderWarningDistance {
4594        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4595            VarInt(5).encode(dst)?; // action = 5: set warning distance
4596            self.warning_blocks.encode(dst)
4597        }
4598    }
4599
4600    impl Decode for ClientboundSetBorderWarningDistance {
4601        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4602            Ok(Self {
4603                warning_blocks: VarInt::decode(src)?,
4604            })
4605        }
4606    }
4607
4608    // Note: ClientboundSetBorderWarningDelay is the same as ClientboundSetBorderWarningTime
4609    // (action 4). They are the same packet, just aliased differently in your exports.
4610    pub type ClientboundSetBorderWarningDelay = ClientboundSetBorderWarningTime;
4611
4612    // ── ClientboundHorseScreenOpen (0x1F) ─────────────────────────────────────────
4613
4614    #[derive(Debug, Clone, PartialEq)]
4615    pub struct ClientboundHorseScreenOpen {
4616        pub window_id: u8,
4617        pub slot_count: VarInt,
4618        pub entity_id: i32,
4619    }
4620
4621    impl PacketId for ClientboundHorseScreenOpen {
4622        fn packet_id(_ver: u32) -> u8 {
4623            0x1F
4624        }
4625    }
4626
4627    impl Encode for ClientboundHorseScreenOpen {
4628        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4629            dst.put_u8(self.window_id);
4630            self.slot_count.encode(dst)?;
4631            dst.put_i32(self.entity_id);
4632            Ok(())
4633        }
4634    }
4635
4636    impl Decode for ClientboundHorseScreenOpen {
4637        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4638            need(src, 1)?;
4639            let window_id = src.get_u8();
4640            let slot_count = VarInt::decode(src)?;
4641            need(src, 4)?;
4642            let entity_id = src.get_i32();
4643            Ok(Self {
4644                window_id,
4645                slot_count,
4646                entity_id,
4647            })
4648        }
4649    }
4650
4651    // ── ClientboundPlaceGhostRecipe (0x31 in 1.12.2) ──────────────────────────────
4652
4653    #[derive(Debug, Clone, PartialEq)]
4654    pub struct ClientboundPlaceGhostRecipe {
4655        pub window_id: u8,
4656        pub recipe: String,
4657    }
4658
4659    impl PacketId for ClientboundPlaceGhostRecipe {
4660        fn packet_id(_ver: u32) -> u8 {
4661            0x31
4662        }
4663    }
4664
4665    impl Encode for ClientboundPlaceGhostRecipe {
4666        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4667            dst.put_u8(self.window_id);
4668            encode_str(&self.recipe, dst)?;
4669            Ok(())
4670        }
4671    }
4672
4673    impl Decode for ClientboundPlaceGhostRecipe {
4674        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4675            need(src, 1)?;
4676            let window_id = src.get_u8();
4677            let recipe = decode_str(src, "ClientboundPlaceGhostRecipe recipe")?;
4678            Ok(Self { window_id, recipe })
4679        }
4680    }
4681
4682    // ── ClientboundResetScore (not in 1.12.2 — stub only) ────────────────────────
4683    // This packet does not exist in 1.12.2. Keep as raw passthrough.
4684    #[derive(Debug, Clone, PartialEq)]
4685    pub struct ClientboundResetScore {
4686        pub raw: Vec<u8>,
4687    }
4688    impl PacketId for ClientboundResetScore {
4689        fn packet_id(_ver: u32) -> u8 {
4690            0xFF
4691        }
4692    }
4693    impl Encode for ClientboundResetScore {
4694        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4695            dst.put_slice(&self.raw);
4696            Ok(())
4697        }
4698    }
4699    impl Decode for ClientboundResetScore {
4700        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4701            let len = src.remaining();
4702            let mut raw = vec![0u8; len];
4703            src.copy_to_slice(&mut raw);
4704            Ok(Self { raw })
4705        }
4706    }
4707
4708    // ── ClientboundStopSound (0x48 in 1.12.2) ────────────────────────────────────
4709
4710    #[derive(Debug, Clone, PartialEq)]
4711    pub struct ClientboundStopSound {
4712        pub flags: u8,
4713        pub source: Option<VarInt>, // present if flags & 1
4714        pub sound: Option<String>,  // present if flags & 2
4715    }
4716
4717    impl PacketId for ClientboundStopSound {
4718        fn packet_id(_ver: u32) -> u8 {
4719            0x48
4720        }
4721    }
4722
4723    impl Encode for ClientboundStopSound {
4724        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4725            dst.put_u8(self.flags);
4726            if self.flags & 1 != 0 {
4727                if let Some(s) = &self.source {
4728                    s.encode(dst)?;
4729                }
4730            }
4731            if self.flags & 2 != 0 {
4732                if let Some(s) = &self.sound {
4733                    encode_str(s, dst)?;
4734                }
4735            }
4736            Ok(())
4737        }
4738    }
4739
4740    impl Decode for ClientboundStopSound {
4741        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4742            need(src, 1)?;
4743            let flags = src.get_u8();
4744            let source = if flags & 1 != 0 {
4745                Some(VarInt::decode(src)?)
4746            } else {
4747                None
4748            };
4749            let sound = if flags & 2 != 0 {
4750                Some(decode_str(src, "ClientboundStopSound sound")?)
4751            } else {
4752                None
4753            };
4754            Ok(Self {
4755                flags,
4756                source,
4757                sound,
4758            })
4759        }
4760    }
4761
4762    // ── ServerboundClickWindow (0x07) ─────────────────────────────────────────────
4763
4764    #[derive(Debug, Clone, PartialEq)]
4765    pub struct ServerboundClickWindow {
4766        pub window_id: u8,
4767        pub slot: i16,
4768        pub button: i8,
4769        pub action_number: i16,
4770        pub mode: VarInt,
4771        pub clicked_item: LegacySlot,
4772    }
4773
4774    impl PacketId for ServerboundClickWindow {
4775        fn packet_id(_ver: u32) -> u8 {
4776            0x07
4777        }
4778    }
4779
4780    impl Encode for ServerboundClickWindow {
4781        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4782            dst.put_u8(self.window_id);
4783            dst.put_i16(self.slot);
4784            dst.put_i8(self.button);
4785            dst.put_i16(self.action_number);
4786            self.mode.encode(dst)?;
4787            self.clicked_item.encode(dst)?;
4788            Ok(())
4789        }
4790    }
4791
4792    impl Decode for ServerboundClickWindow {
4793        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4794            need(src, 1 + 2 + 1 + 2)?;
4795            let window_id = src.get_u8();
4796            let slot = src.get_i16();
4797            let button = src.get_i8();
4798            let action_number = src.get_i16();
4799            let mode = VarInt::decode(src)?;
4800            let clicked_item = LegacySlot::decode(src)?;
4801            Ok(Self {
4802                window_id,
4803                slot,
4804                button,
4805                action_number,
4806                mode,
4807                clicked_item,
4808            })
4809        }
4810    }
4811
4812    // ── ServerboundRecipeBookChangeSettings (0x16) ────────────────────────────────
4813
4814    #[derive(Debug, Clone, PartialEq)]
4815    pub struct ServerboundRecipeBookChangeSettings {
4816        pub book_id: VarInt, // 0 = crafting, 1 = furnace
4817        pub open: bool,
4818        pub filter: bool,
4819    }
4820
4821    impl PacketId for ServerboundRecipeBookChangeSettings {
4822        fn packet_id(_ver: u32) -> u8 {
4823            0x16
4824        }
4825    }
4826
4827    impl Encode for ServerboundRecipeBookChangeSettings {
4828        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4829            self.book_id.encode(dst)?;
4830            dst.put_u8(self.open as u8);
4831            dst.put_u8(self.filter as u8);
4832            Ok(())
4833        }
4834    }
4835
4836    impl Decode for ServerboundRecipeBookChangeSettings {
4837        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4838            let book_id = VarInt::decode(src)?;
4839            need(src, 1 + 1)?;
4840            let open = src.get_u8() != 0;
4841            let filter = src.get_u8() != 0;
4842            Ok(Self {
4843                book_id,
4844                open,
4845                filter,
4846            })
4847        }
4848    }
4849
4850    // ── ServerboundPickItem (0x15 in 1.12.2) ──────────────────────────────────────
4851
4852    #[derive(Debug, Clone, PartialEq)]
4853    pub struct ServerboundPickItem {
4854        pub slot_to_use: VarInt,
4855    }
4856
4857    impl PacketId for ServerboundPickItem {
4858        fn packet_id(_ver: u32) -> u8 {
4859            0x15
4860        }
4861    }
4862
4863    impl Encode for ServerboundPickItem {
4864        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4865            self.slot_to_use.encode(dst)
4866        }
4867    }
4868
4869    impl Decode for ServerboundPickItem {
4870        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4871            Ok(Self {
4872                slot_to_use: VarInt::decode(src)?,
4873            })
4874        }
4875    }
4876
4877    // ── ServerboundPlaceRecipe (0x17 in 1.12.2) ───────────────────────────────────
4878
4879    #[derive(Debug, Clone, PartialEq)]
4880    pub struct ServerboundPlaceRecipe {
4881        pub window_id: i8,
4882        pub recipe: String,
4883        pub make_all: bool,
4884    }
4885
4886    impl PacketId for ServerboundPlaceRecipe {
4887        fn packet_id(_ver: u32) -> u8 {
4888            0x17
4889        }
4890    }
4891
4892    impl Encode for ServerboundPlaceRecipe {
4893        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4894            dst.put_i8(self.window_id);
4895            encode_str(&self.recipe, dst)?;
4896            dst.put_u8(self.make_all as u8);
4897            Ok(())
4898        }
4899    }
4900
4901    impl Decode for ServerboundPlaceRecipe {
4902        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4903            need(src, 1)?;
4904            let window_id = src.get_i8();
4905            let recipe = decode_str(src, "ServerboundPlaceRecipe recipe")?;
4906            need(src, 1)?;
4907            let make_all = src.get_u8() != 0;
4908            Ok(Self {
4909                window_id,
4910                recipe,
4911                make_all,
4912            })
4913        }
4914    }
4915
4916    // ── ServerboundSetBeaconEffect (0x19 in 1.12.2) ───────────────────────────────
4917
4918    #[derive(Debug, Clone, PartialEq)]
4919    pub struct ServerboundSetBeaconEffect {
4920        pub primary_effect: VarInt, // potion effect id
4921        pub secondary_effect: VarInt,
4922    }
4923
4924    impl PacketId for ServerboundSetBeaconEffect {
4925        fn packet_id(_ver: u32) -> u8 {
4926            0x19
4927        }
4928    }
4929
4930    impl Encode for ServerboundSetBeaconEffect {
4931        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4932            self.primary_effect.encode(dst)?;
4933            self.secondary_effect.encode(dst)
4934        }
4935    }
4936
4937    impl Decode for ServerboundSetBeaconEffect {
4938        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4939            Ok(Self {
4940                primary_effect: VarInt::decode(src)?,
4941                secondary_effect: VarInt::decode(src)?,
4942            })
4943        }
4944    }
4945
4946    // ── ServerboundSetStructureBlock (0x1E in 1.12.2) ─────────────────────────────
4947
4948    #[derive(Debug, Clone, PartialEq)]
4949    pub struct ServerboundSetStructureBlock {
4950        pub location: u64,
4951        pub action: VarInt, // 0=update data, 1=save, 2=load, 3=detect size
4952        pub mode: VarInt,   // 0=save, 1=load, 2=corner, 3=data
4953        pub name: String,
4954        pub offset_x: i8,
4955        pub offset_y: i8,
4956        pub offset_z: i8,
4957        pub size_x: i8,
4958        pub size_y: i8,
4959        pub size_z: i8,
4960        pub mirror: VarInt,   // 0=none, 1=left-right, 2=front-back
4961        pub rotation: VarInt, // 0=none, 1=cw90, 2=cw180, 3=ccw90
4962        pub metadata: String,
4963        pub integrity: f32,
4964        pub seed: VarInt,
4965        pub flags: u8, // bit 0=ignore entities, bit 1=show air, bit 2=show bounding box
4966    }
4967
4968    impl PacketId for ServerboundSetStructureBlock {
4969        fn packet_id(_ver: u32) -> u8 {
4970            0x1E
4971        }
4972    }
4973
4974    impl Encode for ServerboundSetStructureBlock {
4975        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
4976            dst.put_u64(self.location);
4977            self.action.encode(dst)?;
4978            self.mode.encode(dst)?;
4979            encode_str(&self.name, dst)?;
4980            dst.put_i8(self.offset_x);
4981            dst.put_i8(self.offset_y);
4982            dst.put_i8(self.offset_z);
4983            dst.put_i8(self.size_x);
4984            dst.put_i8(self.size_y);
4985            dst.put_i8(self.size_z);
4986            self.mirror.encode(dst)?;
4987            self.rotation.encode(dst)?;
4988            encode_str(&self.metadata, dst)?;
4989            dst.put_f32(self.integrity);
4990            self.seed.encode(dst)?;
4991            dst.put_u8(self.flags);
4992            Ok(())
4993        }
4994    }
4995
4996    impl Decode for ServerboundSetStructureBlock {
4997        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
4998            need(src, 8)?;
4999            let location = src.get_u64();
5000            let action = VarInt::decode(src)?;
5001            let mode = VarInt::decode(src)?;
5002            let name = decode_str(src, "ServerboundSetStructureBlock name")?;
5003            need(src, 6)?;
5004            let offset_x = src.get_i8();
5005            let offset_y = src.get_i8();
5006            let offset_z = src.get_i8();
5007            let size_x = src.get_i8();
5008            let size_y = src.get_i8();
5009            let size_z = src.get_i8();
5010            let mirror = VarInt::decode(src)?;
5011            let rotation = VarInt::decode(src)?;
5012            let metadata = decode_str(src, "ServerboundSetStructureBlock metadata")?;
5013            need(src, 4)?;
5014            let integrity = src.get_f32();
5015            let seed = VarInt::decode(src)?;
5016            need(src, 1)?;
5017            let flags = src.get_u8();
5018            Ok(Self {
5019                location,
5020                action,
5021                mode,
5022                name,
5023                offset_x,
5024                offset_y,
5025                offset_z,
5026                size_x,
5027                size_y,
5028                size_z,
5029                mirror,
5030                rotation,
5031                metadata,
5032                integrity,
5033                seed,
5034                flags,
5035            })
5036        }
5037    }
5038
5039    // ── ServerboundSelectTrade (0x1F in 1.12.2) ───────────────────────────────────
5040
5041    #[derive(Debug, Clone, PartialEq)]
5042    pub struct ServerboundSelectTrade {
5043        pub selected_slot: VarInt,
5044    }
5045
5046    impl PacketId for ServerboundSelectTrade {
5047        fn packet_id(_ver: u32) -> u8 {
5048            0x1F
5049        }
5050    }
5051
5052    impl Encode for ServerboundSelectTrade {
5053        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
5054            self.selected_slot.encode(dst)
5055        }
5056    }
5057
5058    impl Decode for ServerboundSelectTrade {
5059        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
5060            Ok(Self {
5061                selected_slot: VarInt::decode(src)?,
5062            })
5063        }
5064    }
5065
5066    // ── ServerboundUpdateCommandBlock (0x21 in 1.12.2) ───────────────────────────
5067
5068    #[derive(Debug, Clone, PartialEq)]
5069    pub struct ServerboundUpdateCommandBlock {
5070        pub location: u64,
5071        pub command: String,
5072        pub mode: VarInt, // 0=sequence, 1=auto, 2=redstone
5073        pub flags: u8,    // bit 0=track output, bit 1=conditional, bit 2=automatic
5074    }
5075
5076    impl PacketId for ServerboundUpdateCommandBlock {
5077        fn packet_id(_ver: u32) -> u8 {
5078            0x21
5079        }
5080    }
5081
5082    impl Encode for ServerboundUpdateCommandBlock {
5083        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
5084            dst.put_u64(self.location);
5085            encode_str(&self.command, dst)?;
5086            self.mode.encode(dst)?;
5087            dst.put_u8(self.flags);
5088            Ok(())
5089        }
5090    }
5091
5092    impl Decode for ServerboundUpdateCommandBlock {
5093        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
5094            need(src, 8)?;
5095            let location = src.get_u64();
5096            let command = decode_str(src, "ServerboundUpdateCommandBlock command")?;
5097            let mode = VarInt::decode(src)?;
5098            need(src, 1)?;
5099            let flags = src.get_u8();
5100            Ok(Self {
5101                location,
5102                command,
5103                mode,
5104                flags,
5105            })
5106        }
5107    }
5108
5109    // ── ServerboundUpdateCommandBlockMinecart (0x22 in 1.12.2) ───────────────────
5110
5111    #[derive(Debug, Clone, PartialEq)]
5112    pub struct ServerboundUpdateCommandBlockMinecart {
5113        pub entity_id: VarInt,
5114        pub command: String,
5115        pub track_output: bool,
5116    }
5117
5118    impl PacketId for ServerboundUpdateCommandBlockMinecart {
5119        fn packet_id(_ver: u32) -> u8 {
5120            0x22
5121        }
5122    }
5123
5124    impl Encode for ServerboundUpdateCommandBlockMinecart {
5125        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
5126            self.entity_id.encode(dst)?;
5127            encode_str(&self.command, dst)?;
5128            dst.put_u8(self.track_output as u8);
5129            Ok(())
5130        }
5131    }
5132
5133    impl Decode for ServerboundUpdateCommandBlockMinecart {
5134        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
5135            let entity_id = VarInt::decode(src)?;
5136            let command = decode_str(src, "ServerboundUpdateCommandBlockMinecart command")?;
5137            need(src, 1)?;
5138            let track_output = src.get_u8() != 0;
5139            Ok(Self {
5140                entity_id,
5141                command,
5142                track_output,
5143            })
5144        }
5145    }
5146}