Skip to main content

kojacoord_protocol/versions/v1_16_5/play/
mod.rs

1use bytes::{Buf, BufMut, Bytes, BytesMut};
2
3use crate::codec::{Decode, Encode, PacketId};
4use crate::error::ProtocolError;
5use crate::types::VarInt;
6
7pub use packets::{
8    ClientboundAcknowledgePlayerDigging, ClientboundAdvancements, ClientboundAttachEntity,
9    ClientboundAwardStats, ClientboundBlockAction, ClientboundBlockBreakAnimation,
10    ClientboundBlockChange, ClientboundBlockEntityData, ClientboundBossBar, ClientboundCamera,
11    ClientboundChangeGameState, ClientboundChatMessage, ClientboundChunkData,
12    ClientboundCloseWindow, ClientboundCollectItem, ClientboundCombatEvent,
13    ClientboundCraftRecipeResponse, ClientboundDeclareCommands, ClientboundDeclareRecipes,
14    ClientboundDestroyEntities, ClientboundDisconnect, ClientboundDisplayScoreboard,
15    ClientboundEffect, ClientboundEntityAnimation, ClientboundEntityEffect,
16    ClientboundEntityEquipment, ClientboundEntityHeadLook, ClientboundEntityMetadata,
17    ClientboundEntityPosition, ClientboundEntityPositionAndRotation, ClientboundEntityProperties,
18    ClientboundEntityRotation, ClientboundEntitySoundEffect, ClientboundEntityStatus,
19    ClientboundEntityTeleport, ClientboundEntityVelocity, ClientboundExplosion,
20    ClientboundFacePlayer, ClientboundHeldItemChange, ClientboundInitializeWorldBorder,
21    ClientboundJoinGame, ClientboundKeepAlive, ClientboundMap, ClientboundMultiBlockChange,
22    ClientboundNamedSoundEffect, ClientboundNbtQueryResponse, ClientboundOpenBook,
23    ClientboundOpenHorseWindow, ClientboundOpenSignEditor, ClientboundOpenWindow,
24    ClientboundParticle, ClientboundPlayerAbilities, ClientboundPlayerInfo,
25    ClientboundPlayerListHeaderAndFooter, ClientboundPlayerPosition, ClientboundPluginMessage,
26    ClientboundRemoveEntityEffect, ClientboundResourcePackSend, ClientboundRespawn,
27    ClientboundScoreboardObjective, ClientboundSelectAdvancementsTab, ClientboundServerDifficulty,
28    ClientboundSetCooldown, ClientboundSetExperience, ClientboundSetPassengers, ClientboundSetSlot,
29    ClientboundSoundEffect, ClientboundSpawnEntity, ClientboundSpawnExperienceOrb,
30    ClientboundSpawnLivingEntity, ClientboundSpawnPainting, ClientboundSpawnPlayer,
31    ClientboundSpawnPosition, ClientboundStopSound, ClientboundTabComplete, ClientboundTags,
32    ClientboundTeams, ClientboundTimeUpdate, ClientboundTitle, ClientboundTradeList,
33    ClientboundUnloadChunk, ClientboundUpdateHealth, ClientboundUpdateLight,
34    ClientboundUpdateScore, ClientboundUpdateViewDistance, ClientboundUpdateViewPosition,
35    ClientboundVehicleMove, ClientboundWindowConfirmation, ClientboundWindowItems,
36    ClientboundWindowProperty, ClientboundWorldBorder, InteractAction, ServerboundChatMessage,
37    ServerboundClickWindow, ServerboundClickWindowButton, ServerboundCloseWindow,
38    ServerboundCraftRecipeRequest, ServerboundCreativeModeSlot, ServerboundEditBook,
39    ServerboundEntityAction, ServerboundGenerateStructure, ServerboundHeldItemChange,
40    ServerboundInteract, ServerboundKeepAlive, ServerboundLockDifficulty, ServerboundMovePlayerPos,
41    ServerboundMovePlayerPosRot, ServerboundMovePlayerPosRotSb, ServerboundMovePlayerRot,
42    ServerboundMovePlayerStatus, ServerboundPickItem, ServerboundPlaceRecipe,
43    ServerboundPlayerAbilities, ServerboundPlayerBlockPlacement, ServerboundPlayerDigging,
44    ServerboundPluginMessage, ServerboundQueryBlockNbt, ServerboundQueryEntityNbt,
45    ServerboundResourcePackStatus, ServerboundSelectTrade, ServerboundSetBeaconEffect,
46    ServerboundSetDifficulty, ServerboundSpectate, ServerboundSteerBoat, ServerboundSteerVehicle,
47    ServerboundSwingArm, ServerboundTabComplete, ServerboundTeleportConfirm,
48    ServerboundUpdateCommandBlock, ServerboundUpdateCommandBlockMinecart,
49    ServerboundUpdateJigsawBlock, ServerboundUpdateSign, ServerboundUpdateStructureBlock,
50    ServerboundUseEntity, ServerboundUseItem, ServerboundVehicleMove,
51};
52
53fn need(src: &Bytes, n: usize) -> Result<(), ProtocolError> {
54    if src.remaining() < n {
55        return Err(ProtocolError::Io(std::io::Error::new(
56            std::io::ErrorKind::UnexpectedEof,
57            "Not enough bytes",
58        )));
59    }
60    Ok(())
61}
62
63fn encode_string(s: &str, dst: &mut BytesMut) -> Result<(), ProtocolError> {
64    let bytes = s.as_bytes();
65    VarInt(bytes.len() as i32).encode(dst)?;
66    dst.put_slice(bytes);
67    Ok(())
68}
69
70fn decode_string(src: &mut Bytes) -> Result<String, ProtocolError> {
71    let len = VarInt::decode(src)?.0 as usize;
72    if src.remaining() < len {
73        return Err(ProtocolError::Io(std::io::Error::new(
74            std::io::ErrorKind::UnexpectedEof,
75            "Missing bytes for string",
76        )));
77    }
78    let mut buf = vec![0u8; len];
79    src.copy_to_slice(&mut buf);
80    String::from_utf8(buf).map_err(|_| {
81        ProtocolError::Io(std::io::Error::new(
82            std::io::ErrorKind::InvalidData,
83            "Invalid UTF-8 in string",
84        ))
85    })
86}
87
88mod packets {
89    use super::*;
90
91    macro_rules! raw_packet {
92        ($name:ident, $id:expr) => {
93            #[derive(Debug, Clone, PartialEq)]
94            pub struct $name {
95                pub raw: Vec<u8>,
96            }
97            impl PacketId for $name {
98                fn packet_id(_ver: u32) -> u8 {
99                    $id
100                }
101            }
102            impl Encode for $name {
103                fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
104                    dst.put_slice(&self.raw);
105                    Ok(())
106                }
107            }
108            impl Decode for $name {
109                fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
110                    let len = src.remaining();
111                    let mut raw = vec![0u8; len];
112                    src.copy_to_slice(&mut raw);
113                    Ok(Self { raw })
114                }
115            }
116        };
117    }
118
119    #[derive(Debug, Clone, PartialEq)]
120    pub struct ClientboundSpawnEntity {
121        pub entity_id: VarInt,
122        pub entity_uuid: uuid::Uuid,
123        pub entity_type: VarInt,
124        pub x: f64,
125        pub y: f64,
126        pub z: f64,
127        pub pitch: i8,
128        pub yaw: i8,
129        pub head_pitch: i8,
130        pub data: VarInt,
131        pub velocity_x: i16,
132        pub velocity_y: i16,
133        pub velocity_z: i16,
134    }
135
136    impl PacketId for ClientboundSpawnEntity {
137        fn packet_id(_ver: u32) -> u8 {
138            0x00
139        }
140    }
141
142    impl Encode for ClientboundSpawnEntity {
143        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
144            self.entity_id.encode(dst)?;
145            let (hi, lo) = self.entity_uuid.as_u64_pair();
146            dst.put_i64(hi as i64);
147            dst.put_i64(lo as i64);
148            self.entity_type.encode(dst)?;
149            dst.put_f64(self.x);
150            dst.put_f64(self.y);
151            dst.put_f64(self.z);
152            dst.put_i8(self.pitch);
153            dst.put_i8(self.yaw);
154            dst.put_i8(self.head_pitch);
155            self.data.encode(dst)?;
156            dst.put_i16(self.velocity_x);
157            dst.put_i16(self.velocity_y);
158            dst.put_i16(self.velocity_z);
159            Ok(())
160        }
161    }
162
163    impl Decode for ClientboundSpawnEntity {
164        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
165            let entity_id = VarInt::decode(src)?;
166            let hi = src.get_i64();
167            let lo = src.get_i64();
168            let entity_uuid = uuid::Uuid::from_u64_pair(hi as u64, lo as u64);
169            let entity_type = VarInt::decode(src)?;
170            let x = src.get_f64();
171            let y = src.get_f64();
172            let z = src.get_f64();
173            let pitch = src.get_i8();
174            let yaw = src.get_i8();
175            let head_pitch = src.get_i8();
176            let data = VarInt::decode(src)?;
177            let velocity_x = src.get_i16();
178            let velocity_y = src.get_i16();
179            let velocity_z = src.get_i16();
180            Ok(Self {
181                entity_id,
182                entity_uuid,
183                entity_type,
184                x,
185                y,
186                z,
187                pitch,
188                yaw,
189                head_pitch,
190                data,
191                velocity_x,
192                velocity_y,
193                velocity_z,
194            })
195        }
196    }
197
198    raw_packet!(ClientboundSpawnExperienceOrb, 0x01);
199
200    #[derive(Debug, Clone, PartialEq)]
201    pub struct ClientboundSpawnLivingEntity {
202        pub entity_id: VarInt,
203        pub entity_uuid: uuid::Uuid,
204        pub entity_type: VarInt,
205        pub x: f64,
206        pub y: f64,
207        pub z: f64,
208        pub yaw: i8,
209        pub pitch: i8,
210        pub head_pitch: i8,
211        pub velocity_x: i16,
212        pub velocity_y: i16,
213        pub velocity_z: i16,
214    }
215
216    impl PacketId for ClientboundSpawnLivingEntity {
217        fn packet_id(_ver: u32) -> u8 {
218            0x02
219        }
220    }
221
222    impl Encode for ClientboundSpawnLivingEntity {
223        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
224            self.entity_id.encode(dst)?;
225            let (hi, lo) = self.entity_uuid.as_u64_pair();
226            dst.put_i64(hi as i64);
227            dst.put_i64(lo as i64);
228            self.entity_type.encode(dst)?;
229            dst.put_f64(self.x);
230            dst.put_f64(self.y);
231            dst.put_f64(self.z);
232            dst.put_i8(self.yaw);
233            dst.put_i8(self.pitch);
234            dst.put_i8(self.head_pitch);
235            dst.put_i16(self.velocity_x);
236            dst.put_i16(self.velocity_y);
237            dst.put_i16(self.velocity_z);
238            Ok(())
239        }
240    }
241
242    impl Decode for ClientboundSpawnLivingEntity {
243        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
244            let entity_id = VarInt::decode(src)?;
245            let hi = src.get_i64();
246            let lo = src.get_i64();
247            let entity_uuid = uuid::Uuid::from_u64_pair(hi as u64, lo as u64);
248            let entity_type = VarInt::decode(src)?;
249            let x = src.get_f64();
250            let y = src.get_f64();
251            let z = src.get_f64();
252            let yaw = src.get_i8();
253            let pitch = src.get_i8();
254            let head_pitch = src.get_i8();
255            let velocity_x = src.get_i16();
256            let velocity_y = src.get_i16();
257            let velocity_z = src.get_i16();
258            Ok(Self {
259                entity_id,
260                entity_uuid,
261                entity_type,
262                x,
263                y,
264                z,
265                yaw,
266                pitch,
267                head_pitch,
268                velocity_x,
269                velocity_y,
270                velocity_z,
271            })
272        }
273    }
274
275    raw_packet!(ClientboundSpawnPainting, 0x03);
276
277    #[derive(Debug, Clone, PartialEq)]
278    pub struct ClientboundSpawnPlayer {
279        pub entity_id: VarInt,
280        pub player_uuid: uuid::Uuid,
281        pub x: f64,
282        pub y: f64,
283        pub z: f64,
284        pub yaw: i8,
285        pub pitch: i8,
286    }
287
288    impl PacketId for ClientboundSpawnPlayer {
289        fn packet_id(_ver: u32) -> u8 {
290            0x04
291        }
292    }
293
294    impl Encode for ClientboundSpawnPlayer {
295        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
296            self.entity_id.encode(dst)?;
297            let (hi, lo) = self.player_uuid.as_u64_pair();
298            dst.put_i64(hi as i64);
299            dst.put_i64(lo as i64);
300            dst.put_f64(self.x);
301            dst.put_f64(self.y);
302            dst.put_f64(self.z);
303            dst.put_i8(self.yaw);
304            dst.put_i8(self.pitch);
305            Ok(())
306        }
307    }
308
309    impl Decode for ClientboundSpawnPlayer {
310        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
311            let entity_id = VarInt::decode(src)?;
312            let hi = src.get_i64();
313            let lo = src.get_i64();
314            let player_uuid = uuid::Uuid::from_u64_pair(hi as u64, lo as u64);
315            let x = src.get_f64();
316            let y = src.get_f64();
317            let z = src.get_f64();
318            let yaw = src.get_i8();
319            let pitch = src.get_i8();
320            Ok(Self {
321                entity_id,
322                player_uuid,
323                x,
324                y,
325                z,
326                yaw,
327                pitch,
328            })
329        }
330    }
331
332    raw_packet!(ClientboundEntityAnimation, 0x05);
333    raw_packet!(ClientboundAwardStats, 0x06);
334    raw_packet!(ClientboundAcknowledgePlayerDigging, 0x07);
335    raw_packet!(ClientboundBlockBreakAnimation, 0x08);
336    raw_packet!(ClientboundBlockEntityData, 0x09);
337    raw_packet!(ClientboundBlockAction, 0x0A);
338
339    #[derive(Debug, Clone, PartialEq)]
340    pub struct ClientboundBlockChange {
341        pub location: VarInt,
342        pub block_state: VarInt,
343    }
344
345    impl PacketId for ClientboundBlockChange {
346        fn packet_id(_ver: u32) -> u8 {
347            0x0B
348        }
349    }
350
351    impl Encode for ClientboundBlockChange {
352        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
353            self.location.encode(dst)?;
354            self.block_state.encode(dst)?;
355            Ok(())
356        }
357    }
358
359    impl Decode for ClientboundBlockChange {
360        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
361            let location = VarInt::decode(src)?;
362            let block_state = VarInt::decode(src)?;
363            Ok(Self {
364                location,
365                block_state,
366            })
367        }
368    }
369
370    raw_packet!(ClientboundBossBar, 0x0C);
371    raw_packet!(ClientboundServerDifficulty, 0x0D);
372
373    raw_packet!(ClientboundTabComplete, 0x0F);
374    raw_packet!(ClientboundDeclareCommands, 0x10);
375    raw_packet!(ClientboundWindowConfirmation, 0x11);
376
377    #[derive(Debug, Clone, PartialEq)]
378    pub struct ClientboundCloseWindow {
379        pub window_id: u8,
380    }
381
382    impl PacketId for ClientboundCloseWindow {
383        fn packet_id(_ver: u32) -> u8 {
384            0x12
385        }
386    }
387
388    impl Encode for ClientboundCloseWindow {
389        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
390            dst.put_u8(self.window_id);
391            Ok(())
392        }
393    }
394
395    impl Decode for ClientboundCloseWindow {
396        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
397            need(src, 1)?;
398            let window_id = src.get_u8();
399            Ok(Self { window_id })
400        }
401    }
402
403    raw_packet!(ClientboundWindowProperty, 0x14);
404    raw_packet!(ClientboundSetCooldown, 0x16);
405
406    #[derive(Debug, Clone, PartialEq)]
407    pub struct ClientboundWindowItems {
408        pub window_id: u8,
409        pub slots: Vec<crate::types::Slot>,
410        pub carried_item: crate::types::Slot,
411    }
412
413    impl PacketId for ClientboundWindowItems {
414        fn packet_id(_ver: u32) -> u8 {
415            0x13
416        }
417    }
418
419    impl Encode for ClientboundWindowItems {
420        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
421            dst.put_u8(self.window_id);
422            VarInt(self.slots.len() as i32).encode(dst)?;
423            for slot in &self.slots {
424                slot.encode(dst)?;
425            }
426            self.carried_item.encode(dst)?;
427            Ok(())
428        }
429    }
430
431    impl Decode for ClientboundWindowItems {
432        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
433            need(src, 1)?;
434            let window_id = src.get_u8();
435            let count = VarInt::decode(src)?.0 as usize;
436            let mut slots = Vec::with_capacity(count);
437            for _ in 0..count {
438                slots.push(crate::types::Slot::decode(src)?);
439            }
440            let carried_item = crate::types::Slot::decode(src)?;
441            Ok(Self {
442                window_id,
443                slots,
444                carried_item,
445            })
446        }
447    }
448
449    #[derive(Debug, Clone, PartialEq)]
450    pub struct ClientboundSetSlot {
451        pub window_id: u8,
452        pub slot: i16,
453        pub slot_data: crate::types::Slot,
454    }
455
456    impl PacketId for ClientboundSetSlot {
457        fn packet_id(_ver: u32) -> u8 {
458            0x15
459        }
460    }
461
462    impl Encode for ClientboundSetSlot {
463        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
464            dst.put_u8(self.window_id);
465            dst.put_i16(self.slot);
466            self.slot_data.encode(dst)?;
467            Ok(())
468        }
469    }
470
471    impl Decode for ClientboundSetSlot {
472        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
473            need(src, 1 + 2)?;
474            let window_id = src.get_u8();
475            let slot = src.get_i16();
476            let slot_data = crate::types::Slot::decode(src)?;
477            Ok(Self {
478                window_id,
479                slot,
480                slot_data,
481            })
482        }
483    }
484
485    raw_packet!(ClientboundEntityStatus, 0x1A);
486    raw_packet!(ClientboundExplosion, 0x1B);
487    raw_packet!(ClientboundUnloadChunk, 0x1C);
488    raw_packet!(ClientboundChangeGameState, 0x1D);
489    raw_packet!(ClientboundOpenHorseWindow, 0x1E);
490
491    raw_packet!(ClientboundInitializeWorldBorder, 0x20);
492    raw_packet!(ClientboundChunkData, 0x21);
493    raw_packet!(ClientboundEffect, 0x22);
494    raw_packet!(ClientboundParticle, 0x23);
495    raw_packet!(ClientboundUpdateLight, 0x24);
496
497    raw_packet!(ClientboundMap, 0x25);
498    raw_packet!(ClientboundTradeList, 0x26);
499
500    #[derive(Debug, Clone, PartialEq)]
501    pub struct ClientboundEntityPosition {
502        pub entity_id: VarInt,
503        pub delta_x: i16,
504        pub delta_y: i16,
505        pub delta_z: i16,
506        pub on_ground: bool,
507    }
508
509    impl PacketId for ClientboundEntityPosition {
510        fn packet_id(_ver: u32) -> u8 {
511            0x27
512        }
513    }
514
515    impl Encode for ClientboundEntityPosition {
516        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
517            self.entity_id.encode(dst)?;
518            dst.put_i16(self.delta_x);
519            dst.put_i16(self.delta_y);
520            dst.put_i16(self.delta_z);
521            dst.put_u8(if self.on_ground { 1 } else { 0 });
522            Ok(())
523        }
524    }
525
526    impl Decode for ClientboundEntityPosition {
527        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
528            let entity_id = VarInt::decode(src)?;
529            let delta_x = src.get_i16();
530            let delta_y = src.get_i16();
531            let delta_z = src.get_i16();
532            let on_ground = src.get_u8() != 0;
533            Ok(Self {
534                entity_id,
535                delta_x,
536                delta_y,
537                delta_z,
538                on_ground,
539            })
540        }
541    }
542
543    #[derive(Debug, Clone, PartialEq)]
544    pub struct ClientboundEntityPositionAndRotation {
545        pub entity_id: VarInt,
546        pub delta_x: i16,
547        pub delta_y: i16,
548        pub delta_z: i16,
549        pub yaw: i8,
550        pub pitch: i8,
551        pub on_ground: bool,
552    }
553
554    impl PacketId for ClientboundEntityPositionAndRotation {
555        fn packet_id(_ver: u32) -> u8 {
556            0x28
557        }
558    }
559
560    impl Encode for ClientboundEntityPositionAndRotation {
561        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
562            self.entity_id.encode(dst)?;
563            dst.put_i16(self.delta_x);
564            dst.put_i16(self.delta_y);
565            dst.put_i16(self.delta_z);
566            dst.put_i8(self.yaw);
567            dst.put_i8(self.pitch);
568            dst.put_u8(if self.on_ground { 1 } else { 0 });
569            Ok(())
570        }
571    }
572
573    impl Decode for ClientboundEntityPositionAndRotation {
574        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
575            let entity_id = VarInt::decode(src)?;
576            let delta_x = src.get_i16();
577            let delta_y = src.get_i16();
578            let delta_z = src.get_i16();
579            let yaw = src.get_i8();
580            let pitch = src.get_i8();
581            let on_ground = src.get_u8() != 0;
582            Ok(Self {
583                entity_id,
584                delta_x,
585                delta_y,
586                delta_z,
587                yaw,
588                pitch,
589                on_ground,
590            })
591        }
592    }
593
594    #[derive(Debug, Clone, PartialEq)]
595    pub struct ClientboundEntityRotation {
596        pub entity_id: VarInt,
597        pub yaw: i8,
598        pub pitch: i8,
599        pub on_ground: bool,
600    }
601
602    impl PacketId for ClientboundEntityRotation {
603        fn packet_id(_ver: u32) -> u8 {
604            0x29
605        }
606    }
607
608    impl Encode for ClientboundEntityRotation {
609        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
610            self.entity_id.encode(dst)?;
611            dst.put_i8(self.yaw);
612            dst.put_i8(self.pitch);
613            dst.put_u8(if self.on_ground { 1 } else { 0 });
614            Ok(())
615        }
616    }
617
618    impl Decode for ClientboundEntityRotation {
619        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
620            let entity_id = VarInt::decode(src)?;
621            let yaw = src.get_i8();
622            let pitch = src.get_i8();
623            let on_ground = src.get_u8() != 0;
624            Ok(Self {
625                entity_id,
626                yaw,
627                pitch,
628                on_ground,
629            })
630        }
631    }
632
633    raw_packet!(ClientboundVehicleMove, 0x2A);
634    raw_packet!(ClientboundOpenBook, 0x2B);
635
636    #[derive(Debug, Clone, PartialEq)]
637    pub struct ClientboundOpenWindow {
638        pub window_id: VarInt,
639        pub window_type: VarInt,
640        pub window_title: String,
641    }
642
643    impl PacketId for ClientboundOpenWindow {
644        fn packet_id(_ver: u32) -> u8 {
645            0x2C
646        }
647    }
648
649    impl Encode for ClientboundOpenWindow {
650        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
651            self.window_id.encode(dst)?;
652            self.window_type.encode(dst)?;
653            encode_string(&self.window_title, dst)?;
654            Ok(())
655        }
656    }
657
658    impl Decode for ClientboundOpenWindow {
659        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
660            let window_id = VarInt::decode(src)?;
661            let window_type = VarInt::decode(src)?;
662            let window_title = decode_string(src)?;
663            Ok(Self {
664                window_id,
665                window_type,
666                window_title,
667            })
668        }
669    }
670
671    raw_packet!(ClientboundOpenSignEditor, 0x2D);
672    raw_packet!(ClientboundCraftRecipeResponse, 0x2E);
673
674    #[derive(Debug, Clone, PartialEq)]
675    pub struct ClientboundPlayerAbilities {
676        pub flags: u8,
677        pub flying_speed: f32,
678        pub walking_speed: f32,
679    }
680
681    impl PacketId for ClientboundPlayerAbilities {
682        fn packet_id(_ver: u32) -> u8 {
683            0x2F
684        }
685    }
686
687    impl Encode for ClientboundPlayerAbilities {
688        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
689            dst.put_u8(self.flags);
690            dst.put_f32(self.flying_speed);
691            dst.put_f32(self.walking_speed);
692            Ok(())
693        }
694    }
695
696    impl Decode for ClientboundPlayerAbilities {
697        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
698            need(src, 1 + 4 + 4)?;
699            let flags = src.get_u8();
700            let flying_speed = src.get_f32();
701            let walking_speed = src.get_f32();
702            Ok(Self {
703                flags,
704                flying_speed,
705                walking_speed,
706            })
707        }
708    }
709
710    raw_packet!(ClientboundCombatEvent, 0x30);
711    raw_packet!(ClientboundPlayerInfo, 0x31);
712    raw_packet!(ClientboundFacePlayer, 0x31);
713
714    #[derive(Debug, Clone, PartialEq)]
715    pub struct ClientboundDestroyEntities {
716        pub entity_ids: VarInt,
717        pub count: VarInt,
718        pub entities: Vec<VarInt>,
719    }
720
721    impl PacketId for ClientboundDestroyEntities {
722        fn packet_id(_ver: u32) -> u8 {
723            0x36
724        }
725    }
726
727    impl Encode for ClientboundDestroyEntities {
728        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
729            self.entity_ids.encode(dst)?;
730            for entity in &self.entities {
731                entity.encode(dst)?;
732            }
733            Ok(())
734        }
735    }
736
737    impl Decode for ClientboundDestroyEntities {
738        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
739            let entity_ids = VarInt::decode(src)?;
740            let count = entity_ids.0 as usize;
741            let mut entities = Vec::with_capacity(count);
742            for _ in 0..count {
743                entities.push(VarInt::decode(src)?);
744            }
745            Ok(Self {
746                entity_ids,
747                count: entity_ids,
748                entities,
749            })
750        }
751    }
752
753    raw_packet!(ClientboundRemoveEntityEffect, 0x37);
754    raw_packet!(ClientboundResourcePackSend, 0x38);
755
756    #[derive(Debug, Clone, PartialEq)]
757    pub struct ClientboundEntityHeadLook {
758        pub entity_id: VarInt,
759        pub head_yaw: i8,
760    }
761
762    impl PacketId for ClientboundEntityHeadLook {
763        fn packet_id(_ver: u32) -> u8 {
764            0x3A
765        }
766    }
767
768    impl Encode for ClientboundEntityHeadLook {
769        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
770            self.entity_id.encode(dst)?;
771            dst.put_i8(self.head_yaw);
772            Ok(())
773        }
774    }
775
776    impl Decode for ClientboundEntityHeadLook {
777        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
778            let entity_id = VarInt::decode(src)?;
779            let head_yaw = src.get_i8();
780            Ok(Self {
781                entity_id,
782                head_yaw,
783            })
784        }
785    }
786
787    raw_packet!(ClientboundMultiBlockChange, 0x3B);
788    raw_packet!(ClientboundSelectAdvancementsTab, 0x3C);
789    raw_packet!(ClientboundWorldBorder, 0x3D);
790    raw_packet!(ClientboundCamera, 0x3E);
791
792    #[derive(Debug, Clone, PartialEq)]
793    pub struct ClientboundHeldItemChange {
794        pub slot: i8,
795    }
796
797    impl PacketId for ClientboundHeldItemChange {
798        fn packet_id(_ver: u32) -> u8 {
799            0x3F
800        }
801    }
802
803    impl Encode for ClientboundHeldItemChange {
804        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
805            dst.put_i8(self.slot);
806            Ok(())
807        }
808    }
809
810    impl Decode for ClientboundHeldItemChange {
811        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
812            need(src, 1)?;
813            let slot = src.get_i8();
814            Ok(Self { slot })
815        }
816    }
817
818    raw_packet!(ClientboundUpdateViewPosition, 0x40);
819    raw_packet!(ClientboundUpdateViewDistance, 0x41);
820
821    #[derive(Debug, Clone, PartialEq)]
822    pub struct ClientboundSpawnPosition {
823        pub location: VarInt,
824    }
825
826    impl PacketId for ClientboundSpawnPosition {
827        fn packet_id(_ver: u32) -> u8 {
828            0x42
829        }
830    }
831
832    impl Encode for ClientboundSpawnPosition {
833        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
834            self.location.encode(dst)?;
835            Ok(())
836        }
837    }
838
839    impl Decode for ClientboundSpawnPosition {
840        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
841            let location = VarInt::decode(src)?;
842            Ok(Self { location })
843        }
844    }
845
846    raw_packet!(ClientboundDisplayScoreboard, 0x43);
847    raw_packet!(ClientboundEntityMetadata, 0x44);
848    raw_packet!(ClientboundAttachEntity, 0x45);
849
850    #[derive(Debug, Clone, PartialEq)]
851    pub struct ClientboundEntityVelocity {
852        pub entity_id: VarInt,
853        pub velocity_x: i16,
854        pub velocity_y: i16,
855        pub velocity_z: i16,
856    }
857
858    impl PacketId for ClientboundEntityVelocity {
859        fn packet_id(_ver: u32) -> u8 {
860            0x46
861        }
862    }
863
864    impl Encode for ClientboundEntityVelocity {
865        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
866            self.entity_id.encode(dst)?;
867            dst.put_i16(self.velocity_x);
868            dst.put_i16(self.velocity_y);
869            dst.put_i16(self.velocity_z);
870            Ok(())
871        }
872    }
873
874    impl Decode for ClientboundEntityVelocity {
875        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
876            let entity_id = VarInt::decode(src)?;
877            let velocity_x = src.get_i16();
878            let velocity_y = src.get_i16();
879            let velocity_z = src.get_i16();
880            Ok(Self {
881                entity_id,
882                velocity_x,
883                velocity_y,
884                velocity_z,
885            })
886        }
887    }
888
889    #[derive(Debug, Clone, PartialEq)]
890    pub struct ClientboundEntityEquipment {
891        pub entity_id: i32,
892        pub slot: VarInt,
893        pub item: crate::types::Slot,
894    }
895
896    impl PacketId for ClientboundEntityEquipment {
897        fn packet_id(_ver: u32) -> u8 {
898            0x47
899        }
900    }
901
902    impl Encode for ClientboundEntityEquipment {
903        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
904            VarInt(self.entity_id).encode(dst)?;
905            self.slot.encode(dst)?;
906            self.item.encode(dst)?;
907            Ok(())
908        }
909    }
910
911    impl Decode for ClientboundEntityEquipment {
912        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
913            let entity_id = VarInt::decode(src)?.0;
914            let slot = VarInt::decode(src)?;
915            let item = crate::types::Slot::decode(src)?;
916            Ok(Self {
917                entity_id,
918                slot,
919                item,
920            })
921        }
922    }
923
924    #[derive(Debug, Clone, PartialEq)]
925    pub struct ClientboundSetExperience {
926        pub experience_bar: f32,
927        pub level: VarInt,
928        pub total_experience: VarInt,
929    }
930
931    impl PacketId for ClientboundSetExperience {
932        fn packet_id(_ver: u32) -> u8 {
933            0x48
934        }
935    }
936
937    impl Encode for ClientboundSetExperience {
938        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
939            dst.put_f32(self.experience_bar);
940            self.level.encode(dst)?;
941            self.total_experience.encode(dst)?;
942            Ok(())
943        }
944    }
945
946    impl Decode for ClientboundSetExperience {
947        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
948            need(src, 4)?;
949            let experience_bar = src.get_f32();
950            let level = VarInt::decode(src)?;
951            let total_experience = VarInt::decode(src)?;
952            Ok(Self {
953                experience_bar,
954                level,
955                total_experience,
956            })
957        }
958    }
959
960    #[derive(Debug, Clone, PartialEq)]
961    pub struct ClientboundUpdateHealth {
962        pub health: f32,
963        pub food: VarInt,
964        pub food_saturation: f32,
965    }
966
967    impl PacketId for ClientboundUpdateHealth {
968        fn packet_id(_ver: u32) -> u8 {
969            0x49
970        }
971    }
972
973    impl Encode for ClientboundUpdateHealth {
974        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
975            dst.put_f32(self.health);
976            self.food.encode(dst)?;
977            dst.put_f32(self.food_saturation);
978            Ok(())
979        }
980    }
981
982    impl Decode for ClientboundUpdateHealth {
983        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
984            need(src, 4)?;
985            let health = src.get_f32();
986            let food = VarInt::decode(src)?;
987            let food_saturation = src.get_f32();
988            Ok(Self {
989                health,
990                food,
991                food_saturation,
992            })
993        }
994    }
995
996    raw_packet!(ClientboundScoreboardObjective, 0x4A);
997    raw_packet!(ClientboundSetPassengers, 0x4B);
998    raw_packet!(ClientboundTeams, 0x4C);
999    raw_packet!(ClientboundUpdateScore, 0x4D);
1000
1001    #[derive(Debug, Clone, PartialEq)]
1002    pub struct ClientboundTimeUpdate {
1003        pub world_age: i64,
1004        pub time_of_day: i64,
1005    }
1006
1007    impl PacketId for ClientboundTimeUpdate {
1008        fn packet_id(_ver: u32) -> u8 {
1009            0x4E
1010        }
1011    }
1012
1013    impl Encode for ClientboundTimeUpdate {
1014        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1015            dst.put_i64(self.world_age);
1016            dst.put_i64(self.time_of_day);
1017            Ok(())
1018        }
1019    }
1020
1021    impl Decode for ClientboundTimeUpdate {
1022        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1023            need(src, 8 + 8)?;
1024            let world_age = src.get_i64();
1025            let time_of_day = src.get_i64();
1026            Ok(Self {
1027                world_age,
1028                time_of_day,
1029            })
1030        }
1031    }
1032
1033    #[derive(Debug, Clone, PartialEq)]
1034    pub struct ClientboundTitle {
1035        pub action: VarInt,
1036        pub title_text: Option<String>,
1037        pub fade_in: i32,
1038        pub stay: i32,
1039        pub fade_out: i32,
1040    }
1041
1042    impl PacketId for ClientboundTitle {
1043        fn packet_id(_ver: u32) -> u8 {
1044            0x4F
1045        }
1046    }
1047
1048    impl Encode for ClientboundTitle {
1049        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1050            self.action.encode(dst)?;
1051            if let Some(ref text) = self.title_text {
1052                encode_string(text, dst)?;
1053                dst.put_i32(self.fade_in);
1054                dst.put_i32(self.stay);
1055                dst.put_i32(self.fade_out);
1056            }
1057            Ok(())
1058        }
1059    }
1060
1061    impl Decode for ClientboundTitle {
1062        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1063            let action = VarInt::decode(src)?;
1064            let mut title_text = None;
1065            let mut fade_in = 0;
1066            let mut stay = 0;
1067            let mut fade_out = 0;
1068            if action.0 == 0 || action.0 == 2 {
1069                title_text = Some(decode_string(src)?);
1070                fade_in = src.get_i32();
1071                stay = src.get_i32();
1072                fade_out = src.get_i32();
1073            }
1074            Ok(Self {
1075                action,
1076                title_text,
1077                fade_in,
1078                stay,
1079                fade_out,
1080            })
1081        }
1082    }
1083
1084    raw_packet!(ClientboundEntitySoundEffect, 0x50);
1085    raw_packet!(ClientboundStopSound, 0x52);
1086    raw_packet!(ClientboundSoundEffect, 0x4D);
1087
1088    #[derive(Debug, Clone, PartialEq)]
1089    pub struct ClientboundPlayerListHeaderAndFooter {
1090        pub header: String,
1091        pub footer: String,
1092    }
1093
1094    impl PacketId for ClientboundPlayerListHeaderAndFooter {
1095        fn packet_id(_ver: u32) -> u8 {
1096            0x53
1097        }
1098    }
1099
1100    impl Encode for ClientboundPlayerListHeaderAndFooter {
1101        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1102            encode_string(&self.header, dst)?;
1103            encode_string(&self.footer, dst)?;
1104            Ok(())
1105        }
1106    }
1107
1108    impl Decode for ClientboundPlayerListHeaderAndFooter {
1109        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1110            let header = decode_string(src)?;
1111            let footer = decode_string(src)?;
1112            Ok(Self { header, footer })
1113        }
1114    }
1115
1116    raw_packet!(ClientboundNbtQueryResponse, 0x54);
1117    raw_packet!(ClientboundCollectItem, 0x55);
1118
1119    #[derive(Debug, Clone, PartialEq)]
1120    pub struct ClientboundEntityTeleport {
1121        pub entity_id: VarInt,
1122        pub x: f64,
1123        pub y: f64,
1124        pub z: f64,
1125        pub yaw: i8,
1126        pub pitch: i8,
1127        pub on_ground: bool,
1128    }
1129
1130    impl PacketId for ClientboundEntityTeleport {
1131        fn packet_id(_ver: u32) -> u8 {
1132            0x56
1133        }
1134    }
1135
1136    impl Encode for ClientboundEntityTeleport {
1137        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1138            self.entity_id.encode(dst)?;
1139            dst.put_f64(self.x);
1140            dst.put_f64(self.y);
1141            dst.put_f64(self.z);
1142            dst.put_i8(self.yaw);
1143            dst.put_i8(self.pitch);
1144            dst.put_u8(if self.on_ground { 1 } else { 0 });
1145            Ok(())
1146        }
1147    }
1148
1149    impl Decode for ClientboundEntityTeleport {
1150        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1151            let entity_id = VarInt::decode(src)?;
1152            let x = src.get_f64();
1153            let y = src.get_f64();
1154            let z = src.get_f64();
1155            let yaw = src.get_i8();
1156            let pitch = src.get_i8();
1157            let on_ground = src.get_u8() != 0;
1158            Ok(Self {
1159                entity_id,
1160                x,
1161                y,
1162                z,
1163                yaw,
1164                pitch,
1165                on_ground,
1166            })
1167        }
1168    }
1169
1170    raw_packet!(ClientboundAdvancements, 0x57);
1171    raw_packet!(ClientboundEntityProperties, 0x58);
1172    raw_packet!(ClientboundEntityEffect, 0x59);
1173    raw_packet!(ClientboundDeclareRecipes, 0x5A);
1174    raw_packet!(ClientboundTags, 0x5B);
1175
1176    #[derive(Debug, Clone, PartialEq)]
1177    pub struct ServerboundTeleportConfirm {
1178        pub teleport_id: VarInt,
1179    }
1180
1181    impl PacketId for ServerboundTeleportConfirm {
1182        fn packet_id(_ver: u32) -> u8 {
1183            0x00
1184        }
1185    }
1186
1187    impl Encode for ServerboundTeleportConfirm {
1188        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1189            self.teleport_id.encode(dst)?;
1190            Ok(())
1191        }
1192    }
1193
1194    impl Decode for ServerboundTeleportConfirm {
1195        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1196            let teleport_id = VarInt::decode(src)?;
1197            Ok(Self { teleport_id })
1198        }
1199    }
1200
1201    raw_packet!(ServerboundQueryBlockNbt, 0x01);
1202    raw_packet!(ServerboundSetDifficulty, 0x02);
1203
1204    raw_packet!(ServerboundTabComplete, 0x06);
1205    raw_packet!(ServerboundClickWindowButton, 0x08);
1206    raw_packet!(ServerboundClickWindow, 0x09);
1207    raw_packet!(ServerboundCloseWindow, 0x0A);
1208    raw_packet!(ServerboundEditBook, 0x0C);
1209    raw_packet!(ServerboundQueryEntityNbt, 0x0D);
1210
1211    raw_packet!(ServerboundGenerateStructure, 0x0F);
1212
1213    raw_packet!(ServerboundLockDifficulty, 0x11);
1214
1215    raw_packet!(ServerboundMovePlayerStatus, 0x15);
1216    raw_packet!(ServerboundVehicleMove, 0x16);
1217    raw_packet!(ServerboundSteerBoat, 0x17);
1218    raw_packet!(ServerboundPickItem, 0x18);
1219    raw_packet!(ServerboundCraftRecipeRequest, 0x19);
1220    raw_packet!(ServerboundPlayerAbilities, 0x1A);
1221    raw_packet!(ServerboundPlayerDigging, 0x1B);
1222    raw_packet!(ServerboundEntityAction, 0x1C);
1223    raw_packet!(ServerboundSteerVehicle, 0x1D);
1224    raw_packet!(ServerboundPlaceRecipe, 0x1F);
1225    raw_packet!(ServerboundMovePlayerPosRotSb, 0x20);
1226    raw_packet!(ServerboundResourcePackStatus, 0x21);
1227    raw_packet!(ServerboundSelectTrade, 0x22);
1228    raw_packet!(ServerboundSetBeaconEffect, 0x23);
1229    raw_packet!(ServerboundHeldItemChange, 0x25);
1230    raw_packet!(ServerboundUpdateCommandBlock, 0x26);
1231    raw_packet!(ServerboundUpdateCommandBlockMinecart, 0x27);
1232    raw_packet!(ServerboundCreativeModeSlot, 0x28);
1233    raw_packet!(ServerboundUpdateJigsawBlock, 0x29);
1234    raw_packet!(ServerboundUpdateStructureBlock, 0x2A);
1235    raw_packet!(ServerboundUpdateSign, 0x2B);
1236    raw_packet!(ServerboundSwingArm, 0x2C);
1237    raw_packet!(ServerboundSpectate, 0x2D);
1238    raw_packet!(ServerboundPlayerBlockPlacement, 0x2E);
1239    raw_packet!(ServerboundUseItem, 0x2F);
1240
1241    raw_packet!(ServerboundUseEntity, 0x0E);
1242
1243    #[derive(Debug, Clone, PartialEq)]
1244    pub struct ClientboundNamedSoundEffect {
1245        pub sound_name: String,
1246        pub sound_category: VarInt,
1247        pub effect_position_x: i32,
1248        pub effect_position_y: i32,
1249        pub effect_position_z: i32,
1250        pub volume: f32,
1251        pub pitch: f32,
1252    }
1253
1254    impl PacketId for ClientboundNamedSoundEffect {
1255        fn packet_id(_ver: u32) -> u8 {
1256            0x19
1257        }
1258    }
1259
1260    impl Encode for ClientboundNamedSoundEffect {
1261        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1262            encode_string(&self.sound_name, dst)?;
1263            self.sound_category.encode(dst)?;
1264            dst.put_i32(self.effect_position_x);
1265            dst.put_i32(self.effect_position_y);
1266            dst.put_i32(self.effect_position_z);
1267            dst.put_f32(self.volume);
1268            dst.put_f32(self.pitch);
1269            Ok(())
1270        }
1271    }
1272
1273    impl Decode for ClientboundNamedSoundEffect {
1274        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1275            let sound_name = decode_string(src)?;
1276            let sound_category = VarInt::decode(src)?;
1277            if src.remaining() < 20 {
1278                return Err(ProtocolError::Io(std::io::Error::new(
1279                    std::io::ErrorKind::UnexpectedEof,
1280                    "Missing bytes for ClientboundNamedSoundEffect position/volume/pitch",
1281                )));
1282            }
1283            let effect_position_x = src.get_i32();
1284            let effect_position_y = src.get_i32();
1285            let effect_position_z = src.get_i32();
1286            let volume = src.get_f32();
1287            let pitch = src.get_f32();
1288            Ok(Self {
1289                sound_name,
1290                sound_category,
1291                effect_position_x,
1292                effect_position_y,
1293                effect_position_z,
1294                volume,
1295                pitch,
1296            })
1297        }
1298    }
1299
1300    #[derive(Debug, Clone, PartialEq)]
1301    pub struct ClientboundJoinGame {
1302        pub entity_id: i32,
1303        pub is_hardcore: bool,
1304        pub game_mode: u8,
1305        pub previous_game_mode: i8,
1306        pub world_names: Vec<String>,
1307        pub dimension: String,
1308        pub world_name: String,
1309        pub hashed_seed: i64,
1310        pub max_players: VarInt,
1311        pub view_distance: VarInt,
1312        pub reduced_debug_info: bool,
1313        pub enable_respawn_screen: bool,
1314        pub is_debug: bool,
1315        pub is_flat: bool,
1316    }
1317
1318    impl PacketId for ClientboundJoinGame {
1319        fn packet_id(_ver: u32) -> u8 {
1320            0x24
1321        }
1322    }
1323
1324    impl Encode for ClientboundJoinGame {
1325        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1326            dst.put_i32(self.entity_id);
1327            dst.put_u8(self.is_hardcore as u8);
1328            dst.put_u8(self.game_mode);
1329            dst.put_i8(self.previous_game_mode);
1330            VarInt(self.world_names.len() as i32).encode(dst)?;
1331            for name in &self.world_names {
1332                encode_string(name, dst)?;
1333            }
1334            encode_string(&self.dimension, dst)?;
1335            encode_string(&self.world_name, dst)?;
1336            dst.put_i64(self.hashed_seed);
1337            self.max_players.encode(dst)?;
1338            self.view_distance.encode(dst)?;
1339            dst.put_u8(self.reduced_debug_info as u8);
1340            dst.put_u8(self.enable_respawn_screen as u8);
1341            dst.put_u8(self.is_debug as u8);
1342            dst.put_u8(self.is_flat as u8);
1343            Ok(())
1344        }
1345    }
1346
1347    impl Decode for ClientboundJoinGame {
1348        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1349            if src.remaining() < 4 {
1350                return Err(ProtocolError::Io(std::io::Error::new(
1351                    std::io::ErrorKind::UnexpectedEof,
1352                    "Missing bytes for ClientboundJoinGame entity_id",
1353                )));
1354            }
1355            let entity_id = src.get_i32();
1356            if src.remaining() < 3 {
1357                return Err(ProtocolError::Io(std::io::Error::new(
1358                    std::io::ErrorKind::UnexpectedEof,
1359                    "Missing bytes for ClientboundJoinGame flags",
1360                )));
1361            }
1362            let is_hardcore = src.get_u8() != 0;
1363            let game_mode = src.get_u8();
1364            let previous_game_mode = src.get_i8();
1365            let world_count = VarInt::decode(src)?.0 as usize;
1366            let mut world_names = Vec::with_capacity(world_count);
1367            for _ in 0..world_count {
1368                world_names.push(decode_string(src)?);
1369            }
1370            let dimension = decode_string(src)?;
1371            let world_name = decode_string(src)?;
1372            if src.remaining() < 8 {
1373                return Err(ProtocolError::Io(std::io::Error::new(
1374                    std::io::ErrorKind::UnexpectedEof,
1375                    "Missing bytes for ClientboundJoinGame hashed_seed",
1376                )));
1377            }
1378            let hashed_seed = src.get_i64();
1379            let max_players = VarInt::decode(src)?;
1380            let view_distance = VarInt::decode(src)?;
1381            if src.remaining() < 4 {
1382                return Err(ProtocolError::Io(std::io::Error::new(
1383                    std::io::ErrorKind::UnexpectedEof,
1384                    "Missing bytes for ClientboundJoinGame boolean flags",
1385                )));
1386            }
1387            let reduced_debug_info = src.get_u8() != 0;
1388            let enable_respawn_screen = src.get_u8() != 0;
1389            let is_debug = src.get_u8() != 0;
1390            let is_flat = src.get_u8() != 0;
1391            Ok(Self {
1392                entity_id,
1393                is_hardcore,
1394                game_mode,
1395                previous_game_mode,
1396                world_names,
1397                dimension,
1398                world_name,
1399                hashed_seed,
1400                max_players,
1401                view_distance,
1402                reduced_debug_info,
1403                enable_respawn_screen,
1404                is_debug,
1405                is_flat,
1406            })
1407        }
1408    }
1409
1410    #[derive(Debug, Clone, PartialEq)]
1411    pub struct ClientboundRespawn {
1412        pub dimension: String,
1413        pub world_name: String,
1414        pub hashed_seed: i64,
1415        pub game_mode: u8,
1416        pub previous_game_mode: i8,
1417        pub is_debug: bool,
1418        pub is_flat: bool,
1419        pub copy_metadata: bool,
1420    }
1421
1422    impl PacketId for ClientboundRespawn {
1423        fn packet_id(_ver: u32) -> u8 {
1424            0x39
1425        }
1426    }
1427
1428    impl Encode for ClientboundRespawn {
1429        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1430            encode_string(&self.dimension, dst)?;
1431            encode_string(&self.world_name, dst)?;
1432            dst.put_i64(self.hashed_seed);
1433            dst.put_u8(self.game_mode);
1434            dst.put_i8(self.previous_game_mode);
1435            dst.put_u8(self.is_debug as u8);
1436            dst.put_u8(self.is_flat as u8);
1437            dst.put_u8(self.copy_metadata as u8);
1438            Ok(())
1439        }
1440    }
1441
1442    impl Decode for ClientboundRespawn {
1443        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1444            let dimension = decode_string(src)?;
1445            let world_name = decode_string(src)?;
1446            if src.remaining() < 8 {
1447                return Err(ProtocolError::Io(std::io::Error::new(
1448                    std::io::ErrorKind::UnexpectedEof,
1449                    "Missing bytes for ClientboundRespawn hashed_seed",
1450                )));
1451            }
1452            let hashed_seed = src.get_i64();
1453            if src.remaining() < 3 {
1454                return Err(ProtocolError::Io(std::io::Error::new(
1455                    std::io::ErrorKind::UnexpectedEof,
1456                    "Missing bytes for ClientboundRespawn game modes",
1457                )));
1458            }
1459            let game_mode = src.get_u8();
1460            let previous_game_mode = src.get_i8();
1461            if src.remaining() < 3 {
1462                return Err(ProtocolError::Io(std::io::Error::new(
1463                    std::io::ErrorKind::UnexpectedEof,
1464                    "Missing bytes for ClientboundRespawn flags",
1465                )));
1466            }
1467            let is_debug = src.get_u8() != 0;
1468            let is_flat = src.get_u8() != 0;
1469            let copy_metadata = src.get_u8() != 0;
1470            Ok(Self {
1471                dimension,
1472                world_name,
1473                hashed_seed,
1474                game_mode,
1475                previous_game_mode,
1476                is_debug,
1477                is_flat,
1478                copy_metadata,
1479            })
1480        }
1481    }
1482
1483    #[derive(Debug, Clone, PartialEq)]
1484    pub struct ClientboundPlayerPosition {
1485        pub x: f64,
1486        pub y: f64,
1487        pub z: f64,
1488        pub yaw: f32,
1489        pub pitch: f32,
1490        pub flags: u8,
1491        pub teleport_id: VarInt,
1492    }
1493
1494    impl PacketId for ClientboundPlayerPosition {
1495        fn packet_id(_ver: u32) -> u8 {
1496            0x34
1497        }
1498    }
1499
1500    impl Encode for ClientboundPlayerPosition {
1501        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1502            dst.put_f64(self.x);
1503            dst.put_f64(self.y);
1504            dst.put_f64(self.z);
1505            dst.put_f32(self.yaw);
1506            dst.put_f32(self.pitch);
1507            dst.put_u8(self.flags);
1508            self.teleport_id.encode(dst)
1509        }
1510    }
1511
1512    impl Decode for ClientboundPlayerPosition {
1513        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1514            if src.remaining() < 8 * 3 + 4 * 2 + 1 {
1515                return Err(ProtocolError::Io(std::io::Error::new(
1516                    std::io::ErrorKind::UnexpectedEof,
1517                    "Missing bytes for ClientboundPlayerPosition",
1518                )));
1519            }
1520            let x = src.get_f64();
1521            let y = src.get_f64();
1522            let z = src.get_f64();
1523            let yaw = src.get_f32();
1524            let pitch = src.get_f32();
1525            let flags = src.get_u8();
1526            let teleport_id = VarInt::decode(src)?;
1527            Ok(Self {
1528                x,
1529                y,
1530                z,
1531                yaw,
1532                pitch,
1533                flags,
1534                teleport_id,
1535            })
1536        }
1537    }
1538
1539    #[derive(Debug, Clone, PartialEq)]
1540    pub struct ClientboundKeepAlive {
1541        pub keep_alive_id: i64,
1542    }
1543
1544    impl PacketId for ClientboundKeepAlive {
1545        fn packet_id(_ver: u32) -> u8 {
1546            0x1F
1547        }
1548    }
1549
1550    impl Encode for ClientboundKeepAlive {
1551        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1552            dst.put_i64(self.keep_alive_id);
1553            Ok(())
1554        }
1555    }
1556
1557    impl Decode for ClientboundKeepAlive {
1558        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1559            if src.remaining() < 8 {
1560                return Err(ProtocolError::Io(std::io::Error::new(
1561                    std::io::ErrorKind::UnexpectedEof,
1562                    "Missing bytes for ClientboundKeepAlive",
1563                )));
1564            }
1565            Ok(Self {
1566                keep_alive_id: src.get_i64(),
1567            })
1568        }
1569    }
1570
1571    #[derive(Debug, Clone, PartialEq)]
1572    pub struct ServerboundKeepAlive {
1573        pub keep_alive_id: i64,
1574    }
1575
1576    impl PacketId for ServerboundKeepAlive {
1577        fn packet_id(_ver: u32) -> u8 {
1578            0x10
1579        }
1580    }
1581
1582    impl Encode for ServerboundKeepAlive {
1583        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1584            dst.put_i64(self.keep_alive_id);
1585            Ok(())
1586        }
1587    }
1588
1589    impl Decode for ServerboundKeepAlive {
1590        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1591            if src.remaining() < 8 {
1592                return Err(ProtocolError::Io(std::io::Error::new(
1593                    std::io::ErrorKind::UnexpectedEof,
1594                    "Missing bytes for ServerboundKeepAlive",
1595                )));
1596            }
1597            Ok(Self {
1598                keep_alive_id: src.get_i64(),
1599            })
1600        }
1601    }
1602
1603    #[derive(Debug, Clone, PartialEq)]
1604    pub struct ClientboundChatMessage {
1605        pub json_message: String,
1606        pub position: u8,
1607        pub sender: uuid::Uuid,
1608    }
1609
1610    impl PacketId for ClientboundChatMessage {
1611        fn packet_id(_ver: u32) -> u8 {
1612            0x0E
1613        }
1614    }
1615
1616    impl Encode for ClientboundChatMessage {
1617        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1618            encode_string(&self.json_message, dst)?;
1619            dst.put_u8(self.position);
1620            let (hi, lo) = self.sender.as_u64_pair();
1621            dst.put_i64(hi as i64);
1622            dst.put_i64(lo as i64);
1623            Ok(())
1624        }
1625    }
1626
1627    impl Decode for ClientboundChatMessage {
1628        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1629            let json_message = decode_string(src)?;
1630            if src.remaining() < 1 + 16 {
1631                return Err(ProtocolError::Io(std::io::Error::new(
1632                    std::io::ErrorKind::UnexpectedEof,
1633                    "Missing bytes for ClientboundChatMessage position/sender",
1634                )));
1635            }
1636            let position = src.get_u8();
1637            let hi = src.get_i64() as u64;
1638            let lo = src.get_i64() as u64;
1639            let sender = uuid::Uuid::from_u64_pair(hi, lo);
1640            Ok(Self {
1641                json_message,
1642                position,
1643                sender,
1644            })
1645        }
1646    }
1647
1648    #[derive(Debug, Clone, PartialEq)]
1649    pub struct ServerboundChatMessage {
1650        pub message: String,
1651    }
1652
1653    impl PacketId for ServerboundChatMessage {
1654        fn packet_id(_ver: u32) -> u8 {
1655            0x03
1656        }
1657    }
1658
1659    impl Encode for ServerboundChatMessage {
1660        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1661            encode_string(&self.message, dst)
1662        }
1663    }
1664
1665    impl Decode for ServerboundChatMessage {
1666        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1667            Ok(Self {
1668                message: decode_string(src)?,
1669            })
1670        }
1671    }
1672
1673    #[derive(Debug, Clone, PartialEq)]
1674    pub struct ServerboundMovePlayerPos {
1675        pub x: f64,
1676        pub feet_y: f64,
1677        pub z: f64,
1678        pub on_ground: bool,
1679    }
1680
1681    impl PacketId for ServerboundMovePlayerPos {
1682        fn packet_id(_ver: u32) -> u8 {
1683            0x12
1684        }
1685    }
1686
1687    impl Encode for ServerboundMovePlayerPos {
1688        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1689            dst.put_f64(self.x);
1690            dst.put_f64(self.feet_y);
1691            dst.put_f64(self.z);
1692            dst.put_u8(self.on_ground as u8);
1693            Ok(())
1694        }
1695    }
1696
1697    impl Decode for ServerboundMovePlayerPos {
1698        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1699            if src.remaining() < 8 * 3 + 1 {
1700                return Err(ProtocolError::Io(std::io::Error::new(
1701                    std::io::ErrorKind::UnexpectedEof,
1702                    "Missing bytes for ServerboundMovePlayerPos",
1703                )));
1704            }
1705            Ok(Self {
1706                x: src.get_f64(),
1707                feet_y: src.get_f64(),
1708                z: src.get_f64(),
1709                on_ground: src.get_u8() != 0,
1710            })
1711        }
1712    }
1713
1714    #[derive(Debug, Clone, PartialEq)]
1715    pub struct ServerboundMovePlayerRot {
1716        pub yaw: f32,
1717        pub pitch: f32,
1718        pub on_ground: bool,
1719    }
1720
1721    impl PacketId for ServerboundMovePlayerRot {
1722        fn packet_id(_ver: u32) -> u8 {
1723            0x13
1724        }
1725    }
1726
1727    impl Encode for ServerboundMovePlayerRot {
1728        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1729            dst.put_f32(self.yaw);
1730            dst.put_f32(self.pitch);
1731            dst.put_u8(self.on_ground as u8);
1732            Ok(())
1733        }
1734    }
1735
1736    impl Decode for ServerboundMovePlayerRot {
1737        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1738            if src.remaining() < 4 * 2 + 1 {
1739                return Err(ProtocolError::Io(std::io::Error::new(
1740                    std::io::ErrorKind::UnexpectedEof,
1741                    "Missing bytes for ServerboundMovePlayerRot",
1742                )));
1743            }
1744            Ok(Self {
1745                yaw: src.get_f32(),
1746                pitch: src.get_f32(),
1747                on_ground: src.get_u8() != 0,
1748            })
1749        }
1750    }
1751
1752    #[derive(Debug, Clone, PartialEq)]
1753    pub struct ServerboundMovePlayerPosRot {
1754        pub x: f64,
1755        pub feet_y: f64,
1756        pub z: f64,
1757        pub yaw: f32,
1758        pub pitch: f32,
1759        pub on_ground: bool,
1760    }
1761
1762    impl PacketId for ServerboundMovePlayerPosRot {
1763        fn packet_id(_ver: u32) -> u8 {
1764            0x14
1765        }
1766    }
1767
1768    impl Encode for ServerboundMovePlayerPosRot {
1769        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1770            dst.put_f64(self.x);
1771            dst.put_f64(self.feet_y);
1772            dst.put_f64(self.z);
1773            dst.put_f32(self.yaw);
1774            dst.put_f32(self.pitch);
1775            dst.put_u8(self.on_ground as u8);
1776            Ok(())
1777        }
1778    }
1779
1780    impl Decode for ServerboundMovePlayerPosRot {
1781        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1782            if src.remaining() < 8 * 3 + 4 * 2 + 1 {
1783                return Err(ProtocolError::Io(std::io::Error::new(
1784                    std::io::ErrorKind::UnexpectedEof,
1785                    "Missing bytes for ServerboundMovePlayerPosRot",
1786                )));
1787            }
1788            Ok(Self {
1789                x: src.get_f64(),
1790                feet_y: src.get_f64(),
1791                z: src.get_f64(),
1792                yaw: src.get_f32(),
1793                pitch: src.get_f32(),
1794                on_ground: src.get_u8() != 0,
1795            })
1796        }
1797    }
1798
1799    #[derive(Debug, Clone, PartialEq)]
1800    pub enum InteractAction {
1801        Interact {
1802            hand: VarInt,
1803        },
1804        Attack,
1805        InteractAt {
1806            target_x: f32,
1807            target_y: f32,
1808            target_z: f32,
1809            hand: VarInt,
1810        },
1811    }
1812
1813    #[derive(Debug, Clone, PartialEq)]
1814    pub struct ServerboundInteract {
1815        pub entity_id: VarInt,
1816        pub action: InteractAction,
1817        pub sneaking: bool,
1818    }
1819
1820    impl PacketId for ServerboundInteract {
1821        fn packet_id(_ver: u32) -> u8 {
1822            0x0E
1823        }
1824    }
1825
1826    impl Encode for ServerboundInteract {
1827        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1828            self.entity_id.encode(dst)?;
1829            match &self.action {
1830                InteractAction::Interact { hand } => {
1831                    VarInt(0).encode(dst)?;
1832                    hand.encode(dst)?;
1833                },
1834                InteractAction::Attack => {
1835                    VarInt(1).encode(dst)?;
1836                },
1837                InteractAction::InteractAt {
1838                    target_x,
1839                    target_y,
1840                    target_z,
1841                    hand,
1842                } => {
1843                    VarInt(2).encode(dst)?;
1844                    dst.put_f32(*target_x);
1845                    dst.put_f32(*target_y);
1846                    dst.put_f32(*target_z);
1847                    hand.encode(dst)?;
1848                },
1849            }
1850            dst.put_u8(self.sneaking as u8);
1851            Ok(())
1852        }
1853    }
1854
1855    impl Decode for ServerboundInteract {
1856        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1857            let entity_id = VarInt::decode(src)?;
1858            let action = match VarInt::decode(src)?.0 {
1859                0 => InteractAction::Interact {
1860                    hand: VarInt::decode(src)?,
1861                },
1862                1 => InteractAction::Attack,
1863                2 => {
1864                    if src.remaining() < 12 {
1865                        return Err(ProtocolError::Io(std::io::Error::new(
1866                            std::io::ErrorKind::UnexpectedEof,
1867                            "Missing bytes for InteractAt",
1868                        )));
1869                    }
1870                    InteractAction::InteractAt {
1871                        target_x: src.get_f32(),
1872                        target_y: src.get_f32(),
1873                        target_z: src.get_f32(),
1874                        hand: VarInt::decode(src)?,
1875                    }
1876                },
1877                _ => {
1878                    return Err(ProtocolError::Io(std::io::Error::new(
1879                        std::io::ErrorKind::InvalidData,
1880                        "Unknown InteractAction type",
1881                    )))
1882                },
1883            };
1884            if src.remaining() < 1 {
1885                return Err(ProtocolError::Io(std::io::Error::new(
1886                    std::io::ErrorKind::UnexpectedEof,
1887                    "Missing bytes for sneaking",
1888                )));
1889            }
1890            Ok(Self {
1891                entity_id,
1892                action,
1893                sneaking: src.get_u8() != 0,
1894            })
1895        }
1896    }
1897
1898    #[derive(Debug, Clone, PartialEq)]
1899    pub struct ClientboundPluginMessage {
1900        pub channel: String,
1901        pub data: Vec<u8>,
1902    }
1903
1904    impl PacketId for ClientboundPluginMessage {
1905        fn packet_id(_ver: u32) -> u8 {
1906            0x17
1907        }
1908    }
1909
1910    impl Encode for ClientboundPluginMessage {
1911        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1912            encode_string(&self.channel, dst)?;
1913            dst.put_slice(&self.data);
1914            Ok(())
1915        }
1916    }
1917
1918    impl Decode for ClientboundPluginMessage {
1919        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1920            let channel = decode_string(src)?;
1921            let data = src.copy_to_bytes(src.remaining()).to_vec();
1922            Ok(Self { channel, data })
1923        }
1924    }
1925
1926    #[derive(Debug, Clone, PartialEq)]
1927    pub struct ServerboundPluginMessage {
1928        pub channel: String,
1929        pub data: Vec<u8>,
1930    }
1931
1932    impl PacketId for ServerboundPluginMessage {
1933        fn packet_id(_ver: u32) -> u8 {
1934            0x0B
1935        }
1936    }
1937
1938    impl Encode for ServerboundPluginMessage {
1939        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1940            encode_string(&self.channel, dst)?;
1941            dst.put_slice(&self.data);
1942            Ok(())
1943        }
1944    }
1945
1946    impl Decode for ServerboundPluginMessage {
1947        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1948            let channel = decode_string(src)?;
1949            let data = src.copy_to_bytes(src.remaining()).to_vec();
1950            Ok(Self { channel, data })
1951        }
1952    }
1953
1954    #[derive(Debug, Clone, PartialEq)]
1955    pub struct ClientboundDisconnect {
1956        pub reason: String,
1957    }
1958
1959    impl PacketId for ClientboundDisconnect {
1960        fn packet_id(_ver: u32) -> u8 {
1961            0x19
1962        }
1963    }
1964
1965    impl Encode for ClientboundDisconnect {
1966        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
1967            encode_string(&self.reason, dst)
1968        }
1969    }
1970
1971    impl Decode for ClientboundDisconnect {
1972        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
1973            Ok(Self {
1974                reason: decode_string(src)?,
1975            })
1976        }
1977    }
1978}
1979
1980#[cfg(test)]
1981mod tests {
1982    use super::*;
1983
1984    macro_rules! roundtrip {
1985        ($T:ty, $val:expr) => {{
1986            let v = $val;
1987            let mut buf = BytesMut::new();
1988            v.encode(&mut buf).unwrap();
1989            let mut b = buf.freeze();
1990            assert_eq!(<$T>::decode(&mut b).unwrap(), v);
1991        }};
1992    }
1993
1994    #[test]
1995    fn join_game_roundtrip() {
1996        roundtrip!(
1997            ClientboundJoinGame,
1998            ClientboundJoinGame {
1999                entity_id: 1,
2000                is_hardcore: false,
2001                game_mode: 0,
2002                previous_game_mode: -1,
2003                world_names: vec!["minecraft:overworld".to_string()],
2004                dimension: "minecraft:overworld".to_string(),
2005                world_name: "minecraft:overworld".to_string(),
2006                hashed_seed: 12345678,
2007                max_players: VarInt(20),
2008                view_distance: VarInt(10),
2009                reduced_debug_info: false,
2010                enable_respawn_screen: true,
2011                is_debug: false,
2012                is_flat: false,
2013            }
2014        );
2015    }
2016
2017    #[test]
2018    fn respawn_roundtrip() {
2019        roundtrip!(
2020            ClientboundRespawn,
2021            ClientboundRespawn {
2022                dimension: "minecraft:the_nether".to_string(),
2023                world_name: "minecraft:the_nether".to_string(),
2024                hashed_seed: 0,
2025                game_mode: 0,
2026                previous_game_mode: -1,
2027                is_debug: false,
2028                is_flat: false,
2029                copy_metadata: true,
2030            }
2031        );
2032    }
2033
2034    #[test]
2035    fn player_position_roundtrip() {
2036        roundtrip!(
2037            ClientboundPlayerPosition,
2038            ClientboundPlayerPosition {
2039                x: 10.0,
2040                y: 64.0,
2041                z: -10.0,
2042                yaw: 0.0,
2043                pitch: 0.0,
2044                flags: 0,
2045                teleport_id: VarInt(1),
2046            }
2047        );
2048    }
2049
2050    #[test]
2051    fn keepalive_roundtrip() {
2052        roundtrip!(
2053            ClientboundKeepAlive,
2054            ClientboundKeepAlive {
2055                keep_alive_id: 111222333_i64
2056            }
2057        );
2058        roundtrip!(
2059            ServerboundKeepAlive,
2060            ServerboundKeepAlive {
2061                keep_alive_id: 111222333_i64
2062            }
2063        );
2064    }
2065
2066    #[test]
2067    fn chat_roundtrip() {
2068        roundtrip!(
2069            ClientboundChatMessage,
2070            ClientboundChatMessage {
2071                json_message: r#"{"text":"hi"}"#.to_string(),
2072                position: 1,
2073                sender: uuid::Uuid::nil(),
2074            }
2075        );
2076        roundtrip!(
2077            ServerboundChatMessage,
2078            ServerboundChatMessage {
2079                message: "hello".to_string()
2080            }
2081        );
2082    }
2083
2084    #[test]
2085    fn move_roundtrips() {
2086        roundtrip!(
2087            ServerboundMovePlayerPos,
2088            ServerboundMovePlayerPos {
2089                x: 0.0,
2090                feet_y: 64.0,
2091                z: 0.0,
2092                on_ground: true
2093            }
2094        );
2095        roundtrip!(
2096            ServerboundMovePlayerRot,
2097            ServerboundMovePlayerRot {
2098                yaw: 45.0,
2099                pitch: -20.0,
2100                on_ground: false
2101            }
2102        );
2103        roundtrip!(
2104            ServerboundMovePlayerPosRot,
2105            ServerboundMovePlayerPosRot {
2106                x: 5.0,
2107                feet_y: 65.0,
2108                z: -5.0,
2109                yaw: 90.0,
2110                pitch: 0.0,
2111                on_ground: true
2112            }
2113        );
2114    }
2115
2116    #[test]
2117    fn interact_roundtrips() {
2118        roundtrip!(
2119            ServerboundInteract,
2120            ServerboundInteract {
2121                entity_id: VarInt(3),
2122                action: InteractAction::Attack,
2123                sneaking: true
2124            }
2125        );
2126        roundtrip!(
2127            ServerboundInteract,
2128            ServerboundInteract {
2129                entity_id: VarInt(4),
2130                action: InteractAction::Interact { hand: VarInt(0) },
2131                sneaking: false
2132            }
2133        );
2134        roundtrip!(
2135            ServerboundInteract,
2136            ServerboundInteract {
2137                entity_id: VarInt(5),
2138                action: InteractAction::InteractAt {
2139                    target_x: 0.5,
2140                    target_y: 1.0,
2141                    target_z: 0.5,
2142                    hand: VarInt(1)
2143                },
2144                sneaking: false,
2145            }
2146        );
2147    }
2148
2149    #[test]
2150    fn plugin_message_roundtrip() {
2151        roundtrip!(
2152            ClientboundPluginMessage,
2153            ClientboundPluginMessage {
2154                channel: "minecraft:brand".to_string(),
2155                data: b"velocity".to_vec()
2156            }
2157        );
2158        roundtrip!(
2159            ServerboundPluginMessage,
2160            ServerboundPluginMessage {
2161                channel: "minecraft:brand".to_string(),
2162                data: b"vanilla".to_vec()
2163            }
2164        );
2165    }
2166
2167    #[test]
2168    fn disconnect_roundtrip() {
2169        roundtrip!(
2170            ClientboundDisconnect,
2171            ClientboundDisconnect {
2172                reason: r#"{"text":"cya"}"#.to_string()
2173            }
2174        );
2175    }
2176
2177    #[test]
2178    fn raw_stubs_roundtrip() {
2179        roundtrip!(
2180            ClientboundChunkData,
2181            ClientboundChunkData {
2182                raw: vec![0xFF; 32]
2183            }
2184        );
2185        roundtrip!(
2186            ClientboundEntityMetadata,
2187            ClientboundEntityMetadata { raw: vec![] }
2188        );
2189        roundtrip!(
2190            ServerboundPlayerDigging,
2191            ServerboundPlayerDigging { raw: vec![0x00; 8] }
2192        );
2193        roundtrip!(
2194            ServerboundPlayerBlockPlacement,
2195            ServerboundPlayerBlockPlacement {
2196                raw: vec![0xAB, 0xCD]
2197            }
2198        );
2199    }
2200
2201    #[test]
2202    fn packet_ids() {
2203        assert_eq!(ClientboundJoinGame::packet_id(754), 0x24);
2204        assert_eq!(ClientboundRespawn::packet_id(754), 0x39);
2205        assert_eq!(ClientboundPlayerPosition::packet_id(754), 0x34);
2206        assert_eq!(ClientboundKeepAlive::packet_id(754), 0x1F);
2207        assert_eq!(ServerboundKeepAlive::packet_id(754), 0x10);
2208        assert_eq!(ClientboundChatMessage::packet_id(754), 0x0E);
2209        assert_eq!(ServerboundChatMessage::packet_id(754), 0x03);
2210        assert_eq!(ServerboundMovePlayerPos::packet_id(754), 0x12);
2211        assert_eq!(ServerboundMovePlayerRot::packet_id(754), 0x13);
2212        assert_eq!(ServerboundMovePlayerPosRot::packet_id(754), 0x14);
2213        assert_eq!(ServerboundInteract::packet_id(754), 0x0E);
2214        assert_eq!(ClientboundPluginMessage::packet_id(754), 0x17);
2215        assert_eq!(ServerboundPluginMessage::packet_id(754), 0x0B);
2216        assert_eq!(ClientboundDisconnect::packet_id(754), 0x19);
2217
2218        assert_eq!(ClientboundSpawnEntity::packet_id(754), 0x00);
2219        assert_eq!(ClientboundSpawnPlayer::packet_id(754), 0x04);
2220        assert_eq!(ClientboundBlockChange::packet_id(754), 0x0B);
2221        assert_eq!(ClientboundChunkData::packet_id(754), 0x21);
2222        assert_eq!(ClientboundEntityVelocity::packet_id(754), 0x46);
2223        assert_eq!(ClientboundUpdateHealth::packet_id(754), 0x49);
2224        assert_eq!(ClientboundDeclareRecipes::packet_id(754), 0x5A);
2225        assert_eq!(ClientboundTags::packet_id(754), 0x5B);
2226        assert_eq!(ServerboundTeleportConfirm::packet_id(754), 0x00);
2227        assert_eq!(ServerboundPlayerDigging::packet_id(754), 0x1B);
2228        assert_eq!(ServerboundPlayerBlockPlacement::packet_id(754), 0x2E);
2229        assert_eq!(ServerboundUseItem::packet_id(754), 0x2F);
2230    }
2231}