From 7f2e2033cad5d9739c433202844c4a8f0dd1b27f 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/types/metadata.rs | 205 +++++++++++++++++++++++++++++---- 1 file changed, 180 insertions(+), 25 deletions(-) diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 396b983..498cd01 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -38,32 +38,161 @@ impl MetadataKey { } } -pub struct Metadata { +pub struct Metadata18 { map: HashMap, } -impl Metadata { - pub fn new() -> Metadata { - Metadata { map: HashMap::new() } +pub struct Metadata19 { + map: HashMap, +} + +trait MetadataBase: fmt::Debug + Default { + fn map(&self) -> &HashMap; + fn map_mut(&mut self) -> &mut HashMap; + + fn get(&self, key: &MetadataKey) -> Option<&T> { + self.map().get(&key.index).map(T::unwrap) } - pub fn get(&self, key: &MetadataKey) -> Option<&T> { - self.map.get(&key.index).map(T::unwrap) - } - - pub fn put(&mut self, key: &MetadataKey, val: T) { - self.map.insert(key.index, val.wrap()); + fn put(&mut self, key: &MetadataKey, val: T) { + self.map_mut().insert(key.index, val.wrap()); } fn put_raw(&mut self, index: i32, val: T) { - self.map.insert(index, val.wrap()); + self.map_mut().insert(index, val.wrap()); + } + + fn fmt2(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Metadata[ ")?; + for (k, v) in self.map() { + write!(f, "{:?}={:?}, ", k, v)?; + } + write!(f, "]") } } -impl Serializable for Metadata { +impl MetadataBase for Metadata18 { + fn map(&self) -> &HashMap { &self.map } + fn map_mut(&mut self) -> &mut HashMap { &mut self.map } +} + +impl MetadataBase for Metadata19 { + fn map(&self) -> &HashMap { &self.map } + fn map_mut(&mut self) -> &mut HashMap { &mut self.map } +} + +impl Metadata18 { + pub fn new() -> Self { + Self { map: HashMap::new() } + } +} + +impl Metadata19 { + pub fn new() -> Self { + Self { map: HashMap::new() } + } +} + + + +impl Serializable for Metadata18 { fn read_from(buf: &mut R) -> Result { - let mut m = Metadata::new(); + let mut m = Self::new(); + loop { + let ty_index = u8::read_from(buf)? as i32; + if ty_index == 0x7f { + break; + } + let index = ty_index & 0x1f; + let ty = ty_index >> 5; + + match ty { + 0 => m.put_raw(index, i8::read_from(buf)?), + 1 => m.put_raw(index, i16::read_from(buf)?), + 2 => m.put_raw(index, i32::read_from(buf)?), + 3 => m.put_raw(index, f32::read_from(buf)?), + 4 => m.put_raw(index, String::read_from(buf)?), + 5 => m.put_raw(index, Option::::read_from(buf)?), + 6 => m.put_raw(index, + [i32::read_from(buf)?, + i32::read_from(buf)?, + i32::read_from(buf)?]), + 7 => m.put_raw(index, + [f32::read_from(buf)?, + f32::read_from(buf)?, + f32::read_from(buf)?]), + _ => return Err(protocol::Error::Err("unknown metadata type".to_owned())), + } + } + Ok(m) + } + + fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { + for (k, v) in &self.map { + if (*k as u8) > 0x1f { + panic!("write metadata index {:x} > 0x1f", *k as u8); + } + + let ty_index: u8 = *k as u8; + const TYPE_SHIFT: usize = 5; + + match *v + { + Value::Byte(ref val) => { + u8::write_to(&(ty_index | (0 << TYPE_SHIFT)), buf)?; + val.write_to(buf)?; + } + Value::Short(ref val) => { + u8::write_to(&(ty_index | (1 << TYPE_SHIFT)), buf)?; + val.write_to(buf)?; + } + + Value::Int(ref val) => { + u8::write_to(&(ty_index | (2 << TYPE_SHIFT)), buf)?; + val.write_to(buf)?; + } + Value::Float(ref val) => { + u8::write_to(&(ty_index | (3 << TYPE_SHIFT)), buf)?; + val.write_to(buf)?; + } + Value::String(ref val) => { + u8::write_to(&(ty_index | (4 << TYPE_SHIFT)), buf)?; + val.write_to(buf)?; + } + Value::OptionalItemStack(ref val) => { + u8::write_to(&(ty_index | (5 << TYPE_SHIFT)), buf)?; + val.write_to(buf)?; + } + Value::Vector(ref val) => { + u8::write_to(&(ty_index | (6 << TYPE_SHIFT)), buf)?; + val[0].write_to(buf)?; + val[1].write_to(buf)?; + val[2].write_to(buf)?; + } + Value::Rotation(ref val) => { + u8::write_to(&(ty_index | (7 << TYPE_SHIFT)), buf)?; + val[0].write_to(buf)?; + val[1].write_to(buf)?; + val[2].write_to(buf)?; + } + + Value::FormatComponent(_) | Value::Bool(_) | Value::Position(_) | + Value::OptionalPosition(_) | Value::Direction(_) | Value::OptionalUUID(_) | + Value::Block(_) | Value::NBTTag(_) => { + panic!("attempted to write 1.9+ metadata to 1.8"); + } + } + } + u8::write_to(&0x7f, buf)?; + Ok(()) + } +} + +impl Serializable for Metadata19 { + + fn read_from(buf: &mut R) -> Result { + let mut m = Self::new(); loop { let index = u8::read_from(buf)? as i32; if index == 0xFF { @@ -179,6 +308,7 @@ impl Serializable for Metadata { // TODO: write NBT tags metadata //nbt::Tag(*val).write_to(buf)?; } + _ => panic!("unexpected metadata"), } } u8::write_to(&0xFF, buf)?; @@ -186,25 +316,25 @@ impl Serializable for Metadata { } } -impl fmt::Debug for Metadata { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Metadata[ ")?; - for (k, v) in &self.map { - write!(f, "{:?}={:?}, ", k, v)?; - } - write!(f, "]") +// TODO: is it possible to implement these traits on MetadataBase instead? +impl fmt::Debug for Metadata19 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt2(f) } } +impl fmt::Debug for Metadata18 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt2(f) } } + +impl Default for Metadata19 { + fn default() -> Self { + Self::new() } } - -impl Default for Metadata { - fn default() -> Metadata { - Metadata::new() +impl Default for Metadata18 { + fn default() -> Self { + Self::new() } } #[derive(Debug)] pub enum Value { Byte(i8), + Short(i16), Int(i32), Float(f32), String(String), @@ -212,6 +342,7 @@ pub enum Value { OptionalItemStack(Option), Bool(bool), Vector([f32; 3]), + Rotation([i32; 3]), Position(Position), OptionalPosition(Option), Direction(protocol::VarInt), // TODO: Proper type @@ -237,6 +368,18 @@ impl MetaValue for i8 { } } +impl MetaValue for i16 { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Short(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Short(self) + } +} + impl MetaValue for i32 { fn unwrap(value: &Value) -> &Self { match *value { @@ -309,6 +452,18 @@ impl MetaValue for bool { } } +impl MetaValue for [i32; 3] { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Rotation(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Rotation(self) + } +} + impl MetaValue for [f32; 3] { fn unwrap(value: &Value) -> &Self { match *value { @@ -406,7 +561,7 @@ mod test { #[test] fn basic() { - let mut m = Metadata::new(); + let mut m = Metadata19::new(); m.put(&TEST, "Hello world".to_owned());