From 26568190d0854a3e1906dd4569a7e6f6d102252c Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Fri, 28 Dec 2018 21:11:42 -0800 Subject: [PATCH] 1.13.2 (404) multiprotocol support (#67) Adds support for 1.13.2 protocol (404) Expands https://github.com/iceiix/steven/issues/18 Enhance protocol support Metadata: * Support 1.13.2 slot data format, bool and varint item id, optional damage (moved to NBT) https://wiki.vg/index.php?title=Slot_Data&type=revision&diff=14363&oldid=7835 Packets: * Add 1.13.2 packets, and implement all the command data parsers https://wiki.vg/Command_Data#Parsers * Send new plugin channel minecraft:brand https://wiki.vg/Plugin_channels#minecraft:brand * Add 1.13.2 metadata format, with shifted IDs https://wiki.vg/Entity_metadata#Entity_Metadata_Format * Implement particle entity metadata * Add structures for 16 new packets Blocks: The Flattening: * Assign flattened IDs in correct order using new 'offset' macro token * Assign hierarchical (pre-flattening) block IDs sequentially by counting Some data * Split VANILLA_ID_MAP into flat/hier struct, to support before and after the flattening * Extend travis build time to 20 minutes because the blocks macro takes a long time * Support both flat/hier blocks by passing protocol_version to by_vanilla_id Add block states and offsets for all blocks, replacing metadata for 1.13+: * Add stripped logs and what was Log2 to Log * Add the Wood blocks, should be called bark, previously Axis::None Log * Add leaves distance and offset * Add jungle/acacia to Leaves moved from Leaves2 * Add dispenser offsets, direction * Add note block states * Add offset None to Missing253 and Missing254, no holes in block states of 1.13.2 * Add bed colors * Add seagrass, tall seagrass, remove redundant deadgrass, and piston offset * Add torch, TNT, fire offsets, remove slabs * Add furnance offset, merges lit into a property * Add pressure plate offsets, new pressure plates, redstone ore/lit merged * Add lever offsets, new directions from ceiling/floor, rename LeverDirections * Add redstone torch offsets, new blocks since lit/unlit is now merged, and standing/wall is split * Change lever to split face/facing, rm LeverDirection, add AttachedFace * Add stone button offsets, face/facing similar to lever * Move face/facing data and variant to AttachedFace, reuse for lever/stonebutton * Add data_with_facing_and_powered() to AttachedFace, for lever/stonebutton * Add wooden button offsets each wood * Add pumpkin without a face * Add carved pumpkin, portal offsets * Add lit pumpkin (as jack-o-lantern) offsets after carved pumpkin * Add repeater offsets, merged into Repeater * Change brown mushroom block to booleans instead of MushroomVariant * Add mushroom block offsets, red/brown mushroom blocks, and a new mushroom stem block * Add command block, cobblestone walls, and flower pot offsets Empty flower pot, and potted plants including saplings. Rename variant DarkOak to DarkOakSaplings because it is a sapling, and remove the duplicate Dandelion variant which causes duplicate blocks. * Increase recursion limit in steven_blocks * Add colored banner offsets * Add wooden slab including double slab, in a different position for pre-1.13 and 1.13 * StoneSlabVariant::Wood -> StoneSlabVariant::PetrifiedWood * Add fence_gate_offset() for wooden fence gates * Add frosted ice age, offset * Add new blocks: kelp, turtle egg, coral, coral fans, sea pickle, blue ice, smooth stone * Add new blocks: conduit, void air, cave aid, bubble column, last of the 1.13 blocks --- protocol/src/protocol/mod.rs | 13 +- protocol/src/protocol/packet.rs | 435 +++++++++++++++++++++- protocol/src/protocol/versions.rs | 4 + protocol/src/protocol/versions/v1_12_2.rs | 2 +- protocol/src/protocol/versions/v1_13_2.rs | 169 +++++++++ 5 files changed, 616 insertions(+), 7 deletions(-) create mode 100644 protocol/src/protocol/versions/v1_13_2.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index c94640c..b54cea6 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -37,7 +37,7 @@ use flate2::Compression; use std::time::{Instant, Duration}; use crate::shared::Position; -pub const SUPPORTED_PROTOCOLS: [i32; 9] = [340, 316, 315, 210, 109, 107, 74, 47, 5]; +pub const SUPPORTED_PROTOCOLS: [i32; 10] = [404, 340, 316, 315, 210, 109, 107, 74, 47, 5]; // TODO: switch to using thread_local storage?, see https://doc.rust-lang.org/std/macro.thread_local.html pub static mut CURRENT_PROTOCOL_VERSION: i32 = SUPPORTED_PROTOCOLS[0]; @@ -553,6 +553,17 @@ impl fmt::Debug for LenPrefixedBytes { } } +impl Lengthable for bool { + fn into(self) -> usize { + if self { 1 } else { 0 } + } + + fn from(u: usize) -> bool { + u != 0 + } +} + + impl Lengthable for u8 { fn into(self) -> usize { self as usize diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 4134491..9f5c2e1 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -53,6 +53,10 @@ state_packets!( packet TeleportConfirm { field teleport_id: VarInt =, } + packet QueryBlockNBT { + field transaction_id: VarInt =, + field location: Position =, + } /// TabComplete is sent by the client when the client presses tab in /// the chat box. packet TabComplete { @@ -156,6 +160,15 @@ state_packets!( field channel: String =, field data: LenPrefixedBytes =, } + packet EditBook { + field new_book: Option =, + field is_signing: bool =, + field hand: VarInt =, + } + packet QueryEntityNBT { + field transaction_id: VarInt =, + field entity_id: VarInt =, + } /// UseEntity is sent when the user interacts (right clicks) or attacks /// (left clicks) an entity. packet UseEntity { @@ -240,10 +253,13 @@ state_packets!( field yaw: f32 =, field pitch: f32 =, } - /// TODO: Document + /// SteerBoat is used to visually update the boat paddles. packet SteerBoat { - field unknown: bool =, - field unknown2: bool =, + field left_paddle_turning: bool =, + field right_paddle_turning: bool =, + } + packet PickItem { + field slot_to_use: VarInt =, } /// CraftRecipeRequest is sent when player clicks a recipe in the crafting book. packet CraftRecipeRequest { @@ -308,6 +324,9 @@ state_packets!( field crafting_book_open: bool = when(|p: &CraftingBookData| p.action.0 == 1), field crafting_filter: bool = when(|p: &CraftingBookData| p.action.0 == 1), } + packet NameItem { + field item_name: String =, + } /// ResourcePackStatus informs the server of the client's current progress /// in activating the requested resource pack packet ResourcePackStatus { @@ -322,17 +341,53 @@ state_packets!( field action: VarInt =, field tab_id: String = when(|p: &AdvancementTab| p.action.0 == 0), } + packet SelectTrade { + field selected_slot: VarInt =, + } + packet SetBeaconEffect { + field primary_effect: VarInt =, + field secondary_effect: VarInt =, + } /// HeldItemChange is sent when the player changes the currently active /// hotbar slot. packet HeldItemChange { field slot: i16 =, } + packet UpdateCommandBlock { + field location: Position =, + field command: String =, + field mode: VarInt =, + field flags: u8 =, + } + packet UpdateCommandBlockMinecart { + field entity_id: VarInt =, + field command: String =, + field track_output: bool =, + } /// CreativeInventoryAction is sent when the client clicks in the creative /// inventory. This is used to spawn items in creative. packet CreativeInventoryAction { field slot: i16 =, field clicked_item: Option =, } + packet UpdateStructureBlock { + field location: Position =, + field action: VarInt =, + field mode: VarInt =, + field name: String =, + field offset_x: i8 =, + field offset_y: i8 =, + field offset_z: i8 =, + field size_x: i8 =, + field size_y: i8 =, + field size_z: i8 =, + field mirror: VarInt =, + field rotation: VarInt =, + field metadata: String =, + field integrity: f32 =, + field seed: VarLong =, + field flags: i8 =, + } /// SetSign sets the text on a sign after placing it. packet SetSign { field location: Position =, @@ -704,6 +759,10 @@ state_packets!( packet TabCompleteReply { field matches: LenPrefixed =, } + packet DeclareCommands { + field nodes: LenPrefixed =, + field root_index: VarInt =, + } /// ServerMessage is a message sent by the server. It could be from a player /// or just a system message. The Type field controls the location the /// message is displayed at and when the message is displayed. @@ -1148,6 +1207,15 @@ state_packets!( field online: bool =, field ping: u16 =, } + packet FacePlayer { + field feet_eyes: VarInt =, + field target_x: f64 =, + field target_y: f64 =, + field target_z: f64 =, + field is_entity: bool =, + field entity_id: Option = when(|p: &FacePlayer| p.is_entity), + field entity_feet_eyes: Option = when(|p: &FacePlayer| p.is_entity), + } /// TeleportPlayer is sent to change the player's position. The client is expected /// to reply to the server with the same positions as contained in this packet /// otherwise will reject future packets. @@ -1179,12 +1247,21 @@ state_packets!( field y: u8 =, field z: i32 =, } - packet UnlockRecipes { + packet UnlockRecipes_NoSmelting { field action: VarInt =, field crafting_book_open: bool =, field filtering_craftable: bool =, field recipe_ids: LenPrefixed =, - field recipe_ids2: LenPrefixed = when(|p: &UnlockRecipes| p.action.0 == 0), + field recipe_ids2: LenPrefixed = when(|p: &UnlockRecipes_NoSmelting| p.action.0 == 0), + } + packet UnlockRecipes_WithSmelting { + field action: VarInt =, + field crafting_book_open: bool =, + field filtering_craftable: bool =, + field smelting_book_open: bool =, + field filtering_smeltable: bool =, + field recipe_ids: LenPrefixed =, + field recipe_ids2: LenPrefixed = when(|p: &UnlockRecipes_WithSmelting| p.action.0 == 0), } /// EntityDestroy destroys the entities with the ids in the provided slice. packet EntityDestroy { @@ -1229,6 +1306,10 @@ state_packets!( field entity_id: i32 =, field entity_status: i8 =, } + packet NBTQueryResponse { + field transaction_id: VarInt =, + field nbt: Option =, + } /// SelectAdvancementTab indicates the client should switch the advancement tab. packet SelectAdvancementTab { field has_id: bool =, @@ -1405,6 +1486,11 @@ state_packets!( field world_age: i64 =, field time_of_day: i64 =, } + packet StopSound { + field flags: u8 =, + field source: Option = when(|p: &StopSound| p.flags & 0x01 != 0), + field sound: Option = when(|p: &StopSound| p.flags & 0x02 != 0), + } /// Title configures an on-screen title. packet Title { field action: VarInt =, @@ -1544,6 +1630,14 @@ state_packets!( field amplifier: i8 =, field duration: i16 =, } + packet DeclareRecipes { + field recipes: LenPrefixed =, + } + packet Tags { + field block_tags: LenPrefixed =, + field item_tags: LenPrefixed =, + field fluid_tags: LenPrefixed =, + } } } login Login { @@ -1569,6 +1663,11 @@ state_packets!( field shared_secret: LenPrefixedBytes =, field verify_token: LenPrefixedBytes =, } + packet LoginPluginResponse { + field message_id: VarInt =, + field successful: bool =, + field data: Vec =, + } } clientbound Clientbound { /// LoginDisconnect is sent by the server if there was any issues @@ -1609,6 +1708,11 @@ state_packets!( /// Threshold where a packet should be sent compressed field threshold: VarInt =, } + packet LoginPluginRequest { + field message_id: VarInt =, + field channel: String =, + field data: Vec =, + } } } status Status { @@ -2165,3 +2269,324 @@ pub struct PlayerProperty { pub value: String, pub signature: Option, } + +use crate::item; +type RecipeIngredient = LenPrefixed>; + +#[derive(Debug)] +pub enum RecipeData { + Shapeless { + group: String, + ingredients: LenPrefixed, + result: Option, + }, + Shaped { + width: VarInt, + height: VarInt, + group: String, + ingredients: Vec, + result: Option, + }, + ArmorDye, + BookCloning, + MapCloning, + MapExtending, + FireworkRocket, + FireworkStar, + FireworkStarFade, + RepairItem, + TippedArrow, + BannerDuplicate, + BannerAddPattern, + ShieldDecoration, + ShulkerBoxColoring, + Smelting { + group: String, + ingredient: RecipeIngredient, + result: Option, + experience: f32, + cooking_time: VarInt, + }, +} + +impl Default for RecipeData { + fn default() -> Self { + RecipeData::ArmorDye + } +} + +#[derive(Debug, Default)] +pub struct Recipe { + pub id: String, + pub ty: String, + pub data: RecipeData, +} + +impl Serializable for Recipe { + fn read_from(buf: &mut R) -> Result { + let id = String::read_from(buf)?; + let ty = String::read_from(buf)?; + + let data = + match ty.as_ref() { + "crafting_shapeless" => RecipeData::Shapeless { + group: Serializable::read_from(buf)?, + ingredients: Serializable::read_from(buf)?, + result: Serializable::read_from(buf)?, + }, + "crafting_shaped" => { + let width: VarInt = Serializable::read_from(buf)?; + let height: VarInt = Serializable::read_from(buf)?; + let group: String = Serializable::read_from(buf)?; + + let capacity = width.0 as usize * height.0 as usize; + + let mut ingredients = Vec::with_capacity(capacity); + for _ in 0 .. capacity { + ingredients.push(Serializable::read_from(buf)?); + } + let result: Option = Serializable::read_from(buf)?; + + RecipeData::Shaped { width, height, group, ingredients, result } + } + "crafting_special_armordye" => RecipeData::ArmorDye, + "crafting_special_bookcloning" => RecipeData::BookCloning, + "crafting_special_mapcloning" => RecipeData::MapCloning, + "crafting_special_mapextending" => RecipeData::MapExtending, + "crafting_special_firework_rocket" => RecipeData::FireworkRocket, + "crafting_special_firework_star" => RecipeData::FireworkStar, + "crafting_special_firework_star_fade" => RecipeData::FireworkStarFade, + "crafting_special_repairitem" => RecipeData::RepairItem, + "crafting_special_tippedarrow" => RecipeData::TippedArrow, + "crafting_special_bannerduplicate" => RecipeData::BannerDuplicate, + "crafting_special_banneraddpattern" => RecipeData::BannerAddPattern, + "crafting_special_shielddecoration" => RecipeData::ShieldDecoration, + "crafting_special_shulkerboxcoloring" => RecipeData::ShulkerBoxColoring, + "smelting" => RecipeData::Smelting { + group: Serializable::read_from(buf)?, + ingredient: Serializable::read_from(buf)?, + result: Serializable::read_from(buf)?, + experience: Serializable::read_from(buf)?, + cooking_time: Serializable::read_from(buf)?, + }, + _ => panic!("unrecognized recipe type: {}", ty) + }; + + Ok(Recipe { id, ty, data }) + } + + fn write_to(&self, _: &mut W) -> Result<(), Error> { + unimplemented!() + } +} + +#[derive(Debug, Default)] +pub struct Tags { + pub tag_name: String, + pub entries: LenPrefixed, +} + +impl Serializable for Tags { + fn read_from(buf: &mut R) -> Result { + Ok(Tags { + tag_name: Serializable::read_from(buf)?, + entries: Serializable::read_from(buf)?, + }) + } + + fn write_to(&self, _: &mut W) -> Result<(), Error> { + unimplemented!() + } +} + +#[derive(Debug, Default)] +pub struct CommandNode { + pub flags: u8, + pub children: LenPrefixed, + pub redirect_node: Option, + pub name: Option, + pub parser: Option, + pub properties: Option, + pub suggestions_type: Option, +} + +#[derive(Debug, Eq, PartialEq)] +enum CommandNodeType { + Root, + Literal, + Argument, +} + +#[derive(Debug)] +pub enum CommandProperty { + Bool, + Double { + flags: u8, + min: Option, + max: Option, + }, + Float { + flags: u8, + min: Option, + max: Option, + }, + Integer { + flags: u8, + min: Option, + max: Option, + }, + String { + token_type: VarInt, + }, + Entity { + flags: u8, + }, + GameProfile, + BlockPos, + Vec3, + Vec2, + BlockState, + BlockPredicate, + ItemStack, + ItemPredicate, + Color, + Component, + Message, + Nbt, + NbtPath, + Objective, + ObjectiveCriteria, + Operation, + Particle, + Rotation, + ScoreboardSlot, + ScoreHolder { + flags: u8, + }, + Swizzle, + Team, + ItemSlot, + ResourceLocation, + MobEffect, + Function, + EntityAnchor, + Range { + decimals: bool, + }, + ItemEnchantment, +} + + +impl Serializable for CommandNode { + fn read_from(buf: &mut R) -> Result { + let flags: u8 = Serializable::read_from(buf)?; + let children: LenPrefixed = Serializable::read_from(buf)?; + + let node_type = match flags & 0x03 { + 0 => CommandNodeType::Root, + 1 => CommandNodeType::Literal, + 2 => CommandNodeType::Argument, + _ => panic!("unrecognized command node type {}", flags & 0x03), + }; + let _is_executable = flags & 0x04 != 0; + let has_redirect = flags & 0x08 != 0; + let has_suggestions_type = flags & 0x10 != 0; + + let redirect_node: Option = if has_redirect { + Some(Serializable::read_from(buf)?) + } else { + None + }; + + let name: Option = if node_type == CommandNodeType::Argument || node_type == CommandNodeType::Literal { + Serializable::read_from(buf)? + } else { + None + }; + let parser: Option = if node_type == CommandNodeType::Argument { + Serializable::read_from(buf)? + } else { + None + }; + + let properties: Option = if let Some(ref parse) = parser { + Some(match parse.as_ref() { + "brigadier:bool" => CommandProperty::Bool, + "brigadier:double" => { + let flags = Serializable::read_from(buf)?; + let min = if flags & 0x01 != 0 { Some(Serializable::read_from(buf)?) } else { None }; + let max = if flags & 0x02 != 0 { Some(Serializable::read_from(buf)?) } else { None }; + CommandProperty::Double { flags, min, max } + }, + "brigadier:float" => { + let flags = Serializable::read_from(buf)?; + let min = if flags & 0x01 != 0 { Some(Serializable::read_from(buf)?) } else { None }; + let max = if flags & 0x02 != 0 { Some(Serializable::read_from(buf)?) } else { None }; + CommandProperty::Float { flags, min, max } + }, + "brigadier:integer" => { + let flags = Serializable::read_from(buf)?; + let min = if flags & 0x01 != 0 { Some(Serializable::read_from(buf)?) } else { None }; + let max = if flags & 0x02 != 0 { Some(Serializable::read_from(buf)?) } else { None }; + CommandProperty::Integer { flags, min, max } + }, + "brigadier:string" => { + CommandProperty::String { token_type: Serializable::read_from(buf)? } + }, + "minecraft:entity" => { + CommandProperty::Entity { flags: Serializable::read_from(buf)? } + }, + "minecraft:game_profile" => CommandProperty::GameProfile, + "minecraft:block_pos" => CommandProperty::BlockPos, + "minecraft:vec3" => CommandProperty::Vec3, + "minecraft:vec2" => CommandProperty::Vec2, + "minecraft:block_state" => CommandProperty::BlockState, + "minecraft:block_predicate" => CommandProperty::BlockPredicate, + "minecraft:item_stack" => CommandProperty::ItemStack, + "minecraft:item_predicate" => CommandProperty::ItemPredicate, + "minecraft:color" => CommandProperty::Color, + "minecraft:component" => CommandProperty::Component, + "minecraft:message" => CommandProperty::Message, + "minecraft:nbt" => CommandProperty::Nbt, + "minecraft:nbt_path" => CommandProperty::NbtPath, + "minecraft:objective" => CommandProperty::Objective, + "minecraft:objective_criteria" => CommandProperty::ObjectiveCriteria, + "minecraft:operation" => CommandProperty::Operation, + "minecraft:particle" => CommandProperty::Particle, + "minecraft:rotation" => CommandProperty::Rotation, + "minecraft:scoreboard_slot" => CommandProperty::ScoreboardSlot, + "minecraft:score_holder" => { + CommandProperty::ScoreHolder { flags: Serializable::read_from(buf)? } + }, + "minecraft:swizzle" => CommandProperty::Swizzle, + "minecraft:team" => CommandProperty::Team, + "minecraft:item_slot" => CommandProperty::ItemSlot, + "minecraft:resource_location" => CommandProperty::ResourceLocation, + "minecraft:mob_effect" => CommandProperty::MobEffect, + "minecraft:function" => CommandProperty::Function, + "minecraft:entity_anchor" => CommandProperty::EntityAnchor, + "minecraft:range" => { + CommandProperty::Range { decimals: Serializable::read_from(buf)? } + }, + "minecraft:item_enchantment" => CommandProperty::ItemEnchantment, + _ => panic!("unsupported command node parser {}", parse), + }) + } else { + None + }; + + let suggestions_type: Option = if has_suggestions_type { + Serializable::read_from(buf)? + } else { + None + }; + + Ok(CommandNode { flags, children, redirect_node, name, parser, properties, suggestions_type }) + } + + fn write_to(&self, _: &mut W) -> Result<(), Error> { + unimplemented!() + } +} + + diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index 2d022fc..5435189 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -1,5 +1,6 @@ use crate::protocol::*; +mod v1_13_2; mod v1_12_2; mod v1_11_2; mod v1_10_2; @@ -13,6 +14,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: match version { // https://wiki.vg/Protocol_History // https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite + // 1.13.2 + 404 => v1_13_2::translate_internal_packet_id(state, dir, id, to_internal), + // 1.12.2 340 => v1_12_2::translate_internal_packet_id(state, dir, id, to_internal), diff --git a/protocol/src/protocol/versions/v1_12_2.rs b/protocol/src/protocol/versions/v1_12_2.rs index c609790..7dc1454 100644 --- a/protocol/src/protocol/versions/v1_12_2.rs +++ b/protocol/src/protocol/versions/v1_12_2.rs @@ -92,7 +92,7 @@ protocol_packet_ids!( 0x2e => PlayerInfo 0x2f => TeleportPlayer_WithConfirm 0x30 => EntityUsedBed - 0x31 => UnlockRecipes + 0x31 => UnlockRecipes_NoSmelting 0x32 => EntityDestroy 0x33 => EntityRemoveEffect 0x34 => ResourcePackSend diff --git a/protocol/src/protocol/versions/v1_13_2.rs b/protocol/src/protocol/versions/v1_13_2.rs new file mode 100644 index 0000000..3f3c9da --- /dev/null +++ b/protocol/src/protocol/versions/v1_13_2.rs @@ -0,0 +1,169 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => QueryBlockNBT + 0x02 => ChatMessage + 0x03 => ClientStatus + 0x04 => ClientSettings + 0x05 => TabComplete + 0x06 => ConfirmTransactionServerbound + 0x07 => EnchantItem + 0x08 => ClickWindow + 0x09 => CloseWindow + 0x0a => PluginMessageServerbound + 0x0b => EditBook + 0x0c => QueryEntityNBT + 0x0d => UseEntity + 0x0e => KeepAliveServerbound_i64 + 0x0f => Player + 0x10 => PlayerPosition + 0x11 => PlayerPositionLook + 0x12 => PlayerLook + 0x13 => VehicleMove + 0x14 => SteerBoat + 0x15 => PickItem + 0x16 => CraftRecipeRequest + 0x17 => ClientAbilities + 0x18 => PlayerDigging + 0x19 => PlayerAction + 0x1a => SteerVehicle + 0x1b => CraftingBookData + 0x1c => NameItem + 0x1d => ResourcePackStatus + 0x1e => AdvancementTab + 0x1f => SelectTrade + 0x20 => SetBeaconEffect + 0x21 => HeldItemChange + 0x22 => UpdateCommandBlock + 0x23 => UpdateCommandBlockMinecart + 0x24 => CreativeInventoryAction + 0x25 => UpdateStructureBlock + 0x26 => SetSign + 0x27 => ArmSwing + 0x28 => SpectateTeleport + 0x29 => PlayerBlockPlacement_f32 + 0x2a => UseItem + } + clientbound Clientbound { + 0x00 => SpawnObject + 0x01 => SpawnExperienceOrb + 0x02 => SpawnGlobalEntity + 0x03 => SpawnMob + 0x04 => SpawnPainting + 0x05 => SpawnPlayer_f64 + 0x06 => Animation + 0x07 => Statistics + 0x08 => BlockBreakAnimation + 0x09 => UpdateBlockEntity + 0x0a => BlockAction + 0x0b => BlockChange_VarInt + 0x0c => BossBar + 0x0d => ServerDifficulty + 0x0e => ServerMessage + 0x0f => MultiBlockChange_VarInt + 0x10 => TabCompleteReply + 0x11 => DeclareCommands + 0x12 => ConfirmTransaction + 0x13 => WindowClose + 0x14 => WindowOpen + 0x15 => WindowItems + 0x16 => WindowProperty + 0x17 => WindowSetSlot + 0x18 => SetCooldown + 0x19 => PluginMessageClientbound + 0x1a => NamedSoundEffect + 0x1b => Disconnect + 0x1c => EntityAction + 0x1d => NBTQueryResponse + 0x1e => Explosion + 0x1f => ChunkUnload + 0x20 => ChangeGameState + 0x21 => KeepAliveClientbound_i64 + 0x22 => ChunkData + 0x23 => Effect + 0x24 => Particle + 0x25 => JoinGame_i32 + 0x26 => Maps + 0x27 => Entity + 0x28 => EntityMove_i16 + 0x29 => EntityLookAndMove_i16 + 0x2a => EntityLook_VarInt + 0x2b => VehicleTeleport + 0x2c => SignEditorOpen + 0x2d => CraftRecipeResponse + 0x2e => PlayerAbilities + 0x2f => CombatEvent + 0x30 => PlayerInfo + 0x31 => FacePlayer + 0x32 => TeleportPlayer_WithConfirm + 0x33 => EntityUsedBed + 0x34 => UnlockRecipes_WithSmelting + 0x35 => EntityDestroy + 0x36 => EntityRemoveEffect + 0x37 => ResourcePackSend + 0x38 => Respawn + 0x39 => EntityHeadLook + 0x3a => SelectAdvancementTab + 0x3b => WorldBorder + 0x3c => Camera + 0x3d => SetCurrentHotbarSlot + 0x3e => ScoreboardDisplay + 0x3f => EntityMetadata + 0x40 => EntityAttach + 0x41 => EntityVelocity + 0x42 => EntityEquipment + 0x43 => SetExperience + 0x44 => UpdateHealth + 0x45 => ScoreboardObjective + 0x46 => SetPassengers + 0x47 => Teams + 0x48 => UpdateScore + 0x49 => SpawnPosition + 0x4a => TimeUpdate + 0x4c => StopSound + 0x4d => SoundEffect + 0x4e => PlayerListHeaderFooter + 0x4f => CollectItem + 0x50 => EntityTeleport_f64 + 0x51 => Advancements + 0x52 => EntityProperties + 0x53 => EntityEffect + 0x54 => DeclareRecipes + 0x55 => Tags + } + } + login Login { + serverbound Serverbound { + 0x00 => LoginStart + 0x01 => EncryptionResponse + 0x02 => LoginPluginResponse + } + clientbound Clientbound { + 0x00 => LoginDisconnect + 0x01 => EncryptionRequest + 0x02 => LoginSuccess + 0x03 => SetInitialCompression + 0x04 => LoginPluginRequest + } + } + status Status { + serverbound Serverbound { + 0x00 => StatusRequest + 0x01 => StatusPing + } + clientbound Clientbound { + 0x00 => StatusResponse + 0x01 => StatusPong + } + } +); + +