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<u8>

* 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
This commit is contained in:
ice_iix 2018-12-09 12:03:55 -08:00
parent ad8bcf6aba
commit 7f2e2033ca
1 changed files with 180 additions and 25 deletions

View File

@ -38,32 +38,161 @@ impl <T: MetaValue> MetadataKey<T> {
}
}
pub struct Metadata {
pub struct Metadata18 {
map: HashMap<i32, Value>,
}
impl Metadata {
pub fn new() -> Metadata {
Metadata { map: HashMap::new() }
pub struct Metadata19 {
map: HashMap<i32, Value>,
}
trait MetadataBase: fmt::Debug + Default {
fn map(&self) -> &HashMap<i32, Value>;
fn map_mut(&mut self) -> &mut HashMap<i32, Value>;
fn get<T: MetaValue>(&self, key: &MetadataKey<T>) -> Option<&T> {
self.map().get(&key.index).map(T::unwrap)
}
pub fn get<T: MetaValue>(&self, key: &MetadataKey<T>) -> Option<&T> {
self.map.get(&key.index).map(T::unwrap)
}
pub fn put<T: MetaValue>(&mut self, key: &MetadataKey<T>, val: T) {
self.map.insert(key.index, val.wrap());
fn put<T: MetaValue>(&mut self, key: &MetadataKey<T>, val: T) {
self.map_mut().insert(key.index, val.wrap());
}
fn put_raw<T: MetaValue>(&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<i32, Value> { &self.map }
fn map_mut(&mut self) -> &mut HashMap<i32, Value> { &mut self.map }
}
impl MetadataBase for Metadata19 {
fn map(&self) -> &HashMap<i32, Value> { &self.map }
fn map_mut(&mut self) -> &mut HashMap<i32, Value> { &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<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
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::<item::Stack>::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<W: io::Write>(&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<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
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<item::Stack>),
Bool(bool),
Vector([f32; 3]),
Rotation([i32; 3]),
Position(Position),
OptionalPosition(Option<Position>),
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());