1use bytes::{Buf, BufMut, Bytes, BytesMut};
2
3use crate::codec::{Decode, Encode, PacketId};
4use crate::error::ProtocolError;
5use crate::types::VarInt;
6
7fn need(src: &Bytes, n: usize) -> Result<(), ProtocolError> {
8 if src.remaining() < n {
9 Err(ProtocolError::Io(std::io::Error::new(
10 std::io::ErrorKind::UnexpectedEof,
11 format!("need {} bytes, only {} remaining", n, src.remaining()),
12 )))
13 } else {
14 Ok(())
15 }
16}
17
18fn encode_string(s: &str, dst: &mut BytesMut) -> Result<(), ProtocolError> {
19 VarInt(s.len() as i32).encode(dst)?;
20 dst.extend_from_slice(s.as_bytes());
21 Ok(())
22}
23
24fn decode_string(src: &mut Bytes) -> Result<String, ProtocolError> {
25 let len = VarInt::decode(src)?.0 as usize;
26 need(src, len)?;
27 let bytes = src.copy_to_bytes(len);
28 String::from_utf8(bytes.to_vec()).map_err(|_| {
29 ProtocolError::Io(std::io::Error::new(
30 std::io::ErrorKind::InvalidData,
31 "invalid utf-8 string",
32 ))
33 })
34}
35
36pub use packets::{
37 ClientboundAcknowledgeBlockChange, ClientboundAwardStats, ClientboundBlockAction,
38 ClientboundBlockDestroyStage, ClientboundBlockEntityData, ClientboundBlockUpdate,
39 ClientboundBossBar, ClientboundBundle, ClientboundChangeDifficulty,
40 ClientboundChunkBatchFinished, ClientboundChunkBatchStart, ClientboundChunkBiomes,
41 ClientboundClearTitles, ClientboundCommandSuggestions, ClientboundCommands,
42 ClientboundContainerClose, ClientboundContainerSetContent, ClientboundContainerSetProperty,
43 ClientboundContainerSetSlot, ClientboundCookieRequest, ClientboundCooldown,
44 ClientboundCustomChatCompletions, ClientboundDamageEvent, ClientboundDebugSample,
45 ClientboundDeleteChat, ClientboundDisconnect, ClientboundDisguisedChat,
46 ClientboundEntityAnimation, ClientboundEntityEvent, ClientboundExplosion,
47 ClientboundForgetLevelChunk, ClientboundGameEvent, ClientboundHorseScreenOpen,
48 ClientboundHurtAnimation, ClientboundInitializeBorder, ClientboundKeepAlive,
49 ClientboundLevelChunkWithLight, ClientboundLevelEvent, ClientboundLevelParticles,
50 ClientboundLightUpdate, ClientboundLogin, ClientboundMapItemData, ClientboundMerchantOffers,
51 ClientboundMoveEntityPos, ClientboundMoveEntityPosRot, ClientboundMoveEntityRot,
52 ClientboundMoveVehicle, ClientboundOpenBook, ClientboundOpenScreen, ClientboundOpenSignEditor,
53 ClientboundPing, ClientboundPlaceGhostRecipe, ClientboundPlayerAbilities,
54 ClientboundPlayerChat, ClientboundPlayerCombatEnd, ClientboundPlayerCombatEnter,
55 ClientboundPlayerCombatKill, ClientboundPlayerInfoRemove, ClientboundPlayerInfoUpdate,
56 ClientboundPlayerLookAt, ClientboundPlayerPosition, ClientboundPluginMessage,
57 ClientboundRecipeBookSettings, ClientboundRecipes, ClientboundRemoveEntities,
58 ClientboundRemoveEntityEffect, ClientboundResetScore, ClientboundResourcePackPop,
59 ClientboundResourcePackPush, ClientboundRespawn, ClientboundRotateHead,
60 ClientboundSectionBlocksUpdate, ClientboundSelectAdvancementsTab, ClientboundServerData,
61 ClientboundSetActionBarText, ClientboundSetBorderCenter, ClientboundSetBorderLerpSize,
62 ClientboundSetBorderSize, ClientboundSetBorderWarningDelay,
63 ClientboundSetBorderWarningDistance, ClientboundSetCamera, ClientboundSetCarriedItem,
64 ClientboundSetCursorItem, ClientboundSetEntityLink, ClientboundSetEntityMotion,
65 ClientboundSetEquipment, ClientboundSetExperience, ClientboundSetHealth,
66 ClientboundSetPlayerInventory, ClientboundSetScoreboardObjective,
67 ClientboundSetScoreboardScore, ClientboundSetSimulationDistance, ClientboundSetSubtitleText,
68 ClientboundSetTime, ClientboundSetTitleAnimationTimes, ClientboundSetTitleText,
69 ClientboundSound, ClientboundSoundEntity, ClientboundSpawnEntity,
70 ClientboundSpawnExperienceOrb, ClientboundSpawnPlayer, ClientboundStartConfiguration,
71 ClientboundStopSound, ClientboundStoreCookie, ClientboundSystemChat, ClientboundTabList,
72 ClientboundTagQuery, ClientboundTakeItemEntity, ClientboundTeleportEntity,
73 ClientboundTickingState, ClientboundTickingStep, ClientboundTransfer,
74 ClientboundUpdateAdvancements, ClientboundUpdateAttributes, ClientboundUpdateEffects,
75 ClientboundUpdateRecipes, ClientboundUpdateTags, InteractAction,
76 ServerboundAcceptTeleportation, ServerboundButtonPressed, ServerboundChatCommand,
77 ServerboundChatMessage, ServerboundChatSessionUpdate, ServerboundClickWindow,
78 ServerboundClickWindowButton, ServerboundClientInformation, ServerboundClientStatus,
79 ServerboundCloseWindow, ServerboundCommandSuggestion, ServerboundConfigurationAcknowledged,
80 ServerboundCreativeInventoryAction, ServerboundDifficultyChange, ServerboundDifficultyLock,
81 ServerboundEditBook, ServerboundEntityAction, ServerboundEntityTagQuery,
82 ServerboundHeldItemChange, ServerboundInteract, ServerboundJigsawGenerate,
83 ServerboundKeepAlive, ServerboundMovePlayerPos, ServerboundMovePlayerPosRot,
84 ServerboundMovePlayerRot, ServerboundMovePlayerStatusOnly, ServerboundPaddleBoat,
85 ServerboundPickItem, ServerboundPlaceRecipe, ServerboundPlayerAbilities,
86 ServerboundPlayerAction, ServerboundPlayerWeaponAttack, ServerboundPluginMessage,
87 ServerboundPong, ServerboundRecipeBookChangeSettings, ServerboundRecipeBookSeenRecipe,
88 ServerboundRenameItem, ServerboundResourcePackStatus, ServerboundSelectTrade,
89 ServerboundSetBeaconEffect, ServerboundSetCreativeModeSlot, ServerboundSetMerchantTrade,
90 ServerboundSetStructureBlock, ServerboundSpectate, ServerboundSteerBoat,
91 ServerboundSteerVehicle, ServerboundSwingArm, ServerboundUpdateCommandBlock,
92 ServerboundUpdateCommandBlockMinecart, ServerboundUpdateSign, ServerboundUseItem,
93 ServerboundUseItemOn, ServerboundVehicleMove,
94};
95
96mod packets {
97 use super::*;
98
99 macro_rules! passthrough {
100 ($(#[$meta:meta])* $name:ident, $id:expr) => {
101 $(#[$meta])*
102 #[derive(Debug, Clone, PartialEq)]
103 pub struct $name { pub raw: Vec<u8> }
104
105 impl PacketId for $name {
106 fn packet_id(_ver: u32) -> u8 { $id }
107 }
108
109 impl Encode for $name {
110 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
111 dst.extend_from_slice(&self.raw);
112 Ok(())
113 }
114 }
115
116 impl Decode for $name {
117 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
118 let raw = src.copy_to_bytes(src.remaining()).to_vec();
119 Ok(Self { raw })
120 }
121 }
122 };
123 }
124
125 #[derive(Debug, Clone, PartialEq)]
126 pub struct ClientboundKeepAlive {
127 pub keep_alive_id: i64,
128 }
129
130 impl PacketId for ClientboundKeepAlive {
131 fn packet_id(_ver: u32) -> u8 {
132 0x29
133 }
134 }
135
136 impl Encode for ClientboundKeepAlive {
137 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
138 self.keep_alive_id.encode(dst)
139 }
140 }
141
142 impl Decode for ClientboundKeepAlive {
143 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
144 Ok(Self {
145 keep_alive_id: i64::decode(src)?,
146 })
147 }
148 }
149
150 #[derive(Debug, Clone, PartialEq)]
151 pub struct ClientboundPlayerPosition {
152 pub x: f64,
153 pub y: f64,
154 pub z: f64,
155 pub yaw: f32,
156 pub pitch: f32,
157 pub flags: u8,
158 pub teleport_id: VarInt,
159 }
160
161 impl PacketId for ClientboundPlayerPosition {
162 fn packet_id(_ver: u32) -> u8 {
163 0x43
164 }
165 }
166
167 impl Encode for ClientboundPlayerPosition {
168 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
169 dst.put_f64(self.x);
170 dst.put_f64(self.y);
171 dst.put_f64(self.z);
172 dst.put_f32(self.yaw);
173 dst.put_f32(self.pitch);
174 dst.put_u8(self.flags);
175 self.teleport_id.encode(dst)
176 }
177 }
178
179 impl Decode for ClientboundPlayerPosition {
180 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
181 need(src, 8 + 8 + 8 + 4 + 4 + 1)?;
182 Ok(Self {
183 x: src.get_f64(),
184 y: src.get_f64(),
185 z: src.get_f64(),
186 yaw: src.get_f32(),
187 pitch: src.get_f32(),
188 flags: src.get_u8(),
189 teleport_id: VarInt::decode(src)?,
190 })
191 }
192 }
193
194 #[derive(Debug, Clone, PartialEq)]
195 pub struct ClientboundPluginMessage {
196 pub channel: String,
197 pub data: Vec<u8>,
198 }
199
200 impl PacketId for ClientboundPluginMessage {
201 fn packet_id(_ver: u32) -> u8 {
202 0x1A
203 }
204 }
205
206 impl Encode for ClientboundPluginMessage {
207 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
208 encode_string(&self.channel, dst)?;
209 dst.extend_from_slice(&self.data);
210 Ok(())
211 }
212 }
213
214 impl Decode for ClientboundPluginMessage {
215 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
216 let channel = decode_string(src)?;
217 let data = src.copy_to_bytes(src.remaining()).to_vec();
218 Ok(Self { channel, data })
219 }
220 }
221
222 #[derive(Debug, Clone, PartialEq)]
223 pub struct ClientboundSystemChat {
224 pub json_message: String,
225 pub overlay: bool,
226 }
227
228 impl PacketId for ClientboundSystemChat {
229 fn packet_id(_ver: u32) -> u8 {
230 0x6B
231 }
232 }
233
234 impl Encode for ClientboundSystemChat {
235 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
236 encode_string(&self.json_message, dst)?;
237 dst.put_u8(self.overlay as u8);
238 Ok(())
239 }
240 }
241
242 impl Decode for ClientboundSystemChat {
243 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
244 let json_message = decode_string(src)?;
245 need(src, 1)?;
246 let overlay = src.get_u8() != 0;
247 Ok(Self {
248 json_message,
249 overlay,
250 })
251 }
252 }
253
254 #[derive(Debug, Clone, PartialEq)]
255 pub struct ClientboundLogin {
256 pub entity_id: i32,
257 pub is_hardcore: bool,
258 pub dimension_names: Vec<String>,
259 pub max_players: VarInt,
260 pub view_distance: VarInt,
261 pub simulation_distance: VarInt,
262 pub reduced_debug_info: bool,
263 pub enable_respawn_screen: bool,
264 pub do_limited_crafting: bool,
265 pub dimension_type: VarInt,
266 pub dimension_name: String,
267 pub hashed_seed: i64,
268 pub game_mode: u8,
269 pub previous_game_mode: i8,
270 pub is_debug: bool,
271 pub is_flat: bool,
272 pub death_location: Option<(String, i64)>,
273 pub portal_cooldown: VarInt,
274 pub sea_level: VarInt,
275 }
276
277 impl PacketId for ClientboundLogin {
278 fn packet_id(_ver: u32) -> u8 {
279 0x2E
280 }
281 }
282
283 impl Encode for ClientboundLogin {
284 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
285 dst.put_i32(self.entity_id);
286 dst.put_u8(self.is_hardcore as u8);
287 self.dimension_names.encode(dst)?;
288 self.max_players.encode(dst)?;
289 self.view_distance.encode(dst)?;
290 self.simulation_distance.encode(dst)?;
291 dst.put_u8(self.reduced_debug_info as u8);
292 dst.put_u8(self.enable_respawn_screen as u8);
293 dst.put_u8(self.do_limited_crafting as u8);
294 self.dimension_type.encode(dst)?;
295 encode_string(&self.dimension_name, dst)?;
296 dst.put_i64(self.hashed_seed);
297 dst.put_u8(self.game_mode);
298 dst.put_i8(self.previous_game_mode);
299 dst.put_u8(self.is_debug as u8);
300 dst.put_u8(self.is_flat as u8);
301 match &self.death_location {
302 Some((dim, pos)) => {
303 dst.put_u8(1);
304 encode_string(dim, dst)?;
305 dst.put_i64(*pos);
306 },
307 None => dst.put_u8(0),
308 }
309 self.portal_cooldown.encode(dst)?;
310 self.sea_level.encode(dst)?;
311 Ok(())
312 }
313 }
314
315 impl Decode for ClientboundLogin {
316 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
317 need(src, 4)?;
318 let entity_id = src.get_i32();
319 need(src, 1)?;
320 let is_hardcore = src.get_u8() != 0;
321 let dimension_names = Vec::<String>::decode(src)?;
322 let max_players = VarInt::decode(src)?;
323 let view_distance = VarInt::decode(src)?;
324 let simulation_distance = VarInt::decode(src)?;
325 need(src, 3)?;
326 let reduced_debug_info = src.get_u8() != 0;
327 let enable_respawn_screen = src.get_u8() != 0;
328 let do_limited_crafting = src.get_u8() != 0;
329 let dimension_type = VarInt::decode(src)?;
330 let dimension_name = decode_string(src)?;
331 need(src, 8)?;
332 let hashed_seed = src.get_i64();
333 need(src, 4)?;
334 let game_mode = src.get_u8();
335 let previous_game_mode = src.get_i8();
336 let is_debug = src.get_u8() != 0;
337 let is_flat = src.get_u8() != 0;
338 need(src, 1)?;
339 let death_location = if src.get_u8() != 0 {
340 let dim = decode_string(src)?;
341 need(src, 8)?;
342 let pos = src.get_i64();
343 Some((dim, pos))
344 } else {
345 None
346 };
347 let portal_cooldown = VarInt::decode(src)?;
348 let sea_level = VarInt::decode(src)?;
349 Ok(Self {
350 entity_id,
351 is_hardcore,
352 dimension_names,
353 max_players,
354 view_distance,
355 simulation_distance,
356 reduced_debug_info,
357 enable_respawn_screen,
358 do_limited_crafting,
359 dimension_type,
360 dimension_name,
361 hashed_seed,
362 game_mode,
363 previous_game_mode,
364 is_debug,
365 is_flat,
366 death_location,
367 portal_cooldown,
368 sea_level,
369 })
370 }
371 }
372
373 #[derive(Debug, Clone, PartialEq)]
374 pub struct ClientboundRespawn {
375 pub dimension_type: VarInt,
376 pub dimension_name: String,
377 pub hashed_seed: i64,
378 pub game_mode: u8,
379 pub previous_game_mode: i8,
380 pub is_debug: bool,
381 pub is_flat: bool,
382 pub data_kept: u8,
383 pub death_location: Option<(String, i64)>,
384 pub portal_cooldown: VarInt,
385 pub sea_level: VarInt,
386 }
387
388 impl PacketId for ClientboundRespawn {
389 fn packet_id(_ver: u32) -> u8 {
390 0x4B
391 }
392 }
393
394 impl Encode for ClientboundRespawn {
395 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
396 self.dimension_type.encode(dst)?;
397 encode_string(&self.dimension_name, dst)?;
398 dst.put_i64(self.hashed_seed);
399 dst.put_u8(self.game_mode);
400 dst.put_i8(self.previous_game_mode);
401 dst.put_u8(self.is_debug as u8);
402 dst.put_u8(self.is_flat as u8);
403 dst.put_u8(self.data_kept);
404 match &self.death_location {
405 Some((dim, pos)) => {
406 dst.put_u8(1);
407 encode_string(dim, dst)?;
408 dst.put_i64(*pos);
409 },
410 None => dst.put_u8(0),
411 }
412 self.portal_cooldown.encode(dst)?;
413 self.sea_level.encode(dst)?;
414 Ok(())
415 }
416 }
417
418 impl Decode for ClientboundRespawn {
419 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
420 let dimension_type = VarInt::decode(src)?;
421 let dimension_name = decode_string(src)?;
422 need(src, 8)?;
423 let hashed_seed = src.get_i64();
424 need(src, 4)?;
425 let game_mode = src.get_u8();
426 let previous_game_mode = src.get_i8();
427 let is_debug = src.get_u8() != 0;
428 let is_flat = src.get_u8() != 0;
429 need(src, 1)?;
430 let data_kept = src.get_u8();
431 need(src, 1)?;
432 let death_location = if src.get_u8() != 0 {
433 let dim = decode_string(src)?;
434 need(src, 8)?;
435 let pos = src.get_i64();
436 Some((dim, pos))
437 } else {
438 None
439 };
440 let portal_cooldown = VarInt::decode(src)?;
441 let sea_level = VarInt::decode(src)?;
442 Ok(Self {
443 dimension_type,
444 dimension_name,
445 hashed_seed,
446 game_mode,
447 previous_game_mode,
448 is_debug,
449 is_flat,
450 data_kept,
451 death_location,
452 portal_cooldown,
453 sea_level,
454 })
455 }
456 }
457
458 #[derive(Debug, Clone, PartialEq)]
459 pub struct ClientboundSound {
460 pub sound_type: VarInt,
461 pub sound_name: String,
462 pub sound_category: VarInt,
463 pub effect_pos_x: i32,
464 pub effect_pos_y: i32,
465 pub effect_pos_z: i32,
466 pub volume: f32,
467 pub pitch: f32,
468 pub seed: i64,
469 }
470
471 impl PacketId for ClientboundSound {
472 fn packet_id(_ver: u32) -> u8 {
473 0x67
474 }
475 }
476
477 impl Encode for ClientboundSound {
478 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
479 self.sound_type.encode(dst)?;
480 encode_string(&self.sound_name, dst)?;
481 self.sound_category.encode(dst)?;
482 dst.put_i32(self.effect_pos_x);
483 dst.put_i32(self.effect_pos_y);
484 dst.put_i32(self.effect_pos_z);
485 dst.put_f32(self.volume);
486 dst.put_f32(self.pitch);
487 dst.put_i64(self.seed);
488 Ok(())
489 }
490 }
491
492 impl Decode for ClientboundSound {
493 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
494 let sound_type = VarInt::decode(src)?;
495 let sound_name = decode_string(src)?;
496 let sound_category = VarInt::decode(src)?;
497 need(src, 4 + 4 + 4 + 4 + 4 + 8)?;
498 let effect_pos_x = src.get_i32();
499 let effect_pos_y = src.get_i32();
500 let effect_pos_z = src.get_i32();
501 let volume = src.get_f32();
502 let pitch = src.get_f32();
503 let seed = src.get_i64();
504 Ok(Self {
505 sound_type,
506 sound_name,
507 sound_category,
508 effect_pos_x,
509 effect_pos_y,
510 effect_pos_z,
511 volume,
512 pitch,
513 seed,
514 })
515 }
516 }
517
518 #[derive(Debug, Clone, PartialEq)]
519 pub struct ClientboundDisconnect {
520 pub reason: String,
521 }
522
523 impl PacketId for ClientboundDisconnect {
524 fn packet_id(_ver: u32) -> u8 {
525 0x1E
526 }
527 }
528
529 impl Encode for ClientboundDisconnect {
530 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
531 encode_string(&self.reason, dst)
532 }
533 }
534
535 impl Decode for ClientboundDisconnect {
536 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
537 Ok(Self {
538 reason: decode_string(src)?,
539 })
540 }
541 }
542
543 passthrough!(ClientboundBundle, 0x00);
544 passthrough!(ClientboundSpawnEntity, 0x01);
545 passthrough!(ClientboundSpawnExperienceOrb, 0x02);
546 passthrough!(ClientboundSpawnPlayer, 0x03);
547 passthrough!(ClientboundEntityAnimation, 0x04);
548 passthrough!(ClientboundAwardStats, 0x05);
549 passthrough!(ClientboundAcknowledgeBlockChange, 0x06);
550 passthrough!(ClientboundBlockDestroyStage, 0x07);
551 passthrough!(ClientboundBlockEntityData, 0x08);
552 passthrough!(ClientboundBlockAction, 0x09);
553 passthrough!(ClientboundBlockUpdate, 0x0A);
554 passthrough!(ClientboundBossBar, 0x0B);
555 passthrough!(ClientboundChangeDifficulty, 0x0C);
556 passthrough!(ClientboundChunkBatchFinished, 0x0D);
557 passthrough!(ClientboundChunkBatchStart, 0x0E);
558 passthrough!(ClientboundChunkBiomes, 0x0F);
559 passthrough!(ClientboundClearTitles, 0x10);
560 passthrough!(ClientboundCommandSuggestions, 0x11);
561 passthrough!(ClientboundCommands, 0x12);
562 passthrough!(ClientboundContainerClose, 0x13);
563 passthrough!(ClientboundContainerSetProperty, 0x15);
564
565 #[derive(Debug, Clone, PartialEq)]
566 pub struct ClientboundContainerSetContent {
567 pub window_id: u8,
568 pub slots: Vec<crate::types::Slot>,
569 pub carried_item: crate::types::Slot,
570 }
571
572 impl PacketId for ClientboundContainerSetContent {
573 fn packet_id(_ver: u32) -> u8 {
574 0x14
575 }
576 }
577
578 impl Encode for ClientboundContainerSetContent {
579 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
580 dst.put_u8(self.window_id);
581 VarInt(self.slots.len() as i32).encode(dst)?;
582 for slot in &self.slots {
583 slot.encode(dst)?;
584 }
585 self.carried_item.encode(dst)?;
586 Ok(())
587 }
588 }
589
590 impl Decode for ClientboundContainerSetContent {
591 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
592 need(src, 1)?;
593 let window_id = src.get_u8();
594 let count = VarInt::decode(src)?.0 as usize;
595 let mut slots = Vec::with_capacity(count);
596 for _ in 0..count {
597 slots.push(crate::types::Slot::decode(src)?);
598 }
599 let carried_item = crate::types::Slot::decode(src)?;
600 Ok(Self {
601 window_id,
602 slots,
603 carried_item,
604 })
605 }
606 }
607
608 #[derive(Debug, Clone, PartialEq)]
609 pub struct ClientboundContainerSetSlot {
610 pub window_id: i8,
611 pub slot: i16,
612 pub slot_data: crate::types::Slot,
613 }
614
615 impl PacketId for ClientboundContainerSetSlot {
616 fn packet_id(_ver: u32) -> u8 {
617 0x16
618 }
619 }
620
621 impl Encode for ClientboundContainerSetSlot {
622 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
623 dst.put_i8(self.window_id);
624 dst.put_i16(self.slot);
625 self.slot_data.encode(dst)?;
626 Ok(())
627 }
628 }
629
630 impl Decode for ClientboundContainerSetSlot {
631 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
632 need(src, 1 + 2)?;
633 let window_id = src.get_i8();
634 let slot = src.get_i16();
635 let slot_data = crate::types::Slot::decode(src)?;
636 Ok(Self {
637 window_id,
638 slot,
639 slot_data,
640 })
641 }
642 }
643
644 passthrough!(ClientboundCookieRequest, 0x17);
645 passthrough!(ClientboundCooldown, 0x18);
646 passthrough!(ClientboundCustomChatCompletions, 0x19);
647 passthrough!(ClientboundDamageEvent, 0x1B);
648 passthrough!(ClientboundDebugSample, 0x1C);
649 passthrough!(ClientboundDeleteChat, 0x1D);
650 passthrough!(ClientboundDisguisedChat, 0x1F);
651 passthrough!(ClientboundEntityEvent, 0x20);
652 passthrough!(ClientboundExplosion, 0x21);
653 passthrough!(ClientboundForgetLevelChunk, 0x22);
654 passthrough!(ClientboundGameEvent, 0x23);
655 passthrough!(ClientboundHorseScreenOpen, 0x24);
656 passthrough!(ClientboundHurtAnimation, 0x25);
657 passthrough!(ClientboundInitializeBorder, 0x26);
658 passthrough!(ClientboundLevelChunkWithLight, 0x2A);
659 passthrough!(ClientboundLevelEvent, 0x2B);
660 passthrough!(ClientboundLevelParticles, 0x2C);
661 passthrough!(ClientboundLightUpdate, 0x2D);
662 passthrough!(ClientboundMapItemData, 0x2F);
663 passthrough!(ClientboundMerchantOffers, 0x30);
664 passthrough!(ClientboundMoveEntityPos, 0x31);
665 passthrough!(ClientboundMoveEntityPosRot, 0x32);
666 passthrough!(ClientboundMoveEntityRot, 0x33);
667 passthrough!(ClientboundMoveVehicle, 0x34);
668 passthrough!(ClientboundOpenBook, 0x35);
669 passthrough!(ClientboundOpenScreen, 0x36);
670 passthrough!(ClientboundOpenSignEditor, 0x37);
671 passthrough!(ClientboundPing, 0x38);
672 passthrough!(ClientboundPlaceGhostRecipe, 0x3A);
673 passthrough!(ClientboundPlayerAbilities, 0x3B);
674 passthrough!(ClientboundPlayerChat, 0x3C);
675 passthrough!(ClientboundPlayerCombatEnd, 0x3D);
676 passthrough!(ClientboundPlayerCombatEnter, 0x3E);
677 passthrough!(ClientboundPlayerCombatKill, 0x3F);
678 passthrough!(ClientboundPlayerInfoRemove, 0x40);
679 passthrough!(ClientboundPlayerInfoUpdate, 0x41);
680 passthrough!(ClientboundPlayerLookAt, 0x42);
681 passthrough!(ClientboundRecipeBookSettings, 0x44);
682 passthrough!(ClientboundRecipes, 0x45);
683 passthrough!(ClientboundRemoveEntities, 0x46);
684 passthrough!(ClientboundRemoveEntityEffect, 0x47);
685 passthrough!(ClientboundResetScore, 0x48);
686 passthrough!(ClientboundResourcePackPop, 0x49);
687 passthrough!(ClientboundResourcePackPush, 0x4A);
688 passthrough!(ClientboundRotateHead, 0x4C);
689 passthrough!(ClientboundSectionBlocksUpdate, 0x4D);
690 passthrough!(ClientboundSelectAdvancementsTab, 0x4E);
691 passthrough!(ClientboundServerData, 0x4F);
692 passthrough!(ClientboundSetActionBarText, 0x50);
693 passthrough!(ClientboundSetBorderCenter, 0x51);
694 passthrough!(ClientboundSetBorderLerpSize, 0x52);
695 passthrough!(ClientboundSetBorderSize, 0x53);
696 passthrough!(ClientboundSetBorderWarningDelay, 0x54);
697 passthrough!(ClientboundSetBorderWarningDistance, 0x55);
698 passthrough!(ClientboundSetCamera, 0x56);
699 passthrough!(ClientboundSetCursorItem, 0x57);
700 passthrough!(ClientboundSetEntityLink, 0x58);
701 passthrough!(ClientboundSetEntityMotion, 0x59);
702 passthrough!(ClientboundSetEquipment, 0x5A);
703 passthrough!(ClientboundSetExperience, 0x5B);
704 passthrough!(ClientboundSetHealth, 0x5C);
705 passthrough!(ClientboundSetCarriedItem, 0x5D);
706 passthrough!(ClientboundSetPlayerInventory, 0x5E);
707 passthrough!(ClientboundSetScoreboardObjective, 0x5F);
708 passthrough!(ClientboundSetScoreboardScore, 0x60);
709 passthrough!(ClientboundSetSimulationDistance, 0x61);
710 passthrough!(ClientboundSetSubtitleText, 0x62);
711 passthrough!(ClientboundSetTime, 0x63);
712 passthrough!(ClientboundSetTitleText, 0x64);
713 passthrough!(ClientboundSetTitleAnimationTimes, 0x65);
714 passthrough!(ClientboundSoundEntity, 0x66);
715 passthrough!(ClientboundStartConfiguration, 0x68);
716 passthrough!(ClientboundStopSound, 0x69);
717 passthrough!(ClientboundStoreCookie, 0x6A);
718 passthrough!(ClientboundTabList, 0x6C);
719 passthrough!(ClientboundTagQuery, 0x6D);
720 passthrough!(ClientboundTakeItemEntity, 0x6E);
721 passthrough!(ClientboundTeleportEntity, 0x6F);
722 passthrough!(ClientboundTickingState, 0x70);
723 passthrough!(ClientboundTickingStep, 0x71);
724 passthrough!(ClientboundTransfer, 0x72);
725 passthrough!(ClientboundUpdateAdvancements, 0x73);
726 passthrough!(ClientboundUpdateAttributes, 0x74);
727 passthrough!(ClientboundUpdateEffects, 0x75);
728 passthrough!(ClientboundUpdateRecipes, 0x76);
729 passthrough!(ClientboundUpdateTags, 0x77);
730
731 #[derive(Debug, Clone, PartialEq)]
732 pub struct ServerboundKeepAlive {
733 pub keep_alive_id: i64,
734 }
735
736 impl PacketId for ServerboundKeepAlive {
737 fn packet_id(_ver: u32) -> u8 {
738 0x15
739 }
740 }
741
742 impl Encode for ServerboundKeepAlive {
743 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
744 self.keep_alive_id.encode(dst)
745 }
746 }
747
748 impl Decode for ServerboundKeepAlive {
749 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
750 Ok(Self {
751 keep_alive_id: i64::decode(src)?,
752 })
753 }
754 }
755
756 #[derive(Debug, Clone, PartialEq)]
757 pub struct ServerboundMovePlayerPos {
758 pub x: f64,
759 pub feet_y: f64,
760 pub z: f64,
761 pub on_ground: bool,
762 }
763
764 impl PacketId for ServerboundMovePlayerPos {
765 fn packet_id(_ver: u32) -> u8 {
766 0x17
767 }
768 }
769
770 impl Encode for ServerboundMovePlayerPos {
771 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
772 dst.put_f64(self.x);
773 dst.put_f64(self.feet_y);
774 dst.put_f64(self.z);
775 dst.put_u8(self.on_ground as u8);
776 Ok(())
777 }
778 }
779
780 impl Decode for ServerboundMovePlayerPos {
781 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
782 need(src, 8 + 8 + 8 + 1)?;
783 Ok(Self {
784 x: src.get_f64(),
785 feet_y: src.get_f64(),
786 z: src.get_f64(),
787 on_ground: src.get_u8() != 0,
788 })
789 }
790 }
791
792 #[derive(Debug, Clone, PartialEq)]
793 pub struct ServerboundMovePlayerPosRot {
794 pub x: f64,
795 pub feet_y: f64,
796 pub z: f64,
797 pub yaw: f32,
798 pub pitch: f32,
799 pub on_ground: bool,
800 }
801
802 impl PacketId for ServerboundMovePlayerPosRot {
803 fn packet_id(_ver: u32) -> u8 {
804 0x19
805 }
806 }
807
808 impl Encode for ServerboundMovePlayerPosRot {
809 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
810 dst.put_f64(self.x);
811 dst.put_f64(self.feet_y);
812 dst.put_f64(self.z);
813 dst.put_f32(self.yaw);
814 dst.put_f32(self.pitch);
815 dst.put_u8(self.on_ground as u8);
816 Ok(())
817 }
818 }
819
820 impl Decode for ServerboundMovePlayerPosRot {
821 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
822 need(src, 8 + 8 + 8 + 4 + 4 + 1)?;
823 Ok(Self {
824 x: src.get_f64(),
825 feet_y: src.get_f64(),
826 z: src.get_f64(),
827 yaw: src.get_f32(),
828 pitch: src.get_f32(),
829 on_ground: src.get_u8() != 0,
830 })
831 }
832 }
833
834 #[derive(Debug, Clone, PartialEq)]
835 pub struct ServerboundMovePlayerRot {
836 pub yaw: f32,
837 pub pitch: f32,
838 pub on_ground: bool,
839 }
840
841 impl PacketId for ServerboundMovePlayerRot {
842 fn packet_id(_ver: u32) -> u8 {
843 0x18
844 }
845 }
846
847 impl Encode for ServerboundMovePlayerRot {
848 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
849 dst.put_f32(self.yaw);
850 dst.put_f32(self.pitch);
851 dst.put_u8(self.on_ground as u8);
852 Ok(())
853 }
854 }
855
856 impl Decode for ServerboundMovePlayerRot {
857 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
858 need(src, 4 + 4 + 1)?;
859 Ok(Self {
860 yaw: src.get_f32(),
861 pitch: src.get_f32(),
862 on_ground: src.get_u8() != 0,
863 })
864 }
865 }
866
867 #[derive(Debug, Clone, PartialEq)]
868 pub enum InteractAction {
869 Interact {
870 hand: VarInt,
871 },
872 Attack,
873 InteractAt {
874 target_x: f32,
875 target_y: f32,
876 target_z: f32,
877 hand: VarInt,
878 },
879 }
880
881 #[derive(Debug, Clone, PartialEq)]
882 pub struct ServerboundInteract {
883 pub entity_id: VarInt,
884 pub action: InteractAction,
885 pub sneaking: bool,
886 }
887
888 impl PacketId for ServerboundInteract {
889 fn packet_id(_ver: u32) -> u8 {
890 0x14
891 }
892 }
893
894 impl Encode for ServerboundInteract {
895 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
896 self.entity_id.encode(dst)?;
897 match &self.action {
898 InteractAction::Interact { hand } => {
899 VarInt(0).encode(dst)?;
900 hand.encode(dst)?;
901 },
902 InteractAction::Attack => {
903 VarInt(1).encode(dst)?;
904 },
905 InteractAction::InteractAt {
906 target_x,
907 target_y,
908 target_z,
909 hand,
910 } => {
911 VarInt(2).encode(dst)?;
912 dst.put_f32(*target_x);
913 dst.put_f32(*target_y);
914 dst.put_f32(*target_z);
915 hand.encode(dst)?;
916 },
917 }
918 dst.put_u8(self.sneaking as u8);
919 Ok(())
920 }
921 }
922
923 impl Decode for ServerboundInteract {
924 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
925 let entity_id = VarInt::decode(src)?;
926 let action = match VarInt::decode(src)?.0 {
927 0 => InteractAction::Interact {
928 hand: VarInt::decode(src)?,
929 },
930 1 => InteractAction::Attack,
931 2 => {
932 need(src, 4 + 4 + 4)?;
933 let target_x = src.get_f32();
934 let target_y = src.get_f32();
935 let target_z = src.get_f32();
936 let hand = VarInt::decode(src)?;
937 InteractAction::InteractAt {
938 target_x,
939 target_y,
940 target_z,
941 hand,
942 }
943 },
944 _ => return Err(ProtocolError::UnexpectedEof),
945 };
946 need(src, 1)?;
947 let sneaking = src.get_u8() != 0;
948 Ok(Self {
949 entity_id,
950 action,
951 sneaking,
952 })
953 }
954 }
955
956 #[derive(Debug, Clone, PartialEq)]
957 pub struct ServerboundPluginMessage {
958 pub channel: String,
959 pub data: Vec<u8>,
960 }
961
962 impl PacketId for ServerboundPluginMessage {
963 fn packet_id(_ver: u32) -> u8 {
964 0x0F
965 }
966 }
967
968 impl Encode for ServerboundPluginMessage {
969 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
970 encode_string(&self.channel, dst)?;
971 dst.extend_from_slice(&self.data);
972 Ok(())
973 }
974 }
975
976 impl Decode for ServerboundPluginMessage {
977 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
978 let channel = decode_string(src)?;
979 let data = src.copy_to_bytes(src.remaining()).to_vec();
980 Ok(Self { channel, data })
981 }
982 }
983
984 passthrough!(ServerboundAcceptTeleportation, 0x00);
985 passthrough!(ServerboundMovePlayerStatusOnly, 0x16);
986 passthrough!(ServerboundVehicleMove, 0x1A);
987 passthrough!(ServerboundSteerBoat, 0x1B);
988 passthrough!(ServerboundPaddleBoat, 0x1B);
989 passthrough!(ServerboundPlayerAbilities, 0x1E);
990 passthrough!(ServerboundPlayerAction, 0x1F);
991 passthrough!(ServerboundEntityAction, 0x20);
992 passthrough!(ServerboundSteerVehicle, 0x21);
993 passthrough!(ServerboundPlayerWeaponAttack, 0x22);
994 passthrough!(ServerboundSwingArm, 0x32);
995 passthrough!(ServerboundSpectate, 0x33);
996 passthrough!(ServerboundUseItemOn, 0x34);
997 passthrough!(ServerboundUseItem, 0x35);
998 passthrough!(ServerboundCloseWindow, 0x0E);
999 passthrough!(ServerboundClickWindow, 0x0D);
1000 passthrough!(ServerboundClickWindowButton, 0x0C);
1001 passthrough!(ServerboundCreativeInventoryAction, 0x2E);
1002 passthrough!(ServerboundHeldItemChange, 0x2B);
1003 passthrough!(ServerboundSetCreativeModeSlot, 0x2C);
1004 passthrough!(ServerboundChatMessage, 0x06);
1005 passthrough!(ServerboundChatCommand, 0x05);
1006 passthrough!(ServerboundChatSessionUpdate, 0x07);
1007 passthrough!(ServerboundClientStatus, 0x08);
1008 passthrough!(ServerboundClientInformation, 0x09);
1009 passthrough!(ServerboundResourcePackStatus, 0x2A);
1010 passthrough!(ServerboundPong, 0x24);
1011 passthrough!(ServerboundConfigurationAcknowledged, 0x0B);
1012 passthrough!(ServerboundButtonPressed, 0x0C);
1013 passthrough!(ServerboundCommandSuggestion, 0x0A);
1014 passthrough!(ServerboundDifficultyChange, 0x02);
1015 passthrough!(ServerboundDifficultyLock, 0x12);
1016 passthrough!(ServerboundEditBook, 0x10);
1017 passthrough!(ServerboundEntityTagQuery, 0x11);
1018 passthrough!(ServerboundJigsawGenerate, 0x13);
1019 passthrough!(ServerboundPickItem, 0x1C);
1020 passthrough!(ServerboundPlaceRecipe, 0x1D);
1021 passthrough!(ServerboundRecipeBookChangeSettings, 0x26);
1022 passthrough!(ServerboundRecipeBookSeenRecipe, 0x27);
1023 passthrough!(ServerboundRenameItem, 0x29);
1024 passthrough!(ServerboundSelectTrade, 0x2D);
1025 passthrough!(ServerboundSetMerchantTrade, 0x2D);
1026 passthrough!(ServerboundSetBeaconEffect, 0x2F);
1027 passthrough!(ServerboundSetStructureBlock, 0x36);
1028 passthrough!(ServerboundUpdateCommandBlock, 0x30);
1029 passthrough!(ServerboundUpdateCommandBlockMinecart, 0x31);
1030 passthrough!(ServerboundUpdateSign, 0x37);
1031}
1032
1033#[cfg(test)]
1034mod tests {
1035 use super::*;
1036
1037 macro_rules! roundtrip {
1038 ($T:ty, $val:expr) => {{
1039 let v = $val;
1040 let mut buf = BytesMut::new();
1041 v.encode(&mut buf).unwrap();
1042 let mut b = buf.freeze();
1043 assert_eq!(<$T>::decode(&mut b).unwrap(), v);
1044 }};
1045 }
1046
1047 #[test]
1048 fn keepalive_roundtrip() {
1049 roundtrip!(
1050 ClientboundKeepAlive,
1051 ClientboundKeepAlive {
1052 keep_alive_id: 123456789_i64
1053 }
1054 );
1055 roundtrip!(
1056 ServerboundKeepAlive,
1057 ServerboundKeepAlive {
1058 keep_alive_id: 987654321_i64
1059 }
1060 );
1061 }
1062
1063 #[test]
1064 fn player_position_roundtrip() {
1065 roundtrip!(
1066 ClientboundPlayerPosition,
1067 ClientboundPlayerPosition {
1068 x: -100.0,
1069 y: 70.0,
1070 z: 200.0,
1071 yaw: 90.0,
1072 pitch: -45.0,
1073 flags: 0,
1074 teleport_id: VarInt(5),
1075 }
1076 );
1077 }
1078
1079 #[test]
1080 fn plugin_message_roundtrip() {
1081 roundtrip!(
1082 ClientboundPluginMessage,
1083 ClientboundPluginMessage {
1084 channel: "minecraft:brand".to_string(),
1085 data: b"paper".to_vec(),
1086 }
1087 );
1088 roundtrip!(
1089 ServerboundPluginMessage,
1090 ServerboundPluginMessage {
1091 channel: "minecraft:brand".to_string(),
1092 data: b"vanilla".to_vec(),
1093 }
1094 );
1095 }
1096
1097 #[test]
1098 fn system_chat_roundtrip() {
1099 roundtrip!(
1100 ClientboundSystemChat,
1101 ClientboundSystemChat {
1102 json_message: r#"{"text":"hello"}"#.to_string(),
1103 overlay: false,
1104 }
1105 );
1106 }
1107
1108 #[test]
1109 fn disconnect_roundtrip() {
1110 roundtrip!(
1111 ClientboundDisconnect,
1112 ClientboundDisconnect {
1113 reason: r#"{"text":"bye"}"#.to_string(),
1114 }
1115 );
1116 }
1117
1118 #[test]
1119 fn respawn_roundtrip() {
1120 roundtrip!(
1121 ClientboundRespawn,
1122 ClientboundRespawn {
1123 dimension_type: VarInt(0),
1124 dimension_name: "minecraft:overworld".to_string(),
1125 hashed_seed: 0,
1126 game_mode: 0,
1127 previous_game_mode: -1,
1128 is_debug: false,
1129 is_flat: true,
1130 data_kept: 0,
1131 death_location: None,
1132 portal_cooldown: VarInt(0),
1133 sea_level: VarInt(63),
1134 }
1135 );
1136 }
1137
1138 #[test]
1139 fn sound_roundtrip() {
1140 roundtrip!(
1141 ClientboundSound,
1142 ClientboundSound {
1143 sound_type: VarInt(0),
1144 sound_name: "block.note_block.harp".to_string(),
1145 sound_category: VarInt(2),
1146 effect_pos_x: 0,
1147 effect_pos_y: 2048,
1148 effect_pos_z: 0,
1149 volume: 1.0,
1150 pitch: 1.0,
1151 seed: 0,
1152 }
1153 );
1154 }
1155
1156 #[test]
1157 fn login_roundtrip() {
1158 roundtrip!(
1159 ClientboundLogin,
1160 ClientboundLogin {
1161 entity_id: 0,
1162 is_hardcore: false,
1163 dimension_names: vec!["minecraft:overworld".to_string()],
1164 max_players: VarInt(20),
1165 view_distance: VarInt(8),
1166 simulation_distance: VarInt(8),
1167 reduced_debug_info: false,
1168 enable_respawn_screen: true,
1169 do_limited_crafting: false,
1170 dimension_type: VarInt(0),
1171 dimension_name: "minecraft:overworld".to_string(),
1172 hashed_seed: 0,
1173 game_mode: 3,
1174 previous_game_mode: -1,
1175 is_debug: false,
1176 is_flat: true,
1177 death_location: None,
1178 portal_cooldown: VarInt(0),
1179 sea_level: VarInt(63),
1180 }
1181 );
1182 }
1183
1184 #[test]
1185 fn move_pos_roundtrip() {
1186 roundtrip!(
1187 ServerboundMovePlayerPos,
1188 ServerboundMovePlayerPos {
1189 x: 5.0,
1190 feet_y: 64.0,
1191 z: -2.0,
1192 on_ground: true,
1193 }
1194 );
1195 }
1196
1197 #[test]
1198 fn move_rot_roundtrip() {
1199 roundtrip!(
1200 ServerboundMovePlayerRot,
1201 ServerboundMovePlayerRot {
1202 yaw: 90.0,
1203 pitch: -10.0,
1204 on_ground: true,
1205 }
1206 );
1207 }
1208
1209 #[test]
1210 fn move_pos_rot_roundtrip() {
1211 roundtrip!(
1212 ServerboundMovePlayerPosRot,
1213 ServerboundMovePlayerPosRot {
1214 x: 1.0,
1215 feet_y: 65.0,
1216 z: 2.0,
1217 yaw: 180.0,
1218 pitch: 45.0,
1219 on_ground: false,
1220 }
1221 );
1222 }
1223
1224 #[test]
1225 fn interact_roundtrip() {
1226 roundtrip!(
1227 ServerboundInteract,
1228 ServerboundInteract {
1229 entity_id: VarInt(1),
1230 action: InteractAction::Attack,
1231 sneaking: false,
1232 }
1233 );
1234 roundtrip!(
1235 ServerboundInteract,
1236 ServerboundInteract {
1237 entity_id: VarInt(2),
1238 action: InteractAction::Interact { hand: VarInt(0) },
1239 sneaking: true,
1240 }
1241 );
1242 roundtrip!(
1243 ServerboundInteract,
1244 ServerboundInteract {
1245 entity_id: VarInt(3),
1246 action: InteractAction::InteractAt {
1247 target_x: 0.5,
1248 target_y: 1.0,
1249 target_z: 0.5,
1250 hand: VarInt(0),
1251 },
1252 sneaking: false,
1253 }
1254 );
1255 }
1256
1257 #[test]
1258 fn packet_ids() {
1259 assert_eq!(ClientboundKeepAlive::packet_id(767), 0x29);
1260 assert_eq!(ClientboundPlayerPosition::packet_id(767), 0x43);
1261 assert_eq!(ClientboundPluginMessage::packet_id(767), 0x1A);
1262 assert_eq!(ClientboundSystemChat::packet_id(767), 0x6B);
1263 assert_eq!(ClientboundLogin::packet_id(767), 0x2E);
1264 assert_eq!(ClientboundRespawn::packet_id(767), 0x4B);
1265 assert_eq!(ClientboundSound::packet_id(767), 0x67);
1266 assert_eq!(ClientboundDisconnect::packet_id(767), 0x1E);
1267 assert_eq!(ServerboundKeepAlive::packet_id(767), 0x15);
1268 assert_eq!(ServerboundMovePlayerPos::packet_id(767), 0x17);
1269 assert_eq!(ServerboundMovePlayerPosRot::packet_id(767), 0x19);
1270 assert_eq!(ServerboundMovePlayerRot::packet_id(767), 0x18);
1271 assert_eq!(ServerboundInteract::packet_id(767), 0x14);
1272 assert_eq!(ServerboundPluginMessage::packet_id(767), 0x0F);
1273 assert_eq!(ClientboundBundle::packet_id(767), 0x00);
1274 assert_eq!(ClientboundUpdateTags::packet_id(767), 0x77);
1275 assert_eq!(ServerboundAcceptTeleportation::packet_id(767), 0x00);
1276 assert_eq!(ServerboundUpdateSign::packet_id(767), 0x37);
1277 }
1278}