From afac49389687463f9abc6f18ce678899f014790a Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 9 Dec 2018 12:03:55 -0800 Subject: [PATCH] 1.8.9 (47) multiprotocol support (#57) Protocol 47 (1.8.9-1.8) is the biggest multiprotocol (https://github.com/iceiix/steven/issues/18) change yet: * New chunk format (load_chunk18) * New metadata format (Metadata18) * New packets and changes to 13 packets References: http://wiki.vg/index.php?title=Protocol&oldid=7368 https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite https://wiki.vg/Protocol_History#1.8 https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/1.8/protocol.json 1.8 chunk format: https://wiki.vg/index.php?title=Chunk_Format&oldid=6124 1.9 chunk format: https://wiki.vg/index.php?title=Chunk_Format&oldid=7411 1.8 vs 1.9: https://wiki.vg/index.php?title=Chunk_Format&diff=7411&oldid=6124 https://github.com/PrismarineJS/prismarine-chunk/blob/master/src/pc/1.8/section.js https://github.com/PrismarineJS/prismarine-chunk/blob/master/src/pc/1.8/chunk.js Details: * Add 1.8.9 packet IDs from https://github.com/iceiix/steven/pull/37 * Add ChunkDataBulk, parse the ChunkMeta and save data in Vec * Add EntityEquipment u16 variant, EntityStatus, ChunkData u16 variants * SpawnPlayer with added held item https://wiki.vg/Protocol_History#15w31a Removed Current Item short from Spawn Player (0x0C) * SpawnObject no UUID and optional velocity https://wiki.vg/index.php?title=Protocol&oldid=7368#Spawn_Object https://wiki.vg/Protocol_History#15w31a Added Entity UUID after entity ID to Spawn Object (0x0E) Spawn Object always sends velocity, even if data is 0 * SpawnMob no UUID variant https://wiki.vg/Protocol_History#15w31a Added Entity UUID after entity ID to Spawn Mob (0x0F) * Maps packet without tracking position boolean https://wiki.vg/index.php?title=Protocol&oldid=7368#Map https://wiki.vg/Protocol_History#15w34a Added tracking position boolean to Map (0x34) * Update Entity NBT was removed and Bossbar added (both 0x49) >1.8 https://wiki.vg/index.php?title=Protocol&oldid=7368#Update_Entity_NBT https://wiki.vg/Protocol_History#15w31a Removed Update Entity NBT Packet (0x49) Added Boss Bar packet (0x4 * Use entity without hands https://wiki.vg/index.php?title=Protocol&oldid=7368#Use_Entity https://wiki.vg/Protocol_History#15w31a Added VarInt enum for selected hand in Use Entity (0x02); only sent if type is interact or interact at * Player block placement, held item stack and face byte variant https://wiki.vg/index.php?title=Protocol&oldid=7368#Player_Block_Placement https://wiki.vg/Protocol_History#15w31a Face for Player Block Placement is now a VarInt enum instead of a byte Replaced held item (slot) with VarInt enum selected hand in Player Block Placement * Arm swing without hands, a packet with no fields, uses a ZST https://wiki.vg/index.php?title=Protocol&oldid=7368#Animation_2 https://github.com/iceiix/steven/pull/57#issuecomment-444289008 https://doc.rust-lang.org/nomicon/exotic-sizes.html * ClickWindow uses u8 mode, same as in 15w39c * ClientSettings without hands * SpectateTeleport is added before ResourcePackStatus * Copy load_chunk to load_chunk19 and load_chunk18 * 1.8 chunk reading implementation, load_chunk18 * Support both metadata formats, Metadata18/Metadata19 * Remove fmt::Debug * Implement formatting in MetadataBase and bounce through fmt::Debug --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 139 ++++++++++++++++++++++- protocol/src/protocol/versions.rs | 4 + protocol/src/protocol/versions/v1_8_9.rs | 139 +++++++++++++++++++++++ 4 files changed, 277 insertions(+), 7 deletions(-) create mode 100644 protocol/src/protocol/versions/v1_8_9.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index c247dba..3fcc23d 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; 7] = [340, 316, 315, 210, 109, 107, 74]; +pub const SUPPORTED_PROTOCOLS: [i32; 8] = [340, 316, 315, 210, 109, 107, 74, 47]; /// Helper macro for defining packets diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index aea455f..bf87440 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -92,6 +92,13 @@ state_packets!( field displayed_skin_parts: u8 =, field main_hand: VarInt =, } + packet ClientSettings_u8_Handsfree { + field locale: String =, + field view_distance: u8 =, + field chat_mode: u8 =, + field chat_colors: bool =, + field displayed_skin_parts: u8 =, + } /// ConfirmTransactionServerbound is a reply to ConfirmTransaction. packet ConfirmTransactionServerbound { field id: u8 =, @@ -141,6 +148,13 @@ state_packets!( field target_z: f32 = when(|p: &UseEntity| p.ty.0 == 2), field hand: VarInt = when(|p: &UseEntity| p.ty.0 == 0 || p.ty.0 == 2), } + packet UseEntity_Handsfree { + field target_id: VarInt =, + field ty: VarInt =, + field target_x: f32 = when(|p: &UseEntity_Handsfree| p.ty.0 == 2), + field target_y: f32 = when(|p: &UseEntity_Handsfree| p.ty.0 == 2), + field target_z: f32 = when(|p: &UseEntity_Handsfree| p.ty.0 == 2), + } /// KeepAliveServerbound is sent by a client as a response to a /// KeepAliveClientbound. If the client doesn't reply the server /// may disconnect the client. @@ -273,6 +287,9 @@ state_packets!( packet ArmSwing { field hand: VarInt =, } + packet ArmSwing_Handsfree { + field empty: () =, + } /// SpectateTeleport is sent by clients in spectator mode to teleport to a player. packet SpectateTeleport { field target: UUID =, @@ -294,6 +311,15 @@ state_packets!( field cursor_y: u8 =, field cursor_z: u8 =, } + packet PlayerBlockPlacement_u8_Item { + field location: Position =, + field face: u8 =, + field hand: Option =, + field cursor_x: u8 =, + field cursor_y: u8 =, + field cursor_z: u8 =, + } + /// UseItem is sent when the client tries to use an item. packet UseItem { field hand: VarInt =, @@ -330,6 +356,19 @@ state_packets!( field velocity_y: i16 =, field velocity_z: i16 =, } + packet SpawnObject_i32_NoUUID { + field entity_id: VarInt =, + field ty: u8 =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field pitch: i8 =, + field yaw: i8 =, + field data: i32 =, + field velocity_x: i16 = when(|p: &SpawnObject_i32_NoUUID| p.data != 0), + field velocity_y: i16 = when(|p: &SpawnObject_i32_NoUUID| p.data != 0), + field velocity_z: i16 = when(|p: &SpawnObject_i32_NoUUID| p.data != 0), + } /// SpawnExperienceOrb spawns a single experience orb into the world when /// it is in range of the client. The count controls the amount of experience /// gained when collected. @@ -378,7 +417,7 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata =, + field metadata: types::Metadata19 =, } packet SpawnMob_u8 { field entity_id: VarInt =, @@ -393,7 +432,7 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata =, + field metadata: types::Metadata19 =, } packet SpawnMob_u8_i32 { field entity_id: VarInt =, @@ -408,7 +447,21 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata =, + field metadata: types::Metadata19 =, + } + packet SpawnMob_u8_i32_NoUUID_18 { + field entity_id: VarInt =, + field ty: u8 =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field yaw: i8 =, + field pitch: i8 =, + field head_pitch: i8 =, + field velocity_x: i16 =, + field velocity_y: i16 =, + field velocity_z: i16 =, + field metadata: types::Metadata18 =, } /// SpawnPainting spawns a painting into the world when it is in range of /// the client. The title effects the size and the texture of the painting. @@ -436,7 +489,7 @@ state_packets!( field z: f64 =, field yaw: i8 =, field pitch: i8 =, - field metadata: types::Metadata =, + field metadata: types::Metadata19 =, } packet SpawnPlayer_i32 { field entity_id: VarInt =, @@ -446,7 +499,18 @@ state_packets!( field z: i32 =, field yaw: i8 =, field pitch: i8 =, - field metadata: types::Metadata =, + field metadata: types::Metadata19 =, + } + packet SpawnPlayer_i32_HeldItem_18 { + field entity_id: VarInt =, + field uuid: UUID =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field yaw: i8 =, + field pitch: i8 =, + field current_item: u16 =, + field metadata: types::Metadata18 =, } /// Animation is sent by the server to play an animation on a specific entity. packet Animation { @@ -664,6 +728,18 @@ state_packets!( field bitmask: VarInt =, field data: LenPrefixedBytes =, } + packet ChunkData_NoEntities_u16 { + field chunk_x: i32 =, + field chunk_z: i32 =, + field new: bool =, + field bitmask: u16 =, + field data: LenPrefixedBytes =, + } + packet ChunkDataBulk { + field skylight: bool =, + field chunk_meta: LenPrefixed =, + field chunk_data: Vec =, + } /// Effect plays a sound effect or particle at the target location with the /// volume (of sounds) being relative to the player's position unless /// DisableRelative is set to true. @@ -737,6 +813,16 @@ state_packets!( field z: Option = when(|p: &Maps| p.columns > 0), field data: Option> = when(|p: &Maps| p.columns > 0), } + packet Maps_NoTracking { + field item_damage: VarInt =, + field scale: i8 =, + field icons: LenPrefixed =, + field columns: u8 =, + field rows: Option = when(|p: &Maps_NoTracking| p.columns > 0), + field x: Option = when(|p: &Maps_NoTracking| p.columns > 0), + field z: Option = when(|p: &Maps_NoTracking| p.columns > 0), + field data: Option> = when(|p: &Maps_NoTracking| p.columns > 0), + } /// EntityMove moves the entity with the id by the offsets provided. packet EntityMove_i16 { field entity_id: VarInt =, @@ -782,6 +868,11 @@ state_packets!( packet Entity { field entity_id: VarInt =, } + /// EntityUpdateNBT updates the entity named binary tag. + packet EntityUpdateNBT { + field entity_id: VarInt =, + field nbt: Option =, + } /// Teleports the player's vehicle packet VehicleTeleport { field x: f64 =, @@ -881,6 +972,10 @@ state_packets!( field entity_id: VarInt =, field head_yaw: i8 =, } + packet EntityStatus { + field entity_id: i32 =, + field entity_status: i8 =, + } /// SelectAdvancementTab indicates the client should switch the advancement tab. packet SelectAdvancementTab { field has_id: bool =, @@ -915,7 +1010,11 @@ state_packets!( /// EntityMetadata updates the metadata for an entity. packet EntityMetadata { field entity_id: VarInt =, - field metadata: types::Metadata =, + field metadata: types::Metadata19 =, + } + packet EntityMetadata_18 { + field entity_id: VarInt =, + field metadata: types::Metadata18 =, } /// EntityAttach attaches to entities together, either by mounting or leashing. /// -1 can be used at the EntityID to deattach. @@ -944,6 +1043,11 @@ state_packets!( field slot: VarInt =, field item: Option =, } + packet EntityEquipment_u16 { + field entity_id: VarInt =, + field slot: u16 =, + field item: Option =, + } /// SetExperience updates the experience bar on the client. packet SetExperience { field experience_bar: f32 =, @@ -1266,6 +1370,29 @@ impl Serializable for BlockChangeRecord { } } +#[derive(Debug, Default)] +pub struct ChunkMeta { + pub x: i32, + pub z: i32, + pub bitmask: u16, +} + +impl Serializable for ChunkMeta { + fn read_from(buf: &mut R) -> Result { + Ok(ChunkMeta { + x: Serializable::read_from(buf)?, + z: Serializable::read_from(buf)?, + bitmask: Serializable::read_from(buf)?, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.x.write_to(buf)?; + self.z.write_to(buf)?; + self.bitmask.write_to(buf) + } +} + #[derive(Debug, Default)] pub struct ExplosionRecord { pub x: i8, diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index 259c5af..3e71b28 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -6,6 +6,7 @@ mod v1_10_2; mod v1_9_2; mod v1_9; mod v15w39c; +mod v1_8_9; pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { @@ -32,6 +33,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: // 15w39a/b/c 74 => v15w39c::translate_internal_packet_id(state, dir, id, to_internal), + // 1.8.9 - 1.8 + 47 => v1_8_9::translate_internal_packet_id(state, dir, id, to_internal), + _ => panic!("unsupported protocol version"), } } diff --git a/protocol/src/protocol/versions/v1_8_9.rs b/protocol/src/protocol/versions/v1_8_9.rs new file mode 100644 index 0000000..72d55ff --- /dev/null +++ b/protocol/src/protocol/versions/v1_8_9.rs @@ -0,0 +1,139 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => KeepAliveServerbound_VarInt + 0x01 => ChatMessage + 0x02 => UseEntity_Handsfree + 0x03 => Player + 0x04 => PlayerPosition + 0x05 => PlayerLook + 0x06 => PlayerPositionLook + 0x07 => PlayerDigging_u8 + 0x08 => PlayerBlockPlacement_u8_Item + 0x09 => HeldItemChange + 0x0a => ArmSwing_Handsfree + 0x0b => PlayerAction + 0x0c => SteerVehicle + 0x0d => CloseWindow + 0x0e => ClickWindow_u8 + 0x0f => ConfirmTransactionServerbound + 0x10 => CreativeInventoryAction + 0x11 => EnchantItem + 0x12 => SetSign + 0x13 => ClientAbilities + 0x14 => TabComplete_NoAssume + 0x15 => ClientSettings_u8_Handsfree + 0x16 => ClientStatus + 0x17 => PluginMessageServerbound + 0x18 => SpectateTeleport + 0x19 => ResourcePackStatus + } + clientbound Clientbound { + 0x00 => KeepAliveClientbound_VarInt + 0x01 => JoinGame_i8 + 0x02 => ServerMessage + 0x03 => TimeUpdate + 0x04 => EntityEquipment_u16 + 0x05 => SpawnPosition + 0x06 => UpdateHealth + 0x07 => Respawn + 0x08 => TeleportPlayer_NoConfirm + 0x09 => SetCurrentHotbarSlot + 0x0a => EntityUsedBed + 0x0b => Animation + 0x0c => SpawnPlayer_i32_HeldItem_18 + 0x0d => CollectItem_nocount + 0x0e => SpawnObject_i32_NoUUID + 0x0f => SpawnMob_u8_i32_NoUUID_18 + 0x10 => SpawnPainting_NoUUID + 0x11 => SpawnExperienceOrb_i32 + 0x12 => EntityVelocity + 0x13 => EntityDestroy + 0x14 => Entity + 0x15 => EntityMove_i8 + 0x16 => EntityLook + 0x17 => EntityLookAndMove_i8 + 0x18 => EntityTeleport_i32 + 0x19 => EntityHeadLook + 0x1a => EntityStatus + 0x1b => EntityAttach_leashed + 0x1c => EntityMetadata_18 + 0x1d => EntityEffect + 0x1e => EntityRemoveEffect + 0x1f => SetExperience + 0x20 => EntityProperties + 0x21 => ChunkData_NoEntities_u16 + 0x22 => MultiBlockChange + 0x23 => BlockChange + 0x24 => BlockAction + 0x25 => BlockBreakAnimation + 0x26 => ChunkDataBulk + 0x27 => Explosion + 0x28 => Effect + 0x29 => NamedSoundEffect_u8_NoCategory + 0x2a => Particle + 0x2b => ChangeGameState + 0x2c => SpawnGlobalEntity_i32 + 0x2d => WindowOpen + 0x2e => WindowClose + 0x2f => WindowSetSlot + 0x30 => WindowItems + 0x31 => WindowProperty + 0x32 => ConfirmTransaction + 0x33 => UpdateSign + 0x34 => Maps_NoTracking + 0x35 => UpdateBlockEntity + 0x36 => SignEditorOpen + 0x37 => Statistics + 0x38 => PlayerInfo + 0x39 => PlayerAbilities + 0x3a => TabCompleteReply + 0x3b => ScoreboardObjective + 0x3c => UpdateScore + 0x3d => ScoreboardDisplay + 0x3e => Teams + 0x3f => PluginMessageClientbound + 0x40 => Disconnect + 0x41 => ServerDifficulty + 0x42 => CombatEvent + 0x43 => Camera + 0x44 => WorldBorder + 0x45 => Title_notext_component + 0x46 => SetCompression + 0x47 => PlayerListHeaderFooter + 0x48 => ResourcePackSend + 0x49 => EntityUpdateNBT + } + } + login Login { + serverbound Serverbound { + 0x00 => LoginStart + 0x01 => EncryptionResponse + } + clientbound Clientbound { + 0x00 => LoginDisconnect + 0x01 => EncryptionRequest + 0x02 => LoginSuccess + 0x03 => SetInitialCompression + } + } + status Status { + serverbound Serverbound { + 0x00 => StatusRequest + 0x01 => StatusPing + } + clientbound Clientbound { + 0x00 => StatusResponse + 0x01 => StatusPong + } + } +); + +