From 3cedeb792e507b57a8a70ca99af110f846a8ee58 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Mon, 7 Sep 2015 21:11:00 +0100 Subject: [PATCH 001/160] Initial commit --- protocol/src/protocol/mod.rs | 326 ++++++++++++++++++++++++++++++++ protocol/src/protocol/packet.rs | 96 ++++++++++ 2 files changed, 422 insertions(+) create mode 100644 protocol/src/protocol/mod.rs create mode 100644 protocol/src/protocol/packet.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs new file mode 100644 index 0000000..8c04af7 --- /dev/null +++ b/protocol/src/protocol/mod.rs @@ -0,0 +1,326 @@ +#![allow(dead_code)] +extern crate byteorder; + +use std::net::TcpStream; +use std::io; +use std::io::{Write, Read}; +use std::convert; +use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; + +/// Serializes types into a buffer +macro_rules! serialize_type { + ($dst:expr, $name:expr, u16) => { + $dst.write_u16::($name).unwrap(); + }; + ($dst:expr, $name:expr, i64) => { + $dst.write_i64::($name).unwrap(); + }; + ($dst:expr, $name:expr, VarInt) => { + try!(write_varint($dst, $name)); + }; + ($dst:expr, $name:expr, String) => { + try!(write_varint($dst, $name.len() as i32)); + $dst.extend($name.bytes()); + }; + ($dst:expr, $name:expr, Empty) => { + + }; + ($dst:expr, $name:expr, $ftype:ident) => { + unimplemented!() + }; +} + +/// Deserializes types from a buffer +macro_rules! deserialize_type { + ($src:expr, String) => { + { + let len = read_variant(&mut $src).unwrap(); + let mut ret = String::new(); + (&mut $src).take(len as u64).read_to_string(&mut ret).unwrap(); + ret + } + }; + ($src:expr, i64) => { + $src.read_i64::().unwrap() + }; + ($src:expr, Empty) => { Empty }; + ($src:expr, $ftype:ident) => { + unimplemented!() + }; +} + +/// Helper macro for defining packets +#[macro_export] +macro_rules! state_packets { + ($($state:ident $stateName:ident { + $($dir:ident $dirName:ident { + $($name:ident => $id:expr { + $($field:ident: $field_type:ident),+ + }),* + })+ + })+) => { + use protocol::*; + use std::io; + use std::io::{Read, Write}; + use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; + + pub enum Packet { + $( + $( + $( + $name($state::$dir::$name), + )* + )+ + )+ + } + + $( + pub mod $state { + $( + pub mod $dir { + use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; + use protocol::*; + use std::io; + use std::io::{Read, Write}; + + $( + pub struct $name { + $(pub $field: $field_type),+, + } + + impl PacketType for $name { + + fn packet_id(&self) -> i32{ $id } + + fn write(self, buf: &mut Vec) -> Result<(), io::Error> { + $( + serialize_type!(buf, self.$field, $field_type); + )+ + + Result::Ok(()) + } + } + )* + } + )+ + } + )+ + + /// Returns the packet for the given state, direction and id after parsing the fields + /// from the buffer. + pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut io::Cursor>) -> Option { + match state { + $( + State::$stateName => { + match dir { + $( + Direction::$dirName => { + match id { + $( + $id => Option::Some(Packet::$name($state::$dir::$name { + $($field: deserialize_type!(buf, $field_type)),+, + })), + )* + _ => Option::None + } + } + )+ + } + } + )+ + } + } + } +} + +pub mod packet; + +/// VarInt have a variable size (between 1 and 5 bytes) when encoded based +/// on the size of the number +pub type VarInt = i32; + +/// Encodes a VarInt into the Writer +pub fn write_varint(buf: &mut io::Write, v: VarInt) -> Result<(), io::Error> { + const PART : u32 = 0x7F; + let mut val = v as u32; + loop { + if (val & !PART) == 0 { + try!(buf.write_u8(val as u8)); + return Result::Ok(()); + } + try!(buf.write_u8(((val & PART) | 0x80) as u8)); + val >>= 7; + } +} + +/// Decodes a VarInt from the Reader +pub fn read_variant(buf: &mut io::Read) -> Result { + const PART : u32 = 0x7F; + let mut size = 0; + let mut val = 0u32; + loop { + let b = try!(buf.read_u8()) as u32; + val |= (b & PART) << (size * 7); + size+=1; + if size > 5 { + return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, Error::Err("VarInt too big".to_string()))) + } + if (b & 0x80) == 0 { + break + } + } + + Result::Ok(val as VarInt) +} + +/// Direction is used to define whether packets are going to the +/// server or the client. +pub enum Direction { + Serverbound, + Clientbound +} + +/// The protocol has multiple 'sub-protocols' or states which control which +/// packet an id points to. +#[derive(Clone, Copy)] +pub enum State { + Handshaking, + Play, + Status, + Login +} + +/// Return for any protocol related error. +#[derive(Debug)] +pub enum Error { + Err(String), + IOError(io::Error), +} + +impl convert::From for Error { + fn from(e : io::Error) -> Error { + Error::IOError(e) + } +} + +impl ::std::error::Error for Error { + fn description(&self) -> &str { + match self { + &Error::Err(ref val) => &val[..], + &Error::IOError(ref e) => e.description(), + } + } +} + +impl ::std::fmt::Display for Error { + fn fmt(&self, f : &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match self { + &Error::Err(ref val) => write!(f, "protocol error: {}", val), + &Error::IOError(ref e) => e.fmt(f) + } + } +} + + +/// Helper for empty structs +pub struct Empty; + +pub struct Conn { + stream: TcpStream, + host: String, + port: u16, + direction: Direction, + state: State, +} + +impl Conn { + pub fn new(target: &str) -> Result{ + // TODO SRV record support + let stream = match TcpStream::connect(target) { + Ok(val) => val, + Err(err) => return Result::Err(Error::IOError(err)) + }; + let parts = target.split(":").collect::>(); + Result::Ok(Conn { + stream: stream, + host: parts[0].to_owned(), + port: parts[1].parse().unwrap(), + direction: Direction::Serverbound, + state: State::Handshaking, + }) + } + + // TODO: compression and encryption + + pub fn write_packet(&mut self, packet: T) -> Result<(), Error> { + let mut buf = Vec::new(); + try!(write_varint(&mut buf, packet.packet_id())); + try!(packet.write(&mut buf)); + try!(write_varint(&mut self.stream, buf.len() as i32)); + try!(self.stream.write_all(&buf.into_boxed_slice())); + + Result::Ok(()) + } + + pub fn read_packet(&mut self) -> Result { + let len = try!(read_variant(&mut self.stream)) as usize; + let mut ibuf = Vec::with_capacity(len); + try!((&mut self.stream).take(len as u64).read_to_end(&mut ibuf)); + + let mut buf = io::Cursor::new(ibuf); + let id = try!(read_variant(&mut buf)); + + let dir = match self.direction { + Direction::Clientbound => Direction::Serverbound, + Direction::Serverbound => Direction::Clientbound, + }; + + let packet = packet::packet_by_id(self.state, dir, id, &mut buf); + + match packet { + Some(val) => { + let pos = buf.position() as usize; + let ibuf = buf.into_inner(); + if ibuf.len() < pos { + return Result::Err(Error::Err(format!("Failed to read all of packet 0x{:X}, had {} bytes left", id, ibuf.len() - pos))) + } + Result::Ok(val) + }, + None => Result::Err(Error::Err("missing packet".to_string())) + } + } +} + +pub trait PacketType { + fn packet_id(&self) -> i32; + + fn write(self, buf: &mut Vec) -> Result<(), io::Error>; +} + +#[test] +fn test() { + let mut c = Conn::new("localhost:25565").unwrap(); + + c.write_packet(packet::handshake::serverbound::Handshake{ + protocol_version: 69, + host: "localhost".to_string(), + port: 25565, + next: 1, + }).unwrap(); + c.state = State::Status; + c.write_packet(packet::status::serverbound::StatusRequest{empty: Empty}).unwrap(); + + match c.read_packet().unwrap() { + packet::Packet::StatusResponse(val) => println!("{}", val.status), + _ => panic!("Wrong packet"), + } + + c.write_packet(packet::status::serverbound::StatusPing{ping: 4433}).unwrap(); + + match c.read_packet().unwrap() { + packet::Packet::StatusPong(val) => println!("{}", val.ping), + _ => panic!("Wrong packet"), + } + + panic!("TODO!"); +} diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs new file mode 100644 index 0000000..4819c8a --- /dev/null +++ b/protocol/src/protocol/packet.rs @@ -0,0 +1,96 @@ + +state_packets!( + handshake Handshaking { + serverbound Serverbound { + // Handshake is the first packet sent in the protocol. + // Its used for deciding if the request is a client + // is requesting status information about the server + // (MOTD, players etc) or trying to login to the server. + // + // The host and port fields are not used by the vanilla + // server but are there for virtual server hosting to + // be able to redirect a client to a target server with + // a single address + port. + // + // Some modified servers/proxies use the handshake field + // differently, packing information into the field other + // than the hostname due to the protocol not providing + // any system for custom information to be transfered + // by the client to the server until after login. + Handshake => 0 { + // The protocol version of the connecting client + protocol_version: VarInt, + // The hostname the client connected to + host: String, + // The port the client connected to + port: u16, + // The next protocol state the client wants + next: VarInt + } + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + } + clientbound Clientbound { + } + } + login Login { + serverbound Serverbound { + } + clientbound Clientbound { + } + } + status Status { + serverbound Serverbound { + // StatusRequest is sent by the client instantly after + // switching to the Status protocol state and is used + // to signal the server to send a StatusResponse to the + // client + StatusRequest => 0 { + empty: Empty + }, + // StatusPing is sent by the client after recieving a + // StatusResponse. The client uses the time from sending + // the ping until the time of recieving a pong to measure + // the latency between the client and the server. + StatusPing => 1 { + ping: i64 + } + } + clientbound Clientbound { + // StatusResponse is sent as a reply to a StatusRequest. + // The Status should contain a json encoded structure with + // version information, a player sample, a description/MOTD + // and optionally a favicon. + // + // The structure is as follows + // { + // "version": { + // "name": "1.8.3", + // "protocol": 47, + // }, + // "players": { + // "max": 20, + // "online": 1, + // "sample": [ + // {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"} + // ] + // }, + // "description": "Hello world", + // "favicon": "data:image/png;base64," + // } + StatusResponse => 0 { + status: String + }, + // StatusPong is sent as a reply to a StatusPing. + // The Time field should be exactly the same as the + // one sent by the client. + StatusPong => 1 { + ping: i64 + } + } + } +); From 7c6070fb180a5195ca3fb85c2eac967112d444d8 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Mon, 7 Sep 2015 21:52:36 +0100 Subject: [PATCH 002/160] Improve the bit map --- protocol/src/protocol/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 8c04af7..a834ee0 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -299,6 +299,7 @@ pub trait PacketType { #[test] fn test() { + return; // Skip let mut c = Conn::new("localhost:25565").unwrap(); c.write_packet(packet::handshake::serverbound::Handshake{ From 1ed844a699e659e938b9c8e13bfdcf477c7e33b0 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Tue, 8 Sep 2015 12:57:24 +0100 Subject: [PATCH 003/160] Clean up protocol encoding/decoding --- protocol/src/protocol/mod.rs | 208 ++++++++++++++++++++--------------- 1 file changed, 119 insertions(+), 89 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index a834ee0..4bba560 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -1,54 +1,13 @@ #![allow(dead_code)] extern crate byteorder; +use std::default; use std::net::TcpStream; use std::io; use std::io::{Write, Read}; use std::convert; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; -/// Serializes types into a buffer -macro_rules! serialize_type { - ($dst:expr, $name:expr, u16) => { - $dst.write_u16::($name).unwrap(); - }; - ($dst:expr, $name:expr, i64) => { - $dst.write_i64::($name).unwrap(); - }; - ($dst:expr, $name:expr, VarInt) => { - try!(write_varint($dst, $name)); - }; - ($dst:expr, $name:expr, String) => { - try!(write_varint($dst, $name.len() as i32)); - $dst.extend($name.bytes()); - }; - ($dst:expr, $name:expr, Empty) => { - - }; - ($dst:expr, $name:expr, $ftype:ident) => { - unimplemented!() - }; -} - -/// Deserializes types from a buffer -macro_rules! deserialize_type { - ($src:expr, String) => { - { - let len = read_variant(&mut $src).unwrap(); - let mut ret = String::new(); - (&mut $src).take(len as u64).read_to_string(&mut ret).unwrap(); - ret - } - }; - ($src:expr, i64) => { - $src.read_i64::().unwrap() - }; - ($src:expr, Empty) => { Empty }; - ($src:expr, $ftype:ident) => { - unimplemented!() - }; -} - /// Helper macro for defining packets #[macro_export] macro_rules! state_packets { @@ -61,8 +20,7 @@ macro_rules! state_packets { })+) => { use protocol::*; use std::io; - use std::io::{Read, Write}; - use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; + use protocol::{Serializable}; pub enum Packet { $( @@ -78,12 +36,12 @@ macro_rules! state_packets { pub mod $state { $( pub mod $dir { - use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; use protocol::*; use std::io; - use std::io::{Read, Write}; + use protocol::{Serializable}; $( + #[derive(Default)] pub struct $name { $(pub $field: $field_type),+, } @@ -94,7 +52,7 @@ macro_rules! state_packets { fn write(self, buf: &mut Vec) -> Result<(), io::Error> { $( - serialize_type!(buf, self.$field, $field_type); + try!(self.$field.write_to(buf)); )+ Result::Ok(()) @@ -108,7 +66,7 @@ macro_rules! state_packets { /// Returns the packet for the given state, direction and id after parsing the fields /// from the buffer. - pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut io::Cursor>) -> Option { + pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut io::Cursor>) -> Result, io::Error> { match state { $( State::$stateName => { @@ -117,11 +75,15 @@ macro_rules! state_packets { Direction::$dirName => { match id { $( - $id => Option::Some(Packet::$name($state::$dir::$name { - $($field: deserialize_type!(buf, $field_type)),+, - })), + $id => { + let mut packet : $state::$dir::$name = $state::$dir::$name::default(); + $( + packet.$field = try!($field_type::read_from(&mut buf)); + )+ + Result::Ok(Option::Some(Packet::$name(packet))) + }, )* - _ => Option::None + _ => Result::Ok(Option::None) } } )+ @@ -135,42 +97,111 @@ macro_rules! state_packets { pub mod packet; -/// VarInt have a variable size (between 1 and 5 bytes) when encoded based -/// on the size of the number -pub type VarInt = i32; +trait Serializable { + fn read_from(buf: &mut io::Read) -> Result; + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error>; +} -/// Encodes a VarInt into the Writer -pub fn write_varint(buf: &mut io::Write, v: VarInt) -> Result<(), io::Error> { - const PART : u32 = 0x7F; - let mut val = v as u32; - loop { - if (val & !PART) == 0 { - try!(buf.write_u8(val as u8)); - return Result::Ok(()); - } - try!(buf.write_u8(((val & PART) | 0x80) as u8)); - val >>= 7; +impl Serializable for String { + fn read_from(buf: &mut io::Read) -> Result { + let len = try!(VarInt::read_from(buf)).0; + let mut ret = String::new(); + try!(buf.take(len as u64).read_to_string(&mut ret)); + Result::Ok(ret) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + let bytes = self.as_bytes(); + try!(VarInt(bytes.len() as i32).write_to(buf)); + try!(buf.write_all(bytes)); + Result::Ok(()) } } -/// Decodes a VarInt from the Reader -pub fn read_variant(buf: &mut io::Read) -> Result { - const PART : u32 = 0x7F; - let mut size = 0; - let mut val = 0u32; - loop { - let b = try!(buf.read_u8()) as u32; - val |= (b & PART) << (size * 7); - size+=1; - if size > 5 { - return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, Error::Err("VarInt too big".to_string()))) - } - if (b & 0x80) == 0 { - break +impl Serializable for Empty { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(Empty) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + Result::Ok(()) + } +} + +impl Default for Empty { + fn default() -> Empty { Empty } +} + +impl Serializable for i32 { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(try!(buf.read_i32::())) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_i32::(*self)); + Result::Ok(()) + } +} + +impl Serializable for i64 { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(try!(buf.read_i64::())) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_i64::(*self)); + Result::Ok(()) + } +} + +impl Serializable for u16 { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(try!(buf.read_u16::())) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_u16::(*self)); + Result::Ok(()) + } +} + +/// VarInt have a variable size (between 1 and 5 bytes) when encoded based +/// on the size of the number +pub struct VarInt(i32); + +impl Serializable for VarInt { + /// Decodes a VarInt from the Reader + fn read_from(buf: &mut io::Read) -> Result { + const PART : u32 = 0x7F; + let mut size = 0; + let mut val = 0u32; + loop { + let b = try!(buf.read_u8()) as u32; + val |= (b & PART) << (size * 7); + size+=1; + if size > 5 { + return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, Error::Err("VarInt too big".to_string()))) + } + if (b & 0x80) == 0 { + break + } } + + Result::Ok(VarInt(val as i32)) } - Result::Ok(val as VarInt) + /// Encodes a VarInt into the Writer + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + const PART : u32 = 0x7F; + let mut val = self.0 as u32; + loop { + if (val & !PART) == 0 { + try!(buf.write_u8(val as u8)); + return Result::Ok(()); + } + try!(buf.write_u8(((val & PART) | 0x80) as u8)); + val >>= 7; + } + } +} + +impl default::Default for VarInt { + fn default() -> VarInt { VarInt(0) } } /// Direction is used to define whether packets are going to the @@ -254,28 +285,28 @@ impl Conn { pub fn write_packet(&mut self, packet: T) -> Result<(), Error> { let mut buf = Vec::new(); - try!(write_varint(&mut buf, packet.packet_id())); + try!(VarInt(packet.packet_id()).write_to(&mut buf)); try!(packet.write(&mut buf)); - try!(write_varint(&mut self.stream, buf.len() as i32)); + try!(VarInt(buf.len() as i32).write_to(&mut self.stream)); try!(self.stream.write_all(&buf.into_boxed_slice())); Result::Ok(()) } pub fn read_packet(&mut self) -> Result { - let len = try!(read_variant(&mut self.stream)) as usize; + let len = try!(VarInt::read_from(&mut self.stream)).0 as usize; let mut ibuf = Vec::with_capacity(len); try!((&mut self.stream).take(len as u64).read_to_end(&mut ibuf)); let mut buf = io::Cursor::new(ibuf); - let id = try!(read_variant(&mut buf)); + let id = try!(VarInt::read_from(&mut buf)).0; let dir = match self.direction { Direction::Clientbound => Direction::Serverbound, Direction::Serverbound => Direction::Clientbound, }; - let packet = packet::packet_by_id(self.state, dir, id, &mut buf); + let packet = try!(packet::packet_by_id(self.state, dir, id, &mut buf)); match packet { Some(val) => { @@ -299,14 +330,13 @@ pub trait PacketType { #[test] fn test() { - return; // Skip let mut c = Conn::new("localhost:25565").unwrap(); c.write_packet(packet::handshake::serverbound::Handshake{ - protocol_version: 69, + protocol_version: VarInt(69), host: "localhost".to_string(), port: 25565, - next: 1, + next: VarInt(1), }).unwrap(); c.state = State::Status; c.write_packet(packet::status::serverbound::StatusRequest{empty: Empty}).unwrap(); From 11c33e2d8d5f30642dd370dd54f9a615592df9ff Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Thu, 10 Sep 2015 11:49:41 +0100 Subject: [PATCH 004/160] Main part of the protocol complete --- protocol/src/protocol/mod.rs | 396 +++++++++++++++++++++++++++++--- protocol/src/protocol/mojang.rs | 65 ++++++ protocol/src/protocol/packet.rs | 121 +++++++++- 3 files changed, 534 insertions(+), 48 deletions(-) create mode 100644 protocol/src/protocol/mojang.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 4bba560..ace9268 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -1,12 +1,20 @@ #![allow(dead_code)] extern crate byteorder; +extern crate hyper; +extern crate steven_openssl as openssl; +extern crate flate2; +extern crate serde_json; +pub mod mojang; + +use format; use std::default; use std::net::TcpStream; use std::io; use std::io::{Write, Read}; use std::convert; -use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; +use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; +use self::flate2::read::{ZlibDecoder, ZlibEncoder}; /// Helper macro for defining packets #[macro_export] @@ -14,13 +22,12 @@ macro_rules! state_packets { ($($state:ident $stateName:ident { $($dir:ident $dirName:ident { $($name:ident => $id:expr { - $($field:ident: $field_type:ident),+ - }),* + $($field:ident: $field_type:ty = $(when ($cond:expr))*, )+ + })* })+ })+) => { use protocol::*; use std::io; - use protocol::{Serializable}; pub enum Packet { $( @@ -36,9 +43,10 @@ macro_rules! state_packets { pub mod $state { $( pub mod $dir { + #![allow(unused_imports)] use protocol::*; use std::io; - use protocol::{Serializable}; + use format; $( #[derive(Default)] @@ -52,7 +60,9 @@ macro_rules! state_packets { fn write(self, buf: &mut Vec) -> Result<(), io::Error> { $( - try!(self.$field.write_to(buf)); + if true $(&& ($cond(&self)))* { + try!(self.$field.write_to(buf)); + } )+ Result::Ok(()) @@ -76,9 +86,12 @@ macro_rules! state_packets { match id { $( $id => { - let mut packet : $state::$dir::$name = $state::$dir::$name::default(); + use self::$state::$dir::$name; + let mut packet : $name = $name::default(); $( - packet.$field = try!($field_type::read_from(&mut buf)); + if true $(&& ($cond(&packet)))* { + packet.$field = try!(Serializable::read_from(&mut buf)); + } )+ Result::Ok(Option::Some(Packet::$name(packet))) }, @@ -97,11 +110,23 @@ macro_rules! state_packets { pub mod packet; -trait Serializable { +pub trait Serializable { fn read_from(buf: &mut io::Read) -> Result; fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error>; } +impl Serializable for Option where T : Serializable { + fn read_from(buf: &mut io::Read) -> Result, io::Error> { + Result::Ok(Some(try!(T::read_from(buf)))) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + if self.is_some() { + try!(self.as_ref().unwrap().write_to(buf)); + } + Result::Ok(()) + } +} + impl Serializable for String { fn read_from(buf: &mut io::Read) -> Result { let len = try!(VarInt::read_from(buf)).0; @@ -117,17 +142,90 @@ impl Serializable for String { } } -impl Serializable for Empty { - fn read_from(buf: &mut io::Read) -> Result { - Result::Ok(Empty) +impl Serializable for format::Component { + fn read_from(buf: &mut io::Read) -> Result { + let len = try!(VarInt::read_from(buf)).0; + let mut ret = String::new(); + try!(buf.take(len as u64).read_to_string(&mut ret)); + let val : serde_json::Value = serde_json::from_str(&ret[..]).unwrap(); + Result::Ok(Self::from_value(&val)) } fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + let val = serde_json::to_string(&self.to_value()).unwrap(); + let bytes = val.as_bytes(); + try!(VarInt(bytes.len() as i32).write_to(buf)); + try!(buf.write_all(bytes)); Result::Ok(()) } } -impl Default for Empty { - fn default() -> Empty { Empty } +pub struct Position(u64); + +impl Position { + fn new(x: i32, y: i32, z: i32) -> Position { + Position( + (((x as u64) & 0x3FFFFFF) << 38) | + (((y as u64) & 0xFFF) << 26) | + ((z as u64) & 0x3FFFFFF) + ) + } + + fn get_x(&self) -> i32 { + ((self.0 as i64) >> 38) as i32 + } + + fn get_y(&self) -> i32 { + (((self.0 as i64) >> 26) & 0xFFF) as i32 + } + + fn get_z(&self) -> i32 { + ((self.0 as i64) << 38 >> 38) as i32 + } +} + +impl Default for Position { + fn default() -> Position { + Position(0) + } +} + +impl Serializable for Position { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(Position(try!(buf.read_u64::()))) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_u64::(self.0)); + Result::Ok(()) + } +} + +impl Serializable for () { + fn read_from(_: &mut io::Read) -> Result<(), io::Error> { + Result::Ok(()) + } + fn write_to(&self, _: &mut io::Write) -> Result<(), io::Error> { + Result::Ok(()) + } +} + +impl Serializable for bool { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(try!(buf.read_u8()) != 0) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_u8(if *self { 1 } else { 0 })); + Result::Ok(()) + } +} + +impl Serializable for i16 { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(try!(buf.read_i16::())) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_i16::(*self)); + Result::Ok(()) + } } impl Serializable for i32 { @@ -150,6 +248,16 @@ impl Serializable for i64 { } } +impl Serializable for u8 { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(try!(buf.read_u8())) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_u8(*self)); + Result::Ok(()) + } +} + impl Serializable for u16 { fn read_from(buf: &mut io::Read) -> Result { Result::Ok(try!(buf.read_u16::())) @@ -160,10 +268,60 @@ impl Serializable for u16 { } } +pub trait Lengthable : Serializable + Into + From + Copy + Default {} + +pub struct LenPrefixed { + len: L, + pub data: Vec +} + +impl LenPrefixed { + fn new(data: Vec) -> LenPrefixed { + return LenPrefixed { + len: Default::default(), + data: data, + } + } +} + +impl Serializable for LenPrefixed { + fn read_from(buf: &mut io::Read) -> Result, io::Error> { + let len_data : L = try!(Serializable::read_from(buf)); + let len : usize = len_data.into(); + let mut data : Vec = Vec::with_capacity(len); + for _ in 0 .. len { + data.push(try!(Serializable::read_from(buf))); + } + Result::Ok(LenPrefixed{len: len_data, data: data}) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + let len_data : L = self.data.len().into(); + try!(len_data.write_to(buf)); + let ref data = self.data; + for val in data { + try!(val.write_to(buf)); + } + Result::Ok(()) + } +} + +impl Default for LenPrefixed { + fn default() -> Self { + LenPrefixed { + len: default::Default::default(), + data: default::Default::default() + } + } +} + /// VarInt have a variable size (between 1 and 5 bytes) when encoded based /// on the size of the number +#[derive(Clone, Copy)] pub struct VarInt(i32); +impl Lengthable for VarInt {} + impl Serializable for VarInt { /// Decodes a VarInt from the Reader fn read_from(buf: &mut io::Read) -> Result { @@ -204,8 +362,21 @@ impl default::Default for VarInt { fn default() -> VarInt { VarInt(0) } } +impl convert::Into for VarInt { + fn into(self) -> usize { + self.0 as usize + } +} + +impl convert::From for VarInt { + fn from(u: usize) -> VarInt { + VarInt(u as i32) + } +} + /// Direction is used to define whether packets are going to the /// server or the client. +#[derive(Clone, Copy)] pub enum Direction { Serverbound, Clientbound @@ -252,16 +423,18 @@ impl ::std::fmt::Display for Error { } } - -/// Helper for empty structs -pub struct Empty; - pub struct Conn { stream: TcpStream, host: String, port: u16, direction: Direction, state: State, + + cipher: Option, + + compression_threshold: i32, + compression_read: Option>>>, // Pending reset support + compression_write: Option>>>, } impl Conn { @@ -278,27 +451,58 @@ impl Conn { port: parts[1].parse().unwrap(), direction: Direction::Serverbound, state: State::Handshaking, + cipher: Option::None, + compression_threshold: -1, + compression_read: Option::None, + compression_write: Option::None, }) } - // TODO: compression and encryption - pub fn write_packet(&mut self, packet: T) -> Result<(), Error> { let mut buf = Vec::new(); try!(VarInt(packet.packet_id()).write_to(&mut buf)); try!(packet.write(&mut buf)); - try!(VarInt(buf.len() as i32).write_to(&mut self.stream)); - try!(self.stream.write_all(&buf.into_boxed_slice())); + + let mut extra = if self.compression_threshold >= 0 { 1 } else { 0 }; + if self.compression_threshold >= 0 && buf.len() as i32 > self.compression_threshold { + extra = 0; + let uncompressed_size = buf.len(); + let mut new = Vec::new(); + try!(VarInt(uncompressed_size as i32).write_to(&mut new)); + let mut write = self.compression_write.as_mut().unwrap(); + write.reset(io::Cursor::new(buf)); + try!(write.read_to_end(&mut new)); + buf = new; + } + + try!(VarInt(buf.len() as i32 + extra).write_to(self)); + if self.compression_threshold >= 0 && extra == 1 { + try!(VarInt(0).write_to(self)); + } + try!(self.write_all(&buf.into_boxed_slice())); Result::Ok(()) } pub fn read_packet(&mut self) -> Result { - let len = try!(VarInt::read_from(&mut self.stream)).0 as usize; + let len = try!(VarInt::read_from(self)).0 as usize; let mut ibuf = Vec::with_capacity(len); - try!((&mut self.stream).take(len as u64).read_to_end(&mut ibuf)); + try!(self.take(len as u64).read_to_end(&mut ibuf)); let mut buf = io::Cursor::new(ibuf); + + if self.compression_threshold >= 0 { + let uncompressed_size = try!(VarInt::read_from(&mut buf)).0; + if uncompressed_size != 0 { + let mut new = Vec::with_capacity(uncompressed_size as usize); + { + let mut reader = self.compression_read.as_mut().unwrap(); + reader.reset(buf); + try!(reader.read_to_end(&mut new)); + } + buf = io::Cursor::new(new); + } + } let id = try!(VarInt::read_from(&mut buf)).0; let dir = match self.direction { @@ -317,7 +521,70 @@ impl Conn { } Result::Ok(val) }, - None => Result::Err(Error::Err("missing packet".to_string())) + // FIXME + None => Result::Ok(packet::Packet::StatusRequest(packet::status::serverbound::StatusRequest{empty:()}))//Result::Err(Error::Err("missing packet".to_string())) + } + } + + pub fn enable_encyption(&mut self, key: &Vec, decrypt: bool) { + self.cipher = Option::Some(openssl::EVPCipher::new(key, key, decrypt)); + } + + pub fn set_compresssion(&mut self, threshold: i32, read: bool) { + self.compression_threshold = threshold; + if !read { + self.compression_write = Some(ZlibEncoder::new(io::Cursor::new(Vec::new()), flate2::Compression::Default)); + } else { + self.compression_read = Some(ZlibDecoder::new(io::Cursor::new(Vec::new()))); + } + } +} + +impl Read for Conn { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self.cipher.as_mut() { + Option::None => self.stream.read(buf), + Option::Some(cipher) => { + let ret = try!(self.stream.read(buf)); + let data = cipher.decrypt(&buf[..ret]); + for i in 0 .. ret { + buf[i] = data[i]; + } + Ok(ret) + }, + } + } +} + +impl Write for Conn { + fn write(&mut self, buf: &[u8]) -> io::Result { + match self.cipher.as_mut() { + Option::None => self.stream.write(buf), + Option::Some(cipher) => { + let data = cipher.encrypt(buf); + try!(self.stream.write_all(&data[..])); + Ok(buf.len()) + }, + } + } + + fn flush(&mut self) -> io::Result<()> { + self.stream.flush() + } +} + +impl Clone for Conn { + fn clone(&self) -> Self { + Conn { + stream: self.stream.try_clone().unwrap(), + host: self.host.clone(), + port: self.port, + direction: self.direction, + state: self.state, + cipher: Option::None, + compression_threshold: self.compression_threshold, + compression_read: Option::None, + compression_write: Option::None, } } } @@ -336,22 +603,77 @@ fn test() { protocol_version: VarInt(69), host: "localhost".to_string(), port: 25565, - next: VarInt(1), + next: VarInt(2), }).unwrap(); - c.state = State::Status; - c.write_packet(packet::status::serverbound::StatusRequest{empty: Empty}).unwrap(); + c.state = State::Login; + c.write_packet(packet::login::serverbound::LoginStart{username: "Think".to_string()}).unwrap(); - match c.read_packet().unwrap() { - packet::Packet::StatusResponse(val) => println!("{}", val.status), + let packet = match c.read_packet().unwrap() { + packet::Packet::EncryptionRequest(val) => val, _ => panic!("Wrong packet"), - } + }; - c.write_packet(packet::status::serverbound::StatusPing{ping: 4433}).unwrap(); + let mut key = openssl::PublicKey::new(&packet.public_key.data); + let shared = openssl::gen_random(16); - match c.read_packet().unwrap() { - packet::Packet::StatusPong(val) => println!("{}", val.ping), - _ => panic!("Wrong packet"), - } + let shared_e = key.encrypt(&shared); + let token_e = key.encrypt(&packet.verify_token.data); - panic!("TODO!"); + let profile = mojang::Profile{ + username: "Think".to_string(), + id: "b1184d43168441cfa2128b9a3df3b6ab".to_string(), + access_token: "".to_string() + }; + + profile.join_server(&packet.server_id, &shared, &packet.public_key.data); + + c.write_packet(packet::login::serverbound::EncryptionResponse{ + shared_secret: LenPrefixed::new(shared_e), + verify_token: LenPrefixed::new(token_e), + }); + + let mut read = c.clone(); + let mut write = c.clone(); + + read.enable_encyption(&shared, true); + write.enable_encyption(&shared, false); + + loop { match read.read_packet().unwrap() { + packet::Packet::LoginDisconnect(val) => { + panic!("Discconect {}", val.reason); + }, + packet::Packet::SetInitialCompression(val) => { + read.set_compresssion(val.threshold.0, true); + write.set_compresssion(val.threshold.0, false); + println!("Compression: {}", val.threshold.0) + }, + packet::Packet::LoginSuccess(val) => { + println!("Login: {} {}", val.username, val.uuid); + read.state = State::Play; + write.state = State::Play; + break; + } + _ => panic!("Unknown packet"), + } } + + let mut first = true; + let mut count = 0; + loop { match read.read_packet().unwrap() { + packet::Packet::ServerMessage(val) => println!("MSG: {}", val.message), + _ => { + if first { + println!("got packet"); + write.write_packet(packet::play::serverbound::ChatMessage{ + message: "Hello world".to_string(), + }); + first = false; + } + count += 1; + if count > 200 { + break; + } + } + } } + + unimplemented!(); } diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs new file mode 100644 index 0000000..2941ad8 --- /dev/null +++ b/protocol/src/protocol/mojang.rs @@ -0,0 +1,65 @@ +extern crate steven_openssl as openssl; +extern crate serde_json; +extern crate hyper; + +pub struct Profile { + pub username: String, + pub id: String, + pub access_token: String +} + +const JOIN_URL: &'static str = "https://sessionserver.mojang.com/session/minecraft/join"; + +impl Profile { + pub fn join_server(&self, server_id: &String, shared_key: &Vec, public_key: &Vec) { + let mut sha1 = openssl::SHA1::new(); + sha1.update(server_id.as_bytes()); + sha1.update(&shared_key[..]); + sha1.update(&public_key[..]); + let mut hash = sha1.bytes(); + + // Mojang uses a hex method which allows for + // negatives so we have to account for that. + let negative = hash[0] & 0x80 == 0x80; + if negative { + twos_compliment(&mut hash); + } + let hash_str = hash.iter().map(|b| format!("{:02X}", b)).collect::>().connect(""); + let hash_val = hash_str.trim_matches('0'); + let hash_str = if negative { + "-".to_owned() + &hash_val[..] + } else { + hash_val.to_owned() + }; + + let join_msg = serde_json::builder::ObjectBuilder::new() + .insert("accessToken", &self.access_token) + .insert("selectedProfile", &self.id) + .insert("serverId", hash_str) + .unwrap(); + let join = serde_json::to_string(&join_msg).unwrap(); + + let client = hyper::Client::new(); + let res = client.post(JOIN_URL) + .body(&join) + .header(hyper::header::ContentType("application/json".parse().unwrap())) + .send().unwrap(); + + let ret: serde_json::Value = match serde_json::from_reader(res) { + Result::Ok(val) => val, + Result::Err(_) => return, + }; + panic!("{:?}", ret); + } +} + +fn twos_compliment(data: &mut Vec) { + let mut carry = true; + for i in (0 .. data.len()).rev() { + data[i] = !data[i]; + if carry { + carry = data[i] == 0xFF; + data[i] += 1; + } + } +} diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 4819c8a..02d9e68 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -17,15 +17,15 @@ state_packets!( // than the hostname due to the protocol not providing // any system for custom information to be transfered // by the client to the server until after login. - Handshake => 0 { + Handshake => 0x00 { // The protocol version of the connecting client - protocol_version: VarInt, + protocol_version: VarInt =, // The hostname the client connected to - host: String, + host: String =, // The port the client connected to - port: u16, + port: u16 =, // The next protocol state the client wants - next: VarInt + next: VarInt =, } } clientbound Clientbound { @@ -33,14 +33,113 @@ state_packets!( } play Play { serverbound Serverbound { + // TabComplete is sent by the client when the client presses tab in + // the chat box. + TabComplete => 0x00 { + text: String =, + has_target: bool =, + target: Option = when(|p: &TabComplete| p.has_target == true), + } + // ChatMessage is sent by the client when it sends a chat message or + // executes a command (prefixed by '/'). + ChatMessage => 0x01 { + message: String =, + } + // ClientStatus is sent to update the client's status + ClientStatus => 0x02 { + action_id: VarInt =, + } + // ClientSettings is sent by the client to update its current settings. + ClientSettings => 0x03 { + locale: String =, + view_distance: u8 =, + chat_mode: u8 =, + chat_colors: bool =, + displayed_skin_parts: u8 =, + main_hand: VarInt =, + } + // ConfirmTransactionServerbound is a reply to ConfirmTransaction. + ConfirmTransactionServerbound => 0x04 { + id: u8 =, + action_number: i16 =, + accepted: bool =, + } + // EnchantItem is sent when the client enchants an item. + EnchantItem => 0x05 { + id: u8 =, + enchantment: u8 =, + } + // ClickWindow is sent when the client clicks in a window. + ClickWindow => 0x06 { + id: u8 =, + slot: i16 =, + button: u8 =, + action_number: u16 =, + mode: u8 =, + clicked_item: ()=, // TODO + } } clientbound Clientbound { + ServerMessage => 15 { + message: format::Component =, + position: u8 =, + } } } login Login { serverbound Serverbound { + // LoginStart is sent immeditately after switching into the login + // state. The passed username is used by the server to authenticate + // the player in online mode. + LoginStart => 0 { + username: String =, + } + // EncryptionResponse is sent as a reply to EncryptionRequest. All + // packets following this one must be encrypted with AES/CFB8 + // encryption. + EncryptionResponse => 1 { + // The key for the AES/CFB8 cipher encrypted with the + // public key + shared_secret: LenPrefixed =, + // The verify token from the request encrypted with the + // public key + verify_token: LenPrefixed =, + } } clientbound Clientbound { + // LoginDisconnect is sent by the server if there was any issues + // authenticating the player during login or the general server + // issues (e.g. too many players). + LoginDisconnect => 0 { + reason: format::Component =, + } + // EncryptionRequest is sent by the server if the server is in + // online mode. If it is not sent then its assumed the server is + // in offline mode. + EncryptionRequest => 1 { + // Generally empty, left in from legacy auth + // but is still used by the client if provided + server_id: String =, + // A RSA Public key serialized in x.509 PRIX format + public_key: LenPrefixed =, + // Token used by the server to verify encryption is working + // correctly + verify_token: LenPrefixed =, + } + // LoginSuccess is sent by the server if the player successfully + // authenicates with the session servers (online mode) or straight + // after LoginStart (offline mode). + LoginSuccess => 2 { + // String encoding of a uuid (with hyphens) + uuid: String =, + username: String =, + } + // SetInitialCompression sets the compression threshold during the + // login state. + SetInitialCompression => 3 { + // Threshold where a packet should be sent compressed + threshold: VarInt =, + } } } status Status { @@ -50,14 +149,14 @@ state_packets!( // to signal the server to send a StatusResponse to the // client StatusRequest => 0 { - empty: Empty - }, + empty: () =, + } // StatusPing is sent by the client after recieving a // StatusResponse. The client uses the time from sending // the ping until the time of recieving a pong to measure // the latency between the client and the server. StatusPing => 1 { - ping: i64 + ping: i64 =, } } clientbound Clientbound { @@ -83,13 +182,13 @@ state_packets!( // "favicon": "data:image/png;base64," // } StatusResponse => 0 { - status: String - }, + status: String =, + } // StatusPong is sent as a reply to a StatusPing. // The Time field should be exactly the same as the // one sent by the client. StatusPong => 1 { - ping: i64 + ping: i64 =, } } } From fe6a56ce3a5722f5d13d40310802a2632b2e5902 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Thu, 10 Sep 2015 11:49:41 +0100 Subject: [PATCH 005/160] Main part of the protocol complete --- protocol/src/format.rs | 254 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 protocol/src/format.rs diff --git a/protocol/src/format.rs b/protocol/src/format.rs new file mode 100644 index 0000000..a762cc0 --- /dev/null +++ b/protocol/src/format.rs @@ -0,0 +1,254 @@ +extern crate serde_json; + +use std::fmt; + +#[derive(Debug)] +pub enum Component { + Text(TextComponent), + None +} + +impl Component { + pub fn from_value(v: &serde_json::Value) -> Self { + let modifier = Modifier::from_value(v); + if let Some(val) = v.as_string() { + Component::Text(TextComponent{text: val.to_owned(), modifier: modifier}) + } else if v.find("text").is_some(){ + Component::Text(TextComponent::from_value(v, modifier)) + } else { + Component::None + } + } + + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } +} + +impl fmt::Display for Component { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Component::Text(ref txt) => write!(f, "{}", txt), + Component::None => Result::Ok(()), + } + } +} + +impl Default for Component { + fn default() -> Self { + Component::None + } +} + +#[derive(Debug)] +pub struct Modifier { + pub extra: Option>, + pub bold: Option, + pub italic: Option, + pub underlined: Option, + pub strikethrough: Option, + pub obfuscated: Option, + pub color: Option, + + // click_event + // hover_event + // insertion +} + +impl Modifier { + pub fn from_value(v: &serde_json::Value) -> Self { + let mut m = Modifier { + bold: v.find("bold").map_or(Option::None, |v| v.as_boolean()), + italic: v.find("italic").map_or(Option::None, |v| v.as_boolean()), + underlined: v.find("underlined").map_or(Option::None, |v| v.as_boolean()), + strikethrough: v.find("strikethrough").map_or(Option::None, |v| v.as_boolean()), + obfuscated: v.find("obfuscated").map_or(Option::None, |v| v.as_boolean()), + color: v.find("color").map_or(Option::None, |v| v.as_string()).map(|v| Color::from_string(&v.to_owned())), + extra: Option::None, + }; + if let Some(extra) = v.find("extra") { + if let Some(data) = extra.as_array() { + let mut ex = Vec::new(); + for e in data { + ex.push(Component::from_value(e)); + } + m.extra = Some(ex); + } + } + m + } + + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } +} + +#[derive(Debug)] +pub struct TextComponent { + pub text: String, + pub modifier: Modifier, +} + +impl TextComponent { + pub fn from_value(v: &serde_json::Value, modifier: Modifier) -> Self { + TextComponent { + text: v.find("text").unwrap().as_string().unwrap_or("").to_owned(), + modifier: modifier, + } + } + + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } +} + +impl fmt::Display for TextComponent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(write!(f, "{}", self.text)); + if let Some(ref extra) = self.modifier.extra { + for c in extra { + try!(write!(f, "{}", c)); + } + } + Result::Ok(()) + } +} + +#[derive(Debug)] +pub enum Color { + Black, + DarkBlue, + DarkGreen, + DarkAqua, + DarkRed, + DarkPurple, + Gold, + Gray, + DarkGray, + Blue, + Green, + Aqua, + Red, + LightPurple, + Yellow, + White, + RGB(u8, u8, u8) +} + +impl fmt::Display for Color { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +impl Color { + fn from_string(val: &String) -> Self { + match val.as_ref() { + "black" => Color::Black, + "dark_blue" => Color::DarkBlue, + "dark_green" => Color::DarkGreen, + "dark_aqua" => Color::DarkAqua, + "dark_red" => Color::DarkRed, + "dark_purple" => Color::DarkPurple, + "gold" => Color::Gold, + "gray" => Color::Gray, + "dark_gray" => Color::DarkGray, + "blue" => Color::Blue, + "green" => Color::Green, + "aqua" => Color::Aqua, + "red" => Color::Red, + "light_purple" => Color::LightPurple, + "yellow" => Color::Yellow, + "white" => Color::White, + val if val.len() == 7 && val.as_bytes()[0] == b'#' => { + let r = match u8::from_str_radix(&val[1..3], 16) { + Ok(r) => r, + Err(_) => return Color::White, + }; + let g = match u8::from_str_radix(&val[3..5], 16) { + Ok(g) => g, + Err(_) => return Color::White, + }; + let b = match u8::from_str_radix(&val[5..7], 16) { + Ok(b) => b, + Err(_) => return Color::White, + }; + Color::RGB( + r, + g, + b + ) + } + _ => Color::White, + } + } + + fn to_string(&self) -> String { + match *self { + Color::Black => "black".to_owned(), + Color::DarkBlue => "dark_blue".to_owned(), + Color::DarkGreen => "dark_green".to_owned(), + Color::DarkAqua => "dark_aqua".to_owned(), + Color::DarkRed => "dark_red".to_owned(), + Color::DarkPurple => "dark_purple".to_owned(), + Color::Gold => "gold".to_owned(), + Color::Gray => "gray".to_owned(), + Color::DarkGray => "dark_gray".to_owned(), + Color::Blue => "blue".to_owned(), + Color::Green => "green".to_owned(), + Color::Aqua => "aqua".to_owned(), + Color::Red => "red".to_owned(), + Color::LightPurple => "light_purple".to_owned(), + Color::Yellow => "yellow".to_owned(), + Color::White => "white".to_owned(), + Color::RGB(r, g, b) => format!("#{:02X}{:02X}{:02X}", r, g, b), + } + } + + #[allow(dead_code)] + fn to_rgb(&self) -> (u8, u8, u8) { + match *self { + Color::Black =>(0, 0, 0), + Color::DarkBlue =>(0, 0, 170), + Color::DarkGreen =>(0, 170, 0), + Color::DarkAqua =>(0, 170, 170), + Color::DarkRed =>(170, 0, 0), + Color::DarkPurple =>(170, 0, 170), + Color::Gold =>(255, 170, 0), + Color::Gray =>(170, 170, 170), + Color::DarkGray =>(85, 85, 85), + Color::Blue =>(85, 85, 255), + Color::Green =>(85, 255, 85), + Color::Aqua =>(85, 255, 255), + Color::Red =>(255, 85, 85), + Color::LightPurple =>(255, 85, 255), + Color::Yellow =>(255, 255, 85), + Color::White =>(255, 255, 255), + Color::RGB(r, g, b) => (r, g, b), + } + } +} + +#[test] +fn test_color_from() { + let test = Color::from_string(&"#FF0000".to_owned()); + match test { + Color::RGB(r, g, b) => assert!(r == 255 && g == 0 && b == 0), + _ => panic!("Wrong type"), + } + let test = Color::from_string(&"#123456".to_owned()); + match test { + Color::RGB(r, g, b) => assert!(r == 0x12 && g == 0x34 && b == 0x56), + _ => panic!("Wrong type"), + } + let test = Color::from_string(&"red".to_owned()); + match test { + Color::Red => {}, + _ => panic!("Wrong type"), + } + let test = Color::from_string(&"dark_blue".to_owned()); + match test { + Color::DarkBlue => {}, + _ => panic!("Wrong type"), + } +} From c5d00f6dc8b2bba562de8cdfb898d5503d5ae2fb Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Thu, 10 Sep 2015 11:58:42 +0100 Subject: [PATCH 006/160] Tabs to spaces --- protocol/src/protocol/mojang.rs | 22 +- protocol/src/protocol/packet.rs | 350 ++++++++++++++++---------------- 2 files changed, 186 insertions(+), 186 deletions(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 2941ad8..e126793 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -3,9 +3,9 @@ extern crate serde_json; extern crate hyper; pub struct Profile { - pub username: String, - pub id: String, - pub access_token: String + pub username: String, + pub id: String, + pub access_token: String } const JOIN_URL: &'static str = "https://sessionserver.mojang.com/session/minecraft/join"; @@ -54,12 +54,12 @@ impl Profile { } fn twos_compliment(data: &mut Vec) { - let mut carry = true; - for i in (0 .. data.len()).rev() { - data[i] = !data[i]; - if carry { - carry = data[i] == 0xFF; - data[i] += 1; - } - } + let mut carry = true; + for i in (0 .. data.len()).rev() { + data[i] = !data[i]; + if carry { + carry = data[i] == 0xFF; + data[i] += 1; + } + } } diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 02d9e68..fcc07e1 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1,186 +1,186 @@ state_packets!( - handshake Handshaking { - serverbound Serverbound { - // Handshake is the first packet sent in the protocol. - // Its used for deciding if the request is a client - // is requesting status information about the server - // (MOTD, players etc) or trying to login to the server. - // - // The host and port fields are not used by the vanilla - // server but are there for virtual server hosting to - // be able to redirect a client to a target server with - // a single address + port. - // - // Some modified servers/proxies use the handshake field - // differently, packing information into the field other - // than the hostname due to the protocol not providing - // any system for custom information to be transfered - // by the client to the server until after login. - Handshake => 0x00 { - // The protocol version of the connecting client - protocol_version: VarInt =, - // The hostname the client connected to - host: String =, - // The port the client connected to - port: u16 =, - // The next protocol state the client wants - next: VarInt =, - } - } - clientbound Clientbound { - } - } - play Play { - serverbound Serverbound { - // TabComplete is sent by the client when the client presses tab in - // the chat box. - TabComplete => 0x00 { - text: String =, - has_target: bool =, - target: Option = when(|p: &TabComplete| p.has_target == true), - } - // ChatMessage is sent by the client when it sends a chat message or - // executes a command (prefixed by '/'). - ChatMessage => 0x01 { - message: String =, - } - // ClientStatus is sent to update the client's status - ClientStatus => 0x02 { - action_id: VarInt =, - } - // ClientSettings is sent by the client to update its current settings. - ClientSettings => 0x03 { - locale: String =, - view_distance: u8 =, - chat_mode: u8 =, - chat_colors: bool =, - displayed_skin_parts: u8 =, - main_hand: VarInt =, - } - // ConfirmTransactionServerbound is a reply to ConfirmTransaction. - ConfirmTransactionServerbound => 0x04 { - id: u8 =, - action_number: i16 =, - accepted: bool =, - } - // EnchantItem is sent when the client enchants an item. - EnchantItem => 0x05 { - id: u8 =, - enchantment: u8 =, - } - // ClickWindow is sent when the client clicks in a window. - ClickWindow => 0x06 { - id: u8 =, - slot: i16 =, - button: u8 =, - action_number: u16 =, - mode: u8 =, - clicked_item: ()=, // TODO - } - } - clientbound Clientbound { - ServerMessage => 15 { - message: format::Component =, - position: u8 =, - } - } - } - login Login { - serverbound Serverbound { - // LoginStart is sent immeditately after switching into the login - // state. The passed username is used by the server to authenticate - // the player in online mode. - LoginStart => 0 { - username: String =, - } - // EncryptionResponse is sent as a reply to EncryptionRequest. All - // packets following this one must be encrypted with AES/CFB8 - // encryption. - EncryptionResponse => 1 { - // The key for the AES/CFB8 cipher encrypted with the - // public key - shared_secret: LenPrefixed =, - // The verify token from the request encrypted with the - // public key - verify_token: LenPrefixed =, - } - } - clientbound Clientbound { - // LoginDisconnect is sent by the server if there was any issues - // authenticating the player during login or the general server - // issues (e.g. too many players). - LoginDisconnect => 0 { - reason: format::Component =, - } - // EncryptionRequest is sent by the server if the server is in - // online mode. If it is not sent then its assumed the server is - // in offline mode. - EncryptionRequest => 1 { - // Generally empty, left in from legacy auth - // but is still used by the client if provided - server_id: String =, - // A RSA Public key serialized in x.509 PRIX format - public_key: LenPrefixed =, - // Token used by the server to verify encryption is working - // correctly - verify_token: LenPrefixed =, - } - // LoginSuccess is sent by the server if the player successfully - // authenicates with the session servers (online mode) or straight - // after LoginStart (offline mode). - LoginSuccess => 2 { - // String encoding of a uuid (with hyphens) - uuid: String =, - username: String =, - } - // SetInitialCompression sets the compression threshold during the - // login state. - SetInitialCompression => 3 { - // Threshold where a packet should be sent compressed - threshold: VarInt =, - } - } - } - status Status { - serverbound Serverbound { + handshake Handshaking { + serverbound Serverbound { + // Handshake is the first packet sent in the protocol. + // Its used for deciding if the request is a client + // is requesting status information about the server + // (MOTD, players etc) or trying to login to the server. + // + // The host and port fields are not used by the vanilla + // server but are there for virtual server hosting to + // be able to redirect a client to a target server with + // a single address + port. + // + // Some modified servers/proxies use the handshake field + // differently, packing information into the field other + // than the hostname due to the protocol not providing + // any system for custom information to be transfered + // by the client to the server until after login. + Handshake => 0x00 { + // The protocol version of the connecting client + protocol_version: VarInt =, + // The hostname the client connected to + host: String =, + // The port the client connected to + port: u16 =, + // The next protocol state the client wants + next: VarInt =, + } + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + // TabComplete is sent by the client when the client presses tab in + // the chat box. + TabComplete => 0x00 { + text: String =, + has_target: bool =, + target: Option = when(|p: &TabComplete| p.has_target == true), + } + // ChatMessage is sent by the client when it sends a chat message or + // executes a command (prefixed by '/'). + ChatMessage => 0x01 { + message: String =, + } + // ClientStatus is sent to update the client's status + ClientStatus => 0x02 { + action_id: VarInt =, + } + // ClientSettings is sent by the client to update its current settings. + ClientSettings => 0x03 { + locale: String =, + view_distance: u8 =, + chat_mode: u8 =, + chat_colors: bool =, + displayed_skin_parts: u8 =, + main_hand: VarInt =, + } + // ConfirmTransactionServerbound is a reply to ConfirmTransaction. + ConfirmTransactionServerbound => 0x04 { + id: u8 =, + action_number: i16 =, + accepted: bool =, + } + // EnchantItem is sent when the client enchants an item. + EnchantItem => 0x05 { + id: u8 =, + enchantment: u8 =, + } + // ClickWindow is sent when the client clicks in a window. + ClickWindow => 0x06 { + id: u8 =, + slot: i16 =, + button: u8 =, + action_number: u16 =, + mode: u8 =, + clicked_item: ()=, // TODO + } + } + clientbound Clientbound { + ServerMessage => 15 { + message: format::Component =, + position: u8 =, + } + } + } + login Login { + serverbound Serverbound { + // LoginStart is sent immeditately after switching into the login + // state. The passed username is used by the server to authenticate + // the player in online mode. + LoginStart => 0 { + username: String =, + } + // EncryptionResponse is sent as a reply to EncryptionRequest. All + // packets following this one must be encrypted with AES/CFB8 + // encryption. + EncryptionResponse => 1 { + // The key for the AES/CFB8 cipher encrypted with the + // public key + shared_secret: LenPrefixed =, + // The verify token from the request encrypted with the + // public key + verify_token: LenPrefixed =, + } + } + clientbound Clientbound { + // LoginDisconnect is sent by the server if there was any issues + // authenticating the player during login or the general server + // issues (e.g. too many players). + LoginDisconnect => 0 { + reason: format::Component =, + } + // EncryptionRequest is sent by the server if the server is in + // online mode. If it is not sent then its assumed the server is + // in offline mode. + EncryptionRequest => 1 { + // Generally empty, left in from legacy auth + // but is still used by the client if provided + server_id: String =, + // A RSA Public key serialized in x.509 PRIX format + public_key: LenPrefixed =, + // Token used by the server to verify encryption is working + // correctly + verify_token: LenPrefixed =, + } + // LoginSuccess is sent by the server if the player successfully + // authenicates with the session servers (online mode) or straight + // after LoginStart (offline mode). + LoginSuccess => 2 { + // String encoding of a uuid (with hyphens) + uuid: String =, + username: String =, + } + // SetInitialCompression sets the compression threshold during the + // login state. + SetInitialCompression => 3 { + // Threshold where a packet should be sent compressed + threshold: VarInt =, + } + } + } + status Status { + serverbound Serverbound { // StatusRequest is sent by the client instantly after // switching to the Status protocol state and is used // to signal the server to send a StatusResponse to the // client - StatusRequest => 0 { - empty: () =, - } + StatusRequest => 0 { + empty: () =, + } // StatusPing is sent by the client after recieving a // StatusResponse. The client uses the time from sending // the ping until the time of recieving a pong to measure // the latency between the client and the server. - StatusPing => 1 { - ping: i64 =, - } - } - clientbound Clientbound { - // StatusResponse is sent as a reply to a StatusRequest. - // The Status should contain a json encoded structure with - // version information, a player sample, a description/MOTD - // and optionally a favicon. - // - // The structure is as follows - // { - // "version": { - // "name": "1.8.3", - // "protocol": 47, - // }, - // "players": { - // "max": 20, - // "online": 1, - // "sample": [ - // {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"} - // ] - // }, - // "description": "Hello world", - // "favicon": "data:image/png;base64," - // } + StatusPing => 1 { + ping: i64 =, + } + } + clientbound Clientbound { + // StatusResponse is sent as a reply to a StatusRequest. + // The Status should contain a json encoded structure with + // version information, a player sample, a description/MOTD + // and optionally a favicon. + // + // The structure is as follows + // { + // "version": { + // "name": "1.8.3", + // "protocol": 47, + // }, + // "players": { + // "max": 20, + // "online": 1, + // "sample": [ + // {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"} + // ] + // }, + // "description": "Hello world", + // "favicon": "data:image/png;base64," + // } StatusResponse => 0 { status: String =, } @@ -190,6 +190,6 @@ state_packets!( StatusPong => 1 { ping: i64 =, } - } - } + } + } ); From 25a033268ceb094f2862ef32e1dddff3faaef3fb Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Thu, 10 Sep 2015 11:58:42 +0100 Subject: [PATCH 007/160] Tabs to spaces --- protocol/src/format.rs | 402 ++++++++++++++++++++--------------------- 1 file changed, 201 insertions(+), 201 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index a762cc0..7928967 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -4,251 +4,251 @@ use std::fmt; #[derive(Debug)] pub enum Component { - Text(TextComponent), - None + Text(TextComponent), + None } impl Component { - pub fn from_value(v: &serde_json::Value) -> Self { - let modifier = Modifier::from_value(v); - if let Some(val) = v.as_string() { - Component::Text(TextComponent{text: val.to_owned(), modifier: modifier}) - } else if v.find("text").is_some(){ - Component::Text(TextComponent::from_value(v, modifier)) - } else { - Component::None - } - } + pub fn from_value(v: &serde_json::Value) -> Self { + let modifier = Modifier::from_value(v); + if let Some(val) = v.as_string() { + Component::Text(TextComponent{text: val.to_owned(), modifier: modifier}) + } else if v.find("text").is_some(){ + Component::Text(TextComponent::from_value(v, modifier)) + } else { + Component::None + } + } - pub fn to_value(&self) -> serde_json::Value { - unimplemented!() - } + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } } impl fmt::Display for Component { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Component::Text(ref txt) => write!(f, "{}", txt), - Component::None => Result::Ok(()), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Component::Text(ref txt) => write!(f, "{}", txt), + Component::None => Result::Ok(()), + } + } } impl Default for Component { - fn default() -> Self { - Component::None - } + fn default() -> Self { + Component::None + } } #[derive(Debug)] pub struct Modifier { - pub extra: Option>, - pub bold: Option, - pub italic: Option, - pub underlined: Option, - pub strikethrough: Option, - pub obfuscated: Option, - pub color: Option, + pub extra: Option>, + pub bold: Option, + pub italic: Option, + pub underlined: Option, + pub strikethrough: Option, + pub obfuscated: Option, + pub color: Option, - // click_event - // hover_event - // insertion + // click_event + // hover_event + // insertion } impl Modifier { - pub fn from_value(v: &serde_json::Value) -> Self { - let mut m = Modifier { - bold: v.find("bold").map_or(Option::None, |v| v.as_boolean()), - italic: v.find("italic").map_or(Option::None, |v| v.as_boolean()), - underlined: v.find("underlined").map_or(Option::None, |v| v.as_boolean()), - strikethrough: v.find("strikethrough").map_or(Option::None, |v| v.as_boolean()), - obfuscated: v.find("obfuscated").map_or(Option::None, |v| v.as_boolean()), - color: v.find("color").map_or(Option::None, |v| v.as_string()).map(|v| Color::from_string(&v.to_owned())), - extra: Option::None, - }; - if let Some(extra) = v.find("extra") { - if let Some(data) = extra.as_array() { - let mut ex = Vec::new(); - for e in data { - ex.push(Component::from_value(e)); - } - m.extra = Some(ex); - } - } - m - } + pub fn from_value(v: &serde_json::Value) -> Self { + let mut m = Modifier { + bold: v.find("bold").map_or(Option::None, |v| v.as_boolean()), + italic: v.find("italic").map_or(Option::None, |v| v.as_boolean()), + underlined: v.find("underlined").map_or(Option::None, |v| v.as_boolean()), + strikethrough: v.find("strikethrough").map_or(Option::None, |v| v.as_boolean()), + obfuscated: v.find("obfuscated").map_or(Option::None, |v| v.as_boolean()), + color: v.find("color").map_or(Option::None, |v| v.as_string()).map(|v| Color::from_string(&v.to_owned())), + extra: Option::None, + }; + if let Some(extra) = v.find("extra") { + if let Some(data) = extra.as_array() { + let mut ex = Vec::new(); + for e in data { + ex.push(Component::from_value(e)); + } + m.extra = Some(ex); + } + } + m + } - pub fn to_value(&self) -> serde_json::Value { - unimplemented!() - } + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } } #[derive(Debug)] pub struct TextComponent { - pub text: String, - pub modifier: Modifier, + pub text: String, + pub modifier: Modifier, } impl TextComponent { - pub fn from_value(v: &serde_json::Value, modifier: Modifier) -> Self { - TextComponent { - text: v.find("text").unwrap().as_string().unwrap_or("").to_owned(), - modifier: modifier, - } - } + pub fn from_value(v: &serde_json::Value, modifier: Modifier) -> Self { + TextComponent { + text: v.find("text").unwrap().as_string().unwrap_or("").to_owned(), + modifier: modifier, + } + } - pub fn to_value(&self) -> serde_json::Value { - unimplemented!() - } + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } } impl fmt::Display for TextComponent { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}", self.text)); - if let Some(ref extra) = self.modifier.extra { - for c in extra { - try!(write!(f, "{}", c)); - } - } - Result::Ok(()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(write!(f, "{}", self.text)); + if let Some(ref extra) = self.modifier.extra { + for c in extra { + try!(write!(f, "{}", c)); + } + } + Result::Ok(()) + } } #[derive(Debug)] pub enum Color { - Black, - DarkBlue, - DarkGreen, - DarkAqua, - DarkRed, - DarkPurple, - Gold, - Gray, - DarkGray, - Blue, - Green, - Aqua, - Red, - LightPurple, - Yellow, - White, - RGB(u8, u8, u8) + Black, + DarkBlue, + DarkGreen, + DarkAqua, + DarkRed, + DarkPurple, + Gold, + Gray, + DarkGray, + Blue, + Green, + Aqua, + Red, + LightPurple, + Yellow, + White, + RGB(u8, u8, u8) } impl fmt::Display for Color { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_string()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } } impl Color { - fn from_string(val: &String) -> Self { - match val.as_ref() { - "black" => Color::Black, - "dark_blue" => Color::DarkBlue, - "dark_green" => Color::DarkGreen, - "dark_aqua" => Color::DarkAqua, - "dark_red" => Color::DarkRed, - "dark_purple" => Color::DarkPurple, - "gold" => Color::Gold, - "gray" => Color::Gray, - "dark_gray" => Color::DarkGray, - "blue" => Color::Blue, - "green" => Color::Green, - "aqua" => Color::Aqua, - "red" => Color::Red, - "light_purple" => Color::LightPurple, - "yellow" => Color::Yellow, - "white" => Color::White, - val if val.len() == 7 && val.as_bytes()[0] == b'#' => { - let r = match u8::from_str_radix(&val[1..3], 16) { - Ok(r) => r, - Err(_) => return Color::White, - }; - let g = match u8::from_str_radix(&val[3..5], 16) { - Ok(g) => g, - Err(_) => return Color::White, - }; - let b = match u8::from_str_radix(&val[5..7], 16) { - Ok(b) => b, - Err(_) => return Color::White, - }; - Color::RGB( - r, - g, - b - ) - } - _ => Color::White, - } - } + fn from_string(val: &String) -> Self { + match val.as_ref() { + "black" => Color::Black, + "dark_blue" => Color::DarkBlue, + "dark_green" => Color::DarkGreen, + "dark_aqua" => Color::DarkAqua, + "dark_red" => Color::DarkRed, + "dark_purple" => Color::DarkPurple, + "gold" => Color::Gold, + "gray" => Color::Gray, + "dark_gray" => Color::DarkGray, + "blue" => Color::Blue, + "green" => Color::Green, + "aqua" => Color::Aqua, + "red" => Color::Red, + "light_purple" => Color::LightPurple, + "yellow" => Color::Yellow, + "white" => Color::White, + val if val.len() == 7 && val.as_bytes()[0] == b'#' => { + let r = match u8::from_str_radix(&val[1..3], 16) { + Ok(r) => r, + Err(_) => return Color::White, + }; + let g = match u8::from_str_radix(&val[3..5], 16) { + Ok(g) => g, + Err(_) => return Color::White, + }; + let b = match u8::from_str_radix(&val[5..7], 16) { + Ok(b) => b, + Err(_) => return Color::White, + }; + Color::RGB( + r, + g, + b + ) + } + _ => Color::White, + } + } - fn to_string(&self) -> String { - match *self { - Color::Black => "black".to_owned(), - Color::DarkBlue => "dark_blue".to_owned(), - Color::DarkGreen => "dark_green".to_owned(), - Color::DarkAqua => "dark_aqua".to_owned(), - Color::DarkRed => "dark_red".to_owned(), - Color::DarkPurple => "dark_purple".to_owned(), - Color::Gold => "gold".to_owned(), - Color::Gray => "gray".to_owned(), - Color::DarkGray => "dark_gray".to_owned(), - Color::Blue => "blue".to_owned(), - Color::Green => "green".to_owned(), - Color::Aqua => "aqua".to_owned(), - Color::Red => "red".to_owned(), - Color::LightPurple => "light_purple".to_owned(), - Color::Yellow => "yellow".to_owned(), - Color::White => "white".to_owned(), - Color::RGB(r, g, b) => format!("#{:02X}{:02X}{:02X}", r, g, b), - } - } + fn to_string(&self) -> String { + match *self { + Color::Black => "black".to_owned(), + Color::DarkBlue => "dark_blue".to_owned(), + Color::DarkGreen => "dark_green".to_owned(), + Color::DarkAqua => "dark_aqua".to_owned(), + Color::DarkRed => "dark_red".to_owned(), + Color::DarkPurple => "dark_purple".to_owned(), + Color::Gold => "gold".to_owned(), + Color::Gray => "gray".to_owned(), + Color::DarkGray => "dark_gray".to_owned(), + Color::Blue => "blue".to_owned(), + Color::Green => "green".to_owned(), + Color::Aqua => "aqua".to_owned(), + Color::Red => "red".to_owned(), + Color::LightPurple => "light_purple".to_owned(), + Color::Yellow => "yellow".to_owned(), + Color::White => "white".to_owned(), + Color::RGB(r, g, b) => format!("#{:02X}{:02X}{:02X}", r, g, b), + } + } - #[allow(dead_code)] - fn to_rgb(&self) -> (u8, u8, u8) { - match *self { - Color::Black =>(0, 0, 0), - Color::DarkBlue =>(0, 0, 170), - Color::DarkGreen =>(0, 170, 0), - Color::DarkAqua =>(0, 170, 170), - Color::DarkRed =>(170, 0, 0), - Color::DarkPurple =>(170, 0, 170), - Color::Gold =>(255, 170, 0), - Color::Gray =>(170, 170, 170), - Color::DarkGray =>(85, 85, 85), - Color::Blue =>(85, 85, 255), - Color::Green =>(85, 255, 85), - Color::Aqua =>(85, 255, 255), - Color::Red =>(255, 85, 85), - Color::LightPurple =>(255, 85, 255), - Color::Yellow =>(255, 255, 85), - Color::White =>(255, 255, 255), - Color::RGB(r, g, b) => (r, g, b), - } - } + #[allow(dead_code)] + fn to_rgb(&self) -> (u8, u8, u8) { + match *self { + Color::Black =>(0, 0, 0), + Color::DarkBlue =>(0, 0, 170), + Color::DarkGreen =>(0, 170, 0), + Color::DarkAqua =>(0, 170, 170), + Color::DarkRed =>(170, 0, 0), + Color::DarkPurple =>(170, 0, 170), + Color::Gold =>(255, 170, 0), + Color::Gray =>(170, 170, 170), + Color::DarkGray =>(85, 85, 85), + Color::Blue =>(85, 85, 255), + Color::Green =>(85, 255, 85), + Color::Aqua =>(85, 255, 255), + Color::Red =>(255, 85, 85), + Color::LightPurple =>(255, 85, 255), + Color::Yellow =>(255, 255, 85), + Color::White =>(255, 255, 255), + Color::RGB(r, g, b) => (r, g, b), + } + } } #[test] fn test_color_from() { - let test = Color::from_string(&"#FF0000".to_owned()); - match test { - Color::RGB(r, g, b) => assert!(r == 255 && g == 0 && b == 0), - _ => panic!("Wrong type"), - } - let test = Color::from_string(&"#123456".to_owned()); - match test { - Color::RGB(r, g, b) => assert!(r == 0x12 && g == 0x34 && b == 0x56), - _ => panic!("Wrong type"), - } - let test = Color::from_string(&"red".to_owned()); - match test { - Color::Red => {}, - _ => panic!("Wrong type"), - } - let test = Color::from_string(&"dark_blue".to_owned()); - match test { - Color::DarkBlue => {}, - _ => panic!("Wrong type"), - } + let test = Color::from_string(&"#FF0000".to_owned()); + match test { + Color::RGB(r, g, b) => assert!(r == 255 && g == 0 && b == 0), + _ => panic!("Wrong type"), + } + let test = Color::from_string(&"#123456".to_owned()); + match test { + Color::RGB(r, g, b) => assert!(r == 0x12 && g == 0x34 && b == 0x56), + _ => panic!("Wrong type"), + } + let test = Color::from_string(&"red".to_owned()); + match test { + Color::Red => {}, + _ => panic!("Wrong type"), + } + let test = Color::from_string(&"dark_blue".to_owned()); + match test { + Color::DarkBlue => {}, + _ => panic!("Wrong type"), + } } From 880cf0d9121b2315aeb33b3235e83d40e47cc6a6 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sat, 12 Sep 2015 20:31:26 +0100 Subject: [PATCH 008/160] Complete protocol implementation --- protocol/src/protocol/mod.rs | 348 ++++++++--- protocol/src/protocol/packet.rs | 986 +++++++++++++++++++++++++++++++- 2 files changed, 1242 insertions(+), 92 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index ace9268..2f7496e 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -7,7 +7,9 @@ extern crate serde_json; pub mod mojang; +use nbt; use format; +use std::fmt; use std::default; use std::net::TcpStream; use std::io; @@ -21,7 +23,8 @@ use self::flate2::read::{ZlibDecoder, ZlibEncoder}; macro_rules! state_packets { ($($state:ident $stateName:ident { $($dir:ident $dirName:ident { - $($name:ident => $id:expr { + $( + $name:ident => $id:expr { $($field:ident: $field_type:ty = $(when ($cond:expr))*, )+ })* })+ @@ -29,6 +32,7 @@ macro_rules! state_packets { use protocol::*; use std::io; + #[derive(Debug)] pub enum Packet { $( $( @@ -47,9 +51,12 @@ macro_rules! state_packets { use protocol::*; use std::io; use format; + use nbt; + use types; + use item; $( - #[derive(Default)] + #[derive(Default, Debug)] pub struct $name { $(pub $field: $field_type),+, } @@ -58,7 +65,7 @@ macro_rules! state_packets { fn packet_id(&self) -> i32{ $id } - fn write(self, buf: &mut Vec) -> Result<(), io::Error> { + fn write(self, buf: &mut io::Write) -> Result<(), io::Error> { $( if true $(&& ($cond(&self)))* { try!(self.$field.write_to(buf)); @@ -76,7 +83,7 @@ macro_rules! state_packets { /// Returns the packet for the given state, direction and id after parsing the fields /// from the buffer. - pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut io::Cursor>) -> Result, io::Error> { + pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut io::Read) -> Result, io::Error> { match state { $( State::$stateName => { @@ -115,6 +122,42 @@ pub trait Serializable { fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error>; } +impl Serializable for Vec { + fn read_from(buf: &mut io::Read) -> Result , io::Error> { + let mut v = Vec::new(); + try!(buf.read_to_end(&mut v)); + Ok(v) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + buf.write_all(&self[..]) + } +} + +impl Serializable for Option{ + fn read_from(buf: &mut io::Read) -> Result, io::Error> { + let ty = try!(buf.read_u8()); + if ty == 0 { + Result::Ok(None) + } else { + let name = try!(nbt::read_string(buf)); + let tag = try!(nbt::Tag::read_from(buf)); + Result::Ok(Some(nbt::NamedTag(name, tag))) + } + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + match *self { + Some(ref val) => { + try!(buf.write_u8(10)); + try!(nbt::write_string(buf, &val.0)); + try!(val.1.write_to(buf)); + } + None => try!(buf.write_u8(0)), + } + Result::Ok(()) + } +} + impl Serializable for Option where T : Serializable { fn read_from(buf: &mut io::Read) -> Result, io::Error> { Result::Ok(Some(try!(T::read_from(buf)))) @@ -159,46 +202,6 @@ impl Serializable for format::Component { } } -pub struct Position(u64); - -impl Position { - fn new(x: i32, y: i32, z: i32) -> Position { - Position( - (((x as u64) & 0x3FFFFFF) << 38) | - (((y as u64) & 0xFFF) << 26) | - ((z as u64) & 0x3FFFFFF) - ) - } - - fn get_x(&self) -> i32 { - ((self.0 as i64) >> 38) as i32 - } - - fn get_y(&self) -> i32 { - (((self.0 as i64) >> 26) & 0xFFF) as i32 - } - - fn get_z(&self) -> i32 { - ((self.0 as i64) << 38 >> 38) as i32 - } -} - -impl Default for Position { - fn default() -> Position { - Position(0) - } -} - -impl Serializable for Position { - fn read_from(buf: &mut io::Read) -> Result { - Result::Ok(Position(try!(buf.read_u64::()))) - } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { - try!(buf.write_u64::(self.0)); - Result::Ok(()) - } -} - impl Serializable for () { fn read_from(_: &mut io::Read) -> Result<(), io::Error> { Result::Ok(()) @@ -218,6 +221,16 @@ impl Serializable for bool { } } +impl Serializable for i8 { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(try!(buf.read_i8())) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_i8(*self)); + Result::Ok(()) + } +} + impl Serializable for i16 { fn read_from(buf: &mut io::Read) -> Result { Result::Ok(try!(buf.read_i16::())) @@ -268,7 +281,54 @@ impl Serializable for u16 { } } -pub trait Lengthable : Serializable + Into + From + Copy + Default {} +impl Serializable for f32 { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(try!(buf.read_f32::())) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_f32::(*self)); + Result::Ok(()) + } +} + +impl Serializable for f64 { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(try!(buf.read_f64::())) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_f64::(*self)); + Result::Ok(()) + } +} + +#[derive(Debug)] +pub struct UUID(u64, u64); + +impl Default for UUID { + fn default() -> Self { UUID(0, 0) } +} + +impl Serializable for UUID { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok( + UUID( + try!(buf.read_u64::()), + try!(buf.read_u64::()), + ) + ) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_u64::(self.0)); + try!(buf.write_u64::(self.1)); + Result::Ok(()) + } +} + + +pub trait Lengthable : Serializable + Copy + Default { + fn into(self) -> usize; + fn from(usize) -> Self; +} pub struct LenPrefixed { len: L, @@ -296,7 +356,7 @@ impl Serializable for LenPrefixed { } fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { - let len_data : L = self.data.len().into(); + let len_data : L = L::from(self.data.len()); try!(len_data.write_to(buf)); let ref data = self.data; for val in data { @@ -306,6 +366,7 @@ impl Serializable for LenPrefixed { } } + impl Default for LenPrefixed { fn default() -> Self { LenPrefixed { @@ -315,12 +376,94 @@ impl Default for LenPrefixed { } } +impl fmt::Debug for LenPrefixed { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.data.fmt(f) + } +} + +// Optimization +pub struct LenPrefixedBytes { + len: L, + pub data: Vec +} + +impl LenPrefixedBytes { + fn new(data: Vec) -> LenPrefixedBytes { + return LenPrefixedBytes { + len: Default::default(), + data: data, + } + } +} + +impl Serializable for LenPrefixedBytes { + fn read_from(buf: &mut io::Read) -> Result, io::Error> { + let len_data : L = try!(Serializable::read_from(buf)); + let len : usize = len_data.into(); + let mut data : Vec = Vec::with_capacity(len); + try!(buf.take(len as u64).read_to_end(&mut data)); + Result::Ok(LenPrefixedBytes{len: len_data, data: data}) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + let len_data : L = L::from(self.data.len()); + try!(len_data.write_to(buf)); + try!(buf.write_all(&self.data[..])); + Result::Ok(()) + } +} + + +impl Default for LenPrefixedBytes { + fn default() -> Self { + LenPrefixedBytes { + len: default::Default::default(), + data: default::Default::default() + } + } +} + +impl fmt::Debug for LenPrefixedBytes { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.data.fmt(f) + } +} + +impl Lengthable for i16 { + fn into(self) -> usize { + self as usize + } + + fn from(u: usize) -> i16 { + u as i16 + } +} + +impl Lengthable for i32 { + fn into(self) -> usize { + self as usize + } + + fn from(u: usize) -> i32 { + u as i32 + } +} + /// VarInt have a variable size (between 1 and 5 bytes) when encoded based /// on the size of the number #[derive(Clone, Copy)] -pub struct VarInt(i32); +pub struct VarInt(pub i32); -impl Lengthable for VarInt {} +impl Lengthable for VarInt { + fn into(self) -> usize { + self.0 as usize + } + + fn from(u: usize) -> VarInt { + VarInt(u as i32) + } +} impl Serializable for VarInt { /// Decodes a VarInt from the Reader @@ -333,7 +476,7 @@ impl Serializable for VarInt { val |= (b & PART) << (size * 7); size+=1; if size > 5 { - return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, Error::Err("VarInt too big".to_string()))) + return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, Error::Err("VarInt too big".to_owned()))) } if (b & 0x80) == 0 { break @@ -362,15 +505,70 @@ impl default::Default for VarInt { fn default() -> VarInt { VarInt(0) } } -impl convert::Into for VarInt { - fn into(self) -> usize { - self.0 as usize +impl fmt::Debug for VarInt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) } } -impl convert::From for VarInt { - fn from(u: usize) -> VarInt { - VarInt(u as i32) +/// VarLong have a variable size (between 1 and 10 bytes) when encoded based +/// on the size of the number +#[derive(Clone, Copy)] +pub struct VarLong(pub i64); + +impl Lengthable for VarLong { + fn into(self) -> usize { + self.0 as usize + } + + fn from(u: usize) -> VarLong { + VarLong(u as i64) + } +} + +impl Serializable for VarLong { + /// Decodes a VarLong from the Reader + fn read_from(buf: &mut io::Read) -> Result { + const PART : u64 = 0x7F; + let mut size = 0; + let mut val = 0u64; + loop { + let b = try!(buf.read_u8()) as u64; + val |= (b & PART) << (size * 7); + size+=1; + if size > 10 { + return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, Error::Err("VarLong too big".to_owned()))) + } + if (b & 0x80) == 0 { + break + } + } + + Result::Ok(VarLong(val as i64)) + } + + /// Encodes a VarLong into the Writer + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + const PART : u64 = 0x7F; + let mut val = self.0 as u64; + loop { + if (val & !PART) == 0 { + try!(buf.write_u8(val as u8)); + return Result::Ok(()); + } + try!(buf.write_u8(((val & PART) | 0x80) as u8)); + val >>= 7; + } + } +} + +impl default::Default for VarLong { + fn default() -> VarLong { VarLong(0) } +} + +impl fmt::Debug for VarLong { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) } } @@ -516,13 +714,12 @@ impl Conn { Some(val) => { let pos = buf.position() as usize; let ibuf = buf.into_inner(); - if ibuf.len() < pos { + if ibuf.len() != pos { return Result::Err(Error::Err(format!("Failed to read all of packet 0x{:X}, had {} bytes left", id, ibuf.len() - pos))) } Result::Ok(val) }, - // FIXME - None => Result::Ok(packet::Packet::StatusRequest(packet::status::serverbound::StatusRequest{empty:()}))//Result::Err(Error::Err("missing packet".to_string())) + None => Result::Err(Error::Err("missing packet".to_owned())) } } @@ -592,25 +789,25 @@ impl Clone for Conn { pub trait PacketType { fn packet_id(&self) -> i32; - fn write(self, buf: &mut Vec) -> Result<(), io::Error>; + fn write(self, buf: &mut io::Write) -> Result<(), io::Error>; } -#[test] -fn test() { +// #[test] +pub fn test() { let mut c = Conn::new("localhost:25565").unwrap(); c.write_packet(packet::handshake::serverbound::Handshake{ - protocol_version: VarInt(69), - host: "localhost".to_string(), + protocol_version: VarInt(71), + host: "localhost".to_owned(), port: 25565, next: VarInt(2), }).unwrap(); c.state = State::Login; - c.write_packet(packet::login::serverbound::LoginStart{username: "Think".to_string()}).unwrap(); + c.write_packet(packet::login::serverbound::LoginStart{username: "Think".to_owned()}).unwrap(); let packet = match c.read_packet().unwrap() { packet::Packet::EncryptionRequest(val) => val, - _ => panic!("Wrong packet"), + val => panic!("Wrong packet: {:?}", val), }; let mut key = openssl::PublicKey::new(&packet.public_key.data); @@ -620,17 +817,17 @@ fn test() { let token_e = key.encrypt(&packet.verify_token.data); let profile = mojang::Profile{ - username: "Think".to_string(), - id: "b1184d43168441cfa2128b9a3df3b6ab".to_string(), - access_token: "".to_string() + username: "Think".to_owned(), + id: "b1184d43168441cfa2128b9a3df3b6ab".to_owned(), + access_token: "".to_owned() }; profile.join_server(&packet.server_id, &shared, &packet.public_key.data); c.write_packet(packet::login::serverbound::EncryptionResponse{ - shared_secret: LenPrefixed::new(shared_e), - verify_token: LenPrefixed::new(token_e), - }); + shared_secret: LenPrefixedBytes::new(shared_e), + verify_token: LenPrefixedBytes::new(token_e), + }).unwrap(); let mut read = c.clone(); let mut write = c.clone(); @@ -660,12 +857,13 @@ fn test() { let mut count = 0; loop { match read.read_packet().unwrap() { packet::Packet::ServerMessage(val) => println!("MSG: {}", val.message), - _ => { + packet::Packet::ChunkData(_) => {}, + val => { + println!("{:?}", val); if first { - println!("got packet"); write.write_packet(packet::play::serverbound::ChatMessage{ - message: "Hello world".to_string(), - }); + message: "Hello world".to_owned(), + }).unwrap(); first = false; } count += 1; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index fcc07e1..f201ba6 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1,3 +1,4 @@ +use format; state_packets!( handshake Handshaking { @@ -38,7 +39,7 @@ state_packets!( TabComplete => 0x00 { text: String =, has_target: bool =, - target: Option = when(|p: &TabComplete| p.has_target == true), + target: Option = when(|p: &TabComplete| p.has_target == true), } // ChatMessage is sent by the client when it sends a chat message or // executes a command (prefixed by '/'). @@ -76,14 +77,712 @@ state_packets!( button: u8 =, action_number: u16 =, mode: u8 =, - clicked_item: ()=, // TODO + clicked_item: Option =, // TODO + } + // CloseWindow is sent when the client closes a window. + CloseWindow => 0x07 { + id: u8 =, + } + // PluginMessageServerbound is used for custom messages between the client + // and server. This is mainly for plugins/mods but vanilla has a few channels + // registered too. + PluginMessageServerbound => 0x08 { + channel: String =, + data: Vec =, + } + // UseEntity is sent when the user interacts (right clicks) or attacks + // (left clicks) an entity. + UseEntity => 0x09 { + target_id: VarInt =, + ty: VarInt =, + target_x: f32 = when(|p: &UseEntity| p.ty.0 == 2), + target_y: f32 = when(|p: &UseEntity| p.ty.0 == 2), + target_z: f32 = when(|p: &UseEntity| p.ty.0 == 2), + hand: VarInt = when(|p: &UseEntity| p.ty.0 == 0 || 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. + KeepAliveServerbound => 0x0A { + id: VarInt =, + } + // PlayerPosition is used to update the player's position. + PlayerPosition => 0x0B { + x: f64 =, + y: f64 =, + z: f64 =, + on_ground: bool =, + } + // PlayerPositionLook is a combination of PlayerPosition and + // PlayerLook. + PlayerPositionLook => 0x0C { + x: f64 =, + y: f64 =, + z: f64 =, + yaw: f32 =, + pitch: f32 =, + on_ground: bool =, + } + // PlayerLook is used to update the player's rotation. + PlayerLook => 0x0D { + yaw: f32 =, + pitch: f32 =, + on_ground: bool =, + } + // Player is used to update whether the player is on the ground or not. + Player => 0x0E { + on_ground: bool =, + } + // ClientAbilities is used to modify the players current abilities. + // Currently flying is the only one + ClientAbilities => 0x0F { + flags: u8 =, + flying_speed: f32 =, + walking_speed: f32 =, + } + // PlayerDigging is sent when the client starts/stops digging a block. + // It also can be sent for droppping items and eating/shooting. + PlayerDigging => 0x10 { + status: u8 =, + location: types::Position =, + face: u8 =, + } + // PlayerAction is sent when a player preforms various actions. + PlayerAction => 0x11 { + entity_id: VarInt =, + action_id: VarInt =, + jump_boost: VarInt =, + } + // SteerVehicle is sent by the client when steers or preforms an action + // on a vehicle. + SteerVehicle => 0x12 { + sideways: f32 =, + forward: f32 =, + flags: u8 =, + } + // ResourcePackStatus informs the server of the client's current progress + // in activating the requested resource pack + ResourcePackStatus => 0x13 { + hash: String =, + result: VarInt =, + } + // HeldItemChange is sent when the player changes the currently active + // hotbar slot. + HeldItemChange => 0x14 { + slot: i16 =, + } + // CreativeInventoryAction is sent when the client clicks in the creative + // inventory. This is used to spawn items in creative. + CreativeInventoryAction => 0x15 { + slot: i16 =, + clicked_item: Option =, + } + // SetSign sets the text on a sign after placing it. + SetSign => 0x16 { + location: types::Position =, + line1: String =, + line2: String =, + line3: String =, + line4: String =, + } + // ArmSwing is sent by the client when the player left clicks (to swing their + // arm). + ArmSwing => 0x17 { + hand: VarInt =, + } + // SpectateTeleport is sent by clients in spectator mode to teleport to a player. + SpectateTeleport => 0x18 { + target: UUID =, + } + // PlayerBlockPlacement is sent when the client tries to place a block. + PlayerBlockPlacement => 0x19 { + location: types::Position =, + face: VarInt =, + hand: VarInt =, + cursor_x: u8 =, + cursor_y: u8 =, + cursor_z: u8 =, + } + // UseItem is sent when the client tries to use an item. + UseItem => 0x1A { + hand: VarInt =, } } clientbound Clientbound { - ServerMessage => 15 { + // SpawnObject is used to spawn an object or vehicle into the world when it + // is in range of the client. + SpawnObject => 0x00 { + entity_id: VarInt =, + uuid: UUID =, + ty: u8 =, + x: i32 =, + y: i32 =, + z: i32 =, + pitch: i8 =, + yaw: i8 =, + data: i32 =, + velocity_x: i16 =, + velocity_y: i16 =, + velocity_z: i16 =, + } + // 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. + SpawnExperienceOrb => 0x01 { + entity_id: VarInt =, + x: i32 =, + y: i32 =, + z: i32 =, + count: i16 =, + } + // SpawnGlobalEntity spawns an entity which is visible from anywhere in the + // world. Currently only used for lightning. + SpawnGlobalEntity => 0x02 { + entity_id: VarInt =, + ty: u8 =, + x: i32 =, + y: i32 =, + z: i32 =, + } + // SpawnMob is used to spawn a living entity into the world when it is in + // range of the client. + SpawnMob => 0x03 { + entity_id: VarInt =, + uuid: UUID =, + ty: u8 =, + x: i32 =, + y: i32 =, + z: i32 =, + yaw: i8 =, + pitch: i8 =, + head_pitch: i8 =, + velocity_x: i16 =, + velocity_y: i16 =, + velocity_z: i16 =, + metadata: types::Metadata =, + } + // 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. + SpawnPainting => 0x04 { + entity_id: VarInt =, + title: String =, + location: types::Position =, + direction: u8 =, + } + // SpawnPlayer is used to spawn a player when they are in range of the client. + // This packet alone isn't enough to display the player as the skin and username + // information is in the player information packet. + SpawnPlayer => 0x05 { + entity_id: VarInt =, + uuid: UUID =, + x: i32 =, + y: i32 =, + z: i32 =, + yaw: i8 =, + pitch: i8 =, + metadata: types::Metadata =, + } + // Animation is sent by the server to play an animation on a specific entity. + Animation => 0x06 { + entity_id: VarInt =, + animation_id: u8 =, + } + // Statistics is used to update the statistics screen for the client. + Statistics => 0x07 { + statistices: LenPrefixed =, + } + // BlockBreakAnimation is used to create and update the block breaking + // animation played when a player starts digging a block. + BlockBreakAnimation => 0x08 { + entity_id: VarInt =, + location: types::Position =, + stage: i8 =, + } + // UpdateBlockEntity updates the nbt tag of a block entity in the + // world. + UpdateBlockEntity => 0x09 { + location: types::Position =, + action: u8 =, + nbt: Option =, + } + // BlockAction triggers different actions depending on the target block. + BlockAction => 0x0A { + location: types::Position =, + byte1: u8 =, + byte2: u8 =, + block_type: VarInt =, + } + // BlockChange is used to update a single block on the client. + BlockChange => 0x0B { + location: types::Position =, + block_id: VarInt =, + } + // BossBar displays and/or changes a boss bar that is displayed on the + // top of the client's screen. This is normally used for bosses such as + // the ender dragon or the wither. + BossBar => 0x0C { + uuid: UUID =, + action: VarInt =, + title: format::Component = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 3), + health: f32 = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 2), + color: VarInt = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 4), + style: VarInt = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 4), + flags: u8 = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 5), + } + // ServerDifficulty changes the displayed difficulty in the client's menu + // as well as some ui changes for hardcore. + ServerDifficulty => 0x0D { + difficulty: u8 =, + } + // TabCompleteReply is sent as a reply to a tab completion request. + // The matches should be possible completions for the command/chat the + // player sent. + TabCompleteReply => 0x0E { + matches: LenPrefixed =, + } + // 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. + ServerMessage => 0x0F { message: format::Component =, + // 0 - Chat message, 1 - System message, 2 - Action bar message position: u8 =, } + // MultiBlockChange is used to update a batch of blocks in a single packet. + MultiBlockChange => 0x10 { + chunk_x: i32 =, + chunk_y: i32 =, + records: LenPrefixed =, + } + // ConfirmTransaction notifies the client whether a transaction was successful + // or failed (e.g. due to lag). + ConfirmTransaction => 0x11 { + id: u8 =, + action_number: i16 =, + accepted: bool =, + } + // WindowClose forces the client to close the window with the given id, + // e.g. a chest getting destroyed. + WindowClose => 0x12 { + id: u8 =, + } + // WindowOpen tells the client to open the inventory window of the given + // type. The ID is used to reference the instance of the window in + // other packets. + WindowOpen => 0x13 { + id: u8 =, + ty: String =, + title: format::Component =, + slot_count: u8 =, + entity_id: i32 = when(|p: &WindowOpen| p.ty == "EntityHorse"), + } + // WindowItems sets every item in a window. + WindowItems => 0x14 { + id: u8 =, + items: LenPrefixed> =, + } + // WindowProperty changes the value of a property of a window. Properties + // vary depending on the window type. + WindowProperty => 0x15 { + id: u8 =, + property: i16 =, + value: i16 =, + } + // WindowSetSlot changes an itemstack in one of the slots in a window. + WindowSetSlot => 0x16 { + id: u8 =, + property: i16 =, + item: Option =, + } + // SetCooldown disables a set item (by id) for the set number of ticks + SetCooldown => 0x17 { + item_id: VarInt =, + ticks: VarInt =, + } + // PluginMessageClientbound is used for custom messages between the client + // and server. This is mainly for plugins/mods but vanilla has a few channels + // registered too. + PluginMessageClientbound => 0x18 { + channel: String =, + data: Vec =, + } + // Disconnect causes the client to disconnect displaying the passed reason. + Disconnect => 0x19 { + reason: format::Component =, + } + // EntityAction causes an entity to preform an action based on the passed + // id. + EntityAction => 0x1A { + entity_id: i32 =, + action_id: u8 =, + } + // Explosion is sent when an explosion is triggered (tnt, creeper etc). + // This plays the effect and removes the effected blocks. + Explosion => 0x1B { + x: f32 =, + y: f32 =, + z: f32 =, + radius: f32 =, + records: LenPrefixed =, + velocity_x: f32 =, + velocity_y: f32 =, + velocity_z: f32 =, + } + // ChunkUnload tells the client to unload the chunk at the specified + // position. + ChunkUnload => 0x1C { + x: i32 =, + z: i32 =, + } + // SetCompression updates the compression threshold. + SetCompression => 0x1D { + threshold: VarInt =, + } + // ChangeGameState is used to modify the game's state like gamemode or + // weather. + ChangeGameState => 0x1E { + reason: u8 =, + value: f32 =, + } + // KeepAliveClientbound is sent by a server to check if the + // client is still responding and keep the connection open. + // The client should reply with the KeepAliveServerbound + // packet setting ID to the same as this one. + KeepAliveClientbound => 0x1F { + id: VarInt =, + } + // ChunkData sends or updates a single chunk on the client. If New is set + // then biome data should be sent too. + ChunkData => 0x20 { + chunk_x: i32 =, + chunk_z: i32 =, + new: bool =, + bitmask: VarInt =, + data: LenPrefixedBytes =, + } + // 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. + Effect => 0x21 { + effect_id: i32 =, + location: types::Position =, + data: i32 =, + disable_relative: bool =, + } + // Particle spawns particles at the target location with the various + // modifiers. + Particle => 0x22 { + particle_id: i32 =, + long_distance: bool =, + x: f32 =, + y: f32 =, + z: f32 =, + offset_x: f32 =, + offset_y: f32 =, + offset_z: f32 =, + speed: f32 =, + count: i32 =, + data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38), + data2: VarInt = when(|p: &Particle| p.particle_id == 36), + } + // SoundEffect plays the named sound at the target location. + SoundEffect => 0x23 { + name: String =, + x: i32 =, + y: i32 =, + z: i32 =, + volume: f32 =, + pitch: u8 =, + } + // JoinGame is sent after completing the login process. This + // sets the initial state for the client. + JoinGame => 0x24 { + // The entity id the client will be referenced by + entity_id: i32 =, + // The starting gamemode of the client + gamemode: u8 =, + // The dimension the client is starting in + dimension: i8 =, + // The difficuilty setting for the server + difficulty: u8 =, + // The max number of players on the server + max_players: u8 =, + // The level type of the server + level_type: String =, + // Whether the client should reduce the amount of debug + // information it displays in F3 mode + reduced_debug_info: bool =, + } + // Maps updates a single map's contents + Maps => 0x25 { + item_damage: VarInt =, + scale: i8 =, + tracking_position: bool =, + icons: LenPrefixed =, + columns: u8 =, + rows: Option = when(|p: &Maps| p.columns > 0), + x: Option = when(|p: &Maps| p.columns > 0), + z: Option = when(|p: &Maps| p.columns > 0), + data: Option> = when(|p: &Maps| p.columns > 0), + } + // EntityMove moves the entity with the id by the offsets provided. + EntityMove => 0x26 { + entity_id: VarInt =, + delta_x: i8 =, + delta_y: i8 =, + delta_z: i8 =, + on_ground: bool =, + } + // EntityLookAndMove is a combination of EntityMove and EntityLook. + EntityLookAndMove => 0x27 { + entity_id: VarInt =, + delta_x: i8 =, + delta_y: i8 =, + delta_z: i8 =, + yaw: i8 =, + pitch: i8 =, + on_ground: bool =, + } + // EntityLook rotates the entity to the new angles provided. + EntityLook => 0x28 { + entity_id: VarInt =, + yaw: i8 =, + pitch: i8 =, + on_ground: bool =, + } + // Entity does nothing. It is a result of subclassing used in Minecraft. + Entity => 0x29 { + entity_id: VarInt =, + } + // SignEditorOpen causes the client to open the editor for a sign so that + // it can write to it. Only sent in vanilla when the player places a sign. + SignEditorOpen => 0x2A { + location: types::Position =, + } + // PlayerAbilities is used to modify the players current abilities. Flying, + // creative, god mode etc. + PlayerAbilities => 0x2B { + flags: u8 =, + flying_speed: f32 =, + walking_speed: f32 =, + } + // CombatEvent is used for... you know, I never checked. I have no + // clue. + CombatEvent => 0x2C { + event: VarInt =, + direction: Option = when(|p: &CombatEvent| p.event.0 == 1), + player_id: Option = when(|p: &CombatEvent| p.event.0 == 2), + entity_id: Option = when(|p: &CombatEvent| p.event.0 == 1 || p.event.0 == 2), + message: Option = when(|p: &CombatEvent| p.event.0 == 2), + } + // PlayerInfo is sent by the server for every player connected to the server + // to provide skin and username information as well as ping and gamemode info. + PlayerInfo => 0x2D { + inner: packet::PlayerInfoData =, // Ew but watcha gonna do? + } + // 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. + TeleportPlayer => 0x2E { + x: f64 =, + y: f64 =, + z: f64 =, + yaw: f32 =, + pitch: f32 =, + flags: u8 =, + } + // EntityUsedBed is sent by the server when a player goes to bed. + EntityUsedBed => 0x2F { + entity_id: VarInt =, + location: types::Position =, + } + // EntityDestroy destroys the entities with the ids in the provided slice. + EntityDestroy => 0x30 { + entity_ids: LenPrefixed =, + } + // EntityRemoveEffect removes an effect from an entity. + EntityRemoveEffect => 0x31 { + entity_id: VarInt =, + effect_id: i8 =, + } + // ResourcePackSend causes the client to check its cache for the requested + // resource packet and download it if its missing. Once the resource pack + // is obtained the client will use it. + ResourcePackSend => 0x32 { + url: String =, + hash: String =, + } + // Respawn is sent to respawn the player after death or when they move worlds. + Respawn => 0x33 { + dimension: i32 =, + difficulty: u8 =, + gamemode: u8 =, + level_type: String =, + } + // EntityHeadLook rotates an entity's head to the new angle. + EntityHeadLook => 0x34 { + entity_id: VarInt =, + head_yaw: i8 =, + } + // WorldBorder configures the world's border. + WorldBorder => 0x35 { + action: VarInt =, + old_radius: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1), + new_radius: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1 || p.action.0 == 0), + speed: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1), + x: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 2), + z: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 2), + portal_boundary: Option = when(|p: &WorldBorder| p.action.0 == 3), + warning_time: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 4), + warning_blocks: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 5), + } + // Camera causes the client to spectate the entity with the passed id. + // Use the player's id to de-spectate. + Camera => 0x36 { + target_id: VarInt =, + } + // SetCurrentHotbarSlot changes the player's currently selected hotbar item. + SetCurrentHotbarSlot => 0x37 { + slot: u8 =, + } + // ScoreboardDisplay is used to set the display position of a scoreboard. + ScoreboardDisplay => 0x38 { + position: u8 =, + name: String =, + } + // EntityMetadata updates the metadata for an entity. + EntityMetadata => 0x39 { + entity_id: VarInt =, + metadata: types::Metadata =, + } + // EntityAttach attaches to entities together, either by mounting or leashing. + // -1 can be used at the EntityID to deattach. + EntityAttach => 0x3A { + entity_id: i32 =, + vehicle: i32 =, + leash: bool =, + } + // EntityVelocity sets the velocity of an entity in 1/8000 of a block + // per a tick. + EntityVelocity => 0x3B { + entity_id: VarInt =, + velocity_x: i16 =, + velocity_y: i16 =, + velocity_z: i16 =, + } + // EntityEquipment is sent to display an item on an entity, like a sword + // or armor. Slot 0 is the held item and slots 1 to 4 are boots, leggings + // chestplate and helmet respectively. + EntityEquipment => 0x3C { + entity_id: VarInt =, + slot: VarInt =, + item: Option =, + } + // SetExperience updates the experience bar on the client. + SetExperience => 0x3D { + experience_bar: f32 =, + level: VarInt =, + total_experience: VarInt =, + } + // UpdateHealth is sent by the server to update the player's health and food. + UpdateHealth => 0x3E { + health: f32 =, + food: VarInt =, + food_saturation: f32 =, + } + // ScoreboardObjective creates/updates a scoreboard objective. + ScoreboardObjective => 0x3F { + name: String =, + mode: u8 =, + value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), + ty: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), + } + // Teams creates and updates teams + Teams => 0x40 { + name: String =, + mode: u8 =, + display_name: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + prefix: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + suffix: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + flags: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + name_tag_visibility: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + collision_rule: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + color: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + players: Option> = when(|p: &Teams| p.mode == 0 || p.mode == 3 || p.mode == 4), + } + // UpdateScore is used to update or remove an item from a scoreboard + // objective. + UpdateScore => 0x41 { + name: String =, + action: u8 =, + object_name: String =, + value: Option = when(|p: &UpdateScore| p.action != 1), + } + // SpawnPosition is sent to change the player's current spawn point. Currently + // only used by the client for the compass. + SpawnPosition => 0x42 { + location: types::Position =, + } + // TimeUpdate is sent to sync the world's time to the client, the client + // will manually tick the time itself so this doesn't need to sent repeatedly + // but if the server or client has issues keeping up this can fall out of sync + // so it is a good idea to sent this now and again + TimeUpdate => 0x43 { + world_age: i64 =, + time_of_day: i64 =, + } + // Title configures an on-screen title. + Title => 0x44 { + action: VarInt =, + title: Option = when(|p: &Title| p.action.0 == 0), + sub_title: Option = when(|p: &Title| p.action.0 == 1), + fade_in: Option = when(|p: &Title| p.action.0 == 2), + fade_stay: Option = when(|p: &Title| p.action.0 == 2), + fade_out: Option = when(|p: &Title| p.action.0 == 2), + } + // UpdateSign sets or changes the text on a sign. + UpdateSign => 0x45 { + location: types::Position =, + line1: format::Component =, + line2: format::Component =, + line3: format::Component =, + line4: format::Component =, + } + // PlayerListHeaderFooter updates the header/footer of the player list. + PlayerListHeaderFooter => 0x46 { + header: format::Component =, + footer: format::Component =, + } + // CollectItem causes the collected item to fly towards the collector. This + // does not destroy the entity. + CollectItem => 0x47 { + collected_entity_id: VarInt =, + collector_entity_id: VarInt =, + } + // EntityTeleport teleports the entity to the target location. This is + // sent if the entity moves further than EntityMove allows. + EntityTeleport => 0x48 { + entity_id: VarInt =, + x: i32 =, + y: i32 =, + z: i32 =, + yaw: i8 =, + pitch: i8 =, + on_ground: bool =, + } + // EntityProperties updates the properties for an entity. + EntityProperties => 0x49 { + entity_id: VarInt =, + properties: LenPrefixed =, + } + // EntityEffect applies a status effect to an entity for a given duration. + EntityEffect => 0x4A { + entity_id: VarInt =, + effect_id: i8 =, + amplifier: i8 =, + duration: VarInt =, + hide_particles: bool =, + } } } login Login { @@ -91,52 +790,52 @@ state_packets!( // LoginStart is sent immeditately after switching into the login // state. The passed username is used by the server to authenticate // the player in online mode. - LoginStart => 0 { + LoginStart => 0x00 { username: String =, } // EncryptionResponse is sent as a reply to EncryptionRequest. All // packets following this one must be encrypted with AES/CFB8 // encryption. - EncryptionResponse => 1 { + EncryptionResponse => 0x01 { // The key for the AES/CFB8 cipher encrypted with the // public key - shared_secret: LenPrefixed =, + shared_secret: LenPrefixedBytes =, // The verify token from the request encrypted with the // public key - verify_token: LenPrefixed =, + verify_token: LenPrefixedBytes =, } } clientbound Clientbound { // LoginDisconnect is sent by the server if there was any issues // authenticating the player during login or the general server // issues (e.g. too many players). - LoginDisconnect => 0 { + LoginDisconnect => 0x00 { reason: format::Component =, } // EncryptionRequest is sent by the server if the server is in // online mode. If it is not sent then its assumed the server is // in offline mode. - EncryptionRequest => 1 { + EncryptionRequest => 0x01 { // Generally empty, left in from legacy auth // but is still used by the client if provided server_id: String =, // A RSA Public key serialized in x.509 PRIX format - public_key: LenPrefixed =, + public_key: LenPrefixedBytes =, // Token used by the server to verify encryption is working // correctly - verify_token: LenPrefixed =, + verify_token: LenPrefixedBytes =, } // LoginSuccess is sent by the server if the player successfully // authenicates with the session servers (online mode) or straight // after LoginStart (offline mode). - LoginSuccess => 2 { + LoginSuccess => 0x02 { // String encoding of a uuid (with hyphens) uuid: String =, username: String =, } // SetInitialCompression sets the compression threshold during the // login state. - SetInitialCompression => 3 { + SetInitialCompression => 0x03 { // Threshold where a packet should be sent compressed threshold: VarInt =, } @@ -148,14 +847,14 @@ state_packets!( // switching to the Status protocol state and is used // to signal the server to send a StatusResponse to the // client - StatusRequest => 0 { + StatusRequest => 0x00 { empty: () =, } // StatusPing is sent by the client after recieving a // StatusResponse. The client uses the time from sending // the ping until the time of recieving a pong to measure // the latency between the client and the server. - StatusPing => 1 { + StatusPing => 0x01 { ping: i64 =, } } @@ -181,15 +880,268 @@ state_packets!( // "description": "Hello world", // "favicon": "data:image/png;base64," // } - StatusResponse => 0 { + StatusResponse => 0x00 { status: String =, } // StatusPong is sent as a reply to a StatusPing. // The Time field should be exactly the same as the // one sent by the client. - StatusPong => 1 { + StatusPong => 0x01 { ping: i64 =, } } } ); + +#[derive(Debug, Default)] +pub struct Statistic { + pub name: String, + pub value: VarInt, +} + +impl Serializable for Statistic { + fn read_from(buf: &mut io::Read) -> Result { + Ok(Statistic { + name: try!(Serializable::read_from(buf)), + value: try!(Serializable::read_from(buf)), + }) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(self.name.write_to(buf)); + self.value.write_to(buf) + } +} + +#[derive(Debug, Default)] +pub struct BlockChangeRecord { + pub xz: u8, + pub y: u8, + pub block_id: VarInt, +} + +impl Serializable for BlockChangeRecord { + fn read_from(buf: &mut io::Read) -> Result { + Ok(BlockChangeRecord{ + xz: try!(Serializable::read_from(buf)), + y: try!(Serializable::read_from(buf)), + block_id: try!(Serializable::read_from(buf)), + }) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(self.xz.write_to(buf)); + try!(self.y.write_to(buf)); + self.block_id.write_to(buf) + } +} + +#[derive(Debug, Default)] +pub struct ExplosionRecord { + pub x: i8, + pub y: i8, + pub z: i8, +} + +impl Serializable for ExplosionRecord { + fn read_from(buf: &mut io::Read) -> Result { + Ok(ExplosionRecord{ + x: try!(Serializable::read_from(buf)), + y: try!(Serializable::read_from(buf)), + z: try!(Serializable::read_from(buf)), + }) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(self.x.write_to(buf)); + try!(self.y.write_to(buf)); + self.z.write_to(buf) + } +} + +#[derive(Debug)] +pub struct MapIcon { + pub direction_type: i8, + pub x: i8, + pub z: i8, +} + +impl Serializable for MapIcon { + fn read_from(buf: &mut io::Read) -> Result { + Ok(MapIcon{ + direction_type: try!(Serializable::read_from(buf)), + x: try!(Serializable::read_from(buf)), + z: try!(Serializable::read_from(buf)), + }) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(self.direction_type.write_to(buf)); + try!(self.x.write_to(buf)); + self.z.write_to(buf) + } +} + +impl Default for MapIcon { + fn default() -> Self { + MapIcon { .. Default::default() } + } +} + +#[derive(Debug, Default)] +pub struct EntityProperty { + pub key: String, + pub value: f64, + pub modifiers: LenPrefixed, +} + +impl Serializable for EntityProperty { + fn read_from(buf: &mut io::Read) -> Result { + Ok(EntityProperty{ + key: try!(Serializable::read_from(buf)), + value: try!(Serializable::read_from(buf)), + modifiers: try!(Serializable::read_from(buf)), + }) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(self.key.write_to(buf)); + try!(self.value.write_to(buf)); + self.modifiers.write_to(buf) + } +} + +#[derive(Debug, Default)] +pub struct PropertyModifier { + pub uuid: UUID, + pub amount: f64, + pub operation: i8, +} + +impl Serializable for PropertyModifier { + fn read_from(buf: &mut io::Read) -> Result { + Ok(PropertyModifier{ + uuid: try!(Serializable::read_from(buf)), + amount: try!(Serializable::read_from(buf)), + operation: try!(Serializable::read_from(buf)), + }) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(self.uuid.write_to(buf)); + try!(self.amount.write_to(buf)); + self.operation.write_to(buf) + } +} + +#[derive(Debug)] +pub struct PlayerInfoData { + pub action: VarInt, + pub players: Vec, +} + +impl Serializable for PlayerInfoData { + fn read_from(buf: &mut io::Read) -> Result { + let mut m = PlayerInfoData{ + action: try!(Serializable::read_from(buf)), + players: Vec::new(), + }; + let len = try!(VarInt::read_from(buf)); + for _ in 0 .. len.0 { + let uuid = try!(UUID::read_from(buf)); + match m.action.0 { + 0 => { + let name = try!(String::read_from(buf)); + let mut props = Vec::new(); + let plen = try!(VarInt::read_from(buf)).0; + for _ in 0 .. plen { + let mut prop = PlayerProperty { + name: try!(String::read_from(buf)), + value: try!(String::read_from(buf)), + signature: Default::default(), + }; + if try!(bool::read_from(buf)) { + prop.signature = Some(try!(String::read_from(buf))); + } + props.push(prop); + } + let p = PlayerDetail::Add { + uuid: uuid, + name: name, + properties: props, + gamemode: try!(Serializable::read_from(buf)), + ping: try!(Serializable::read_from(buf)), + display: { + if try!(bool::read_from(buf)) { + Some(try!(Serializable::read_from(buf))) + } else { + None + } + }, + }; + m.players.push(p); + }, + 1 => { + m.players.push(PlayerDetail::UpdateGamemode{ + uuid: uuid, + gamemode: try!(Serializable::read_from(buf)), + }) + }, + 2 => { + m.players.push(PlayerDetail::UpdateLatency{ + uuid: uuid, + ping: try!(Serializable::read_from(buf)), + }) + }, + 3 => { + m.players.push(PlayerDetail::UpdateDisplayName{ + uuid: uuid, + display: { + if try!(bool::read_from(buf)) { + Some(try!(Serializable::read_from(buf))) + } else { + None + } + }, + }) + }, + 4 => { + m.players.push(PlayerDetail::Remove{ + uuid: uuid, + }) + }, + _ => panic!(), + } + } + Ok(m) + } + + fn write_to(&self, _: &mut io::Write) -> Result<(), io::Error> { + unimplemented!() // I'm lazy + } +} + +impl Default for PlayerInfoData { + fn default() -> Self { + PlayerInfoData { + action: VarInt(0), + players: Vec::new(), + } + } +} + +#[derive(Debug)] +pub enum PlayerDetail { + Add{uuid: UUID, name: String, properties: Vec, gamemode: VarInt, ping: VarInt, display: Option}, + UpdateGamemode{uuid: UUID, gamemode: VarInt}, + UpdateLatency{uuid: UUID, ping: VarInt}, + UpdateDisplayName{uuid: UUID, display: Option}, + Remove{uuid: UUID}, +} + +#[derive(Debug)] +pub struct PlayerProperty { + pub name: String, + pub value: String, + pub signature: Option, +} From 2d10d38e4c272adef624d4a5a15c776a882f3b07 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sat, 12 Sep 2015 20:31:26 +0100 Subject: [PATCH 009/160] Complete protocol implementation --- protocol/src/format.rs | 10 +- protocol/src/item.rs | 54 +++++ protocol/src/nbt/mod.rs | 274 ++++++++++++++++++++++++ protocol/src/types/blockpos.rs | 54 +++++ protocol/src/types/metadata.rs | 380 +++++++++++++++++++++++++++++++++ protocol/src/types/mod.rs | 6 + 6 files changed, 772 insertions(+), 6 deletions(-) create mode 100644 protocol/src/item.rs create mode 100644 protocol/src/nbt/mod.rs create mode 100644 protocol/src/types/blockpos.rs create mode 100644 protocol/src/types/metadata.rs create mode 100644 protocol/src/types/mod.rs diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 7928967..26ec1e7 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -4,8 +4,7 @@ use std::fmt; #[derive(Debug)] pub enum Component { - Text(TextComponent), - None + Text(TextComponent) } impl Component { @@ -16,7 +15,7 @@ impl Component { } else if v.find("text").is_some(){ Component::Text(TextComponent::from_value(v, modifier)) } else { - Component::None + Component::Text(TextComponent{text: "".to_owned(), modifier: modifier}) } } @@ -29,18 +28,17 @@ impl fmt::Display for Component { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Component::Text(ref txt) => write!(f, "{}", txt), - Component::None => Result::Ok(()), } } } impl Default for Component { fn default() -> Self { - Component::None + Component::Text(TextComponent{text: "".to_owned(), modifier: Default::default()}) } } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Modifier { pub extra: Option>, pub bold: Option, diff --git a/protocol/src/item.rs b/protocol/src/item.rs new file mode 100644 index 0000000..27661f2 --- /dev/null +++ b/protocol/src/item.rs @@ -0,0 +1,54 @@ +extern crate byteorder; + +use nbt; +use protocol::{Serializable}; +use std::io; +use std::io::{Read, Write}; +use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; + +#[derive(Debug)] +pub struct Stack { + id: isize, + count: isize, + damage: isize, + tag: Option, +} + + +impl Default for Stack { + fn default() -> Stack { + Stack { + id: -1, + count: 0, + damage: 0, + tag: None, + } + } +} + +impl Serializable for Option { + fn read_from(buf: &mut io::Read) -> Result, io::Error> { + let id = try!(buf.read_i16::()); + if id == -1 { + return Ok(None); + } + Ok(Some(Stack{ + id: id as isize, + count: try!(buf.read_u8()) as isize, + damage: try!(buf.read_i16::()) as isize, + tag: try!(Serializable::read_from(buf)), + })) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + match *self { + Some(ref val) => { + try!(buf.write_i16::(val.id as i16)); + try!(buf.write_u8(val.count as u8)); + try!(buf.write_i16::(val.damage as i16)); + try!(val.tag.write_to(buf)); + }, + None => try!(buf.write_i16::(-1)), + } + Result::Ok(()) + } +} diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs new file mode 100644 index 0000000..7abfd9e --- /dev/null +++ b/protocol/src/nbt/mod.rs @@ -0,0 +1,274 @@ +extern crate byteorder; + +use std::collections::HashMap; +use std::io; +use std::io::{Read, Write}; + +use super::protocol::{Serializable}; +use super::protocol; +use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; + +#[derive(Debug)] +pub enum Tag { + End, + Byte(i8), + Short(i16), + Int(i32), + Long(i64), + Float(f32), + Double(f64), + ByteArray(Vec), + String(String), + List(Vec), + Compound(HashMap), + IntArray(Vec), +} + +#[derive(Debug)] +pub struct NamedTag(pub String, pub Tag); + +impl Tag { + + pub fn new_compound() -> Tag { + Tag::Compound(HashMap::new()) + } + + pub fn new_list() -> Tag { + Tag::List(Vec::new()) + } + + /// Returns the tag with the given name from the compound. + /// + /// # Panics + /// Panics when the tag isn't a compound. + pub fn get(&self, name: &str) -> Option<&Tag> { + match *self { + Tag::Compound(ref val) => val.get(name), + _ => panic!("not a compound tag"), + } + } + + /// Places the tag into the compound using the given name. + /// + /// # Panics + /// Panics when the tag isn't a compound. + pub fn put(&mut self, name: &str, tag: Tag) { + match *self { + Tag::Compound(ref mut val) => val.insert(name.to_owned(), tag), + _ => panic!("not a compound tag"), + }; + } + + pub fn is_compound(&self) -> bool { + match *self { + Tag::Compound(_) => true, + _ => false, + } + } + + pub fn as_byte(&self) -> Option { + match *self { + Tag::Byte(val) => Some(val), + _ => None, + } + } + + pub fn as_short(&self) -> Option { + match *self { + Tag::Short(val) => Some(val), + _ => None, + } + } + + pub fn as_int(&self) -> Option { + match *self { + Tag::Int(val) => Some(val), + _ => None, + } + } + + pub fn as_long(&self) -> Option { + match *self { + Tag::Long(val) => Some(val), + _ => None, + } + } + + pub fn as_float(&self) -> Option { + match *self { + Tag::Float(val) => Some(val), + _ => None, + } + } + + pub fn as_double(&self) -> Option { + match *self { + Tag::Double(val) => Some(val), + _ => None, + } + } + + pub fn as_byte_array(&self) -> Option<&[u8]> { + match *self { + Tag::ByteArray(ref val) => Some(&val[..]), + _ => None, + } + } + + pub fn as_string(&self) -> Option<&str> { + match *self { + Tag::String(ref val) => Some(&val[..]), + _ => None, + } + } + + pub fn as_list(&self) -> Option<&[Tag]> { + match *self { + Tag::List(ref val) => Some(&val[..]), + _ => None, + } + } + + pub fn as_compound(&self) -> Option<&HashMap> { + match *self { + Tag::Compound(ref val) => Some(val), + _ => None, + } + } + + pub fn as_int_array(&self) -> Option<&[i32]> { + match *self { + Tag::IntArray(ref val) => Some(&val[..]), + _ => None, + } + } + + fn internal_id(&self) -> u8 { + match *self { + Tag::End => 0, + Tag::Byte(_) => 1, + Tag::Short(_) => 2, + Tag::Int(_) => 3, + Tag::Long(_) => 4, + Tag::Float(_) => 5, + Tag::Double(_) => 6, + Tag::ByteArray(_) => 7, + Tag::String(_) => 8, + Tag::List(_) => 9, + Tag::Compound(_) => 10, + Tag::IntArray(_) => 11, + } + } + + fn read_type(id: u8, buf: &mut io::Read) -> Result { + match id { + 0 => unreachable!(), + 1 => Ok(Tag::Byte(try!(buf.read_i8()))), + 2 => Ok(Tag::Short(try!(buf.read_i16::()))), + 3 => Ok(Tag::Int(try!(buf.read_i32::()))), + 4 => Ok(Tag::Long(try!(buf.read_i64::()))), + 5 => Ok(Tag::Float(try!(buf.read_f32::()))), + 6 => Ok(Tag::Double(try!(buf.read_f64::()))), + 7 => Ok(Tag::ByteArray({ + let len : i32 = try!(Serializable::read_from(buf)); + let mut data = Vec::with_capacity(len as usize); + try!(buf.take(len as u64).read_to_end(&mut data)); + data + })), + 8 => Ok(Tag::String(try!(read_string(buf)))), + 9 => { + let mut l = Vec::new(); + let ty = try!(buf.read_u8()); + let len : i32 = try!(Serializable::read_from(buf)); + for _ in 0 .. len { + l.push(try!(Tag::read_type(ty, buf))); + } + Ok(Tag::List(l)) + }, + 10 => { + let mut c = Tag::new_compound(); + loop { + let ty = try!(buf.read_u8()); + if ty == 0 { + break; + } + let name: String = try!(read_string(buf)); + c.put(&name[..], try!(Tag::read_type(ty, buf))); + } + Ok(c) + }, + 11 => Ok(Tag::IntArray({ + let len : i32 = try!(Serializable::read_from(buf)); + let mut data = Vec::with_capacity(len as usize); + for _ in 0 .. len { + data.push(try!(buf.read_i32::())); + } + data + })), + _ => Err(io::Error::new(io::ErrorKind::InvalidData, protocol::Error::Err("invalid tag".to_owned()))), + } + } +} + +impl Serializable for Tag { + fn read_from(buf: &mut io::Read) -> Result { + Tag::read_type(10, buf) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + match *self { + Tag::End => {}, + Tag::Byte(val) => try!(buf.write_i8(val)), + Tag::Short(val) => try!(buf.write_i16::(val)), + Tag::Int(val) => try!(buf.write_i32::(val)), + Tag::Long(val) => try!(buf.write_i64::(val)), + Tag::Float(val) => try!(buf.write_f32::(val)), + Tag::Double(val) => try!(buf.write_f64::(val)), + Tag::ByteArray(ref val) => { + try!((val.len() as i32).write_to(buf)); + try!(buf.write_all(val)); + }, + Tag::String(ref val) => try!(write_string(buf, val)), + Tag::List(ref val) => { + if val.is_empty() { + try!(buf.write_u8(0)); + try!(buf.write_i32::(0)); + } else { + try!(buf.write_u8(val[0].internal_id())); + try!(buf.write_i32::(val.len() as i32)); + for e in val { + try!(e.write_to(buf)); + } + } + }, + Tag::Compound(ref val) => { + for (k, v) in val { + try!(v.internal_id().write_to(buf)); + try!(write_string(buf, k)); + try!(v.write_to(buf)); + } + try!(buf.write_u8(0)); + }, + Tag::IntArray(ref val) => { + try!((val.len() as i32).write_to(buf)); + for v in val { + try!(v.write_to(buf)); + } + }, + } + Result::Ok(()) + } +} + +pub fn write_string(buf: &mut io::Write, s: &String)-> io::Result<()> { + let data = s.as_bytes(); + try!((data.len() as i16).write_to(buf)); + buf.write_all(data) +} + +pub fn read_string(buf: &mut io::Read) -> io::Result { + let len: i16 = try!(buf.read_i16::()); + let mut ret = String::new(); + try!(buf.take(len as u64).read_to_string(&mut ret)); + Result::Ok(ret) +} diff --git a/protocol/src/types/blockpos.rs b/protocol/src/types/blockpos.rs new file mode 100644 index 0000000..5f71af0 --- /dev/null +++ b/protocol/src/types/blockpos.rs @@ -0,0 +1,54 @@ +extern crate byteorder; + +use std::fmt; +use protocol::{Serializable}; +use std::io; +use std::io::{Read, Write}; +use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; + +#[derive(Clone, Copy)] +pub struct Position(u64); + +impl Position { + fn new(x: i32, y: i32, z: i32) -> Position { + Position( + (((x as u64) & 0x3FFFFFF) << 38) | + (((y as u64) & 0xFFF) << 26) | + ((z as u64) & 0x3FFFFFF) + ) + } + + fn get_x(&self) -> i32 { + ((self.0 as i64) >> 38) as i32 + } + + fn get_y(&self) -> i32 { + (((self.0 as i64) >> 26) & 0xFFF) as i32 + } + + fn get_z(&self) -> i32 { + ((self.0 as i64) << 38 >> 38) as i32 + } +} + +impl Default for Position { + fn default() -> Position { + Position(0) + } +} + +impl fmt::Debug for Position { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "<{},{},{}>", self.get_x(), self.get_y(), self.get_z()) + } +} + +impl Serializable for Position { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(Position(try!(buf.read_u64::()))) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_u64::(self.0)); + Result::Ok(()) + } +} diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs new file mode 100644 index 0000000..3b78b42 --- /dev/null +++ b/protocol/src/types/metadata.rs @@ -0,0 +1,380 @@ + +use std::collections::HashMap; +use std::marker::PhantomData; +use std::io; +use std::io::{Read, Write}; +use std::fmt; +use protocol; +use protocol::{Serializable}; +use format; +use item; + +pub struct MetadataKey { + index: i32, + ty: PhantomData, +} + +impl MetadataKey { + // TODO: Make const later when possible + /*const*/ fn new(index: i32) -> MetadataKey { + MetadataKey { + index: index, + ty: PhantomData, + } + } +} + +pub struct Metadata { + map: HashMap, +} + +impl Metadata { + pub fn new() -> Metadata { + Metadata { map: HashMap::new() } + } + + 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_raw(&mut self, index: i32, val: T) { + self.map.insert(index, val.wrap()); + } +} + +impl Serializable for Metadata { + + fn read_from(buf: &mut io::Read) -> Result { + let mut m = Metadata::new(); + loop { + let index = try!(u8::read_from(buf)) as i32; + if index == 0xFF { + break; + } + let ty = try!(u8::read_from(buf)); + match ty { + 0 => m.put_raw(index, try!(i8::read_from(buf))), + 1 => m.put_raw(index, try!(protocol::VarInt::read_from(buf)).0), + 2 => m.put_raw(index, try!(f32::read_from(buf))), + 3 => m.put_raw(index, try!(String::read_from(buf))), + 4 => m.put_raw(index, try!(format::Component::read_from(buf))), + 5 => m.put_raw(index, try!(Option::::read_from(buf))), + 6 => m.put_raw(index, try!(bool::read_from(buf))), + 7 => m.put_raw(index, [ + try!(f32::read_from(buf)), + try!(f32::read_from(buf)), + try!(f32::read_from(buf)) + ]), + 8 => m.put_raw(index, try!(super::Position::read_from(buf))), + 9 => { + if try!(bool::read_from(buf)) { + m.put_raw(index, try!(Option::::read_from(buf))); + } else { + m.put_raw::>(index, None); + } + }, + 10 => m.put_raw(index, try!(protocol::VarInt::read_from(buf))), + 11 => { + if try!(bool::read_from(buf)) { + m.put_raw(index, try!(Option::::read_from(buf))); + } else { + m.put_raw::>(index, None); + } + }, + 12 => m.put_raw(index, try!(protocol::VarInt::read_from(buf)).0 as u16), + _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, protocol::Error::Err("unknown metadata type".to_owned()))), + } + } + Ok(m) + } + + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + for (k, v) in &self.map { + try!((*k as u8).write_to(buf)); + match *v { + Value::Byte(ref val) => { + try!(u8::write_to(&0, buf)); + try!(val.write_to(buf)); + }, + Value::Int(ref val) => { + try!(u8::write_to(&1, buf)); + try!(protocol::VarInt(*val).write_to(buf)); + }, + Value::Float(ref val) => { + try!(u8::write_to(&2, buf)); + try!(val.write_to(buf)); + }, + Value::String(ref val) => { + try!(u8::write_to(&3, buf)); + try!(val.write_to(buf)); + }, + Value::FormatComponent(ref val) => { + try!(u8::write_to(&4, buf)); + try!(val.write_to(buf)); + }, + Value::OptionalItemStack(ref val) => { + try!(u8::write_to(&5, buf)); + try!(val.write_to(buf)); + }, + Value::Bool(ref val) => { + try!(u8::write_to(&6, buf)); + try!(val.write_to(buf)); + }, + Value::Vector(ref val) => { + try!(u8::write_to(&7, buf)); + try!(val[0].write_to(buf)); + try!(val[1].write_to(buf)); + try!(val[2].write_to(buf)); + }, + Value::Position(ref val) => { + try!(u8::write_to(&8, buf)); + try!(val.write_to(buf)); + }, + Value::OptionalPosition(ref val) => { + try!(u8::write_to(&9, buf)); + try!(val.is_some().write_to(buf)); + try!(val.write_to(buf)); + }, + Value::Direction(ref val) => { + try!(u8::write_to(&10, buf)); + try!(val.write_to(buf)); + } + Value::OptionalUUID(ref val) => { + try!(u8::write_to(&11, buf)); + try!(val.is_some().write_to(buf)); + try!(val.write_to(buf)); + }, + Value::Block(ref val) => { + try!(u8::write_to(&11, buf)); + try!(protocol::VarInt(*val as i32).write_to(buf)); + } + } + } + try!(u8::write_to(&0xFF, buf)); + Ok(()) + } +} + +impl fmt::Debug for Metadata { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(write!(f, "Metadata[ ")); + for (k, v) in &self.map { + try!(write!(f, "{:?}={:?}, ", k, v)); + } + write!(f, "]") + } +} + +impl Default for Metadata { + fn default() -> Metadata { + Metadata::new() + } +} + +#[derive(Debug)] +enum Value { + Byte(i8), + Int(i32), + Float(f32), + String(String), + FormatComponent(format::Component), + OptionalItemStack(Option), + Bool(bool), + Vector([f32; 3]), + Position(super::Position), + OptionalPosition(Option), + Direction(protocol::VarInt), // TODO: Proper type + OptionalUUID(Option), + Block(u16), // TODO: Proper type +} + +pub trait MetaValue { + fn unwrap(&Value) -> &Self; + fn wrap(self) -> Value; +} + +impl MetaValue for i8 { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Byte(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Byte(self) + } +} + +impl MetaValue for i32 { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Int(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Int(self) + } +} + +impl MetaValue for f32 { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Float(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Float(self) + } +} + +impl MetaValue for String { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::String(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::String(self) + } +} + +impl MetaValue for format::Component { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::FormatComponent(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::FormatComponent(self) + } +} + +impl MetaValue for Option { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::OptionalItemStack(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::OptionalItemStack(self) + } +} + +impl MetaValue for bool { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Bool(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Bool(self) + } +} + +impl MetaValue for [f32; 3] { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Vector(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Vector(self) + } +} + +impl MetaValue for super::Position { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Position(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Position(self) + } +} + +impl MetaValue for Option { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::OptionalPosition(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::OptionalPosition(self) + } +} + +impl MetaValue for protocol::VarInt { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Direction(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Direction(self) + } +} + +impl MetaValue for Option { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::OptionalUUID(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::OptionalUUID(self) + } +} + +impl MetaValue for u16 { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Block(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Block(self) + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::marker::PhantomData; + + const TEST: MetadataKey = + MetadataKey { + index: 0, + ty: PhantomData, + }; + + #[test] + fn basic() { + let mut m = Metadata::new(); + + m.put(&TEST, "Hello world".to_owned()); + + match m.get(&TEST) { + Some(val) => { + assert!(val == "Hello world"); + } + None => panic!("failed"), + } + } +} diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs new file mode 100644 index 0000000..082cb1a --- /dev/null +++ b/protocol/src/types/mod.rs @@ -0,0 +1,6 @@ + +mod blockpos; +pub use self::blockpos::*; + +mod metadata; +pub use self::metadata::*; From 0f13be54fadc3885e897f3cb32867d484fe6213c Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Thu, 17 Sep 2015 16:04:25 +0100 Subject: [PATCH 010/160] Base of ui complete --- protocol/src/protocol/mod.rs | 13 ++++++------- protocol/src/protocol/mojang.rs | 8 +++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 2f7496e..45d74ef 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -1,9 +1,7 @@ #![allow(dead_code)] -extern crate byteorder; -extern crate hyper; -extern crate steven_openssl as openssl; -extern crate flate2; -extern crate serde_json; + +use openssl; +use serde_json; pub mod mojang; @@ -15,8 +13,9 @@ use std::net::TcpStream; use std::io; use std::io::{Write, Read}; use std::convert; -use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; -use self::flate2::read::{ZlibDecoder, ZlibEncoder}; +use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; +use flate2::read::{ZlibDecoder, ZlibEncoder}; +use flate2; /// Helper macro for defining packets #[macro_export] diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index e126793..c692e30 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -1,6 +1,8 @@ -extern crate steven_openssl as openssl; -extern crate serde_json; -extern crate hyper; + + +use openssl; +use serde_json; +use hyper; pub struct Profile { pub username: String, From b9d7063099ef031861cea60fe9328009737872a7 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Thu, 17 Sep 2015 16:04:25 +0100 Subject: [PATCH 011/160] Base of ui complete --- protocol/src/format.rs | 2 +- protocol/src/item.rs | 3 +-- protocol/src/nbt/mod.rs | 3 +-- protocol/src/types/metadata.rs | 1 + 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 26ec1e7..8cef4af 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -1,5 +1,5 @@ -extern crate serde_json; +use serde_json; use std::fmt; #[derive(Debug)] diff --git a/protocol/src/item.rs b/protocol/src/item.rs index 27661f2..2cdd046 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -1,10 +1,9 @@ -extern crate byteorder; use nbt; use protocol::{Serializable}; use std::io; use std::io::{Read, Write}; -use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; +use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; #[derive(Debug)] pub struct Stack { diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index 7abfd9e..9bfd5f7 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -1,4 +1,3 @@ -extern crate byteorder; use std::collections::HashMap; use std::io; @@ -6,7 +5,7 @@ use std::io::{Read, Write}; use super::protocol::{Serializable}; use super::protocol; -use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; +use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; #[derive(Debug)] pub enum Tag { diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 3b78b42..7ad261e 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -15,6 +15,7 @@ pub struct MetadataKey { } impl MetadataKey { + #[allow(dead_code)] // TODO: Make const later when possible /*const*/ fn new(index: i32) -> MetadataKey { MetadataKey { From 5681f1d69ca5fdbaec91ada6bb7e26b263d37183 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Thu, 17 Sep 2015 16:21:56 +0100 Subject: [PATCH 012/160] Add license --- protocol/src/protocol/mod.rs | 14 ++++++++++++++ protocol/src/protocol/mojang.rs | 14 +++++++++++++- protocol/src/protocol/packet.rs | 14 ++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 45d74ef..1aacef2 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -1,3 +1,17 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #![allow(dead_code)] use openssl; diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index c692e30..8798f85 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -1,4 +1,16 @@ - +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. use openssl; use serde_json; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index f201ba6..fad09f5 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1,3 +1,17 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use format; state_packets!( From 906a44d9d43a523e5593a547ee085b0d9656dcae Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Thu, 17 Sep 2015 16:21:56 +0100 Subject: [PATCH 013/160] Add license --- protocol/src/format.rs | 13 +++++++++++++ protocol/src/item.rs | 13 +++++++++++++ protocol/src/nbt/mod.rs | 13 +++++++++++++ protocol/src/types/blockpos.rs | 14 ++++++++++++++ protocol/src/types/metadata.rs | 13 +++++++++++++ protocol/src/types/mod.rs | 13 +++++++++++++ 6 files changed, 79 insertions(+) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 8cef4af..d68d7a6 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -1,3 +1,16 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. use serde_json; use std::fmt; diff --git a/protocol/src/item.rs b/protocol/src/item.rs index 2cdd046..5f143ad 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -1,3 +1,16 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. use nbt; use protocol::{Serializable}; diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index 9bfd5f7..4100448 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -1,3 +1,16 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. use std::collections::HashMap; use std::io; diff --git a/protocol/src/types/blockpos.rs b/protocol/src/types/blockpos.rs index 5f71af0..7e36c55 100644 --- a/protocol/src/types/blockpos.rs +++ b/protocol/src/types/blockpos.rs @@ -1,3 +1,17 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + extern crate byteorder; use std::fmt; diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 7ad261e..f4281b6 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -1,3 +1,16 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. use std::collections::HashMap; use std::marker::PhantomData; diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index 082cb1a..681b872 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -1,3 +1,16 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. mod blockpos; pub use self::blockpos::*; From 999dde1931060135fe658e339dfeb82e4073f3a6 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 18 Sep 2015 22:02:08 +0100 Subject: [PATCH 014/160] Base of ui system --- protocol/src/protocol/packet.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index fad09f5..9db8a03 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -998,7 +998,11 @@ impl Serializable for MapIcon { impl Default for MapIcon { fn default() -> Self { - MapIcon { .. Default::default() } + MapIcon { + direction_type: 0, + x: 0, + z: 0, + } } } From 63731ca4503c2d1e76efb81c8a8ee4a162c43ed0 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Mon, 21 Sep 2015 13:08:06 +0100 Subject: [PATCH 015/160] Basic logo work --- protocol/src/types/bit/map.rs | 90 +++++++++++++++++++++++++++++++++++ protocol/src/types/bit/mod.rs | 19 ++++++++ protocol/src/types/bit/set.rs | 56 ++++++++++++++++++++++ protocol/src/types/mod.rs | 2 + 4 files changed, 167 insertions(+) create mode 100644 protocol/src/types/bit/map.rs create mode 100644 protocol/src/types/bit/mod.rs create mode 100644 protocol/src/types/bit/set.rs diff --git a/protocol/src/types/bit/map.rs b/protocol/src/types/bit/map.rs new file mode 100644 index 0000000..8d289c0 --- /dev/null +++ b/protocol/src/types/bit/map.rs @@ -0,0 +1,90 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub struct Map { + bits: Vec, + bit_size: usize, + length: usize +} + +#[test] +fn test_map() { + let mut map = Map::new(4096, 4); + for i in 0 .. 4096 { + for j in 0 .. 16 { + map.set(i, j); + if map.get(i) != j { + panic!("Fail"); + } + } + } +} + +#[test] +fn test_map_odd() { + for size in 1 .. 16 { + let mut map = Map::new(64*3, size); + let max = (1 << size) - 1; + for i in 0 .. 64*3 { + for j in 0 .. max { + map.set(i, j); + if map.get(i) != j { + panic!("Index: {} wanted {} and got {}", i, j, map.get(i)); + } + } + } + } +} + +impl Map { + pub fn new(len: usize, size: usize) -> Map { + let mut map = Map { + bit_size: size, + length: len, + bits: Vec::with_capacity((len*size)/64) + }; + for _ in 0 .. len { + map.bits.push(0) + } + map + } + + pub fn set(&mut self, i: usize, val: usize) { + let i = i * self.bit_size; + let pos = i / 64; + let mask = (1 << self.bit_size) - 1; + let ii = i % 64; + self.bits[pos] = (self.bits[pos] & !(mask << ii )) | ((val << ii) as u64); + let pos2 = (i + self.bit_size - 1) / 64; + if pos2 != pos { + let used = 64 - ii; + let rem = self.bit_size - used; + self.bits[pos2] = self.bits[pos2] >> rem << rem | (val as u64 >> used); + } + } + + pub fn get(&mut self, i: usize) -> usize { + let i = i * self.bit_size; + let pos = i / 64; + let mask = (1 << self.bit_size) - 1; + let ii = i % 64; + let pos2 = (i + self.bit_size - 1) / 64; + if pos2 != pos { + let used = 64 - ii; + (((self.bits[pos] >> ii) | (self.bits[pos2] << used)) & mask) as usize + } else { + ((self.bits[pos] >> ii) & mask) as usize + } + } +} diff --git a/protocol/src/types/bit/mod.rs b/protocol/src/types/bit/mod.rs new file mode 100644 index 0000000..bc345c6 --- /dev/null +++ b/protocol/src/types/bit/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub mod set; +pub mod map; + +pub use self::set::Set; +pub use self::map::Map; diff --git a/protocol/src/types/bit/set.rs b/protocol/src/types/bit/set.rs new file mode 100644 index 0000000..995ce69 --- /dev/null +++ b/protocol/src/types/bit/set.rs @@ -0,0 +1,56 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub struct Set { + data : Vec +} + +#[test] +fn test_set() { + let mut set = Set::new(200); + for i in 0 .. 200 { + if i % 3 == 0 { + set.set(i, true) + } + } + for i in 0 .. 200 { + if set.get(i) != (i%3 == 0) { + panic!("Fail") + } + } +} + +impl Set { + pub fn new(size: usize) -> Set { + let mut set = Set { + data: Vec::with_capacity(size) + }; + for _ in 0 .. size { + set.data.push(0) + } + set + } + + pub fn set(&mut self, i: usize, v: bool) { + if v { + self.data[i>>6] |= 1 << (i & 0x3F) + } else { + self.data[i>>6] &= !(1 << (i & 0x3F)) + } + } + + pub fn get(&mut self, i: usize) -> bool { + return (self.data[i>>6] & (1 << (i & 0x3F))) != 0 + } +} diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index 681b872..034015f 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -17,3 +17,5 @@ pub use self::blockpos::*; mod metadata; pub use self::metadata::*; + +pub mod bit; \ No newline at end of file From 1920e9e9e27d7538e443b10920c18fb31fe33be7 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Wed, 23 Sep 2015 20:16:25 +0100 Subject: [PATCH 016/160] Base of server list --- protocol/src/format.rs | 103 ++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 47 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index d68d7a6..6cbe786 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -15,26 +15,27 @@ use serde_json; use std::fmt; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Component { Text(TextComponent) } impl Component { - pub fn from_value(v: &serde_json::Value) -> Self { - let modifier = Modifier::from_value(v); - if let Some(val) = v.as_string() { - Component::Text(TextComponent{text: val.to_owned(), modifier: modifier}) - } else if v.find("text").is_some(){ - Component::Text(TextComponent::from_value(v, modifier)) - } else { - Component::Text(TextComponent{text: "".to_owned(), modifier: modifier}) - } + pub fn from_value(v: &serde_json::Value) -> Self { + let mut modifier = Modifier::from_value(v); + if let Some(val) = v.as_string() { + Component::Text(TextComponent{text: val.to_owned(), modifier: modifier}) + } else if v.find("text").is_some(){ + Component::Text(TextComponent::from_value(v, modifier)) + } else { + modifier.color = Some(Color::RGB(255, 0, 0)); + Component::Text(TextComponent{text: "UNHANDLED".to_owned(), modifier: modifier}) } + } - pub fn to_value(&self) -> serde_json::Value { - unimplemented!() - } + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } } impl fmt::Display for Component { @@ -51,7 +52,7 @@ impl Default for Component { } } -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct Modifier { pub extra: Option>, pub bold: Option, @@ -67,50 +68,59 @@ pub struct Modifier { } impl Modifier { - pub fn from_value(v: &serde_json::Value) -> Self { - let mut m = Modifier { - bold: v.find("bold").map_or(Option::None, |v| v.as_boolean()), - italic: v.find("italic").map_or(Option::None, |v| v.as_boolean()), - underlined: v.find("underlined").map_or(Option::None, |v| v.as_boolean()), - strikethrough: v.find("strikethrough").map_or(Option::None, |v| v.as_boolean()), - obfuscated: v.find("obfuscated").map_or(Option::None, |v| v.as_boolean()), - color: v.find("color").map_or(Option::None, |v| v.as_string()).map(|v| Color::from_string(&v.to_owned())), - extra: Option::None, - }; - if let Some(extra) = v.find("extra") { - if let Some(data) = extra.as_array() { - let mut ex = Vec::new(); - for e in data { - ex.push(Component::from_value(e)); - } - m.extra = Some(ex); + pub fn from_value(v: &serde_json::Value) -> Self { + let mut m = Modifier { + bold: v.find("bold").map_or(Option::None, |v| v.as_boolean()), + italic: v.find("italic").map_or(Option::None, |v| v.as_boolean()), + underlined: v.find("underlined").map_or(Option::None, |v| v.as_boolean()), + strikethrough: v.find("strikethrough").map_or(Option::None, |v| v.as_boolean()), + obfuscated: v.find("obfuscated").map_or(Option::None, |v| v.as_boolean()), + color: v.find("color").map_or(Option::None, |v| v.as_string()).map(|v| Color::from_string(&v.to_owned())), + extra: Option::None, + }; + if let Some(extra) = v.find("extra") { + if let Some(data) = extra.as_array() { + let mut ex = Vec::new(); + for e in data { + ex.push(Component::from_value(e)); } + m.extra = Some(ex); } - m } + m + } - pub fn to_value(&self) -> serde_json::Value { - unimplemented!() - } + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TextComponent { pub text: String, - pub modifier: Modifier, + pub modifier: Modifier, } impl TextComponent { - pub fn from_value(v: &serde_json::Value, modifier: Modifier) -> Self { - TextComponent { - text: v.find("text").unwrap().as_string().unwrap_or("").to_owned(), - modifier: modifier, + pub fn new(val: &str) -> TextComponent { + TextComponent { + text: val.to_owned(), + modifier: Modifier { + .. Default::default() } } + } - pub fn to_value(&self) -> serde_json::Value { - unimplemented!() + pub fn from_value(v: &serde_json::Value, modifier: Modifier) -> Self { + TextComponent { + text: v.find("text").unwrap().as_string().unwrap_or("").to_owned(), + modifier: modifier, } + } + + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } } impl fmt::Display for TextComponent { @@ -125,7 +135,7 @@ impl fmt::Display for TextComponent { } } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum Color { Black, DarkBlue, @@ -194,7 +204,7 @@ impl Color { } } - fn to_string(&self) -> String { + pub fn to_string(&self) -> String { match *self { Color::Black => "black".to_owned(), Color::DarkBlue => "dark_blue".to_owned(), @@ -216,8 +226,7 @@ impl Color { } } - #[allow(dead_code)] - fn to_rgb(&self) -> (u8, u8, u8) { + pub fn to_rgb(&self) -> (u8, u8, u8) { match *self { Color::Black =>(0, 0, 0), Color::DarkBlue =>(0, 0, 170), From 1897492cf4b62901ebc450fdff35e7e231d3c7e3 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 25 Sep 2015 14:00:49 +0100 Subject: [PATCH 017/160] Kinda functional server list --- protocol/src/protocol/mod.rs | 102 +++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 1aacef2..483f314 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -30,6 +30,9 @@ use std::convert; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; use flate2::read::{ZlibDecoder, ZlibEncoder}; use flate2; +use time; + +pub const SUPPORTED_PROTOCOL: i32 = 73; /// Helper macro for defining packets #[macro_export] @@ -130,7 +133,7 @@ macro_rules! state_packets { pub mod packet; -pub trait Serializable { +pub trait Serializable: Sized { fn read_from(buf: &mut io::Read) -> Result; fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error>; } @@ -651,11 +654,14 @@ pub struct Conn { impl Conn { pub fn new(target: &str) -> Result{ // TODO SRV record support - let stream = match TcpStream::connect(target) { - Ok(val) => val, - Err(err) => return Result::Err(Error::IOError(err)) + let mut parts = target.split(":").collect::>(); + let address = if parts.len() == 1 { + parts.push("25565"); + format!("{}:25565", parts[0]) + } else { + format!("{}:{}", parts[0], parts[1]) }; - let parts = target.split(":").collect::>(); + let stream = try!(TcpStream::connect(&*address)); Result::Ok(Conn { stream: stream, host: parts[0].to_owned(), @@ -748,6 +754,91 @@ impl Conn { self.compression_read = Some(ZlibDecoder::new(io::Cursor::new(Vec::new()))); } } + + pub fn do_status(mut self) -> Result<(Status, time::Duration), Error>{ + use serde_json::Value; + use self::packet::status::serverbound::*; + use self::packet::handshake::serverbound::*; + use self::packet::Packet; + let host = self.host.clone(); + let port = self.port; + try!(self.write_packet(Handshake{ + protocol_version: VarInt(SUPPORTED_PROTOCOL), + host: host, + port: port, + next: VarInt(1), + })); + self.state = State::Status; + + try!(self.write_packet(StatusRequest{empty: ()})); + + let status = if let Packet::StatusResponse(res) = try!(self.read_packet()) { + res.status + } else { + return Err(Error::Err("Wrong packet".to_owned())); + }; + + let start = time::now(); + try!(self.write_packet(StatusPing{ping: 42})); + + if let Packet::StatusPong(_) = try!(self.read_packet()) { + } else { + return Err(Error::Err("Wrong packet".to_owned())); + }; + + let ping = time::now() - start; + + let val: Value = match serde_json::from_str(&status) { + Ok(val) => val, + Err(_) => return Err(Error::Err("Json parse error".to_owned())), + }; + + let invalid_status = || Error::Err("Invalid status".to_owned()); + + let version = try!(val.find("version").ok_or(invalid_status())); + let players = try!(val.find("players").ok_or(invalid_status())); + + Ok((Status { + version: StatusVersion { + name: try!(version.find("name").and_then(Value::as_string).ok_or(invalid_status())).to_owned(), + protocol: try!(version.find("protocol").and_then(Value::as_i64).ok_or(invalid_status())) as i32, + }, + players: StatusPlayers { + max: try!(players.find("max").and_then(Value::as_i64).ok_or(invalid_status())) as i32, + online: try!(players.find("online").and_then(Value::as_i64).ok_or(invalid_status())) as i32, + sample: Vec::new(), // TODO + }, + description: format::Component::from_value(try!(val.find("description").ok_or(invalid_status()))), + favicon: val.find("favicon").and_then(Value::as_string).map(|v| v.to_owned()), + }, ping)) + } +} + +#[derive(Debug)] +pub struct Status { + pub version: StatusVersion, + pub players: StatusPlayers, + pub description: format::Component, + pub favicon: Option, +} + +#[derive(Debug)] +pub struct StatusVersion { + pub name: String, + pub protocol: i32, +} + +#[derive(Debug)] +pub struct StatusPlayers { + pub max: i32, + pub online: i32, + pub sample: Vec, +} + +#[derive(Debug)] +pub struct StatusPlayer { + name: String, + id: String, } impl Read for Conn { @@ -805,6 +896,7 @@ pub trait PacketType { fn write(self, buf: &mut io::Write) -> Result<(), io::Error>; } +// REMOVE ME // #[test] pub fn test() { let mut c = Conn::new("localhost:25565").unwrap(); From 12847e9686b62deca376e2678a4cf5476ed896f9 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 25 Sep 2015 14:00:49 +0100 Subject: [PATCH 018/160] Kinda functional server list --- protocol/src/format.rs | 83 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 6cbe786..f0c9cba 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -14,6 +14,7 @@ use serde_json; use std::fmt; +use std::mem; #[derive(Debug, Clone)] pub enum Component { @@ -272,3 +273,85 @@ fn test_color_from() { _ => panic!("Wrong type"), } } + +const LEGACY_CHAR: char = '§'; + +pub fn convert_legacy(c: &mut Component) { + match c { + &mut Component::Text(ref mut txt) => { + if let Some(ref mut extra) = txt.modifier.extra.as_mut() { + for e in extra.iter_mut() { + convert_legacy(e); + } + } + if txt.text.contains(LEGACY_CHAR) { + let mut parts = Vec::new(); + let mut last = 0; + let mut current = TextComponent::new(""); + { + let mut iter = txt.text.char_indices(); + while let Some((i, c)) = iter.next() { + if c == LEGACY_CHAR { + let next = match iter.next() { + Some(val) => val, + None => break, + }; + let color_char = next.1.to_lowercase().next().unwrap(); + current.text = txt.text[last .. i].to_owned(); + last = next.0 + 1; + + let mut modifier = if (color_char >= 'a' && color_char <= 'f') || (color_char >= '0' && color_char <= '9') { + Default::default() + } else { + current.modifier.clone() + }; + + let new = TextComponent::new(""); + parts.push(Component::Text(mem::replace(&mut current, new))); + + match color_char { + '0' => modifier.color = Some(Color::Black), + '1' => modifier.color = Some(Color::DarkBlue), + '2' => modifier.color = Some(Color::DarkGreen), + '3' => modifier.color = Some(Color::DarkAqua), + '4' => modifier.color = Some(Color::DarkRed), + '5' => modifier.color = Some(Color::DarkPurple), + '6' => modifier.color = Some(Color::Gold), + '7' => modifier.color = Some(Color::Gray), + '8' => modifier.color = Some(Color::DarkGray), + '9' => modifier.color = Some(Color::Blue), + 'a' => modifier.color = Some(Color::Green), + 'b' => modifier.color = Some(Color::Aqua), + 'c' => modifier.color = Some(Color::Red), + 'd' => modifier.color = Some(Color::LightPurple), + 'e' => modifier.color = Some(Color::Yellow), + 'f' => modifier.color = Some(Color::White), + 'k' => modifier.obfuscated = Some(true), + 'l' => modifier.bold = Some(true), + 'm' => modifier.strikethrough = Some(true), + 'n' => modifier.underlined = Some(true), + 'o' => modifier.italic = Some(true), + 'r' => {}, + _ => unimplemented!(), + } + + current.modifier = modifier; + } + } + } + if last < txt.text.len() { + current.text = txt.text[last..].to_owned(); + parts.push(Component::Text(current)); + } + + let old = mem::replace(&mut txt.modifier.extra, Some(parts)); + if let Some(old_extra) = old { + if let Some(ref mut extra) = txt.modifier.extra.as_mut() { + extra.extend(old_extra); + } + } + txt.text = "".to_owned(); + } + }, + } +} \ No newline at end of file From c9f7cf384f36d53b00c305968801b8e5f694a0d3 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 25 Sep 2015 15:20:55 +0100 Subject: [PATCH 019/160] Clean up --- protocol/src/protocol/mojang.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 8798f85..542de20 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -38,7 +38,7 @@ impl Profile { if negative { twos_compliment(&mut hash); } - let hash_str = hash.iter().map(|b| format!("{:02X}", b)).collect::>().connect(""); + let hash_str = hash.iter().map(|b| format!("{:02X}", b)).collect::>().join(""); let hash_val = hash_str.trim_matches('0'); let hash_str = if negative { "-".to_owned() + &hash_val[..] From 70a3683df2a11fb03319078c27c7b0bb72f061d1 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 25 Sep 2015 15:20:55 +0100 Subject: [PATCH 020/160] Clean up --- protocol/src/types/bit/map.rs | 10 +++++++++- protocol/src/types/blockpos.rs | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/protocol/src/types/bit/map.rs b/protocol/src/types/bit/map.rs index 8d289c0..b1b9e05 100644 --- a/protocol/src/types/bit/map.rs +++ b/protocol/src/types/bit/map.rs @@ -60,6 +60,14 @@ impl Map { map } + pub fn resize(self, size: usize) -> Map { + let mut n = Map::new(self.length, size); + for i in 0 .. self.length { + n.set(i, self.get(i)); + } + n + } + pub fn set(&mut self, i: usize, val: usize) { let i = i * self.bit_size; let pos = i / 64; @@ -74,7 +82,7 @@ impl Map { } } - pub fn get(&mut self, i: usize) -> usize { + pub fn get(&self, i: usize) -> usize { let i = i * self.bit_size; let pos = i / 64; let mask = (1 << self.bit_size) - 1; diff --git a/protocol/src/types/blockpos.rs b/protocol/src/types/blockpos.rs index 7e36c55..cc38604 100644 --- a/protocol/src/types/blockpos.rs +++ b/protocol/src/types/blockpos.rs @@ -24,6 +24,7 @@ use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; pub struct Position(u64); impl Position { + #[allow(dead_code)] fn new(x: i32, y: i32, z: i32) -> Position { Position( (((x as u64) & 0x3FFFFFF) << 38) | From ec9159a75b04b03e363680ac5e1d9f23692fb618 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sun, 27 Sep 2015 19:38:58 +0100 Subject: [PATCH 021/160] Minor changes --- protocol/src/protocol/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 483f314..1bc06d2 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -32,7 +32,7 @@ use flate2::read::{ZlibDecoder, ZlibEncoder}; use flate2; use time; -pub const SUPPORTED_PROTOCOL: i32 = 73; +pub const SUPPORTED_PROTOCOL: i32 = 74; /// Helper macro for defining packets #[macro_export] @@ -647,7 +647,7 @@ pub struct Conn { cipher: Option, compression_threshold: i32, - compression_read: Option>>>, // Pending reset support + compression_read: Option>>>, compression_write: Option>>>, } @@ -890,7 +890,7 @@ impl Clone for Conn { } } -pub trait PacketType { +pub trait PacketType: Sized { fn packet_id(&self) -> i32; fn write(self, buf: &mut io::Write) -> Result<(), io::Error>; From dc810c15dd49c9b2b1ce17195b8d4bc4bf5277a4 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Tue, 29 Sep 2015 20:09:36 +0100 Subject: [PATCH 022/160] Tidy up --- protocol/src/format.rs | 4 ++-- protocol/src/types/bit/set.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index f0c9cba..50dc3f9 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -277,8 +277,8 @@ fn test_color_from() { const LEGACY_CHAR: char = '§'; pub fn convert_legacy(c: &mut Component) { - match c { - &mut Component::Text(ref mut txt) => { + match *c { + Component::Text(ref mut txt) => { if let Some(ref mut extra) = txt.modifier.extra.as_mut() { for e in extra.iter_mut() { convert_legacy(e); diff --git a/protocol/src/types/bit/set.rs b/protocol/src/types/bit/set.rs index 995ce69..e74f6c5 100644 --- a/protocol/src/types/bit/set.rs +++ b/protocol/src/types/bit/set.rs @@ -51,6 +51,6 @@ impl Set { } pub fn get(&mut self, i: usize) -> bool { - return (self.data[i>>6] & (1 << (i & 0x3F))) != 0 + (self.data[i>>6] & (1 << (i & 0x3F))) != 0 } } From 46adad05552aa91d2259cd5e49f31a83a3b94001 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Tue, 29 Sep 2015 20:09:36 +0100 Subject: [PATCH 023/160] Tidy up --- protocol/src/protocol/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 1bc06d2..25734cf 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -353,7 +353,7 @@ pub struct LenPrefixed { impl LenPrefixed { fn new(data: Vec) -> LenPrefixed { - return LenPrefixed { + LenPrefixed { len: Default::default(), data: data, } @@ -374,7 +374,7 @@ impl Serializable for LenPrefixed { fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { let len_data : L = L::from(self.data.len()); try!(len_data.write_to(buf)); - let ref data = self.data; + let data = &self.data; for val in data { try!(val.write_to(buf)); } @@ -406,7 +406,7 @@ pub struct LenPrefixedBytes { impl LenPrefixedBytes { fn new(data: Vec) -> LenPrefixedBytes { - return LenPrefixedBytes { + LenPrefixedBytes { len: Default::default(), data: data, } @@ -621,18 +621,18 @@ impl convert::From for Error { impl ::std::error::Error for Error { fn description(&self) -> &str { - match self { - &Error::Err(ref val) => &val[..], - &Error::IOError(ref e) => e.description(), + match *self { + Error::Err(ref val) => &val[..], + Error::IOError(ref e) => e.description(), } } } impl ::std::fmt::Display for Error { fn fmt(&self, f : &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match self { - &Error::Err(ref val) => write!(f, "protocol error: {}", val), - &Error::IOError(ref e) => e.fmt(f) + match *self { + Error::Err(ref val) => write!(f, "protocol error: {}", val), + Error::IOError(ref e) => e.fmt(f) } } } From 35306c62e1852c2599c3a05c82ed93eb0cb00a4e Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Wed, 7 Oct 2015 19:36:59 +0100 Subject: [PATCH 024/160] Reformat using rustfmt --- protocol/src/format.rs | 88 ++++++++++++----------- protocol/src/item.rs | 6 +- protocol/src/nbt/mod.rs | 123 +++++++++++++++++---------------- protocol/src/types/bit/map.rs | 22 +++--- protocol/src/types/bit/set.rs | 20 +++--- protocol/src/types/blockpos.rs | 9 +-- protocol/src/types/metadata.rs | 49 ++++++------- protocol/src/types/mod.rs | 2 +- 8 files changed, 160 insertions(+), 159 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 50dc3f9..49c12ba 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -18,19 +18,25 @@ use std::mem; #[derive(Debug, Clone)] pub enum Component { - Text(TextComponent) + Text(TextComponent), } impl Component { pub fn from_value(v: &serde_json::Value) -> Self { let mut modifier = Modifier::from_value(v); if let Some(val) = v.as_string() { - Component::Text(TextComponent{text: val.to_owned(), modifier: modifier}) - } else if v.find("text").is_some(){ + Component::Text(TextComponent { + text: val.to_owned(), + modifier: modifier, + }) + } else if v.find("text").is_some() { Component::Text(TextComponent::from_value(v, modifier)) } else { modifier.color = Some(Color::RGB(255, 0, 0)); - Component::Text(TextComponent{text: "UNHANDLED".to_owned(), modifier: modifier}) + Component::Text(TextComponent { + text: "UNHANDLED".to_owned(), + modifier: modifier, + }) } } @@ -49,7 +55,10 @@ impl fmt::Display for Component { impl Default for Component { fn default() -> Self { - Component::Text(TextComponent{text: "".to_owned(), modifier: Default::default()}) + Component::Text(TextComponent { + text: "".to_owned(), + modifier: Default::default(), + }) } } @@ -62,12 +71,10 @@ pub struct Modifier { pub strikethrough: Option, pub obfuscated: Option, pub color: Option, - - // click_event - // hover_event - // insertion } +// TODO: Missing events click/hover/insert + impl Modifier { pub fn from_value(v: &serde_json::Value) -> Self { let mut m = Modifier { @@ -76,7 +83,9 @@ impl Modifier { underlined: v.find("underlined").map_or(Option::None, |v| v.as_boolean()), strikethrough: v.find("strikethrough").map_or(Option::None, |v| v.as_boolean()), obfuscated: v.find("obfuscated").map_or(Option::None, |v| v.as_boolean()), - color: v.find("color").map_or(Option::None, |v| v.as_string()).map(|v| Color::from_string(&v.to_owned())), + color: v.find("color") + .map_or(Option::None, |v| v.as_string()) + .map(|v| Color::from_string(&v.to_owned())), extra: Option::None, }; if let Some(extra) = v.find("extra") { @@ -106,9 +115,7 @@ impl TextComponent { pub fn new(val: &str) -> TextComponent { TextComponent { text: val.to_owned(), - modifier: Modifier { - .. Default::default() - } + modifier: Modifier { ..Default::default() }, } } @@ -154,7 +161,7 @@ pub enum Color { LightPurple, Yellow, White, - RGB(u8, u8, u8) + RGB(u8, u8, u8), } impl fmt::Display for Color { @@ -195,11 +202,7 @@ impl Color { Ok(b) => b, Err(_) => return Color::White, }; - Color::RGB( - r, - g, - b - ) + Color::RGB(r, g, b) } _ => Color::White, } @@ -229,22 +232,22 @@ impl Color { pub fn to_rgb(&self) -> (u8, u8, u8) { match *self { - Color::Black =>(0, 0, 0), - Color::DarkBlue =>(0, 0, 170), - Color::DarkGreen =>(0, 170, 0), - Color::DarkAqua =>(0, 170, 170), - Color::DarkRed =>(170, 0, 0), - Color::DarkPurple =>(170, 0, 170), - Color::Gold =>(255, 170, 0), - Color::Gray =>(170, 170, 170), - Color::DarkGray =>(85, 85, 85), - Color::Blue =>(85, 85, 255), - Color::Green =>(85, 255, 85), - Color::Aqua =>(85, 255, 255), - Color::Red =>(255, 85, 85), - Color::LightPurple =>(255, 85, 255), - Color::Yellow =>(255, 255, 85), - Color::White =>(255, 255, 255), + Color::Black => (0, 0, 0), + Color::DarkBlue => (0, 0, 170), + Color::DarkGreen => (0, 170, 0), + Color::DarkAqua => (0, 170, 170), + Color::DarkRed => (170, 0, 0), + Color::DarkPurple => (170, 0, 170), + Color::Gold => (255, 170, 0), + Color::Gray => (170, 170, 170), + Color::DarkGray => (85, 85, 85), + Color::Blue => (85, 85, 255), + Color::Green => (85, 255, 85), + Color::Aqua => (85, 255, 255), + Color::Red => (255, 85, 85), + Color::LightPurple => (255, 85, 255), + Color::Yellow => (255, 255, 85), + Color::White => (255, 255, 255), Color::RGB(r, g, b) => (r, g, b), } } @@ -264,12 +267,12 @@ fn test_color_from() { } let test = Color::from_string(&"red".to_owned()); match test { - Color::Red => {}, + Color::Red => {} _ => panic!("Wrong type"), } let test = Color::from_string(&"dark_blue".to_owned()); match test { - Color::DarkBlue => {}, + Color::DarkBlue => {} _ => panic!("Wrong type"), } } @@ -297,10 +300,11 @@ pub fn convert_legacy(c: &mut Component) { None => break, }; let color_char = next.1.to_lowercase().next().unwrap(); - current.text = txt.text[last .. i].to_owned(); + current.text = txt.text[last..i].to_owned(); last = next.0 + 1; - let mut modifier = if (color_char >= 'a' && color_char <= 'f') || (color_char >= '0' && color_char <= '9') { + let mut modifier = if (color_char >= 'a' && color_char <= 'f') || + (color_char >= '0' && color_char <= '9') { Default::default() } else { current.modifier.clone() @@ -331,7 +335,7 @@ pub fn convert_legacy(c: &mut Component) { 'm' => modifier.strikethrough = Some(true), 'n' => modifier.underlined = Some(true), 'o' => modifier.italic = Some(true), - 'r' => {}, + 'r' => {} _ => unimplemented!(), } @@ -352,6 +356,6 @@ pub fn convert_legacy(c: &mut Component) { } txt.text = "".to_owned(); } - }, + } } -} \ No newline at end of file +} diff --git a/protocol/src/item.rs b/protocol/src/item.rs index 5f143ad..a6ab829 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -13,7 +13,7 @@ // limitations under the License. use nbt; -use protocol::{Serializable}; +use protocol::Serializable; use std::io; use std::io::{Read, Write}; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; @@ -44,7 +44,7 @@ impl Serializable for Option { if id == -1 { return Ok(None); } - Ok(Some(Stack{ + Ok(Some(Stack { id: id as isize, count: try!(buf.read_u8()) as isize, damage: try!(buf.read_i16::()) as isize, @@ -58,7 +58,7 @@ impl Serializable for Option { try!(buf.write_u8(val.count as u8)); try!(buf.write_i16::(val.damage as i16)); try!(val.tag.write_to(buf)); - }, + } None => try!(buf.write_i16::(-1)), } Result::Ok(()) diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index 4100448..32d4a64 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -16,7 +16,7 @@ use std::collections::HashMap; use std::io; use std::io::{Read, Write}; -use super::protocol::{Serializable}; +use super::protocol::Serializable; use super::protocol; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; @@ -173,52 +173,53 @@ impl Tag { } fn read_type(id: u8, buf: &mut io::Read) -> Result { - match id { - 0 => unreachable!(), - 1 => Ok(Tag::Byte(try!(buf.read_i8()))), - 2 => Ok(Tag::Short(try!(buf.read_i16::()))), - 3 => Ok(Tag::Int(try!(buf.read_i32::()))), - 4 => Ok(Tag::Long(try!(buf.read_i64::()))), - 5 => Ok(Tag::Float(try!(buf.read_f32::()))), - 6 => Ok(Tag::Double(try!(buf.read_f64::()))), - 7 => Ok(Tag::ByteArray({ - let len : i32 = try!(Serializable::read_from(buf)); - let mut data = Vec::with_capacity(len as usize); - try!(buf.take(len as u64).read_to_end(&mut data)); - data - })), - 8 => Ok(Tag::String(try!(read_string(buf)))), - 9 => { - let mut l = Vec::new(); - let ty = try!(buf.read_u8()); - let len : i32 = try!(Serializable::read_from(buf)); - for _ in 0 .. len { - l.push(try!(Tag::read_type(ty, buf))); - } - Ok(Tag::List(l)) - }, - 10 => { - let mut c = Tag::new_compound(); - loop { - let ty = try!(buf.read_u8()); - if ty == 0 { - break; - } - let name: String = try!(read_string(buf)); - c.put(&name[..], try!(Tag::read_type(ty, buf))); - } - Ok(c) - }, - 11 => Ok(Tag::IntArray({ - let len : i32 = try!(Serializable::read_from(buf)); - let mut data = Vec::with_capacity(len as usize); - for _ in 0 .. len { - data.push(try!(buf.read_i32::())); - } - data - })), - _ => Err(io::Error::new(io::ErrorKind::InvalidData, protocol::Error::Err("invalid tag".to_owned()))), + match id { + 0 => unreachable!(), + 1 => Ok(Tag::Byte(try!(buf.read_i8()))), + 2 => Ok(Tag::Short(try!(buf.read_i16::()))), + 3 => Ok(Tag::Int(try!(buf.read_i32::()))), + 4 => Ok(Tag::Long(try!(buf.read_i64::()))), + 5 => Ok(Tag::Float(try!(buf.read_f32::()))), + 6 => Ok(Tag::Double(try!(buf.read_f64::()))), + 7 => Ok(Tag::ByteArray({ + let len: i32 = try!(Serializable::read_from(buf)); + let mut data = Vec::with_capacity(len as usize); + try!(buf.take(len as u64).read_to_end(&mut data)); + data + })), + 8 => Ok(Tag::String(try!(read_string(buf)))), + 9 => { + let mut l = Vec::new(); + let ty = try!(buf.read_u8()); + let len: i32 = try!(Serializable::read_from(buf)); + for _ in 0..len { + l.push(try!(Tag::read_type(ty, buf))); + } + Ok(Tag::List(l)) } + 10 => { + let mut c = Tag::new_compound(); + loop { + let ty = try!(buf.read_u8()); + if ty == 0 { + break; + } + let name: String = try!(read_string(buf)); + c.put(&name[..], try!(Tag::read_type(ty, buf))); + } + Ok(c) + } + 11 => Ok(Tag::IntArray({ + let len: i32 = try!(Serializable::read_from(buf)); + let mut data = Vec::with_capacity(len as usize); + for _ in 0..len { + data.push(try!(buf.read_i32::())); + } + data + })), + _ => Err(io::Error::new(io::ErrorKind::InvalidData, + protocol::Error::Err("invalid tag".to_owned()))), + } } } @@ -229,7 +230,7 @@ impl Serializable for Tag { fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { match *self { - Tag::End => {}, + Tag::End => {} Tag::Byte(val) => try!(buf.write_i8(val)), Tag::Short(val) => try!(buf.write_i16::(val)), Tag::Int(val) => try!(buf.write_i32::(val)), @@ -239,20 +240,20 @@ impl Serializable for Tag { Tag::ByteArray(ref val) => { try!((val.len() as i32).write_to(buf)); try!(buf.write_all(val)); - }, + } Tag::String(ref val) => try!(write_string(buf, val)), Tag::List(ref val) => { - if val.is_empty() { - try!(buf.write_u8(0)); - try!(buf.write_i32::(0)); - } else { - try!(buf.write_u8(val[0].internal_id())); - try!(buf.write_i32::(val.len() as i32)); - for e in val { - try!(e.write_to(buf)); - } + if val.is_empty() { + try!(buf.write_u8(0)); + try!(buf.write_i32::(0)); + } else { + try!(buf.write_u8(val[0].internal_id())); + try!(buf.write_i32::(val.len() as i32)); + for e in val { + try!(e.write_to(buf)); } - }, + } + } Tag::Compound(ref val) => { for (k, v) in val { try!(v.internal_id().write_to(buf)); @@ -260,19 +261,19 @@ impl Serializable for Tag { try!(v.write_to(buf)); } try!(buf.write_u8(0)); - }, + } Tag::IntArray(ref val) => { try!((val.len() as i32).write_to(buf)); for v in val { try!(v.write_to(buf)); } - }, + } } Result::Ok(()) } } -pub fn write_string(buf: &mut io::Write, s: &String)-> io::Result<()> { +pub fn write_string(buf: &mut io::Write, s: &String) -> io::Result<()> { let data = s.as_bytes(); try!((data.len() as i16).write_to(buf)); buf.write_all(data) diff --git a/protocol/src/types/bit/map.rs b/protocol/src/types/bit/map.rs index b1b9e05..4814f2a 100644 --- a/protocol/src/types/bit/map.rs +++ b/protocol/src/types/bit/map.rs @@ -15,14 +15,14 @@ pub struct Map { bits: Vec, bit_size: usize, - length: usize + length: usize, } #[test] fn test_map() { let mut map = Map::new(4096, 4); - for i in 0 .. 4096 { - for j in 0 .. 16 { + for i in 0..4096 { + for j in 0..16 { map.set(i, j); if map.get(i) != j { panic!("Fail"); @@ -33,11 +33,11 @@ fn test_map() { #[test] fn test_map_odd() { - for size in 1 .. 16 { - let mut map = Map::new(64*3, size); + for size in 1..16 { + let mut map = Map::new(64 * 3, size); let max = (1 << size) - 1; - for i in 0 .. 64*3 { - for j in 0 .. max { + for i in 0..64 * 3 { + for j in 0..max { map.set(i, j); if map.get(i) != j { panic!("Index: {} wanted {} and got {}", i, j, map.get(i)); @@ -52,9 +52,9 @@ impl Map { let mut map = Map { bit_size: size, length: len, - bits: Vec::with_capacity((len*size)/64) + bits: Vec::with_capacity((len * size) / 64), }; - for _ in 0 .. len { + for _ in 0..len { map.bits.push(0) } map @@ -62,7 +62,7 @@ impl Map { pub fn resize(self, size: usize) -> Map { let mut n = Map::new(self.length, size); - for i in 0 .. self.length { + for i in 0..self.length { n.set(i, self.get(i)); } n @@ -73,7 +73,7 @@ impl Map { let pos = i / 64; let mask = (1 << self.bit_size) - 1; let ii = i % 64; - self.bits[pos] = (self.bits[pos] & !(mask << ii )) | ((val << ii) as u64); + self.bits[pos] = (self.bits[pos] & !(mask << ii)) | ((val << ii) as u64); let pos2 = (i + self.bit_size - 1) / 64; if pos2 != pos { let used = 64 - ii; diff --git a/protocol/src/types/bit/set.rs b/protocol/src/types/bit/set.rs index e74f6c5..123b0f9 100644 --- a/protocol/src/types/bit/set.rs +++ b/protocol/src/types/bit/set.rs @@ -13,19 +13,19 @@ // limitations under the License. pub struct Set { - data : Vec + data: Vec, } #[test] fn test_set() { let mut set = Set::new(200); - for i in 0 .. 200 { + for i in 0..200 { if i % 3 == 0 { set.set(i, true) } } - for i in 0 .. 200 { - if set.get(i) != (i%3 == 0) { + for i in 0..200 { + if set.get(i) != (i % 3 == 0) { panic!("Fail") } } @@ -33,10 +33,8 @@ fn test_set() { impl Set { pub fn new(size: usize) -> Set { - let mut set = Set { - data: Vec::with_capacity(size) - }; - for _ in 0 .. size { + let mut set = Set { data: Vec::with_capacity(size) }; + for _ in 0..size { set.data.push(0) } set @@ -44,13 +42,13 @@ impl Set { pub fn set(&mut self, i: usize, v: bool) { if v { - self.data[i>>6] |= 1 << (i & 0x3F) + self.data[i >> 6] |= 1 << (i & 0x3F) } else { - self.data[i>>6] &= !(1 << (i & 0x3F)) + self.data[i >> 6] &= !(1 << (i & 0x3F)) } } pub fn get(&mut self, i: usize) -> bool { - (self.data[i>>6] & (1 << (i & 0x3F))) != 0 + (self.data[i >> 6] & (1 << (i & 0x3F))) != 0 } } diff --git a/protocol/src/types/blockpos.rs b/protocol/src/types/blockpos.rs index cc38604..8455811 100644 --- a/protocol/src/types/blockpos.rs +++ b/protocol/src/types/blockpos.rs @@ -15,7 +15,7 @@ extern crate byteorder; use std::fmt; -use protocol::{Serializable}; +use protocol::Serializable; use std::io; use std::io::{Read, Write}; use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; @@ -26,11 +26,8 @@ pub struct Position(u64); impl Position { #[allow(dead_code)] fn new(x: i32, y: i32, z: i32) -> Position { - Position( - (((x as u64) & 0x3FFFFFF) << 38) | - (((y as u64) & 0xFFF) << 26) | - ((z as u64) & 0x3FFFFFF) - ) + Position((((x as u64) & 0x3FFFFFF) << 38) | (((y as u64) & 0xFFF) << 26) | + ((z as u64) & 0x3FFFFFF)) } fn get_x(&self) -> i32 { diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index f4281b6..b7443f3 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -18,7 +18,7 @@ use std::io; use std::io::{Read, Write}; use std::fmt; use protocol; -use protocol::{Serializable}; +use protocol::Serializable; use format; use item; @@ -30,7 +30,7 @@ pub struct MetadataKey { impl MetadataKey { #[allow(dead_code)] // TODO: Make const later when possible - /*const*/ fn new(index: i32) -> MetadataKey { + /*const*/ fn new(index: i32) -> MetadataKey { MetadataKey { index: index, ty: PhantomData, @@ -78,11 +78,10 @@ impl Serializable for Metadata { 4 => m.put_raw(index, try!(format::Component::read_from(buf))), 5 => m.put_raw(index, try!(Option::::read_from(buf))), 6 => m.put_raw(index, try!(bool::read_from(buf))), - 7 => m.put_raw(index, [ - try!(f32::read_from(buf)), - try!(f32::read_from(buf)), - try!(f32::read_from(buf)) - ]), + 7 => m.put_raw(index, + [try!(f32::read_from(buf)), + try!(f32::read_from(buf)), + try!(f32::read_from(buf))]), 8 => m.put_raw(index, try!(super::Position::read_from(buf))), 9 => { if try!(bool::read_from(buf)) { @@ -90,7 +89,7 @@ impl Serializable for Metadata { } else { m.put_raw::>(index, None); } - }, + } 10 => m.put_raw(index, try!(protocol::VarInt::read_from(buf))), 11 => { if try!(bool::read_from(buf)) { @@ -98,9 +97,11 @@ impl Serializable for Metadata { } else { m.put_raw::>(index, None); } - }, + } 12 => m.put_raw(index, try!(protocol::VarInt::read_from(buf)).0 as u16), - _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, protocol::Error::Err("unknown metadata type".to_owned()))), + _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, + protocol::Error::Err("unknown metadata type" + .to_owned()))), } } Ok(m) @@ -113,46 +114,46 @@ impl Serializable for Metadata { Value::Byte(ref val) => { try!(u8::write_to(&0, buf)); try!(val.write_to(buf)); - }, + } Value::Int(ref val) => { try!(u8::write_to(&1, buf)); try!(protocol::VarInt(*val).write_to(buf)); - }, + } Value::Float(ref val) => { try!(u8::write_to(&2, buf)); try!(val.write_to(buf)); - }, + } Value::String(ref val) => { try!(u8::write_to(&3, buf)); try!(val.write_to(buf)); - }, + } Value::FormatComponent(ref val) => { try!(u8::write_to(&4, buf)); try!(val.write_to(buf)); - }, + } Value::OptionalItemStack(ref val) => { try!(u8::write_to(&5, buf)); try!(val.write_to(buf)); - }, + } Value::Bool(ref val) => { try!(u8::write_to(&6, buf)); try!(val.write_to(buf)); - }, + } Value::Vector(ref val) => { try!(u8::write_to(&7, buf)); try!(val[0].write_to(buf)); try!(val[1].write_to(buf)); try!(val[2].write_to(buf)); - }, + } Value::Position(ref val) => { try!(u8::write_to(&8, buf)); try!(val.write_to(buf)); - }, + } Value::OptionalPosition(ref val) => { try!(u8::write_to(&9, buf)); try!(val.is_some().write_to(buf)); try!(val.write_to(buf)); - }, + } Value::Direction(ref val) => { try!(u8::write_to(&10, buf)); try!(val.write_to(buf)); @@ -161,7 +162,7 @@ impl Serializable for Metadata { try!(u8::write_to(&11, buf)); try!(val.is_some().write_to(buf)); try!(val.write_to(buf)); - }, + } Value::Block(ref val) => { try!(u8::write_to(&11, buf)); try!(protocol::VarInt(*val as i32).write_to(buf)); @@ -374,9 +375,9 @@ mod test { const TEST: MetadataKey = MetadataKey { - index: 0, - ty: PhantomData, - }; + index: 0, + ty: PhantomData, + }; #[test] fn basic() { diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index 034015f..ca88128 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -18,4 +18,4 @@ pub use self::blockpos::*; mod metadata; pub use self::metadata::*; -pub mod bit; \ No newline at end of file +pub mod bit; From d9e9ddc2b2d886e5f60922e99ec6713bbd74b377 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Wed, 7 Oct 2015 19:36:59 +0100 Subject: [PATCH 025/160] Reformat using rustfmt --- protocol/src/protocol/mod.rs | 225 +++++++++++++++++++------------- protocol/src/protocol/mojang.rs | 21 +-- protocol/src/protocol/packet.rs | 70 ++++++---- 3 files changed, 186 insertions(+), 130 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 25734cf..10f19b1 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -139,7 +139,7 @@ pub trait Serializable: Sized { } impl Serializable for Vec { - fn read_from(buf: &mut io::Read) -> Result , io::Error> { + fn read_from(buf: &mut io::Read) -> Result, io::Error> { let mut v = Vec::new(); try!(buf.read_to_end(&mut v)); Ok(v) @@ -206,7 +206,7 @@ impl Serializable for format::Component { let len = try!(VarInt::read_from(buf)).0; let mut ret = String::new(); try!(buf.take(len as u64).read_to_string(&mut ret)); - let val : serde_json::Value = serde_json::from_str(&ret[..]).unwrap(); + let val: serde_json::Value = serde_json::from_str(&ret[..]).unwrap(); Result::Ok(Self::from_value(&val)) } fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { @@ -232,7 +232,11 @@ impl Serializable for bool { Result::Ok(try!(buf.read_u8()) != 0) } fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { - try!(buf.write_u8(if *self { 1 } else { 0 })); + try!(buf.write_u8(if *self { + 1 + } else { + 0 + })); Result::Ok(()) } } @@ -321,17 +325,15 @@ impl Serializable for f64 { pub struct UUID(u64, u64); impl Default for UUID { - fn default() -> Self { UUID(0, 0) } + fn default() -> Self { + UUID(0, 0) + } } impl Serializable for UUID { fn read_from(buf: &mut io::Read) -> Result { - Result::Ok( - UUID( - try!(buf.read_u64::()), - try!(buf.read_u64::()), - ) - ) + Result::Ok(UUID(try!(buf.read_u64::()), + try!(buf.read_u64::()))) } fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { try!(buf.write_u64::(self.0)); @@ -348,7 +350,7 @@ pub trait Lengthable : Serializable + Copy + Default { pub struct LenPrefixed { len: L, - pub data: Vec + pub data: Vec, } impl LenPrefixed { @@ -362,17 +364,20 @@ impl LenPrefixed { impl Serializable for LenPrefixed { fn read_from(buf: &mut io::Read) -> Result, io::Error> { - let len_data : L = try!(Serializable::read_from(buf)); - let len : usize = len_data.into(); - let mut data : Vec = Vec::with_capacity(len); - for _ in 0 .. len { + let len_data: L = try!(Serializable::read_from(buf)); + let len: usize = len_data.into(); + let mut data: Vec = Vec::with_capacity(len); + for _ in 0..len { data.push(try!(Serializable::read_from(buf))); } - Result::Ok(LenPrefixed{len: len_data, data: data}) + Result::Ok(LenPrefixed { + len: len_data, + data: data, + }) } fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { - let len_data : L = L::from(self.data.len()); + let len_data: L = L::from(self.data.len()); try!(len_data.write_to(buf)); let data = &self.data; for val in data { @@ -387,7 +392,7 @@ impl Default for LenPrefixed { fn default() -> Self { LenPrefixed { len: default::Default::default(), - data: default::Default::default() + data: default::Default::default(), } } } @@ -401,7 +406,7 @@ impl fmt::Debug for LenPrefixed { // Optimization pub struct LenPrefixedBytes { len: L, - pub data: Vec + pub data: Vec, } impl LenPrefixedBytes { @@ -415,15 +420,18 @@ impl LenPrefixedBytes { impl Serializable for LenPrefixedBytes { fn read_from(buf: &mut io::Read) -> Result, io::Error> { - let len_data : L = try!(Serializable::read_from(buf)); - let len : usize = len_data.into(); - let mut data : Vec = Vec::with_capacity(len); + let len_data: L = try!(Serializable::read_from(buf)); + let len: usize = len_data.into(); + let mut data: Vec = Vec::with_capacity(len); try!(buf.take(len as u64).read_to_end(&mut data)); - Result::Ok(LenPrefixedBytes{len: len_data, data: data}) + Result::Ok(LenPrefixedBytes { + len: len_data, + data: data, + }) } fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { - let len_data : L = L::from(self.data.len()); + let len_data: L = L::from(self.data.len()); try!(len_data.write_to(buf)); try!(buf.write_all(&self.data[..])); Result::Ok(()) @@ -435,7 +443,7 @@ impl Default for LenPrefixedBytes { fn default() -> Self { LenPrefixedBytes { len: default::Default::default(), - data: default::Default::default() + data: default::Default::default(), } } } @@ -490,9 +498,10 @@ impl Serializable for VarInt { loop { let b = try!(buf.read_u8()) as u32; val |= (b & PART) << (size * 7); - size+=1; + size += 1; if size > 5 { - return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, Error::Err("VarInt too big".to_owned()))) + return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, + Error::Err("VarInt too big".to_owned()))) } if (b & 0x80) == 0 { break @@ -518,7 +527,9 @@ impl Serializable for VarInt { } impl default::Default for VarInt { - fn default() -> VarInt { VarInt(0) } + fn default() -> VarInt { + VarInt(0) + } } impl fmt::Debug for VarInt { @@ -551,9 +562,10 @@ impl Serializable for VarLong { loop { let b = try!(buf.read_u8()) as u64; val |= (b & PART) << (size * 7); - size+=1; + size += 1; if size > 10 { - return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, Error::Err("VarLong too big".to_owned()))) + return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, + Error::Err("VarLong too big".to_owned()))) } if (b & 0x80) == 0 { break @@ -579,7 +591,9 @@ impl Serializable for VarLong { } impl default::Default for VarLong { - fn default() -> VarLong { VarLong(0) } + fn default() -> VarLong { + VarLong(0) + } } impl fmt::Debug for VarLong { @@ -593,7 +607,7 @@ impl fmt::Debug for VarLong { #[derive(Clone, Copy)] pub enum Direction { Serverbound, - Clientbound + Clientbound, } /// The protocol has multiple 'sub-protocols' or states which control which @@ -603,7 +617,7 @@ pub enum State { Handshaking, Play, Status, - Login + Login, } /// Return for any protocol related error. @@ -614,7 +628,7 @@ pub enum Error { } impl convert::From for Error { - fn from(e : io::Error) -> Error { + fn from(e: io::Error) -> Error { Error::IOError(e) } } @@ -629,10 +643,10 @@ impl ::std::error::Error for Error { } impl ::std::fmt::Display for Error { - fn fmt(&self, f : &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { Error::Err(ref val) => write!(f, "protocol error: {}", val), - Error::IOError(ref e) => e.fmt(f) + Error::IOError(ref e) => e.fmt(f), } } } @@ -647,12 +661,12 @@ pub struct Conn { cipher: Option, compression_threshold: i32, - compression_read: Option>>>, + compression_read: Option>>>, compression_write: Option>>>, } impl Conn { - pub fn new(target: &str) -> Result{ + pub fn new(target: &str) -> Result { // TODO SRV record support let mut parts = target.split(":").collect::>(); let address = if parts.len() == 1 { @@ -680,7 +694,11 @@ impl Conn { try!(VarInt(packet.packet_id()).write_to(&mut buf)); try!(packet.write(&mut buf)); - let mut extra = if self.compression_threshold >= 0 { 1 } else { 0 }; + let mut extra = if self.compression_threshold >= 0 { + 1 + } else { + 0 + }; if self.compression_threshold >= 0 && buf.len() as i32 > self.compression_threshold { extra = 0; let uncompressed_size = buf.len(); @@ -734,11 +752,14 @@ impl Conn { let pos = buf.position() as usize; let ibuf = buf.into_inner(); if ibuf.len() != pos { - return Result::Err(Error::Err(format!("Failed to read all of packet 0x{:X}, had {} bytes left", id, ibuf.len() - pos))) + return Result::Err(Error::Err(format!("Failed to read all of packet 0x{:X}, \ + had {} bytes left", + id, + ibuf.len() - pos))) } Result::Ok(val) - }, - None => Result::Err(Error::Err("missing packet".to_owned())) + } + None => Result::Err(Error::Err("missing packet".to_owned())), } } @@ -749,28 +770,29 @@ impl Conn { pub fn set_compresssion(&mut self, threshold: i32, read: bool) { self.compression_threshold = threshold; if !read { - self.compression_write = Some(ZlibEncoder::new(io::Cursor::new(Vec::new()), flate2::Compression::Default)); + self.compression_write = Some(ZlibEncoder::new(io::Cursor::new(Vec::new()), + flate2::Compression::Default)); } else { self.compression_read = Some(ZlibDecoder::new(io::Cursor::new(Vec::new()))); } } - pub fn do_status(mut self) -> Result<(Status, time::Duration), Error>{ + pub fn do_status(mut self) -> Result<(Status, time::Duration), Error> { use serde_json::Value; use self::packet::status::serverbound::*; use self::packet::handshake::serverbound::*; use self::packet::Packet; let host = self.host.clone(); let port = self.port; - try!(self.write_packet(Handshake{ + try!(self.write_packet(Handshake { protocol_version: VarInt(SUPPORTED_PROTOCOL), host: host, port: port, next: VarInt(1), })); self.state = State::Status; - - try!(self.write_packet(StatusRequest{empty: ()})); + + try!(self.write_packet(StatusRequest { empty: () })); let status = if let Packet::StatusResponse(res) = try!(self.read_packet()) { res.status @@ -779,7 +801,7 @@ impl Conn { }; let start = time::now(); - try!(self.write_packet(StatusPing{ping: 42})); + try!(self.write_packet(StatusPing { ping: 42 })); if let Packet::StatusPong(_) = try!(self.read_packet()) { } else { @@ -800,17 +822,26 @@ impl Conn { Ok((Status { version: StatusVersion { - name: try!(version.find("name").and_then(Value::as_string).ok_or(invalid_status())).to_owned(), - protocol: try!(version.find("protocol").and_then(Value::as_i64).ok_or(invalid_status())) as i32, + name: try!(version.find("name").and_then(Value::as_string).ok_or(invalid_status())) + .to_owned(), + protocol: try!(version.find("protocol") + .and_then(Value::as_i64) + .ok_or(invalid_status())) as i32, }, players: StatusPlayers { - max: try!(players.find("max").and_then(Value::as_i64).ok_or(invalid_status())) as i32, - online: try!(players.find("online").and_then(Value::as_i64).ok_or(invalid_status())) as i32, - sample: Vec::new(), // TODO + max: try!(players.find("max") + .and_then(Value::as_i64) + .ok_or(invalid_status())) as i32, + online: try!(players.find("online") + .and_then(Value::as_i64) + .ok_or(invalid_status())) as i32, + sample: Vec::new(), /* TODO */ }, - description: format::Component::from_value(try!(val.find("description").ok_or(invalid_status()))), + description: format::Component::from_value(try!(val.find("description") + .ok_or(invalid_status()))), favicon: val.find("favicon").and_then(Value::as_string).map(|v| v.to_owned()), - }, ping)) + }, + ping)) } } @@ -848,11 +879,11 @@ impl Read for Conn { Option::Some(cipher) => { let ret = try!(self.stream.read(buf)); let data = cipher.decrypt(&buf[..ret]); - for i in 0 .. ret { + for i in 0..ret { buf[i] = data[i]; } Ok(ret) - }, + } } } } @@ -865,7 +896,7 @@ impl Write for Conn { let data = cipher.encrypt(buf); try!(self.stream.write_all(&data[..])); Ok(buf.len()) - }, + } } } @@ -901,14 +932,16 @@ pub trait PacketType: Sized { pub fn test() { let mut c = Conn::new("localhost:25565").unwrap(); - c.write_packet(packet::handshake::serverbound::Handshake{ - protocol_version: VarInt(71), - host: "localhost".to_owned(), - port: 25565, - next: VarInt(2), - }).unwrap(); + c.write_packet(packet::handshake::serverbound::Handshake { + protocol_version: VarInt(71), + host: "localhost".to_owned(), + port: 25565, + next: VarInt(2), + }) + .unwrap(); c.state = State::Login; - c.write_packet(packet::login::serverbound::LoginStart{username: "Think".to_owned()}).unwrap(); + c.write_packet(packet::login::serverbound::LoginStart { username: "Think".to_owned() }) + .unwrap(); let packet = match c.read_packet().unwrap() { packet::Packet::EncryptionRequest(val) => val, @@ -921,18 +954,19 @@ pub fn test() { let shared_e = key.encrypt(&shared); let token_e = key.encrypt(&packet.verify_token.data); - let profile = mojang::Profile{ + let profile = mojang::Profile { username: "Think".to_owned(), id: "b1184d43168441cfa2128b9a3df3b6ab".to_owned(), - access_token: "".to_owned() + access_token: "".to_owned(), }; profile.join_server(&packet.server_id, &shared, &packet.public_key.data); - c.write_packet(packet::login::serverbound::EncryptionResponse{ - shared_secret: LenPrefixedBytes::new(shared_e), - verify_token: LenPrefixedBytes::new(token_e), - }).unwrap(); + c.write_packet(packet::login::serverbound::EncryptionResponse { + shared_secret: LenPrefixedBytes::new(shared_e), + verify_token: LenPrefixedBytes::new(token_e), + }) + .unwrap(); let mut read = c.clone(); let mut write = c.clone(); @@ -940,35 +974,39 @@ pub fn test() { read.enable_encyption(&shared, true); write.enable_encyption(&shared, false); - loop { match read.read_packet().unwrap() { - packet::Packet::LoginDisconnect(val) => { - panic!("Discconect {}", val.reason); - }, - packet::Packet::SetInitialCompression(val) => { - read.set_compresssion(val.threshold.0, true); - write.set_compresssion(val.threshold.0, false); - println!("Compression: {}", val.threshold.0) - }, - packet::Packet::LoginSuccess(val) => { - println!("Login: {} {}", val.username, val.uuid); - read.state = State::Play; - write.state = State::Play; - break; + loop { + match read.read_packet().unwrap() { + packet::Packet::LoginDisconnect(val) => { + panic!("Discconect {}", val.reason); + } + packet::Packet::SetInitialCompression(val) => { + read.set_compresssion(val.threshold.0, true); + write.set_compresssion(val.threshold.0, false); + println!("Compression: {}", val.threshold.0) + } + packet::Packet::LoginSuccess(val) => { + println!("Login: {} {}", val.username, val.uuid); + read.state = State::Play; + write.state = State::Play; + break; + } + _ => panic!("Unknown packet"), } - _ => panic!("Unknown packet"), - } } + } let mut first = true; let mut count = 0; - loop { match read.read_packet().unwrap() { + loop { + match read.read_packet().unwrap() { packet::Packet::ServerMessage(val) => println!("MSG: {}", val.message), - packet::Packet::ChunkData(_) => {}, + packet::Packet::ChunkData(_) => {} val => { println!("{:?}", val); if first { - write.write_packet(packet::play::serverbound::ChatMessage{ - message: "Hello world".to_owned(), - }).unwrap(); + write.write_packet(packet::play::serverbound::ChatMessage { + message: "Hello world".to_owned(), + }) + .unwrap(); first = false; } count += 1; @@ -976,7 +1014,8 @@ pub fn test() { break; } } - } } + } + } unimplemented!(); } diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 542de20..71a21a6 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -19,7 +19,7 @@ use hyper; pub struct Profile { pub username: String, pub id: String, - pub access_token: String + pub access_token: String, } const JOIN_URL: &'static str = "https://sessionserver.mojang.com/session/minecraft/join"; @@ -34,7 +34,7 @@ impl Profile { // Mojang uses a hex method which allows for // negatives so we have to account for that. - let negative = hash[0] & 0x80 == 0x80; + let negative = hash[0] & 0x80 == 0x80; if negative { twos_compliment(&mut hash); } @@ -47,17 +47,18 @@ impl Profile { }; let join_msg = serde_json::builder::ObjectBuilder::new() - .insert("accessToken", &self.access_token) - .insert("selectedProfile", &self.id) - .insert("serverId", hash_str) - .unwrap(); + .insert("accessToken", &self.access_token) + .insert("selectedProfile", &self.id) + .insert("serverId", hash_str) + .unwrap(); let join = serde_json::to_string(&join_msg).unwrap(); let client = hyper::Client::new(); let res = client.post(JOIN_URL) - .body(&join) - .header(hyper::header::ContentType("application/json".parse().unwrap())) - .send().unwrap(); + .body(&join) + .header(hyper::header::ContentType("application/json".parse().unwrap())) + .send() + .unwrap(); let ret: serde_json::Value = match serde_json::from_reader(res) { Result::Ok(val) => val, @@ -69,7 +70,7 @@ impl Profile { fn twos_compliment(data: &mut Vec) { let mut carry = true; - for i in (0 .. data.len()).rev() { + for i in (0..data.len()).rev() { data[i] = !data[i]; if carry { carry = data[i] == 0xFF; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 9db8a03..944ada8 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -91,7 +91,7 @@ state_packets!( button: u8 =, action_number: u16 =, mode: u8 =, - clicked_item: Option =, // TODO + clicked_item: Option =, } // CloseWindow is sent when the client closes a window. CloseWindow => 0x07 { @@ -936,7 +936,7 @@ pub struct BlockChangeRecord { impl Serializable for BlockChangeRecord { fn read_from(buf: &mut io::Read) -> Result { - Ok(BlockChangeRecord{ + Ok(BlockChangeRecord { xz: try!(Serializable::read_from(buf)), y: try!(Serializable::read_from(buf)), block_id: try!(Serializable::read_from(buf)), @@ -959,7 +959,7 @@ pub struct ExplosionRecord { impl Serializable for ExplosionRecord { fn read_from(buf: &mut io::Read) -> Result { - Ok(ExplosionRecord{ + Ok(ExplosionRecord { x: try!(Serializable::read_from(buf)), y: try!(Serializable::read_from(buf)), z: try!(Serializable::read_from(buf)), @@ -982,7 +982,7 @@ pub struct MapIcon { impl Serializable for MapIcon { fn read_from(buf: &mut io::Read) -> Result { - Ok(MapIcon{ + Ok(MapIcon { direction_type: try!(Serializable::read_from(buf)), x: try!(Serializable::read_from(buf)), z: try!(Serializable::read_from(buf)), @@ -998,7 +998,7 @@ impl Serializable for MapIcon { impl Default for MapIcon { fn default() -> Self { - MapIcon { + MapIcon { direction_type: 0, x: 0, z: 0, @@ -1015,7 +1015,7 @@ pub struct EntityProperty { impl Serializable for EntityProperty { fn read_from(buf: &mut io::Read) -> Result { - Ok(EntityProperty{ + Ok(EntityProperty { key: try!(Serializable::read_from(buf)), value: try!(Serializable::read_from(buf)), modifiers: try!(Serializable::read_from(buf)), @@ -1038,7 +1038,7 @@ pub struct PropertyModifier { impl Serializable for PropertyModifier { fn read_from(buf: &mut io::Read) -> Result { - Ok(PropertyModifier{ + Ok(PropertyModifier { uuid: try!(Serializable::read_from(buf)), amount: try!(Serializable::read_from(buf)), operation: try!(Serializable::read_from(buf)), @@ -1060,19 +1060,19 @@ pub struct PlayerInfoData { impl Serializable for PlayerInfoData { fn read_from(buf: &mut io::Read) -> Result { - let mut m = PlayerInfoData{ + let mut m = PlayerInfoData { action: try!(Serializable::read_from(buf)), players: Vec::new(), }; let len = try!(VarInt::read_from(buf)); - for _ in 0 .. len.0 { - let uuid = try!(UUID::read_from(buf)); + for _ in 0..len.0 { + let uuid = try!(UUID::read_from(buf)); match m.action.0 { 0 => { let name = try!(String::read_from(buf)); let mut props = Vec::new(); let plen = try!(VarInt::read_from(buf)).0; - for _ in 0 .. plen { + for _ in 0..plen { let mut prop = PlayerProperty { name: try!(String::read_from(buf)), value: try!(String::read_from(buf)), @@ -1098,21 +1098,21 @@ impl Serializable for PlayerInfoData { }, }; m.players.push(p); - }, + } 1 => { - m.players.push(PlayerDetail::UpdateGamemode{ + m.players.push(PlayerDetail::UpdateGamemode { uuid: uuid, gamemode: try!(Serializable::read_from(buf)), }) - }, + } 2 => { - m.players.push(PlayerDetail::UpdateLatency{ + m.players.push(PlayerDetail::UpdateLatency { uuid: uuid, ping: try!(Serializable::read_from(buf)), }) - }, + } 3 => { - m.players.push(PlayerDetail::UpdateDisplayName{ + m.players.push(PlayerDetail::UpdateDisplayName { uuid: uuid, display: { if try!(bool::read_from(buf)) { @@ -1122,12 +1122,10 @@ impl Serializable for PlayerInfoData { } }, }) - }, + } 4 => { - m.players.push(PlayerDetail::Remove{ - uuid: uuid, - }) - }, + m.players.push(PlayerDetail::Remove { uuid: uuid }) + } _ => panic!(), } } @@ -1150,11 +1148,29 @@ impl Default for PlayerInfoData { #[derive(Debug)] pub enum PlayerDetail { - Add{uuid: UUID, name: String, properties: Vec, gamemode: VarInt, ping: VarInt, display: Option}, - UpdateGamemode{uuid: UUID, gamemode: VarInt}, - UpdateLatency{uuid: UUID, ping: VarInt}, - UpdateDisplayName{uuid: UUID, display: Option}, - Remove{uuid: UUID}, + Add { + uuid: UUID, + name: String, + properties: Vec, + gamemode: VarInt, + ping: VarInt, + display: Option, + }, + UpdateGamemode { + uuid: UUID, + gamemode: VarInt, + }, + UpdateLatency { + uuid: UUID, + ping: VarInt, + }, + UpdateDisplayName { + uuid: UUID, + display: Option, + }, + Remove { + uuid: UUID, + }, } #[derive(Debug)] From 75654bbc661ee5742e8dc32a10b4b16486899017 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Wed, 16 Mar 2016 18:01:33 +0000 Subject: [PATCH 026/160] Clean up --- protocol/src/item.rs | 1 - protocol/src/types/metadata.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/protocol/src/item.rs b/protocol/src/item.rs index a6ab829..a26e57d 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -15,7 +15,6 @@ use nbt; use protocol::Serializable; use std::io; -use std::io::{Read, Write}; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; #[derive(Debug)] diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index b7443f3..1fbd49f 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -15,7 +15,7 @@ use std::collections::HashMap; use std::marker::PhantomData; use std::io; -use std::io::{Read, Write}; +use std::io::Write; use std::fmt; use protocol; use protocol::Serializable; @@ -191,7 +191,7 @@ impl Default for Metadata { } #[derive(Debug)] -enum Value { +pub enum Value { Byte(i8), Int(i32), Float(f32), From 6d34f11989a3bc28375e3491f551b2e707b30b5d Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Wed, 16 Mar 2016 18:01:33 +0000 Subject: [PATCH 027/160] Clean up --- protocol/src/protocol/mod.rs | 4 +- protocol/src/protocol/packet.rs | 226 ++++++++++++++++---------------- 2 files changed, 115 insertions(+), 115 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 10f19b1..a8b8e2a 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -40,7 +40,7 @@ macro_rules! state_packets { ($($state:ident $stateName:ident { $($dir:ident $dirName:ident { $( - $name:ident => $id:expr { + $name:ident => id($id:expr) { $($field:ident: $field_type:ty = $(when ($cond:expr))*, )+ })* })+ @@ -79,7 +79,7 @@ macro_rules! state_packets { impl PacketType for $name { - fn packet_id(&self) -> i32{ $id } + fn packet_id(&self) -> i32 { $id } fn write(self, buf: &mut io::Write) -> Result<(), io::Error> { $( diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 944ada8..3de1a3f 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -32,7 +32,7 @@ state_packets!( // than the hostname due to the protocol not providing // any system for custom information to be transfered // by the client to the server until after login. - Handshake => 0x00 { + Handshake => id(0x00) { // The protocol version of the connecting client protocol_version: VarInt =, // The hostname the client connected to @@ -50,22 +50,22 @@ state_packets!( serverbound Serverbound { // TabComplete is sent by the client when the client presses tab in // the chat box. - TabComplete => 0x00 { + TabComplete => id(0x00) { text: String =, has_target: bool =, target: Option = when(|p: &TabComplete| p.has_target == true), } // ChatMessage is sent by the client when it sends a chat message or // executes a command (prefixed by '/'). - ChatMessage => 0x01 { + ChatMessage => id(0x01) { message: String =, } // ClientStatus is sent to update the client's status - ClientStatus => 0x02 { + ClientStatus => id(0x02) { action_id: VarInt =, } // ClientSettings is sent by the client to update its current settings. - ClientSettings => 0x03 { + ClientSettings => id(0x03) { locale: String =, view_distance: u8 =, chat_mode: u8 =, @@ -74,18 +74,18 @@ state_packets!( main_hand: VarInt =, } // ConfirmTransactionServerbound is a reply to ConfirmTransaction. - ConfirmTransactionServerbound => 0x04 { + ConfirmTransactionServerbound => id(0x04) { id: u8 =, action_number: i16 =, accepted: bool =, } // EnchantItem is sent when the client enchants an item. - EnchantItem => 0x05 { + EnchantItem => id(0x05) { id: u8 =, enchantment: u8 =, } // ClickWindow is sent when the client clicks in a window. - ClickWindow => 0x06 { + ClickWindow => id(0x06) { id: u8 =, slot: i16 =, button: u8 =, @@ -94,19 +94,19 @@ state_packets!( clicked_item: Option =, } // CloseWindow is sent when the client closes a window. - CloseWindow => 0x07 { + CloseWindow => id(0x07) { id: u8 =, } // PluginMessageServerbound is used for custom messages between the client // and server. This is mainly for plugins/mods but vanilla has a few channels // registered too. - PluginMessageServerbound => 0x08 { + PluginMessageServerbound => id(0x08) { channel: String =, data: Vec =, } // UseEntity is sent when the user interacts (right clicks) or attacks // (left clicks) an entity. - UseEntity => 0x09 { + UseEntity => id(0x09) { target_id: VarInt =, ty: VarInt =, target_x: f32 = when(|p: &UseEntity| p.ty.0 == 2), @@ -117,11 +117,11 @@ state_packets!( // KeepAliveServerbound is sent by a client as a response to a // KeepAliveClientbound. If the client doesn't reply the server // may disconnect the client. - KeepAliveServerbound => 0x0A { + KeepAliveServerbound => id(0x0A) { id: VarInt =, } // PlayerPosition is used to update the player's position. - PlayerPosition => 0x0B { + PlayerPosition => id(0x0B) { x: f64 =, y: f64 =, z: f64 =, @@ -129,7 +129,7 @@ state_packets!( } // PlayerPositionLook is a combination of PlayerPosition and // PlayerLook. - PlayerPositionLook => 0x0C { + PlayerPositionLook => id(0x0C) { x: f64 =, y: f64 =, z: f64 =, @@ -138,61 +138,61 @@ state_packets!( on_ground: bool =, } // PlayerLook is used to update the player's rotation. - PlayerLook => 0x0D { + PlayerLook => id(0x0D) { yaw: f32 =, pitch: f32 =, on_ground: bool =, } // Player is used to update whether the player is on the ground or not. - Player => 0x0E { + Player => id(0x0E) { on_ground: bool =, } // ClientAbilities is used to modify the players current abilities. // Currently flying is the only one - ClientAbilities => 0x0F { + ClientAbilities => id(0x0F) { flags: u8 =, flying_speed: f32 =, walking_speed: f32 =, } // PlayerDigging is sent when the client starts/stops digging a block. // It also can be sent for droppping items and eating/shooting. - PlayerDigging => 0x10 { + PlayerDigging => id(0x10) { status: u8 =, location: types::Position =, face: u8 =, } // PlayerAction is sent when a player preforms various actions. - PlayerAction => 0x11 { + PlayerAction => id(0x11) { entity_id: VarInt =, action_id: VarInt =, jump_boost: VarInt =, } // SteerVehicle is sent by the client when steers or preforms an action // on a vehicle. - SteerVehicle => 0x12 { + SteerVehicle => id(0x12) { sideways: f32 =, forward: f32 =, flags: u8 =, } // ResourcePackStatus informs the server of the client's current progress // in activating the requested resource pack - ResourcePackStatus => 0x13 { + ResourcePackStatus => id(0x13) { hash: String =, result: VarInt =, } // HeldItemChange is sent when the player changes the currently active // hotbar slot. - HeldItemChange => 0x14 { + HeldItemChange => id(0x14) { slot: i16 =, } // CreativeInventoryAction is sent when the client clicks in the creative // inventory. This is used to spawn items in creative. - CreativeInventoryAction => 0x15 { + CreativeInventoryAction => id(0x15) { slot: i16 =, clicked_item: Option =, } // SetSign sets the text on a sign after placing it. - SetSign => 0x16 { + SetSign => id(0x16) { location: types::Position =, line1: String =, line2: String =, @@ -201,15 +201,15 @@ state_packets!( } // ArmSwing is sent by the client when the player left clicks (to swing their // arm). - ArmSwing => 0x17 { + ArmSwing => id(0x17) { hand: VarInt =, } // SpectateTeleport is sent by clients in spectator mode to teleport to a player. - SpectateTeleport => 0x18 { + SpectateTeleport => id(0x18) { target: UUID =, } // PlayerBlockPlacement is sent when the client tries to place a block. - PlayerBlockPlacement => 0x19 { + PlayerBlockPlacement => id(0x19) { location: types::Position =, face: VarInt =, hand: VarInt =, @@ -218,14 +218,14 @@ state_packets!( cursor_z: u8 =, } // UseItem is sent when the client tries to use an item. - UseItem => 0x1A { + UseItem => id(0x1A) { hand: VarInt =, } } clientbound Clientbound { // SpawnObject is used to spawn an object or vehicle into the world when it // is in range of the client. - SpawnObject => 0x00 { + SpawnObject => id(0x00) { entity_id: VarInt =, uuid: UUID =, ty: u8 =, @@ -242,7 +242,7 @@ state_packets!( // 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. - SpawnExperienceOrb => 0x01 { + SpawnExperienceOrb => id(0x01) { entity_id: VarInt =, x: i32 =, y: i32 =, @@ -251,7 +251,7 @@ state_packets!( } // SpawnGlobalEntity spawns an entity which is visible from anywhere in the // world. Currently only used for lightning. - SpawnGlobalEntity => 0x02 { + SpawnGlobalEntity => id(0x02) { entity_id: VarInt =, ty: u8 =, x: i32 =, @@ -260,7 +260,7 @@ state_packets!( } // SpawnMob is used to spawn a living entity into the world when it is in // range of the client. - SpawnMob => 0x03 { + SpawnMob => id(0x03) { entity_id: VarInt =, uuid: UUID =, ty: u8 =, @@ -277,7 +277,7 @@ state_packets!( } // 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. - SpawnPainting => 0x04 { + SpawnPainting => id(0x04) { entity_id: VarInt =, title: String =, location: types::Position =, @@ -286,7 +286,7 @@ state_packets!( // SpawnPlayer is used to spawn a player when they are in range of the client. // This packet alone isn't enough to display the player as the skin and username // information is in the player information packet. - SpawnPlayer => 0x05 { + SpawnPlayer => id(0x05) { entity_id: VarInt =, uuid: UUID =, x: i32 =, @@ -297,44 +297,44 @@ state_packets!( metadata: types::Metadata =, } // Animation is sent by the server to play an animation on a specific entity. - Animation => 0x06 { + Animation => id(0x06) { entity_id: VarInt =, animation_id: u8 =, } // Statistics is used to update the statistics screen for the client. - Statistics => 0x07 { + Statistics => id(0x07) { statistices: LenPrefixed =, } // BlockBreakAnimation is used to create and update the block breaking // animation played when a player starts digging a block. - BlockBreakAnimation => 0x08 { + BlockBreakAnimation => id(0x08) { entity_id: VarInt =, location: types::Position =, stage: i8 =, } // UpdateBlockEntity updates the nbt tag of a block entity in the // world. - UpdateBlockEntity => 0x09 { + UpdateBlockEntity => id(0x09) { location: types::Position =, action: u8 =, nbt: Option =, } // BlockAction triggers different actions depending on the target block. - BlockAction => 0x0A { + BlockAction => id(0x0A) { location: types::Position =, byte1: u8 =, byte2: u8 =, block_type: VarInt =, } // BlockChange is used to update a single block on the client. - BlockChange => 0x0B { + BlockChange => id(0x0B) { location: types::Position =, block_id: VarInt =, } // BossBar displays and/or changes a boss bar that is displayed on the // top of the client's screen. This is normally used for bosses such as // the ender dragon or the wither. - BossBar => 0x0C { + BossBar => id(0x0C) { uuid: UUID =, action: VarInt =, title: format::Component = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 3), @@ -345,45 +345,45 @@ state_packets!( } // ServerDifficulty changes the displayed difficulty in the client's menu // as well as some ui changes for hardcore. - ServerDifficulty => 0x0D { + ServerDifficulty => id(0x0D) { difficulty: u8 =, } // TabCompleteReply is sent as a reply to a tab completion request. // The matches should be possible completions for the command/chat the // player sent. - TabCompleteReply => 0x0E { + TabCompleteReply => id(0x0E) { matches: LenPrefixed =, } // 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. - ServerMessage => 0x0F { + ServerMessage => id(0x0F) { message: format::Component =, // 0 - Chat message, 1 - System message, 2 - Action bar message position: u8 =, } // MultiBlockChange is used to update a batch of blocks in a single packet. - MultiBlockChange => 0x10 { + MultiBlockChange => id(0x10) { chunk_x: i32 =, chunk_y: i32 =, records: LenPrefixed =, } // ConfirmTransaction notifies the client whether a transaction was successful // or failed (e.g. due to lag). - ConfirmTransaction => 0x11 { + ConfirmTransaction => id(0x11) { id: u8 =, action_number: i16 =, accepted: bool =, } // WindowClose forces the client to close the window with the given id, // e.g. a chest getting destroyed. - WindowClose => 0x12 { + WindowClose => id(0x12) { id: u8 =, } // WindowOpen tells the client to open the inventory window of the given // type. The ID is used to reference the instance of the window in // other packets. - WindowOpen => 0x13 { + WindowOpen => id(0x13) { id: u8 =, ty: String =, title: format::Component =, @@ -391,48 +391,48 @@ state_packets!( entity_id: i32 = when(|p: &WindowOpen| p.ty == "EntityHorse"), } // WindowItems sets every item in a window. - WindowItems => 0x14 { + WindowItems => id(0x14) { id: u8 =, items: LenPrefixed> =, } // WindowProperty changes the value of a property of a window. Properties // vary depending on the window type. - WindowProperty => 0x15 { + WindowProperty => id(0x15) { id: u8 =, property: i16 =, value: i16 =, } // WindowSetSlot changes an itemstack in one of the slots in a window. - WindowSetSlot => 0x16 { + WindowSetSlot => id(0x16) { id: u8 =, property: i16 =, item: Option =, } // SetCooldown disables a set item (by id) for the set number of ticks - SetCooldown => 0x17 { + SetCooldown => id(0x17) { item_id: VarInt =, ticks: VarInt =, } // PluginMessageClientbound is used for custom messages between the client // and server. This is mainly for plugins/mods but vanilla has a few channels // registered too. - PluginMessageClientbound => 0x18 { + PluginMessageClientbound => id(0x18) { channel: String =, data: Vec =, } // Disconnect causes the client to disconnect displaying the passed reason. - Disconnect => 0x19 { + Disconnect => id(0x19) { reason: format::Component =, } // EntityAction causes an entity to preform an action based on the passed // id. - EntityAction => 0x1A { + EntityAction => id(0x1A) { entity_id: i32 =, action_id: u8 =, } // Explosion is sent when an explosion is triggered (tnt, creeper etc). // This plays the effect and removes the effected blocks. - Explosion => 0x1B { + Explosion => id(0x1B) { x: f32 =, y: f32 =, z: f32 =, @@ -444,17 +444,17 @@ state_packets!( } // ChunkUnload tells the client to unload the chunk at the specified // position. - ChunkUnload => 0x1C { + ChunkUnload => id(0x1C) { x: i32 =, z: i32 =, } // SetCompression updates the compression threshold. - SetCompression => 0x1D { + SetCompression => id(0x1D) { threshold: VarInt =, } // ChangeGameState is used to modify the game's state like gamemode or // weather. - ChangeGameState => 0x1E { + ChangeGameState => id(0x1E) { reason: u8 =, value: f32 =, } @@ -462,12 +462,12 @@ state_packets!( // client is still responding and keep the connection open. // The client should reply with the KeepAliveServerbound // packet setting ID to the same as this one. - KeepAliveClientbound => 0x1F { + KeepAliveClientbound => id(0x1F) { id: VarInt =, } // ChunkData sends or updates a single chunk on the client. If New is set // then biome data should be sent too. - ChunkData => 0x20 { + ChunkData => id(0x20) { chunk_x: i32 =, chunk_z: i32 =, new: bool =, @@ -477,7 +477,7 @@ state_packets!( // 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. - Effect => 0x21 { + Effect => id(0x21) { effect_id: i32 =, location: types::Position =, data: i32 =, @@ -485,7 +485,7 @@ state_packets!( } // Particle spawns particles at the target location with the various // modifiers. - Particle => 0x22 { + Particle => id(0x22) { particle_id: i32 =, long_distance: bool =, x: f32 =, @@ -500,7 +500,7 @@ state_packets!( data2: VarInt = when(|p: &Particle| p.particle_id == 36), } // SoundEffect plays the named sound at the target location. - SoundEffect => 0x23 { + SoundEffect => id(0x23) { name: String =, x: i32 =, y: i32 =, @@ -510,7 +510,7 @@ state_packets!( } // JoinGame is sent after completing the login process. This // sets the initial state for the client. - JoinGame => 0x24 { + JoinGame => id(0x24) { // The entity id the client will be referenced by entity_id: i32 =, // The starting gamemode of the client @@ -528,7 +528,7 @@ state_packets!( reduced_debug_info: bool =, } // Maps updates a single map's contents - Maps => 0x25 { + Maps => id(0x25) { item_damage: VarInt =, scale: i8 =, tracking_position: bool =, @@ -540,7 +540,7 @@ state_packets!( data: Option> = when(|p: &Maps| p.columns > 0), } // EntityMove moves the entity with the id by the offsets provided. - EntityMove => 0x26 { + EntityMove => id(0x26) { entity_id: VarInt =, delta_x: i8 =, delta_y: i8 =, @@ -548,7 +548,7 @@ state_packets!( on_ground: bool =, } // EntityLookAndMove is a combination of EntityMove and EntityLook. - EntityLookAndMove => 0x27 { + EntityLookAndMove => id(0x27) { entity_id: VarInt =, delta_x: i8 =, delta_y: i8 =, @@ -558,31 +558,31 @@ state_packets!( on_ground: bool =, } // EntityLook rotates the entity to the new angles provided. - EntityLook => 0x28 { + EntityLook => id(0x28) { entity_id: VarInt =, yaw: i8 =, pitch: i8 =, on_ground: bool =, } // Entity does nothing. It is a result of subclassing used in Minecraft. - Entity => 0x29 { + Entity => id(0x29) { entity_id: VarInt =, } // SignEditorOpen causes the client to open the editor for a sign so that // it can write to it. Only sent in vanilla when the player places a sign. - SignEditorOpen => 0x2A { + SignEditorOpen => id(0x2A) { location: types::Position =, } // PlayerAbilities is used to modify the players current abilities. Flying, // creative, god mode etc. - PlayerAbilities => 0x2B { + PlayerAbilities => id(0x2B) { flags: u8 =, flying_speed: f32 =, walking_speed: f32 =, } // CombatEvent is used for... you know, I never checked. I have no // clue. - CombatEvent => 0x2C { + CombatEvent => id(0x2C) { event: VarInt =, direction: Option = when(|p: &CombatEvent| p.event.0 == 1), player_id: Option = when(|p: &CombatEvent| p.event.0 == 2), @@ -591,13 +591,13 @@ state_packets!( } // PlayerInfo is sent by the server for every player connected to the server // to provide skin and username information as well as ping and gamemode info. - PlayerInfo => 0x2D { + PlayerInfo => id(0x2D) { inner: packet::PlayerInfoData =, // Ew but watcha gonna do? } // 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. - TeleportPlayer => 0x2E { + TeleportPlayer => id(0x2E) { x: f64 =, y: f64 =, z: f64 =, @@ -606,40 +606,40 @@ state_packets!( flags: u8 =, } // EntityUsedBed is sent by the server when a player goes to bed. - EntityUsedBed => 0x2F { + EntityUsedBed => id(0x2F) { entity_id: VarInt =, location: types::Position =, } // EntityDestroy destroys the entities with the ids in the provided slice. - EntityDestroy => 0x30 { + EntityDestroy => id(0x30) { entity_ids: LenPrefixed =, } // EntityRemoveEffect removes an effect from an entity. - EntityRemoveEffect => 0x31 { + EntityRemoveEffect => id(0x31) { entity_id: VarInt =, effect_id: i8 =, } // ResourcePackSend causes the client to check its cache for the requested // resource packet and download it if its missing. Once the resource pack // is obtained the client will use it. - ResourcePackSend => 0x32 { + ResourcePackSend => id(0x32) { url: String =, hash: String =, } // Respawn is sent to respawn the player after death or when they move worlds. - Respawn => 0x33 { + Respawn => id(0x33) { dimension: i32 =, difficulty: u8 =, gamemode: u8 =, level_type: String =, } // EntityHeadLook rotates an entity's head to the new angle. - EntityHeadLook => 0x34 { + EntityHeadLook => id(0x34) { entity_id: VarInt =, head_yaw: i8 =, } // WorldBorder configures the world's border. - WorldBorder => 0x35 { + WorldBorder => id(0x35) { action: VarInt =, old_radius: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1), new_radius: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1 || p.action.0 == 0), @@ -652,33 +652,33 @@ state_packets!( } // Camera causes the client to spectate the entity with the passed id. // Use the player's id to de-spectate. - Camera => 0x36 { + Camera => id(0x36) { target_id: VarInt =, } // SetCurrentHotbarSlot changes the player's currently selected hotbar item. - SetCurrentHotbarSlot => 0x37 { + SetCurrentHotbarSlot => id(0x37) { slot: u8 =, } // ScoreboardDisplay is used to set the display position of a scoreboard. - ScoreboardDisplay => 0x38 { + ScoreboardDisplay => id(0x38) { position: u8 =, name: String =, } // EntityMetadata updates the metadata for an entity. - EntityMetadata => 0x39 { + EntityMetadata => id(0x39) { entity_id: VarInt =, metadata: types::Metadata =, } // EntityAttach attaches to entities together, either by mounting or leashing. // -1 can be used at the EntityID to deattach. - EntityAttach => 0x3A { + EntityAttach => id(0x3A) { entity_id: i32 =, vehicle: i32 =, leash: bool =, } // EntityVelocity sets the velocity of an entity in 1/8000 of a block // per a tick. - EntityVelocity => 0x3B { + EntityVelocity => id(0x3B) { entity_id: VarInt =, velocity_x: i16 =, velocity_y: i16 =, @@ -687,32 +687,32 @@ state_packets!( // EntityEquipment is sent to display an item on an entity, like a sword // or armor. Slot 0 is the held item and slots 1 to 4 are boots, leggings // chestplate and helmet respectively. - EntityEquipment => 0x3C { + EntityEquipment => id(0x3C) { entity_id: VarInt =, slot: VarInt =, item: Option =, } // SetExperience updates the experience bar on the client. - SetExperience => 0x3D { + SetExperience => id(0x3D) { experience_bar: f32 =, level: VarInt =, total_experience: VarInt =, } // UpdateHealth is sent by the server to update the player's health and food. - UpdateHealth => 0x3E { + UpdateHealth => id(0x3E) { health: f32 =, food: VarInt =, food_saturation: f32 =, } // ScoreboardObjective creates/updates a scoreboard objective. - ScoreboardObjective => 0x3F { + ScoreboardObjective => id(0x3F) { name: String =, mode: u8 =, value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), ty: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), } // Teams creates and updates teams - Teams => 0x40 { + Teams => id(0x40) { name: String =, mode: u8 =, display_name: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), @@ -726,7 +726,7 @@ state_packets!( } // UpdateScore is used to update or remove an item from a scoreboard // objective. - UpdateScore => 0x41 { + UpdateScore => id(0x41) { name: String =, action: u8 =, object_name: String =, @@ -734,19 +734,19 @@ state_packets!( } // SpawnPosition is sent to change the player's current spawn point. Currently // only used by the client for the compass. - SpawnPosition => 0x42 { + SpawnPosition => id(0x42) { location: types::Position =, } // TimeUpdate is sent to sync the world's time to the client, the client // will manually tick the time itself so this doesn't need to sent repeatedly // but if the server or client has issues keeping up this can fall out of sync // so it is a good idea to sent this now and again - TimeUpdate => 0x43 { + TimeUpdate => id(0x43) { world_age: i64 =, time_of_day: i64 =, } // Title configures an on-screen title. - Title => 0x44 { + Title => id(0x44) { action: VarInt =, title: Option = when(|p: &Title| p.action.0 == 0), sub_title: Option = when(|p: &Title| p.action.0 == 1), @@ -755,7 +755,7 @@ state_packets!( fade_out: Option = when(|p: &Title| p.action.0 == 2), } // UpdateSign sets or changes the text on a sign. - UpdateSign => 0x45 { + UpdateSign => id(0x45) { location: types::Position =, line1: format::Component =, line2: format::Component =, @@ -763,19 +763,19 @@ state_packets!( line4: format::Component =, } // PlayerListHeaderFooter updates the header/footer of the player list. - PlayerListHeaderFooter => 0x46 { + PlayerListHeaderFooter => id(0x46) { header: format::Component =, footer: format::Component =, } // CollectItem causes the collected item to fly towards the collector. This // does not destroy the entity. - CollectItem => 0x47 { + CollectItem => id(0x47) { collected_entity_id: VarInt =, collector_entity_id: VarInt =, } // EntityTeleport teleports the entity to the target location. This is // sent if the entity moves further than EntityMove allows. - EntityTeleport => 0x48 { + EntityTeleport => id(0x48) { entity_id: VarInt =, x: i32 =, y: i32 =, @@ -785,12 +785,12 @@ state_packets!( on_ground: bool =, } // EntityProperties updates the properties for an entity. - EntityProperties => 0x49 { + EntityProperties => id(0x49) { entity_id: VarInt =, properties: LenPrefixed =, } // EntityEffect applies a status effect to an entity for a given duration. - EntityEffect => 0x4A { + EntityEffect => id(0x4A) { entity_id: VarInt =, effect_id: i8 =, amplifier: i8 =, @@ -804,13 +804,13 @@ state_packets!( // LoginStart is sent immeditately after switching into the login // state. The passed username is used by the server to authenticate // the player in online mode. - LoginStart => 0x00 { + LoginStart => id(0x00) { username: String =, } // EncryptionResponse is sent as a reply to EncryptionRequest. All // packets following this one must be encrypted with AES/CFB8 // encryption. - EncryptionResponse => 0x01 { + EncryptionResponse => id(0x01) { // The key for the AES/CFB8 cipher encrypted with the // public key shared_secret: LenPrefixedBytes =, @@ -823,13 +823,13 @@ state_packets!( // LoginDisconnect is sent by the server if there was any issues // authenticating the player during login or the general server // issues (e.g. too many players). - LoginDisconnect => 0x00 { + LoginDisconnect => id(0x00) { reason: format::Component =, } // EncryptionRequest is sent by the server if the server is in // online mode. If it is not sent then its assumed the server is // in offline mode. - EncryptionRequest => 0x01 { + EncryptionRequest => id(0x01) { // Generally empty, left in from legacy auth // but is still used by the client if provided server_id: String =, @@ -842,14 +842,14 @@ state_packets!( // LoginSuccess is sent by the server if the player successfully // authenicates with the session servers (online mode) or straight // after LoginStart (offline mode). - LoginSuccess => 0x02 { + LoginSuccess => id(0x02) { // String encoding of a uuid (with hyphens) uuid: String =, username: String =, } // SetInitialCompression sets the compression threshold during the // login state. - SetInitialCompression => 0x03 { + SetInitialCompression => id(0x03) { // Threshold where a packet should be sent compressed threshold: VarInt =, } @@ -861,14 +861,14 @@ state_packets!( // switching to the Status protocol state and is used // to signal the server to send a StatusResponse to the // client - StatusRequest => 0x00 { + StatusRequest => id(0x00) { empty: () =, } // StatusPing is sent by the client after recieving a // StatusResponse. The client uses the time from sending // the ping until the time of recieving a pong to measure // the latency between the client and the server. - StatusPing => 0x01 { + StatusPing => id(0x01) { ping: i64 =, } } @@ -894,13 +894,13 @@ state_packets!( // "description": "Hello world", // "favicon": "data:image/png;base64," // } - StatusResponse => 0x00 { + StatusResponse => id(0x00) { status: String =, } // StatusPong is sent as a reply to a StatusPing. // The Time field should be exactly the same as the // one sent by the client. - StatusPong => 0x01 { + StatusPong => id(0x01) { ping: i64 =, } } From 30c7dbeaea910ba8d5e2994826d243b55c5aad70 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Wed, 16 Mar 2016 18:25:35 +0000 Subject: [PATCH 028/160] Update copyright --- protocol/src/format.rs | 2 +- protocol/src/item.rs | 2 +- protocol/src/nbt/mod.rs | 2 +- protocol/src/types/bit/map.rs | 2 +- protocol/src/types/bit/mod.rs | 2 +- protocol/src/types/bit/set.rs | 2 +- protocol/src/types/blockpos.rs | 2 +- protocol/src/types/metadata.rs | 2 +- protocol/src/types/mod.rs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 49c12ba..ba73f77 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protocol/src/item.rs b/protocol/src/item.rs index a26e57d..eeeeee3 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index 32d4a64..45be019 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protocol/src/types/bit/map.rs b/protocol/src/types/bit/map.rs index 4814f2a..15136a9 100644 --- a/protocol/src/types/bit/map.rs +++ b/protocol/src/types/bit/map.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protocol/src/types/bit/mod.rs b/protocol/src/types/bit/mod.rs index bc345c6..6754eab 100644 --- a/protocol/src/types/bit/mod.rs +++ b/protocol/src/types/bit/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protocol/src/types/bit/set.rs b/protocol/src/types/bit/set.rs index 123b0f9..a650570 100644 --- a/protocol/src/types/bit/set.rs +++ b/protocol/src/types/bit/set.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protocol/src/types/blockpos.rs b/protocol/src/types/blockpos.rs index 8455811..9b56b72 100644 --- a/protocol/src/types/blockpos.rs +++ b/protocol/src/types/blockpos.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 1fbd49f..78bb7eb 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index ca88128..11e4d7c 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 4cce36bf9c1f21e9f6833731b40308be9c57c57e Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Wed, 16 Mar 2016 18:25:35 +0000 Subject: [PATCH 029/160] Update copyright --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/mojang.rs | 2 +- protocol/src/protocol/packet.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index a8b8e2a..1ccd09d 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 71a21a6..e8b7efe 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 3de1a3f..0a935ee 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1,4 +1,4 @@ -// Copyright 2015 Matthew Collins +// Copyright 2016 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 469afb228b83877b6c0823ac25cb03e55af5de6c Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Thu, 17 Mar 2016 22:18:25 +0000 Subject: [PATCH 030/160] Implementation of components for the entity component system --- protocol/src/types/bit/set.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/protocol/src/types/bit/set.rs b/protocol/src/types/bit/set.rs index a650570..23ef4e8 100644 --- a/protocol/src/types/bit/set.rs +++ b/protocol/src/types/bit/set.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[derive(Clone)] pub struct Set { data: Vec, } @@ -33,11 +34,11 @@ fn test_set() { impl Set { pub fn new(size: usize) -> Set { - let mut set = Set { data: Vec::with_capacity(size) }; - for _ in 0..size { - set.data.push(0) - } - set + Set { data: vec![0; (size + 63) / 64] } + } + + pub fn resize(&mut self, new_size: usize) { + self.data.resize((new_size + 63) / 64, 0); } pub fn set(&mut self, i: usize, v: bool) { @@ -48,7 +49,17 @@ impl Set { } } - pub fn get(&mut self, i: usize) -> bool { + pub fn get(&self, i: usize) -> bool { (self.data[i >> 6] & (1 << (i & 0x3F))) != 0 } + + pub fn includes_set(&self, other: &Set) -> bool { + debug_assert!(self.data.len() == other.data.len()); + for (a, b) in self.data.iter().zip(&other.data) { + if a & b != *b { + return false; + } + } + true + } } From eaea15e4a146b777a47d39fa708ce7b9b18e64d4 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Fri, 18 Mar 2016 10:25:09 +0000 Subject: [PATCH 031/160] Allow searching for entities within the manager --- protocol/src/types/bit/set.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/protocol/src/types/bit/set.rs b/protocol/src/types/bit/set.rs index 23ef4e8..0a620ad 100644 --- a/protocol/src/types/bit/set.rs +++ b/protocol/src/types/bit/set.rs @@ -41,6 +41,10 @@ impl Set { self.data.resize((new_size + 63) / 64, 0); } + pub fn capacity(&self) -> usize { + self.data.len() * 64 + } + pub fn set(&mut self, i: usize, v: bool) { if v { self.data[i >> 6] |= 1 << (i & 0x3F) @@ -54,7 +58,6 @@ impl Set { } pub fn includes_set(&self, other: &Set) -> bool { - debug_assert!(self.data.len() == other.data.len()); for (a, b) in self.data.iter().zip(&other.data) { if a & b != *b { return false; From 826602b4594b62ab6d855500b6d42425281ae43d Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Fri, 18 Mar 2016 11:39:03 +0000 Subject: [PATCH 032/160] Automatically allocate packet ids (Fixes #13) --- protocol/src/protocol/mod.rs | 38 +++++- protocol/src/protocol/packet.rs | 226 ++++++++++++++++---------------- 2 files changed, 147 insertions(+), 117 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 1ccd09d..edc86cd 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -34,13 +34,37 @@ use time; pub const SUPPORTED_PROTOCOL: i32 = 74; +#[doc(hidden)] +macro_rules! create_ids { + ($t:ty, ) => (); + ($t:ty, prev($prev:ident), $name:ident) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = $prev + 1; + ); + ($t:ty, prev($prev:ident), $name:ident, $($n:ident),+) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = $prev + 1; + create_ids!($t, prev($name), $($n),+); + ); + ($t:ty, $name:ident, $($n:ident),+) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = 0; + create_ids!($t, prev($name), $($n),+); + ); + ($t:ty, $name:ident) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = 0; + ); +} + + /// Helper macro for defining packets #[macro_export] macro_rules! state_packets { ($($state:ident $stateName:ident { $($dir:ident $dirName:ident { $( - $name:ident => id($id:expr) { + $name:ident { $($field:ident: $field_type:ty = $(when ($cond:expr))*, )+ })* })+ @@ -61,6 +85,7 @@ macro_rules! state_packets { $( pub mod $state { + $( pub mod $dir { #![allow(unused_imports)] @@ -71,6 +96,11 @@ macro_rules! state_packets { use types; use item; + + pub mod internal_ids { + create_ids!(i32, $($name),*); + } + $( #[derive(Default, Debug)] pub struct $name { @@ -79,7 +109,7 @@ macro_rules! state_packets { impl PacketType for $name { - fn packet_id(&self) -> i32 { $id } + fn packet_id(&self) -> i32 { internal_ids::$name } fn write(self, buf: &mut io::Write) -> Result<(), io::Error> { $( @@ -108,7 +138,7 @@ macro_rules! state_packets { Direction::$dirName => { match id { $( - $id => { + self::$state::$dir::internal_ids::$name => { use self::$state::$dir::$name; let mut packet : $name = $name::default(); $( @@ -780,7 +810,7 @@ impl Conn { pub fn do_status(mut self) -> Result<(Status, time::Duration), Error> { use serde_json::Value; use self::packet::status::serverbound::*; - use self::packet::handshake::serverbound::*; + use self::packet::handshake::serverbound::Handshake; use self::packet::Packet; let host = self.host.clone(); let port = self.port; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 0a935ee..a67d979 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -32,7 +32,7 @@ state_packets!( // than the hostname due to the protocol not providing // any system for custom information to be transfered // by the client to the server until after login. - Handshake => id(0x00) { + Handshake { // The protocol version of the connecting client protocol_version: VarInt =, // The hostname the client connected to @@ -50,22 +50,22 @@ state_packets!( serverbound Serverbound { // TabComplete is sent by the client when the client presses tab in // the chat box. - TabComplete => id(0x00) { + TabComplete { text: String =, has_target: bool =, target: Option = when(|p: &TabComplete| p.has_target == true), } // ChatMessage is sent by the client when it sends a chat message or // executes a command (prefixed by '/'). - ChatMessage => id(0x01) { + ChatMessage { message: String =, } // ClientStatus is sent to update the client's status - ClientStatus => id(0x02) { + ClientStatus { action_id: VarInt =, } // ClientSettings is sent by the client to update its current settings. - ClientSettings => id(0x03) { + ClientSettings { locale: String =, view_distance: u8 =, chat_mode: u8 =, @@ -74,18 +74,18 @@ state_packets!( main_hand: VarInt =, } // ConfirmTransactionServerbound is a reply to ConfirmTransaction. - ConfirmTransactionServerbound => id(0x04) { + ConfirmTransactionServerbound { id: u8 =, action_number: i16 =, accepted: bool =, } // EnchantItem is sent when the client enchants an item. - EnchantItem => id(0x05) { + EnchantItem { id: u8 =, enchantment: u8 =, } // ClickWindow is sent when the client clicks in a window. - ClickWindow => id(0x06) { + ClickWindow { id: u8 =, slot: i16 =, button: u8 =, @@ -94,19 +94,19 @@ state_packets!( clicked_item: Option =, } // CloseWindow is sent when the client closes a window. - CloseWindow => id(0x07) { + CloseWindow { id: u8 =, } // PluginMessageServerbound is used for custom messages between the client // and server. This is mainly for plugins/mods but vanilla has a few channels // registered too. - PluginMessageServerbound => id(0x08) { + PluginMessageServerbound { channel: String =, data: Vec =, } // UseEntity is sent when the user interacts (right clicks) or attacks // (left clicks) an entity. - UseEntity => id(0x09) { + UseEntity { target_id: VarInt =, ty: VarInt =, target_x: f32 = when(|p: &UseEntity| p.ty.0 == 2), @@ -117,11 +117,11 @@ state_packets!( // KeepAliveServerbound is sent by a client as a response to a // KeepAliveClientbound. If the client doesn't reply the server // may disconnect the client. - KeepAliveServerbound => id(0x0A) { + KeepAliveServerbound { id: VarInt =, } // PlayerPosition is used to update the player's position. - PlayerPosition => id(0x0B) { + PlayerPosition { x: f64 =, y: f64 =, z: f64 =, @@ -129,7 +129,7 @@ state_packets!( } // PlayerPositionLook is a combination of PlayerPosition and // PlayerLook. - PlayerPositionLook => id(0x0C) { + PlayerPositionLook { x: f64 =, y: f64 =, z: f64 =, @@ -138,61 +138,61 @@ state_packets!( on_ground: bool =, } // PlayerLook is used to update the player's rotation. - PlayerLook => id(0x0D) { + PlayerLook { yaw: f32 =, pitch: f32 =, on_ground: bool =, } // Player is used to update whether the player is on the ground or not. - Player => id(0x0E) { + Player { on_ground: bool =, } // ClientAbilities is used to modify the players current abilities. // Currently flying is the only one - ClientAbilities => id(0x0F) { + ClientAbilities { flags: u8 =, flying_speed: f32 =, walking_speed: f32 =, } // PlayerDigging is sent when the client starts/stops digging a block. // It also can be sent for droppping items and eating/shooting. - PlayerDigging => id(0x10) { + PlayerDigging { status: u8 =, location: types::Position =, face: u8 =, } // PlayerAction is sent when a player preforms various actions. - PlayerAction => id(0x11) { + PlayerAction { entity_id: VarInt =, action_id: VarInt =, jump_boost: VarInt =, } // SteerVehicle is sent by the client when steers or preforms an action // on a vehicle. - SteerVehicle => id(0x12) { + SteerVehicle { sideways: f32 =, forward: f32 =, flags: u8 =, } // ResourcePackStatus informs the server of the client's current progress // in activating the requested resource pack - ResourcePackStatus => id(0x13) { + ResourcePackStatus { hash: String =, result: VarInt =, } // HeldItemChange is sent when the player changes the currently active // hotbar slot. - HeldItemChange => id(0x14) { + HeldItemChange { slot: i16 =, } // CreativeInventoryAction is sent when the client clicks in the creative // inventory. This is used to spawn items in creative. - CreativeInventoryAction => id(0x15) { + CreativeInventoryAction { slot: i16 =, clicked_item: Option =, } // SetSign sets the text on a sign after placing it. - SetSign => id(0x16) { + SetSign { location: types::Position =, line1: String =, line2: String =, @@ -201,15 +201,15 @@ state_packets!( } // ArmSwing is sent by the client when the player left clicks (to swing their // arm). - ArmSwing => id(0x17) { + ArmSwing { hand: VarInt =, } // SpectateTeleport is sent by clients in spectator mode to teleport to a player. - SpectateTeleport => id(0x18) { + SpectateTeleport { target: UUID =, } // PlayerBlockPlacement is sent when the client tries to place a block. - PlayerBlockPlacement => id(0x19) { + PlayerBlockPlacement { location: types::Position =, face: VarInt =, hand: VarInt =, @@ -218,14 +218,14 @@ state_packets!( cursor_z: u8 =, } // UseItem is sent when the client tries to use an item. - UseItem => id(0x1A) { + UseItem { hand: VarInt =, } } clientbound Clientbound { // SpawnObject is used to spawn an object or vehicle into the world when it // is in range of the client. - SpawnObject => id(0x00) { + SpawnObject { entity_id: VarInt =, uuid: UUID =, ty: u8 =, @@ -242,7 +242,7 @@ state_packets!( // 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. - SpawnExperienceOrb => id(0x01) { + SpawnExperienceOrb { entity_id: VarInt =, x: i32 =, y: i32 =, @@ -251,7 +251,7 @@ state_packets!( } // SpawnGlobalEntity spawns an entity which is visible from anywhere in the // world. Currently only used for lightning. - SpawnGlobalEntity => id(0x02) { + SpawnGlobalEntity { entity_id: VarInt =, ty: u8 =, x: i32 =, @@ -260,7 +260,7 @@ state_packets!( } // SpawnMob is used to spawn a living entity into the world when it is in // range of the client. - SpawnMob => id(0x03) { + SpawnMob { entity_id: VarInt =, uuid: UUID =, ty: u8 =, @@ -277,7 +277,7 @@ state_packets!( } // 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. - SpawnPainting => id(0x04) { + SpawnPainting { entity_id: VarInt =, title: String =, location: types::Position =, @@ -286,7 +286,7 @@ state_packets!( // SpawnPlayer is used to spawn a player when they are in range of the client. // This packet alone isn't enough to display the player as the skin and username // information is in the player information packet. - SpawnPlayer => id(0x05) { + SpawnPlayer { entity_id: VarInt =, uuid: UUID =, x: i32 =, @@ -297,44 +297,44 @@ state_packets!( metadata: types::Metadata =, } // Animation is sent by the server to play an animation on a specific entity. - Animation => id(0x06) { + Animation { entity_id: VarInt =, animation_id: u8 =, } // Statistics is used to update the statistics screen for the client. - Statistics => id(0x07) { + Statistics { statistices: LenPrefixed =, } // BlockBreakAnimation is used to create and update the block breaking // animation played when a player starts digging a block. - BlockBreakAnimation => id(0x08) { + BlockBreakAnimation { entity_id: VarInt =, location: types::Position =, stage: i8 =, } // UpdateBlockEntity updates the nbt tag of a block entity in the // world. - UpdateBlockEntity => id(0x09) { + UpdateBlockEntity { location: types::Position =, action: u8 =, nbt: Option =, } // BlockAction triggers different actions depending on the target block. - BlockAction => id(0x0A) { + BlockAction { location: types::Position =, byte1: u8 =, byte2: u8 =, block_type: VarInt =, } // BlockChange is used to update a single block on the client. - BlockChange => id(0x0B) { + BlockChange { location: types::Position =, block_id: VarInt =, } // BossBar displays and/or changes a boss bar that is displayed on the // top of the client's screen. This is normally used for bosses such as // the ender dragon or the wither. - BossBar => id(0x0C) { + BossBar { uuid: UUID =, action: VarInt =, title: format::Component = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 3), @@ -345,45 +345,45 @@ state_packets!( } // ServerDifficulty changes the displayed difficulty in the client's menu // as well as some ui changes for hardcore. - ServerDifficulty => id(0x0D) { + ServerDifficulty { difficulty: u8 =, } // TabCompleteReply is sent as a reply to a tab completion request. // The matches should be possible completions for the command/chat the // player sent. - TabCompleteReply => id(0x0E) { + TabCompleteReply { matches: LenPrefixed =, } // 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. - ServerMessage => id(0x0F) { + ServerMessage { message: format::Component =, // 0 - Chat message, 1 - System message, 2 - Action bar message position: u8 =, } // MultiBlockChange is used to update a batch of blocks in a single packet. - MultiBlockChange => id(0x10) { + MultiBlockChange { chunk_x: i32 =, chunk_y: i32 =, records: LenPrefixed =, } // ConfirmTransaction notifies the client whether a transaction was successful // or failed (e.g. due to lag). - ConfirmTransaction => id(0x11) { + ConfirmTransaction { id: u8 =, action_number: i16 =, accepted: bool =, } // WindowClose forces the client to close the window with the given id, // e.g. a chest getting destroyed. - WindowClose => id(0x12) { + WindowClose { id: u8 =, } // WindowOpen tells the client to open the inventory window of the given // type. The ID is used to reference the instance of the window in // other packets. - WindowOpen => id(0x13) { + WindowOpen { id: u8 =, ty: String =, title: format::Component =, @@ -391,48 +391,48 @@ state_packets!( entity_id: i32 = when(|p: &WindowOpen| p.ty == "EntityHorse"), } // WindowItems sets every item in a window. - WindowItems => id(0x14) { + WindowItems { id: u8 =, items: LenPrefixed> =, } // WindowProperty changes the value of a property of a window. Properties // vary depending on the window type. - WindowProperty => id(0x15) { + WindowProperty { id: u8 =, property: i16 =, value: i16 =, } // WindowSetSlot changes an itemstack in one of the slots in a window. - WindowSetSlot => id(0x16) { + WindowSetSlot { id: u8 =, property: i16 =, item: Option =, } // SetCooldown disables a set item (by id) for the set number of ticks - SetCooldown => id(0x17) { + SetCooldown { item_id: VarInt =, ticks: VarInt =, } // PluginMessageClientbound is used for custom messages between the client // and server. This is mainly for plugins/mods but vanilla has a few channels // registered too. - PluginMessageClientbound => id(0x18) { + PluginMessageClientbound { channel: String =, data: Vec =, } // Disconnect causes the client to disconnect displaying the passed reason. - Disconnect => id(0x19) { + Disconnect { reason: format::Component =, } // EntityAction causes an entity to preform an action based on the passed // id. - EntityAction => id(0x1A) { + EntityAction { entity_id: i32 =, action_id: u8 =, } // Explosion is sent when an explosion is triggered (tnt, creeper etc). // This plays the effect and removes the effected blocks. - Explosion => id(0x1B) { + Explosion { x: f32 =, y: f32 =, z: f32 =, @@ -444,17 +444,17 @@ state_packets!( } // ChunkUnload tells the client to unload the chunk at the specified // position. - ChunkUnload => id(0x1C) { + ChunkUnload { x: i32 =, z: i32 =, } // SetCompression updates the compression threshold. - SetCompression => id(0x1D) { + SetCompression { threshold: VarInt =, } // ChangeGameState is used to modify the game's state like gamemode or // weather. - ChangeGameState => id(0x1E) { + ChangeGameState { reason: u8 =, value: f32 =, } @@ -462,12 +462,12 @@ state_packets!( // client is still responding and keep the connection open. // The client should reply with the KeepAliveServerbound // packet setting ID to the same as this one. - KeepAliveClientbound => id(0x1F) { + KeepAliveClientbound { id: VarInt =, } // ChunkData sends or updates a single chunk on the client. If New is set // then biome data should be sent too. - ChunkData => id(0x20) { + ChunkData { chunk_x: i32 =, chunk_z: i32 =, new: bool =, @@ -477,7 +477,7 @@ state_packets!( // 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. - Effect => id(0x21) { + Effect { effect_id: i32 =, location: types::Position =, data: i32 =, @@ -485,7 +485,7 @@ state_packets!( } // Particle spawns particles at the target location with the various // modifiers. - Particle => id(0x22) { + Particle { particle_id: i32 =, long_distance: bool =, x: f32 =, @@ -500,7 +500,7 @@ state_packets!( data2: VarInt = when(|p: &Particle| p.particle_id == 36), } // SoundEffect plays the named sound at the target location. - SoundEffect => id(0x23) { + SoundEffect { name: String =, x: i32 =, y: i32 =, @@ -510,7 +510,7 @@ state_packets!( } // JoinGame is sent after completing the login process. This // sets the initial state for the client. - JoinGame => id(0x24) { + JoinGame { // The entity id the client will be referenced by entity_id: i32 =, // The starting gamemode of the client @@ -528,7 +528,7 @@ state_packets!( reduced_debug_info: bool =, } // Maps updates a single map's contents - Maps => id(0x25) { + Maps { item_damage: VarInt =, scale: i8 =, tracking_position: bool =, @@ -540,7 +540,7 @@ state_packets!( data: Option> = when(|p: &Maps| p.columns > 0), } // EntityMove moves the entity with the id by the offsets provided. - EntityMove => id(0x26) { + EntityMove { entity_id: VarInt =, delta_x: i8 =, delta_y: i8 =, @@ -548,7 +548,7 @@ state_packets!( on_ground: bool =, } // EntityLookAndMove is a combination of EntityMove and EntityLook. - EntityLookAndMove => id(0x27) { + EntityLookAndMove { entity_id: VarInt =, delta_x: i8 =, delta_y: i8 =, @@ -558,31 +558,31 @@ state_packets!( on_ground: bool =, } // EntityLook rotates the entity to the new angles provided. - EntityLook => id(0x28) { + EntityLook { entity_id: VarInt =, yaw: i8 =, pitch: i8 =, on_ground: bool =, } // Entity does nothing. It is a result of subclassing used in Minecraft. - Entity => id(0x29) { + Entity { entity_id: VarInt =, } // SignEditorOpen causes the client to open the editor for a sign so that // it can write to it. Only sent in vanilla when the player places a sign. - SignEditorOpen => id(0x2A) { + SignEditorOpen { location: types::Position =, } // PlayerAbilities is used to modify the players current abilities. Flying, // creative, god mode etc. - PlayerAbilities => id(0x2B) { + PlayerAbilities { flags: u8 =, flying_speed: f32 =, walking_speed: f32 =, } // CombatEvent is used for... you know, I never checked. I have no // clue. - CombatEvent => id(0x2C) { + CombatEvent { event: VarInt =, direction: Option = when(|p: &CombatEvent| p.event.0 == 1), player_id: Option = when(|p: &CombatEvent| p.event.0 == 2), @@ -591,13 +591,13 @@ state_packets!( } // PlayerInfo is sent by the server for every player connected to the server // to provide skin and username information as well as ping and gamemode info. - PlayerInfo => id(0x2D) { + PlayerInfo { inner: packet::PlayerInfoData =, // Ew but watcha gonna do? } // 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. - TeleportPlayer => id(0x2E) { + TeleportPlayer { x: f64 =, y: f64 =, z: f64 =, @@ -606,40 +606,40 @@ state_packets!( flags: u8 =, } // EntityUsedBed is sent by the server when a player goes to bed. - EntityUsedBed => id(0x2F) { + EntityUsedBed { entity_id: VarInt =, location: types::Position =, } // EntityDestroy destroys the entities with the ids in the provided slice. - EntityDestroy => id(0x30) { + EntityDestroy { entity_ids: LenPrefixed =, } // EntityRemoveEffect removes an effect from an entity. - EntityRemoveEffect => id(0x31) { + EntityRemoveEffect { entity_id: VarInt =, effect_id: i8 =, } // ResourcePackSend causes the client to check its cache for the requested // resource packet and download it if its missing. Once the resource pack // is obtained the client will use it. - ResourcePackSend => id(0x32) { + ResourcePackSend { url: String =, hash: String =, } // Respawn is sent to respawn the player after death or when they move worlds. - Respawn => id(0x33) { + Respawn { dimension: i32 =, difficulty: u8 =, gamemode: u8 =, level_type: String =, } // EntityHeadLook rotates an entity's head to the new angle. - EntityHeadLook => id(0x34) { + EntityHeadLook { entity_id: VarInt =, head_yaw: i8 =, } // WorldBorder configures the world's border. - WorldBorder => id(0x35) { + WorldBorder { action: VarInt =, old_radius: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1), new_radius: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1 || p.action.0 == 0), @@ -652,33 +652,33 @@ state_packets!( } // Camera causes the client to spectate the entity with the passed id. // Use the player's id to de-spectate. - Camera => id(0x36) { + Camera { target_id: VarInt =, } // SetCurrentHotbarSlot changes the player's currently selected hotbar item. - SetCurrentHotbarSlot => id(0x37) { + SetCurrentHotbarSlot { slot: u8 =, } // ScoreboardDisplay is used to set the display position of a scoreboard. - ScoreboardDisplay => id(0x38) { + ScoreboardDisplay { position: u8 =, name: String =, } // EntityMetadata updates the metadata for an entity. - EntityMetadata => id(0x39) { + EntityMetadata { entity_id: VarInt =, metadata: types::Metadata =, } // EntityAttach attaches to entities together, either by mounting or leashing. // -1 can be used at the EntityID to deattach. - EntityAttach => id(0x3A) { + EntityAttach { entity_id: i32 =, vehicle: i32 =, leash: bool =, } // EntityVelocity sets the velocity of an entity in 1/8000 of a block // per a tick. - EntityVelocity => id(0x3B) { + EntityVelocity { entity_id: VarInt =, velocity_x: i16 =, velocity_y: i16 =, @@ -687,32 +687,32 @@ state_packets!( // EntityEquipment is sent to display an item on an entity, like a sword // or armor. Slot 0 is the held item and slots 1 to 4 are boots, leggings // chestplate and helmet respectively. - EntityEquipment => id(0x3C) { + EntityEquipment { entity_id: VarInt =, slot: VarInt =, item: Option =, } // SetExperience updates the experience bar on the client. - SetExperience => id(0x3D) { + SetExperience { experience_bar: f32 =, level: VarInt =, total_experience: VarInt =, } // UpdateHealth is sent by the server to update the player's health and food. - UpdateHealth => id(0x3E) { + UpdateHealth { health: f32 =, food: VarInt =, food_saturation: f32 =, } // ScoreboardObjective creates/updates a scoreboard objective. - ScoreboardObjective => id(0x3F) { + ScoreboardObjective { name: String =, mode: u8 =, value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), ty: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), } // Teams creates and updates teams - Teams => id(0x40) { + Teams { name: String =, mode: u8 =, display_name: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), @@ -726,7 +726,7 @@ state_packets!( } // UpdateScore is used to update or remove an item from a scoreboard // objective. - UpdateScore => id(0x41) { + UpdateScore { name: String =, action: u8 =, object_name: String =, @@ -734,19 +734,19 @@ state_packets!( } // SpawnPosition is sent to change the player's current spawn point. Currently // only used by the client for the compass. - SpawnPosition => id(0x42) { + SpawnPosition { location: types::Position =, } // TimeUpdate is sent to sync the world's time to the client, the client // will manually tick the time itself so this doesn't need to sent repeatedly // but if the server or client has issues keeping up this can fall out of sync // so it is a good idea to sent this now and again - TimeUpdate => id(0x43) { + TimeUpdate { world_age: i64 =, time_of_day: i64 =, } // Title configures an on-screen title. - Title => id(0x44) { + Title { action: VarInt =, title: Option = when(|p: &Title| p.action.0 == 0), sub_title: Option = when(|p: &Title| p.action.0 == 1), @@ -755,7 +755,7 @@ state_packets!( fade_out: Option = when(|p: &Title| p.action.0 == 2), } // UpdateSign sets or changes the text on a sign. - UpdateSign => id(0x45) { + UpdateSign { location: types::Position =, line1: format::Component =, line2: format::Component =, @@ -763,19 +763,19 @@ state_packets!( line4: format::Component =, } // PlayerListHeaderFooter updates the header/footer of the player list. - PlayerListHeaderFooter => id(0x46) { + PlayerListHeaderFooter { header: format::Component =, footer: format::Component =, } // CollectItem causes the collected item to fly towards the collector. This // does not destroy the entity. - CollectItem => id(0x47) { + CollectItem { collected_entity_id: VarInt =, collector_entity_id: VarInt =, } // EntityTeleport teleports the entity to the target location. This is // sent if the entity moves further than EntityMove allows. - EntityTeleport => id(0x48) { + EntityTeleport { entity_id: VarInt =, x: i32 =, y: i32 =, @@ -785,12 +785,12 @@ state_packets!( on_ground: bool =, } // EntityProperties updates the properties for an entity. - EntityProperties => id(0x49) { + EntityProperties { entity_id: VarInt =, properties: LenPrefixed =, } // EntityEffect applies a status effect to an entity for a given duration. - EntityEffect => id(0x4A) { + EntityEffect { entity_id: VarInt =, effect_id: i8 =, amplifier: i8 =, @@ -804,13 +804,13 @@ state_packets!( // LoginStart is sent immeditately after switching into the login // state. The passed username is used by the server to authenticate // the player in online mode. - LoginStart => id(0x00) { + LoginStart { username: String =, } // EncryptionResponse is sent as a reply to EncryptionRequest. All // packets following this one must be encrypted with AES/CFB8 // encryption. - EncryptionResponse => id(0x01) { + EncryptionResponse { // The key for the AES/CFB8 cipher encrypted with the // public key shared_secret: LenPrefixedBytes =, @@ -823,13 +823,13 @@ state_packets!( // LoginDisconnect is sent by the server if there was any issues // authenticating the player during login or the general server // issues (e.g. too many players). - LoginDisconnect => id(0x00) { + LoginDisconnect { reason: format::Component =, } // EncryptionRequest is sent by the server if the server is in // online mode. If it is not sent then its assumed the server is // in offline mode. - EncryptionRequest => id(0x01) { + EncryptionRequest { // Generally empty, left in from legacy auth // but is still used by the client if provided server_id: String =, @@ -842,14 +842,14 @@ state_packets!( // LoginSuccess is sent by the server if the player successfully // authenicates with the session servers (online mode) or straight // after LoginStart (offline mode). - LoginSuccess => id(0x02) { + LoginSuccess { // String encoding of a uuid (with hyphens) uuid: String =, username: String =, } // SetInitialCompression sets the compression threshold during the // login state. - SetInitialCompression => id(0x03) { + SetInitialCompression { // Threshold where a packet should be sent compressed threshold: VarInt =, } @@ -861,14 +861,14 @@ state_packets!( // switching to the Status protocol state and is used // to signal the server to send a StatusResponse to the // client - StatusRequest => id(0x00) { + StatusRequest { empty: () =, } // StatusPing is sent by the client after recieving a // StatusResponse. The client uses the time from sending // the ping until the time of recieving a pong to measure // the latency between the client and the server. - StatusPing => id(0x01) { + StatusPing { ping: i64 =, } } @@ -894,13 +894,13 @@ state_packets!( // "description": "Hello world", // "favicon": "data:image/png;base64," // } - StatusResponse => id(0x00) { + StatusResponse { status: String =, } // StatusPong is sent as a reply to a StatusPing. // The Time field should be exactly the same as the // one sent by the client. - StatusPong => id(0x01) { + StatusPong { ping: i64 =, } } From f4f0b71e79abb7b59c06696d1cd42b09107dab6d Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Fri, 18 Mar 2016 11:46:37 +0000 Subject: [PATCH 033/160] Correctly mark 15w39c as the supported version --- protocol/src/protocol/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index edc86cd..b2ae6e5 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -963,14 +963,14 @@ pub fn test() { let mut c = Conn::new("localhost:25565").unwrap(); c.write_packet(packet::handshake::serverbound::Handshake { - protocol_version: VarInt(71), + protocol_version: VarInt(SUPPORTED_PROTOCOL), host: "localhost".to_owned(), port: 25565, next: VarInt(2), }) .unwrap(); c.state = State::Login; - c.write_packet(packet::login::serverbound::LoginStart { username: "Think".to_owned() }) + c.write_packet(packet::login::serverbound::LoginStart { username: "Thinkofdeath".to_owned() }) .unwrap(); let packet = match c.read_packet().unwrap() { @@ -985,8 +985,8 @@ pub fn test() { let token_e = key.encrypt(&packet.verify_token.data); let profile = mojang::Profile { - username: "Think".to_owned(), - id: "b1184d43168441cfa2128b9a3df3b6ab".to_owned(), + username: "Thinkofdeath".to_owned(), + id: "4566e69fc90748ee8d71d7ba5aa00d20".to_owned(), access_token: "".to_owned(), }; From ba1fe8e76677574163da63c6ebec5e9873c70cf9 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Fri, 18 Mar 2016 22:24:30 +0000 Subject: [PATCH 034/160] Base implementation for worlds/blocks --- protocol/src/protocol/mod.rs | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index b2ae6e5..dbd9572 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -34,29 +34,6 @@ use time; pub const SUPPORTED_PROTOCOL: i32 = 74; -#[doc(hidden)] -macro_rules! create_ids { - ($t:ty, ) => (); - ($t:ty, prev($prev:ident), $name:ident) => ( - #[allow(non_upper_case_globals)] - pub const $name: $t = $prev + 1; - ); - ($t:ty, prev($prev:ident), $name:ident, $($n:ident),+) => ( - #[allow(non_upper_case_globals)] - pub const $name: $t = $prev + 1; - create_ids!($t, prev($name), $($n),+); - ); - ($t:ty, $name:ident, $($n:ident),+) => ( - #[allow(non_upper_case_globals)] - pub const $name: $t = 0; - create_ids!($t, prev($name), $($n),+); - ); - ($t:ty, $name:ident) => ( - #[allow(non_upper_case_globals)] - pub const $name: $t = 0; - ); -} - /// Helper macro for defining packets #[macro_export] From ddf3a7981cfed5f660c77ab25d01d8577827c4f0 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Fri, 18 Mar 2016 22:24:30 +0000 Subject: [PATCH 035/160] Base implementation for worlds/blocks --- protocol/src/macros.rs | 25 +++++++++++++++++++ protocol/src/types/bit/map.rs | 4 ++-- protocol/src/types/mod.rs | 1 + protocol/src/types/nibble.rs | 45 +++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 protocol/src/macros.rs create mode 100644 protocol/src/types/nibble.rs diff --git a/protocol/src/macros.rs b/protocol/src/macros.rs new file mode 100644 index 0000000..bf2b7a2 --- /dev/null +++ b/protocol/src/macros.rs @@ -0,0 +1,25 @@ + + +#[doc(hidden)] +#[macro_export] +macro_rules! create_ids { + ($t:ty, ) => (); + ($t:ty, prev($prev:ident), $name:ident) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = $prev + 1; + ); + ($t:ty, prev($prev:ident), $name:ident, $($n:ident),+) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = $prev + 1; + create_ids!($t, prev($name), $($n),+); + ); + ($t:ty, $name:ident, $($n:ident),+) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = 0; + create_ids!($t, prev($name), $($n),+); + ); + ($t:ty, $name:ident) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = 0; + ); +} diff --git a/protocol/src/types/bit/map.rs b/protocol/src/types/bit/map.rs index 15136a9..a847bb3 100644 --- a/protocol/src/types/bit/map.rs +++ b/protocol/src/types/bit/map.rs @@ -14,7 +14,7 @@ pub struct Map { bits: Vec, - bit_size: usize, + pub bit_size: usize, length: usize, } @@ -60,7 +60,7 @@ impl Map { map } - pub fn resize(self, size: usize) -> Map { + pub fn resize(&self, size: usize) -> Map { let mut n = Map::new(self.length, size); for i in 0..self.length { n.set(i, self.get(i)); diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index 11e4d7c..682180e 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -19,3 +19,4 @@ mod metadata; pub use self::metadata::*; pub mod bit; +pub mod nibble; diff --git a/protocol/src/types/nibble.rs b/protocol/src/types/nibble.rs new file mode 100644 index 0000000..1b62d2e --- /dev/null +++ b/protocol/src/types/nibble.rs @@ -0,0 +1,45 @@ +// Copyright 2015 Matthew Collins +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +pub struct Array { + data: Vec, +} + +impl Array { + pub fn new(size: usize) -> Array { + Array { + data: vec![0; (size + 1) >> 1], + } + } + + pub fn get(&self, idx: usize) -> u8 { + let val = self.data[idx>>1]; + if idx&1 == 0 { + val & 0xF + } else { + val >> 4 + } + } + + pub fn set(&mut self, idx: usize, val: u8) { + let i = idx >> 1; + let old = self.data[i]; + if idx&1 == 0 { + self.data[i] = (old & 0xF0) | (val & 0xF); + } else { + self.data[i] = (old & 0x0F) | ((val & 0xF) << 4); + } + } +} From 458a36bbf27f63e5348174f47f8d681fe39aef9a Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sun, 20 Mar 2016 12:04:02 +0000 Subject: [PATCH 036/160] Initial work on connecting to servers --- protocol/src/protocol/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index dbd9572..6549828 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -631,6 +631,7 @@ pub enum State { #[derive(Debug)] pub enum Error { Err(String), + Disconnect(format::Component), IOError(io::Error), } @@ -644,6 +645,7 @@ impl ::std::error::Error for Error { fn description(&self) -> &str { match *self { Error::Err(ref val) => &val[..], + Error::Disconnect(_) => "Disconnect", Error::IOError(ref e) => e.description(), } } @@ -653,6 +655,7 @@ impl ::std::fmt::Display for Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { Error::Err(ref val) => write!(f, "protocol error: {}", val), + Error::Disconnect(ref val) => write!(f, "{}", val), Error::IOError(ref e) => e.fmt(f), } } @@ -660,10 +663,10 @@ impl ::std::fmt::Display for Error { pub struct Conn { stream: TcpStream, - host: String, - port: u16, + pub host: String, + pub port: u16, direction: Direction, - state: State, + pub state: State, cipher: Option, From 3d6f5ba904b79b4a067e5ff26b4e8a1b89c0d9ff Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sun, 20 Mar 2016 20:17:21 +0000 Subject: [PATCH 037/160] Work on login screen, added ui buttons and textboxes (plus tab fixes) --- protocol/src/protocol/packet.rs | 46 ++++++++++++++++----------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index a67d979..f126a41 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -511,20 +511,20 @@ state_packets!( // JoinGame is sent after completing the login process. This // sets the initial state for the client. JoinGame { - // The entity id the client will be referenced by + // The entity id the client will be referenced by entity_id: i32 =, - // The starting gamemode of the client + // The starting gamemode of the client gamemode: u8 =, - // The dimension the client is starting in + // The dimension the client is starting in dimension: i8 =, - // The difficuilty setting for the server + // The difficuilty setting for the server difficulty: u8 =, - // The max number of players on the server + // The max number of players on the server max_players: u8 =, - // The level type of the server + // The level type of the server level_type: String =, - // Whether the client should reduce the amount of debug - // information it displays in F3 mode + // Whether the client should reduce the amount of debug + // information it displays in F3 mode reduced_debug_info: bool =, } // Maps updates a single map's contents @@ -879,21 +879,21 @@ state_packets!( // and optionally a favicon. // // The structure is as follows - // { - // "version": { - // "name": "1.8.3", - // "protocol": 47, - // }, - // "players": { - // "max": 20, - // "online": 1, - // "sample": [ - // {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"} - // ] - // }, - // "description": "Hello world", - // "favicon": "data:image/png;base64," - // } + // { + // "version": { + // "name": "1.8.3", + // "protocol": 47, + // }, + // "players": { + // "max": 20, + // "online": 1, + // "sample": [ + // {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"} + // ] + // }, + // "description": "Hello world", + // "favicon": "data:image/png;base64," + // } StatusResponse { status: String =, } From 0d42d59ed9abbdde4b630a1a7fd008ff23b44f1a Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sun, 20 Mar 2016 23:43:31 +0000 Subject: [PATCH 038/160] Fully implement the login screen (Closes #6) --- protocol/src/protocol/mod.rs | 19 ++++++++ protocol/src/protocol/mojang.rs | 86 ++++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 6549828..0fcf3a1 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -16,6 +16,7 @@ use openssl; use serde_json; +use hyper; pub mod mojang; @@ -633,6 +634,8 @@ pub enum Error { Err(String), Disconnect(format::Component), IOError(io::Error), + Json(serde_json::Error), + Hyper(hyper::Error), } impl convert::From for Error { @@ -641,12 +644,26 @@ impl convert::From for Error { } } +impl convert::From for Error { + fn from(e: serde_json::Error) -> Error { + Error::Json(e) + } +} + +impl convert::From for Error { + fn from(e: hyper::Error) -> Error { + Error::Hyper(e) + } +} + impl ::std::error::Error for Error { fn description(&self) -> &str { match *self { Error::Err(ref val) => &val[..], Error::Disconnect(_) => "Disconnect", Error::IOError(ref e) => e.description(), + Error::Json(ref e) => e.description(), + Error::Hyper(ref e) => e.description(), } } } @@ -657,6 +674,8 @@ impl ::std::fmt::Display for Error { Error::Err(ref val) => write!(f, "protocol error: {}", val), Error::Disconnect(ref val) => write!(f, "{}", val), Error::IOError(ref e) => e.fmt(f), + Error::Json(ref e) => e.fmt(f), + Error::Hyper(ref e) => e.fmt(f), } } } diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index e8b7efe..d3aeded 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -14,8 +14,10 @@ use openssl; use serde_json; +use serde_json::builder::ObjectBuilder; use hyper; +#[derive(Clone, Debug)] pub struct Profile { pub username: String, pub id: String, @@ -23,9 +25,85 @@ pub struct Profile { } const JOIN_URL: &'static str = "https://sessionserver.mojang.com/session/minecraft/join"; +const LOGIN_URL: &'static str = "https://authserver.mojang.com/authenticate"; +const REFRESH_URL: &'static str = "https://authserver.mojang.com/refresh"; +const VALIDATE_URL: &'static str = "https://authserver.mojang.com/validate"; impl Profile { - pub fn join_server(&self, server_id: &String, shared_key: &Vec, public_key: &Vec) { + pub fn login(username: &str, password: &str, token: &str) -> Result { + let req_msg = ObjectBuilder::new() + .insert("username", username) + .insert("password", password) + .insert("clientToken", token) + .insert_object("agent", |b| b + .insert("name", "Minecraft") + .insert("version", 1) + ) + .unwrap(); + let req = try!(serde_json::to_string(&req_msg)); + + let client = hyper::Client::new(); + let res = try!(client.post(LOGIN_URL) + .body(&req) + .header(hyper::header::ContentType("application/json".parse().unwrap())) + .send()); + + let ret: serde_json::Value = try!(serde_json::from_reader(res)); + if let Some(error) = ret.find("error").and_then(|v| v.as_string()) { + return Err(super::Error::Err(format!( + "{}: {}", + error, + ret.find("errorMessage").and_then(|v| v.as_string()).unwrap()) + )); + } + Ok(Profile { + username: ret.lookup("selectedProfile.name").and_then(|v| v.as_string()).unwrap().to_owned(), + id: ret.lookup("selectedProfile.id").and_then(|v| v.as_string()).unwrap().to_owned(), + access_token: ret.find("accessToken").and_then(|v| v.as_string()).unwrap().to_owned(), + }) + } + + pub fn refresh(self, token: &str) -> Result { + let req_msg = ObjectBuilder::new() + .insert("accessToken", self.access_token.clone()) + .insert("clientToken", token) + .unwrap(); + let req = try!(serde_json::to_string(&req_msg)); + + let client = hyper::Client::new(); + let res = try!(client.post(VALIDATE_URL) + .body(&req) + .header(hyper::header::ContentType("application/json".parse().unwrap())) + .send()); + + if res.status != hyper::status::StatusCode::NoContent { + println!("Rerefeshing"); + // Refresh needed + let res = try!(client.post(REFRESH_URL) + .body(&req) + .header(hyper::header::ContentType("application/json".parse().unwrap())) + .send()); + + let ret: serde_json::Value = try!(serde_json::from_reader(res)); + if let Some(error) = ret.find("error").and_then(|v| v.as_string()) { + return Err(super::Error::Err(format!( + "{}: {}", + error, + ret.find("errorMessage").and_then(|v| v.as_string()).unwrap()) + )); + } + return Ok(Profile { + username: ret.lookup("selectedProfile.name").and_then(|v| v.as_string()).unwrap().to_owned(), + id: ret.lookup("selectedProfile.id").and_then(|v| v.as_string()).unwrap().to_owned(), + access_token: ret.find("accessToken").and_then(|v| v.as_string()).unwrap().to_owned(), + }); + } + println!("Reuse"); + Ok(self) + } + + // TODO + pub fn join_server(&self, server_id: &str, shared_key: &Vec, public_key: &Vec) { let mut sha1 = openssl::SHA1::new(); sha1.update(server_id.as_bytes()); sha1.update(&shared_key[..]); @@ -46,7 +124,7 @@ impl Profile { hash_val.to_owned() }; - let join_msg = serde_json::builder::ObjectBuilder::new() + let join_msg = ObjectBuilder::new() .insert("accessToken", &self.access_token) .insert("selectedProfile", &self.id) .insert("serverId", hash_str) @@ -66,6 +144,10 @@ impl Profile { }; panic!("{:?}", ret); } + + pub fn is_complete(&self) -> bool { + !self.username.is_empty() && !self.id.is_empty() && !self.access_token.is_empty() + } } fn twos_compliment(data: &mut Vec) { From 46c91db4e01ad82fa4846ca0abe538acb09ebb7f Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Mon, 21 Mar 2016 00:15:57 +0000 Subject: [PATCH 039/160] Remove old debug messages --- protocol/src/protocol/mojang.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index d3aeded..c416787 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -77,7 +77,6 @@ impl Profile { .send()); if res.status != hyper::status::StatusCode::NoContent { - println!("Rerefeshing"); // Refresh needed let res = try!(client.post(REFRESH_URL) .body(&req) @@ -98,7 +97,6 @@ impl Profile { access_token: ret.find("accessToken").and_then(|v| v.as_string()).unwrap().to_owned(), }); } - println!("Reuse"); Ok(self) } From 883bc07d62424c5e129c9272ef434f83ad2c1f26 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Mon, 21 Mar 2016 10:55:31 +0000 Subject: [PATCH 040/160] Allow connecting to servers --- protocol/src/protocol/mod.rs | 4 ++-- protocol/src/protocol/mojang.rs | 18 ++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 0fcf3a1..918fd03 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -362,7 +362,7 @@ pub struct LenPrefixed { } impl LenPrefixed { - fn new(data: Vec) -> LenPrefixed { + pub fn new(data: Vec) -> LenPrefixed { LenPrefixed { len: Default::default(), data: data, @@ -418,7 +418,7 @@ pub struct LenPrefixedBytes { } impl LenPrefixedBytes { - fn new(data: Vec) -> LenPrefixedBytes { + pub fn new(data: Vec) -> LenPrefixedBytes { LenPrefixedBytes { len: Default::default(), data: data, diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index c416787..83bea16 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -100,8 +100,7 @@ impl Profile { Ok(self) } - // TODO - pub fn join_server(&self, server_id: &str, shared_key: &Vec, public_key: &Vec) { + pub fn join_server(&self, server_id: &str, shared_key: &Vec, public_key: &Vec) -> Result<(), super::Error> { let mut sha1 = openssl::SHA1::new(); sha1.update(server_id.as_bytes()); sha1.update(&shared_key[..]); @@ -130,17 +129,16 @@ impl Profile { let join = serde_json::to_string(&join_msg).unwrap(); let client = hyper::Client::new(); - let res = client.post(JOIN_URL) + let res = try!(client.post(JOIN_URL) .body(&join) .header(hyper::header::ContentType("application/json".parse().unwrap())) - .send() - .unwrap(); + .send()); - let ret: serde_json::Value = match serde_json::from_reader(res) { - Result::Ok(val) => val, - Result::Err(_) => return, - }; - panic!("{:?}", ret); + if res.status == hyper::status::StatusCode::NoContent { + Ok(()) + } else { + Err(super::Error::Err("Failed to auth with server".to_owned())) + } } pub fn is_complete(&self) -> bool { From 4129f09c7754eef1114ebbc491a7d03d20299865 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Mon, 21 Mar 2016 10:55:50 +0000 Subject: [PATCH 041/160] Remove old test code --- protocol/src/protocol/mod.rs | 93 ------------------------------------ 1 file changed, 93 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 918fd03..6dc7667 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -955,96 +955,3 @@ pub trait PacketType: Sized { fn write(self, buf: &mut io::Write) -> Result<(), io::Error>; } - -// REMOVE ME -// #[test] -pub fn test() { - let mut c = Conn::new("localhost:25565").unwrap(); - - c.write_packet(packet::handshake::serverbound::Handshake { - protocol_version: VarInt(SUPPORTED_PROTOCOL), - host: "localhost".to_owned(), - port: 25565, - next: VarInt(2), - }) - .unwrap(); - c.state = State::Login; - c.write_packet(packet::login::serverbound::LoginStart { username: "Thinkofdeath".to_owned() }) - .unwrap(); - - let packet = match c.read_packet().unwrap() { - packet::Packet::EncryptionRequest(val) => val, - val => panic!("Wrong packet: {:?}", val), - }; - - let mut key = openssl::PublicKey::new(&packet.public_key.data); - let shared = openssl::gen_random(16); - - let shared_e = key.encrypt(&shared); - let token_e = key.encrypt(&packet.verify_token.data); - - let profile = mojang::Profile { - username: "Thinkofdeath".to_owned(), - id: "4566e69fc90748ee8d71d7ba5aa00d20".to_owned(), - access_token: "".to_owned(), - }; - - profile.join_server(&packet.server_id, &shared, &packet.public_key.data); - - c.write_packet(packet::login::serverbound::EncryptionResponse { - shared_secret: LenPrefixedBytes::new(shared_e), - verify_token: LenPrefixedBytes::new(token_e), - }) - .unwrap(); - - let mut read = c.clone(); - let mut write = c.clone(); - - read.enable_encyption(&shared, true); - write.enable_encyption(&shared, false); - - loop { - match read.read_packet().unwrap() { - packet::Packet::LoginDisconnect(val) => { - panic!("Discconect {}", val.reason); - } - packet::Packet::SetInitialCompression(val) => { - read.set_compresssion(val.threshold.0, true); - write.set_compresssion(val.threshold.0, false); - println!("Compression: {}", val.threshold.0) - } - packet::Packet::LoginSuccess(val) => { - println!("Login: {} {}", val.username, val.uuid); - read.state = State::Play; - write.state = State::Play; - break; - } - _ => panic!("Unknown packet"), - } - } - - let mut first = true; - let mut count = 0; - loop { - match read.read_packet().unwrap() { - packet::Packet::ServerMessage(val) => println!("MSG: {}", val.message), - packet::Packet::ChunkData(_) => {} - val => { - println!("{:?}", val); - if first { - write.write_packet(packet::play::serverbound::ChatMessage { - message: "Hello world".to_owned(), - }) - .unwrap(); - first = false; - } - count += 1; - if count > 200 { - break; - } - } - } - } - - unimplemented!(); -} From 418f3380b5da8c29b22548b5f6fab686511ba886 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Mon, 21 Mar 2016 14:05:13 +0000 Subject: [PATCH 042/160] Implement chunk loading --- protocol/src/protocol/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 6dc7667..7d6d9fd 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -309,6 +309,16 @@ impl Serializable for u16 { } } +impl Serializable for u64 { + fn read_from(buf: &mut io::Read) -> Result { + Result::Ok(try!(buf.read_u64::())) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + try!(buf.write_u64::(*self)); + Result::Ok(()) + } +} + impl Serializable for f32 { fn read_from(buf: &mut io::Read) -> Result { Result::Ok(try!(buf.read_f32::())) From a42c1e412ae141ec897ef50d7faf7b2c6d5f025c Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Mon, 21 Mar 2016 14:05:13 +0000 Subject: [PATCH 043/160] Implement chunk loading --- protocol/src/types/bit/map.rs | 7 +++++++ protocol/src/types/nibble.rs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/protocol/src/types/bit/map.rs b/protocol/src/types/bit/map.rs index a847bb3..ece65fd 100644 --- a/protocol/src/types/bit/map.rs +++ b/protocol/src/types/bit/map.rs @@ -59,6 +59,13 @@ impl Map { } map } + pub fn from_raw(bits: Vec, size: usize) -> Map { + Map { + length: (bits.len()*64 + 63) / size, + bit_size: size, + bits: bits, + } + } pub fn resize(&self, size: usize) -> Map { let mut n = Map::new(self.length, size); diff --git a/protocol/src/types/nibble.rs b/protocol/src/types/nibble.rs index 1b62d2e..91ecdc8 100644 --- a/protocol/src/types/nibble.rs +++ b/protocol/src/types/nibble.rs @@ -14,7 +14,7 @@ pub struct Array { - data: Vec, + pub data: Vec, } impl Array { From a89aff3f1f82175ee83653d744e82307326105dd Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Wed, 23 Mar 2016 21:07:49 +0000 Subject: [PATCH 044/160] Rework block system --- protocol/src/protocol/mojang.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 83bea16..0d9b195 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -152,7 +152,7 @@ fn twos_compliment(data: &mut Vec) { data[i] = !data[i]; if carry { carry = data[i] == 0xFF; - data[i] += 1; + data[i].wrapping_add(1); } } } From 411bfe69154c200141d816440a3eea582d983fea Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Wed, 23 Mar 2016 22:58:09 +0000 Subject: [PATCH 045/160] Fix a bug in twos_compliment's implementation --- protocol/src/protocol/mojang.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 0d9b195..3a324b2 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -109,11 +109,11 @@ impl Profile { // Mojang uses a hex method which allows for // negatives so we have to account for that. - let negative = hash[0] & 0x80 == 0x80; + let negative = (hash[0] & 0x80) == 0x80; if negative { twos_compliment(&mut hash); } - let hash_str = hash.iter().map(|b| format!("{:02X}", b)).collect::>().join(""); + let hash_str = hash.iter().map(|b| format!("{:02x}", b)).collect::>().join(""); let hash_val = hash_str.trim_matches('0'); let hash_str = if negative { "-".to_owned() + &hash_val[..] @@ -152,7 +152,7 @@ fn twos_compliment(data: &mut Vec) { data[i] = !data[i]; if carry { carry = data[i] == 0xFF; - data[i].wrapping_add(1); + data[i] = data[i].wrapping_add(1); } } } From 122978fc49cd377aff8b1288a9f20530e3ac0cf2 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Wed, 23 Mar 2016 23:28:33 +0000 Subject: [PATCH 046/160] Use read_exact instead of take & read_to_end --- protocol/src/protocol/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 7d6d9fd..c832a02 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -760,8 +760,8 @@ impl Conn { pub fn read_packet(&mut self) -> Result { let len = try!(VarInt::read_from(self)).0 as usize; - let mut ibuf = Vec::with_capacity(len); - try!(self.take(len as u64).read_to_end(&mut ibuf)); + let mut ibuf = vec![0; len]; + try!(self.read_exact(&mut ibuf)); let mut buf = io::Cursor::new(ibuf); From ce6f5c963cb587469966d9502364310e68e2b3be Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Thu, 24 Mar 2016 15:39:57 +0000 Subject: [PATCH 047/160] Initial block model support --- protocol/src/types/blockpos.rs | 2 +- protocol/src/types/hash.rs | 24 ++++++++++++++++++++++++ protocol/src/types/mod.rs | 1 + 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 protocol/src/types/hash.rs diff --git a/protocol/src/types/blockpos.rs b/protocol/src/types/blockpos.rs index 9b56b72..03d791c 100644 --- a/protocol/src/types/blockpos.rs +++ b/protocol/src/types/blockpos.rs @@ -17,7 +17,7 @@ extern crate byteorder; use std::fmt; use protocol::Serializable; use std::io; -use std::io::{Read, Write}; +use std::io::Write; use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; #[derive(Clone, Copy)] diff --git a/protocol/src/types/hash.rs b/protocol/src/types/hash.rs new file mode 100644 index 0000000..cf16af9 --- /dev/null +++ b/protocol/src/types/hash.rs @@ -0,0 +1,24 @@ + + +use std::hash::Hasher; + +pub struct FNVHash(u64); + +impl Hasher for FNVHash { + fn write(&mut self, bytes: &[u8]) { + for b in bytes { + self.0 = self.0.wrapping_mul(0x100000001b3); + self.0 ^= *b as u64 + } + } + + fn finish(&self) -> u64 { + self.0 + } +} + +impl Default for FNVHash { + fn default() -> Self { + FNVHash(0xcbf29ce484222325) + } +} diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index 682180e..5d270fd 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -20,3 +20,4 @@ pub use self::metadata::*; pub mod bit; pub mod nibble; +pub mod hash; From 03bdb015e56300b376dbe20f3e2e4689ebefcc9a Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Fri, 25 Mar 2016 20:56:45 +0000 Subject: [PATCH 048/160] Update to 1.9.0 --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 130 +++++++++++++++++++++----------- 2 files changed, 86 insertions(+), 46 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index c832a02..bed109a 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -33,7 +33,7 @@ use flate2::read::{ZlibDecoder, ZlibEncoder}; use flate2; use time; -pub const SUPPORTED_PROTOCOL: i32 = 74; +pub const SUPPORTED_PROTOCOL: i32 = 107; /// Helper macro for defining packets diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index f126a41..d505d58 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -48,10 +48,16 @@ state_packets!( } play Play { serverbound Serverbound { + // TeleportConfirm is sent by the client as a reply to a telport from + // the server. + TeleportConfirm { + teleport_id: VarInt =, + } // TabComplete is sent by the client when the client presses tab in // the chat box. TabComplete { text: String =, + assume_command: bool =, has_target: bool =, target: Option = when(|p: &TabComplete| p.has_target == true), } @@ -68,7 +74,7 @@ state_packets!( ClientSettings { locale: String =, view_distance: u8 =, - chat_mode: u8 =, + chat_mode: VarInt =, chat_colors: bool =, displayed_skin_parts: u8 =, main_hand: VarInt =, @@ -90,7 +96,7 @@ state_packets!( slot: i16 =, button: u8 =, action_number: u16 =, - mode: u8 =, + mode: VarInt =, clicked_item: Option =, } // CloseWindow is sent when the client closes a window. @@ -147,6 +153,19 @@ state_packets!( Player { on_ground: bool =, } + // Sent by the client when in a vehicle instead of the normal move packet. + VehicleMove { + x: f64 =, + y: f64 =, + z: f64 =, + yaw: f32 =, + pitch: f32 =, + } + // TODO: Document + SteerBoat { + unknown: bool =, + unknown2: bool =, + } // ClientAbilities is used to modify the players current abilities. // Currently flying is the only one ClientAbilities { @@ -157,7 +176,7 @@ state_packets!( // PlayerDigging is sent when the client starts/stops digging a block. // It also can be sent for droppping items and eating/shooting. PlayerDigging { - status: u8 =, + status: VarInt =, location: types::Position =, face: u8 =, } @@ -229,9 +248,9 @@ state_packets!( entity_id: VarInt =, uuid: UUID =, ty: u8 =, - x: i32 =, - y: i32 =, - z: i32 =, + x: f64 =, + y: f64 =, + z: f64 =, pitch: i8 =, yaw: i8 =, data: i32 =, @@ -244,9 +263,9 @@ state_packets!( // gained when collected. SpawnExperienceOrb { entity_id: VarInt =, - x: i32 =, - y: i32 =, - z: i32 =, + x: f64 =, + y: f64 =, + z: f64 =, count: i16 =, } // SpawnGlobalEntity spawns an entity which is visible from anywhere in the @@ -254,9 +273,9 @@ state_packets!( SpawnGlobalEntity { entity_id: VarInt =, ty: u8 =, - x: i32 =, - y: i32 =, - z: i32 =, + x: f64 =, + y: f64 =, + z: f64 =, } // SpawnMob is used to spawn a living entity into the world when it is in // range of the client. @@ -264,9 +283,9 @@ state_packets!( entity_id: VarInt =, uuid: UUID =, ty: u8 =, - x: i32 =, - y: i32 =, - z: i32 =, + x: f64 =, + y: f64 =, + z: f64 =, yaw: i8 =, pitch: i8 =, head_pitch: i8 =, @@ -279,6 +298,7 @@ state_packets!( // the client. The title effects the size and the texture of the painting. SpawnPainting { entity_id: VarInt =, + uuid: UUID =, title: String =, location: types::Position =, direction: u8 =, @@ -289,9 +309,9 @@ state_packets!( SpawnPlayer { entity_id: VarInt =, uuid: UUID =, - x: i32 =, - y: i32 =, - z: i32 =, + x: f64 =, + y: f64 =, + z: f64 =, yaw: i8 =, pitch: i8 =, metadata: types::Metadata =, @@ -420,6 +440,16 @@ state_packets!( channel: String =, data: Vec =, } + // Plays a sound by name on the client + NamedSoundEffect { + name: String =, + category: VarInt =, + x: i32 =, + y: i32 =, + z: i32 =, + volume: f32 =, + pitch: u8 =, + } // Disconnect causes the client to disconnect displaying the passed reason. Disconnect { reason: format::Component =, @@ -448,10 +478,6 @@ state_packets!( x: i32 =, z: i32 =, } - // SetCompression updates the compression threshold. - SetCompression { - threshold: VarInt =, - } // ChangeGameState is used to modify the game's state like gamemode or // weather. ChangeGameState { @@ -499,15 +525,6 @@ state_packets!( data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38), data2: VarInt = when(|p: &Particle| p.particle_id == 36), } - // SoundEffect plays the named sound at the target location. - SoundEffect { - name: String =, - x: i32 =, - y: i32 =, - z: i32 =, - volume: f32 =, - pitch: u8 =, - } // JoinGame is sent after completing the login process. This // sets the initial state for the client. JoinGame { @@ -542,17 +559,17 @@ state_packets!( // EntityMove moves the entity with the id by the offsets provided. EntityMove { entity_id: VarInt =, - delta_x: i8 =, - delta_y: i8 =, - delta_z: i8 =, + delta_x: i16 =, + delta_y: i16 =, + delta_z: i16 =, on_ground: bool =, } // EntityLookAndMove is a combination of EntityMove and EntityLook. EntityLookAndMove { entity_id: VarInt =, - delta_x: i8 =, - delta_y: i8 =, - delta_z: i8 =, + delta_x: i16 =, + delta_y: i16 =, + delta_z: i16 =, yaw: i8 =, pitch: i8 =, on_ground: bool =, @@ -568,6 +585,14 @@ state_packets!( Entity { entity_id: VarInt =, } + // Teleports the player's vehicle + VehicleTeleport { + x: f64 =, + y: f64 =, + z: f64 =, + yaw: f32 =, + pitch: f32 =, + } // SignEditorOpen causes the client to open the editor for a sign so that // it can write to it. Only sent in vanilla when the player places a sign. SignEditorOpen { @@ -604,6 +629,7 @@ state_packets!( yaw: f32 =, pitch: f32 =, flags: u8 =, + teleport_id: VarInt =, } // EntityUsedBed is sent by the server when a player goes to bed. EntityUsedBed { @@ -674,7 +700,6 @@ state_packets!( EntityAttach { entity_id: i32 =, vehicle: i32 =, - leash: bool =, } // EntityVelocity sets the velocity of an entity in 1/8000 of a block // per a tick. @@ -711,6 +736,11 @@ state_packets!( value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), ty: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), } + // SetPassengers mounts entities to an entity + SetPassengers { + entity_id: VarInt =, + passengers: LenPrefixed =, + } // Teams creates and updates teams Teams { name: String =, @@ -740,7 +770,7 @@ state_packets!( // TimeUpdate is sent to sync the world's time to the client, the client // will manually tick the time itself so this doesn't need to sent repeatedly // but if the server or client has issues keeping up this can fall out of sync - // so it is a good idea to sent this now and again + // so it is a good idea to send this now and again TimeUpdate { world_age: i64 =, time_of_day: i64 =, @@ -750,9 +780,9 @@ state_packets!( action: VarInt =, title: Option = when(|p: &Title| p.action.0 == 0), sub_title: Option = when(|p: &Title| p.action.0 == 1), - fade_in: Option = when(|p: &Title| p.action.0 == 2), - fade_stay: Option = when(|p: &Title| p.action.0 == 2), - fade_out: Option = when(|p: &Title| p.action.0 == 2), + fade_in: Option = when(|p: &Title| p.action.0 == 2), + fade_stay: Option = when(|p: &Title| p.action.0 == 2), + fade_out: Option = when(|p: &Title| p.action.0 == 2), } // UpdateSign sets or changes the text on a sign. UpdateSign { @@ -762,6 +792,16 @@ state_packets!( line3: format::Component =, line4: format::Component =, } + // SoundEffect plays the named sound at the target location. + SoundEffect { + name: VarInt =, + category: VarInt =, + x: i32 =, + y: i32 =, + z: i32 =, + volume: f32 =, + pitch: u8 =, + } // PlayerListHeaderFooter updates the header/footer of the player list. PlayerListHeaderFooter { header: format::Component =, @@ -777,9 +817,9 @@ state_packets!( // sent if the entity moves further than EntityMove allows. EntityTeleport { entity_id: VarInt =, - x: i32 =, - y: i32 =, - z: i32 =, + x: f64 =, + y: f64 =, + z: f64 =, yaw: i8 =, pitch: i8 =, on_ground: bool =, From 780676ec65f1d45b347fe294e4896042f37d1479 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Fri, 25 Mar 2016 20:56:45 +0000 Subject: [PATCH 049/160] Update to 1.9.0 --- protocol/src/types/metadata.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 78bb7eb..6933d59 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -29,8 +29,7 @@ pub struct MetadataKey { impl MetadataKey { #[allow(dead_code)] - // TODO: Make const later when possible - /*const*/ fn new(index: i32) -> MetadataKey { + const fn new(index: i32) -> MetadataKey { MetadataKey { index: index, ty: PhantomData, From 57bced4a9b314fc1251022159fd8d467d063ad46 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sat, 26 Mar 2016 10:19:16 +0000 Subject: [PATCH 050/160] Collisions and normal style movement --- protocol/src/protocol/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index bed109a..6b218af 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -753,7 +753,7 @@ impl Conn { if self.compression_threshold >= 0 && extra == 1 { try!(VarInt(0).write_to(self)); } - try!(self.write_all(&buf.into_boxed_slice())); + try!(self.write_all(&buf)); Result::Ok(()) } From 9a69cd1fa7d48cedaea64bb1ffe8df2e7c6e4487 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sat, 26 Mar 2016 11:46:37 +0000 Subject: [PATCH 051/160] Move Direction and BlockVertex into better locations --- protocol/src/types/mod.rs | 95 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index 5d270fd..b858aeb 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -21,3 +21,98 @@ pub use self::metadata::*; pub mod bit; pub mod nibble; pub mod hash; + +use model::{PRECOMPUTED_VERTS, BlockVertex}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Direction { + Invalid, + Up, + Down, + North, + South, + West, + East, +} + +impl Direction { + pub fn all() -> Vec { + vec![ + Direction::Up, Direction::Down, + Direction::North, Direction::South, + Direction::West, Direction::East, + ] + } + + pub fn from_string(val: &str) -> Direction { + match val { + "up" => Direction::Up, + "down" => Direction::Down, + "north" => Direction::North, + "south" => Direction::South, + "west" => Direction::West, + "east" => Direction::East, + _ => Direction::Invalid, + } + } + + pub fn opposite(&self) -> Direction { + match *self { + Direction::Up => Direction::Down, + Direction::Down => Direction::Up, + Direction::North => Direction::South, + Direction::South => Direction::North, + Direction::West => Direction::East, + Direction::East => Direction::West, + _ => unreachable!(), + } + } + + pub fn get_verts(&self) -> &'static [BlockVertex; 4] { + match *self { + Direction::Up => PRECOMPUTED_VERTS[0], + Direction::Down => PRECOMPUTED_VERTS[1], + Direction::North => PRECOMPUTED_VERTS[2], + Direction::South => PRECOMPUTED_VERTS[3], + Direction::West => PRECOMPUTED_VERTS[4], + Direction::East => PRECOMPUTED_VERTS[5], + _ => unreachable!(), + } + } + + pub fn get_offset(&self) -> (i32, i32, i32) { + match *self { + Direction::Up => (0, 1, 0), + Direction::Down => (0, -1, 0), + Direction::North => (0, 0, -1), + Direction::South => (0, 0, 1), + Direction::West => (-1, 0, 0), + Direction::East => (1, 0, 0), + _ => unreachable!(), + } + } + + pub fn as_string(&self) -> &'static str { + match *self { + Direction::Up => "up", + Direction::Down => "down", + Direction::North => "north", + Direction::South => "south", + Direction::West => "west", + Direction::East => "east", + Direction::Invalid => "invalid", + } + } + + pub fn index(&self) -> usize { + match *self { + Direction::Up => 0, + Direction::Down => 1, + Direction::North => 2, + Direction::South => 3, + Direction::West => 4, + Direction::East => 5, + _ => unreachable!(), + } + } +} From f6ac1123a2dee50398140317adad9e8c4827305d Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sat, 26 Mar 2016 13:08:01 +0000 Subject: [PATCH 052/160] Implement stairs --- protocol/src/types/mod.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index b858aeb..330263c 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -24,7 +24,7 @@ pub mod hash; use model::{PRECOMPUTED_VERTS, BlockVertex}; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Direction { Invalid, Up, @@ -68,6 +68,30 @@ impl Direction { } } + pub fn clockwise(&self) -> Direction { + match *self { + Direction::Up => Direction::Up, + Direction::Down => Direction::Down, + Direction::East => Direction::South, + Direction::West => Direction::North, + Direction::South => Direction::West, + Direction::North => Direction::East, + _ => unreachable!(), + } + } + + pub fn counter_clockwise(&self) -> Direction { + match *self { + Direction::Up => Direction::Up, + Direction::Down => Direction::Down, + Direction::East => Direction::North, + Direction::West => Direction::South, + Direction::South => Direction::East, + Direction::North => Direction::West, + _ => unreachable!(), + } + } + pub fn get_verts(&self) -> &'static [BlockVertex; 4] { match *self { Direction::Up => PRECOMPUTED_VERTS[0], From 2589b169ca547e58dfd16fa3bbc7e48c686212d5 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sat, 26 Mar 2016 14:24:26 +0000 Subject: [PATCH 053/160] Follow some of clippy's suggestions --- protocol/src/format.rs | 7 +++---- protocol/src/nbt/mod.rs | 2 +- protocol/src/types/bit/map.rs | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index ba73f77..6b4ba38 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -171,8 +171,8 @@ impl fmt::Display for Color { } impl Color { - fn from_string(val: &String) -> Self { - match val.as_ref() { + fn from_string(val: &str) -> Self { + match val { "black" => Color::Black, "dark_blue" => Color::DarkBlue, "dark_green" => Color::DarkGreen, @@ -188,7 +188,6 @@ impl Color { "red" => Color::Red, "light_purple" => Color::LightPurple, "yellow" => Color::Yellow, - "white" => Color::White, val if val.len() == 7 && val.as_bytes()[0] == b'#' => { let r = match u8::from_str_radix(&val[1..3], 16) { Ok(r) => r, @@ -204,7 +203,7 @@ impl Color { }; Color::RGB(r, g, b) } - _ => Color::White, + "white" | _ => Color::White, } } diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index 45be019..4ea53bf 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -273,7 +273,7 @@ impl Serializable for Tag { } } -pub fn write_string(buf: &mut io::Write, s: &String) -> io::Result<()> { +pub fn write_string(buf: &mut io::Write, s: &str) -> io::Result<()> { let data = s.as_bytes(); try!((data.len() as i16).write_to(buf)); buf.write_all(data) diff --git a/protocol/src/types/bit/map.rs b/protocol/src/types/bit/map.rs index ece65fd..a281cdd 100644 --- a/protocol/src/types/bit/map.rs +++ b/protocol/src/types/bit/map.rs @@ -95,11 +95,11 @@ impl Map { let mask = (1 << self.bit_size) - 1; let ii = i % 64; let pos2 = (i + self.bit_size - 1) / 64; - if pos2 != pos { + if pos2 == pos { + ((self.bits[pos] >> ii) & mask) as usize + } else { let used = 64 - ii; (((self.bits[pos] >> ii) | (self.bits[pos2] << used)) & mask) as usize - } else { - ((self.bits[pos] >> ii) & mask) as usize } } } From f3377c17c6e4e7f39ed6813b776d1ec83315bae9 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sat, 26 Mar 2016 14:24:26 +0000 Subject: [PATCH 054/160] Follow some of clippy's suggestions --- protocol/src/protocol/mod.rs | 9 ++++----- protocol/src/protocol/mojang.rs | 6 +++--- protocol/src/protocol/packet.rs | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 6b218af..41f7468 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -802,17 +802,16 @@ impl Conn { } } - pub fn enable_encyption(&mut self, key: &Vec, decrypt: bool) { + pub fn enable_encyption(&mut self, key: &[u8], decrypt: bool) { self.cipher = Option::Some(openssl::EVPCipher::new(key, key, decrypt)); } pub fn set_compresssion(&mut self, threshold: i32, read: bool) { self.compression_threshold = threshold; - if !read { - self.compression_write = Some(ZlibEncoder::new(io::Cursor::new(Vec::new()), - flate2::Compression::Default)); - } else { + if read { self.compression_read = Some(ZlibDecoder::new(io::Cursor::new(Vec::new()))); + } else { + self.compression_write = Some(ZlibEncoder::new(io::Cursor::new(Vec::new()), flate2::Compression::Default)); } } diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 3a324b2..d4f53a8 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -100,11 +100,11 @@ impl Profile { Ok(self) } - pub fn join_server(&self, server_id: &str, shared_key: &Vec, public_key: &Vec) -> Result<(), super::Error> { + pub fn join_server(&self, server_id: &str, shared_key: &[u8], public_key: &[u8]) -> Result<(), super::Error> { let mut sha1 = openssl::SHA1::new(); sha1.update(server_id.as_bytes()); - sha1.update(&shared_key[..]); - sha1.update(&public_key[..]); + sha1.update(shared_key); + sha1.update(public_key); let mut hash = sha1.bytes(); // Mojang uses a hex method which allows for diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index d505d58..3a374c2 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -59,7 +59,7 @@ state_packets!( text: String =, assume_command: bool =, has_target: bool =, - target: Option = when(|p: &TabComplete| p.has_target == true), + target: Option = when(|p: &TabComplete| p.has_target), } // ChatMessage is sent by the client when it sends a chat message or // executes a command (prefixed by '/'). From 3f8bc10bb07e76988c8c1df8908d242e0d6c3e27 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sat, 26 Mar 2016 22:21:47 +0000 Subject: [PATCH 055/160] Initial entity work, moved self handling to an entity --- protocol/src/types/mod.rs | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index 330263c..562cbd0 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -140,3 +140,45 @@ impl Direction { } } } + + + +#[derive(Clone, Copy, Debug)] +pub enum Gamemode { + Survival = 0, + Creative = 1, + Adventure = 2, + Spectator = 3, +} + +impl Gamemode { + pub fn from_int(val: i32) -> Gamemode { + match val { + 3 => Gamemode::Spectator, + 2 => Gamemode::Adventure, + 1 => Gamemode::Creative, + 0 | _ => Gamemode::Survival, + } + } + + pub fn can_fly(&self) -> bool { + match *self { + Gamemode::Creative | Gamemode::Spectator => true, + _ => false, + } + } + + pub fn always_fly(&self) -> bool { + match *self { + Gamemode::Spectator => true, + _ => false, + } + } + + pub fn noclip(&self) -> bool { + match *self { + Gamemode::Spectator => true, + _ => false, + } + } +} From 9593f56eeebee2dafe4e09c9026ae370eb3b5df2 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Mon, 28 Mar 2016 22:10:33 +0100 Subject: [PATCH 056/160] Only trim_left the hash string --- protocol/src/protocol/mojang.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index d4f53a8..45997f5 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -114,7 +114,7 @@ impl Profile { twos_compliment(&mut hash); } let hash_str = hash.iter().map(|b| format!("{:02x}", b)).collect::>().join(""); - let hash_val = hash_str.trim_matches('0'); + let hash_val = hash_str.trim_left_matches('0'); let hash_str = if negative { "-".to_owned() + &hash_val[..] } else { From 463ca00dd780f0e905ff1cd9293869d552b694bd Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Thu, 31 Mar 2016 15:26:07 +0100 Subject: [PATCH 057/160] Handle block updates from the server --- protocol/src/protocol/packet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 3a374c2..cc5fb05 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -385,7 +385,7 @@ state_packets!( // MultiBlockChange is used to update a batch of blocks in a single packet. MultiBlockChange { chunk_x: i32 =, - chunk_y: i32 =, + chunk_z: i32 =, records: LenPrefixed =, } // ConfirmTransaction notifies the client whether a transaction was successful From 88c0f3da289d20857f8c81ffe1f7c37ff880d3f2 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Thu, 31 Mar 2016 15:26:07 +0100 Subject: [PATCH 058/160] Handle block updates from the server --- protocol/src/types/blockpos.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol/src/types/blockpos.rs b/protocol/src/types/blockpos.rs index 03d791c..6623d2b 100644 --- a/protocol/src/types/blockpos.rs +++ b/protocol/src/types/blockpos.rs @@ -25,20 +25,20 @@ pub struct Position(u64); impl Position { #[allow(dead_code)] - fn new(x: i32, y: i32, z: i32) -> Position { + pub fn new(x: i32, y: i32, z: i32) -> Position { Position((((x as u64) & 0x3FFFFFF) << 38) | (((y as u64) & 0xFFF) << 26) | ((z as u64) & 0x3FFFFFF)) } - fn get_x(&self) -> i32 { + pub fn get_x(&self) -> i32 { ((self.0 as i64) >> 38) as i32 } - fn get_y(&self) -> i32 { + pub fn get_y(&self) -> i32 { (((self.0 as i64) >> 26) & 0xFFF) as i32 } - fn get_z(&self) -> i32 { + pub fn get_z(&self) -> i32 { ((self.0 as i64) << 38 >> 38) as i32 } } From 565e5110dbdbe9f68ffbd5594048d1a54bb09666 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Thu, 31 Mar 2016 20:50:54 +0100 Subject: [PATCH 059/160] Support Minecraft 1.9.2 --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 41f7468..3ef6563 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -33,7 +33,7 @@ use flate2::read::{ZlibDecoder, ZlibEncoder}; use flate2; use time; -pub const SUPPORTED_PROTOCOL: i32 = 107; +pub const SUPPORTED_PROTOCOL: i32 = 109; /// Helper macro for defining packets diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index cc5fb05..c49bfab 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -533,7 +533,7 @@ state_packets!( // The starting gamemode of the client gamemode: u8 =, // The dimension the client is starting in - dimension: i8 =, + dimension: i32 =, // The difficuilty setting for the server difficulty: u8 =, // The max number of players on the server From df9db09004da3d97ed8d32cb4605a28329358f8f Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sat, 2 Apr 2016 16:24:50 +0100 Subject: [PATCH 060/160] Fix a bit::Map overflow on 32 bit machines --- protocol/src/types/bit/map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol/src/types/bit/map.rs b/protocol/src/types/bit/map.rs index a281cdd..3695df8 100644 --- a/protocol/src/types/bit/map.rs +++ b/protocol/src/types/bit/map.rs @@ -78,9 +78,9 @@ impl Map { pub fn set(&mut self, i: usize, val: usize) { let i = i * self.bit_size; let pos = i / 64; - let mask = (1 << self.bit_size) - 1; + let mask = (1u64 << self.bit_size) - 1; let ii = i % 64; - self.bits[pos] = (self.bits[pos] & !(mask << ii)) | ((val << ii) as u64); + self.bits[pos] = (self.bits[pos] & !(mask << ii)) | ((val as u64) << ii); let pos2 = (i + self.bit_size - 1) / 64; if pos2 != pos { let used = 64 - ii; From 469a2af1d4d679cd353bab9837f8c4b4f8f08b55 Mon Sep 17 00:00:00 2001 From: llogiq Date: Sun, 3 Apr 2016 02:26:31 +0200 Subject: [PATCH 061/160] Fixed various clippy warnings --- protocol/src/protocol/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 3ef6563..61f9b0a 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -492,7 +492,7 @@ impl Lengthable for i32 { } } -/// VarInt have a variable size (between 1 and 5 bytes) when encoded based +/// `VarInt` have a variable size (between 1 and 5 bytes) when encoded based /// on the size of the number #[derive(Clone, Copy)] pub struct VarInt(pub i32); @@ -508,7 +508,7 @@ impl Lengthable for VarInt { } impl Serializable for VarInt { - /// Decodes a VarInt from the Reader + /// Decodes a `VarInt` from the Reader fn read_from(buf: &mut io::Read) -> Result { const PART : u32 = 0x7F; let mut size = 0; @@ -529,7 +529,7 @@ impl Serializable for VarInt { Result::Ok(VarInt(val as i32)) } - /// Encodes a VarInt into the Writer + /// Encodes a `VarInt` into the Writer fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { const PART : u32 = 0x7F; let mut val = self.0 as u32; @@ -556,7 +556,7 @@ impl fmt::Debug for VarInt { } } -/// VarLong have a variable size (between 1 and 10 bytes) when encoded based +/// `VarLong` have a variable size (between 1 and 10 bytes) when encoded based /// on the size of the number #[derive(Clone, Copy)] pub struct VarLong(pub i64); @@ -572,7 +572,7 @@ impl Lengthable for VarLong { } impl Serializable for VarLong { - /// Decodes a VarLong from the Reader + /// Decodes a `VarLong` from the Reader fn read_from(buf: &mut io::Read) -> Result { const PART : u64 = 0x7F; let mut size = 0; @@ -593,7 +593,7 @@ impl Serializable for VarLong { Result::Ok(VarLong(val as i64)) } - /// Encodes a VarLong into the Writer + /// Encodes a `VarLong` into the Writer fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { const PART : u64 = 0x7F; let mut val = self.0 as u64; @@ -707,7 +707,7 @@ pub struct Conn { impl Conn { pub fn new(target: &str) -> Result { // TODO SRV record support - let mut parts = target.split(":").collect::>(); + let mut parts = target.split(':').collect::>(); let address = if parts.len() == 1 { parts.push("25565"); format!("{}:25565", parts[0]) From 9fef9aa8f12c6b094afa41801fdc9e490fc44454 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sun, 3 Apr 2016 11:20:31 +0100 Subject: [PATCH 062/160] Support connecting to offline mode servers --- protocol/src/protocol/mod.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 61f9b0a..472d95b 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -739,6 +739,9 @@ impl Conn { 0 }; if self.compression_threshold >= 0 && buf.len() as i32 > self.compression_threshold { + if self.compression_write.is_none() { + self.compression_write = Some(ZlibEncoder::new(io::Cursor::new(Vec::new()), flate2::Compression::Default)); + } extra = 0; let uncompressed_size = buf.len(); let mut new = Vec::new(); @@ -766,6 +769,9 @@ impl Conn { let mut buf = io::Cursor::new(ibuf); if self.compression_threshold >= 0 { + if self.compression_read.is_none() { + self.compression_read = Some(ZlibDecoder::new(io::Cursor::new(Vec::new()))); + } let uncompressed_size = try!(VarInt::read_from(&mut buf)).0; if uncompressed_size != 0 { let mut new = Vec::with_capacity(uncompressed_size as usize); @@ -806,13 +812,8 @@ impl Conn { self.cipher = Option::Some(openssl::EVPCipher::new(key, key, decrypt)); } - pub fn set_compresssion(&mut self, threshold: i32, read: bool) { + pub fn set_compresssion(&mut self, threshold: i32) { self.compression_threshold = threshold; - if read { - self.compression_read = Some(ZlibDecoder::new(io::Cursor::new(Vec::new()))); - } else { - self.compression_write = Some(ZlibEncoder::new(io::Cursor::new(Vec::new()), flate2::Compression::Default)); - } } pub fn do_status(mut self) -> Result<(Status, time::Duration), Error> { From da40508291c2790075c9d6986f3f3f907b49582a Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sun, 3 Apr 2016 20:53:40 +0100 Subject: [PATCH 063/160] Replace usages of x,y,z for block positions with Position --- protocol/src/types/blockpos.rs | 66 ------------------ protocol/src/types/metadata.rs | 15 ++-- protocol/src/types/mod.rs | 124 --------------------------------- 3 files changed, 8 insertions(+), 197 deletions(-) delete mode 100644 protocol/src/types/blockpos.rs diff --git a/protocol/src/types/blockpos.rs b/protocol/src/types/blockpos.rs deleted file mode 100644 index 6623d2b..0000000 --- a/protocol/src/types/blockpos.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2016 Matthew Collins -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -extern crate byteorder; - -use std::fmt; -use protocol::Serializable; -use std::io; -use std::io::Write; -use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; - -#[derive(Clone, Copy)] -pub struct Position(u64); - -impl Position { - #[allow(dead_code)] - pub fn new(x: i32, y: i32, z: i32) -> Position { - Position((((x as u64) & 0x3FFFFFF) << 38) | (((y as u64) & 0xFFF) << 26) | - ((z as u64) & 0x3FFFFFF)) - } - - pub fn get_x(&self) -> i32 { - ((self.0 as i64) >> 38) as i32 - } - - pub fn get_y(&self) -> i32 { - (((self.0 as i64) >> 26) & 0xFFF) as i32 - } - - pub fn get_z(&self) -> i32 { - ((self.0 as i64) << 38 >> 38) as i32 - } -} - -impl Default for Position { - fn default() -> Position { - Position(0) - } -} - -impl fmt::Debug for Position { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "<{},{},{}>", self.get_x(), self.get_y(), self.get_z()) - } -} - -impl Serializable for Position { - fn read_from(buf: &mut io::Read) -> Result { - Result::Ok(Position(try!(buf.read_u64::()))) - } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { - try!(buf.write_u64::(self.0)); - Result::Ok(()) - } -} diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 6933d59..eab44a6 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -21,6 +21,7 @@ use protocol; use protocol::Serializable; use format; use item; +use shared::Position; pub struct MetadataKey { index: i32, @@ -81,12 +82,12 @@ impl Serializable for Metadata { [try!(f32::read_from(buf)), try!(f32::read_from(buf)), try!(f32::read_from(buf))]), - 8 => m.put_raw(index, try!(super::Position::read_from(buf))), + 8 => m.put_raw(index, try!(Position::read_from(buf))), 9 => { if try!(bool::read_from(buf)) { - m.put_raw(index, try!(Option::::read_from(buf))); + m.put_raw(index, try!(Option::::read_from(buf))); } else { - m.put_raw::>(index, None); + m.put_raw::>(index, None); } } 10 => m.put_raw(index, try!(protocol::VarInt::read_from(buf))), @@ -199,8 +200,8 @@ pub enum Value { OptionalItemStack(Option), Bool(bool), Vector([f32; 3]), - Position(super::Position), - OptionalPosition(Option), + Position(Position), + OptionalPosition(Option), Direction(protocol::VarInt), // TODO: Proper type OptionalUUID(Option), Block(u16), // TODO: Proper type @@ -307,7 +308,7 @@ impl MetaValue for [f32; 3] { } } -impl MetaValue for super::Position { +impl MetaValue for Position { fn unwrap(value: &Value) -> &Self { match *value { Value::Position(ref val) => val, @@ -319,7 +320,7 @@ impl MetaValue for super::Position { } } -impl MetaValue for Option { +impl MetaValue for Option { fn unwrap(value: &Value) -> &Self { match *value { Value::OptionalPosition(ref val) => val, diff --git a/protocol/src/types/mod.rs b/protocol/src/types/mod.rs index 562cbd0..5232c8c 100644 --- a/protocol/src/types/mod.rs +++ b/protocol/src/types/mod.rs @@ -12,9 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod blockpos; -pub use self::blockpos::*; - mod metadata; pub use self::metadata::*; @@ -22,127 +19,6 @@ pub mod bit; pub mod nibble; pub mod hash; -use model::{PRECOMPUTED_VERTS, BlockVertex}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Direction { - Invalid, - Up, - Down, - North, - South, - West, - East, -} - -impl Direction { - pub fn all() -> Vec { - vec![ - Direction::Up, Direction::Down, - Direction::North, Direction::South, - Direction::West, Direction::East, - ] - } - - pub fn from_string(val: &str) -> Direction { - match val { - "up" => Direction::Up, - "down" => Direction::Down, - "north" => Direction::North, - "south" => Direction::South, - "west" => Direction::West, - "east" => Direction::East, - _ => Direction::Invalid, - } - } - - pub fn opposite(&self) -> Direction { - match *self { - Direction::Up => Direction::Down, - Direction::Down => Direction::Up, - Direction::North => Direction::South, - Direction::South => Direction::North, - Direction::West => Direction::East, - Direction::East => Direction::West, - _ => unreachable!(), - } - } - - pub fn clockwise(&self) -> Direction { - match *self { - Direction::Up => Direction::Up, - Direction::Down => Direction::Down, - Direction::East => Direction::South, - Direction::West => Direction::North, - Direction::South => Direction::West, - Direction::North => Direction::East, - _ => unreachable!(), - } - } - - pub fn counter_clockwise(&self) -> Direction { - match *self { - Direction::Up => Direction::Up, - Direction::Down => Direction::Down, - Direction::East => Direction::North, - Direction::West => Direction::South, - Direction::South => Direction::East, - Direction::North => Direction::West, - _ => unreachable!(), - } - } - - pub fn get_verts(&self) -> &'static [BlockVertex; 4] { - match *self { - Direction::Up => PRECOMPUTED_VERTS[0], - Direction::Down => PRECOMPUTED_VERTS[1], - Direction::North => PRECOMPUTED_VERTS[2], - Direction::South => PRECOMPUTED_VERTS[3], - Direction::West => PRECOMPUTED_VERTS[4], - Direction::East => PRECOMPUTED_VERTS[5], - _ => unreachable!(), - } - } - - pub fn get_offset(&self) -> (i32, i32, i32) { - match *self { - Direction::Up => (0, 1, 0), - Direction::Down => (0, -1, 0), - Direction::North => (0, 0, -1), - Direction::South => (0, 0, 1), - Direction::West => (-1, 0, 0), - Direction::East => (1, 0, 0), - _ => unreachable!(), - } - } - - pub fn as_string(&self) -> &'static str { - match *self { - Direction::Up => "up", - Direction::Down => "down", - Direction::North => "north", - Direction::South => "south", - Direction::West => "west", - Direction::East => "east", - Direction::Invalid => "invalid", - } - } - - pub fn index(&self) -> usize { - match *self { - Direction::Up => 0, - Direction::Down => 1, - Direction::North => 2, - Direction::South => 3, - Direction::West => 4, - Direction::East => 5, - _ => unreachable!(), - } - } -} - - - #[derive(Clone, Copy, Debug)] pub enum Gamemode { Survival = 0, From f282afe8871d2814af8492754f8dbc470f0642c0 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sun, 3 Apr 2016 20:53:40 +0100 Subject: [PATCH 064/160] Replace usages of x,y,z for block positions with Position --- protocol/src/protocol/mod.rs | 21 +++++++++++++++++++++ protocol/src/protocol/packet.rs | 28 ++++++++++++++-------------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 472d95b..1a9aaee 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -32,6 +32,7 @@ use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; use flate2::read::{ZlibDecoder, ZlibEncoder}; use flate2; use time; +use shared::Position; pub const SUPPORTED_PROTOCOL: i32 = 109; @@ -73,6 +74,7 @@ macro_rules! state_packets { use nbt; use types; use item; + use shared::Position; pub mod internal_ids { @@ -620,6 +622,25 @@ impl fmt::Debug for VarLong { } } +impl Serializable for Position { + fn read_from(buf: &mut io::Read) -> Result { + let pos = try!(buf.read_u64::()); + Ok(Position::new( + ((pos as i64) >> 38) as i32, + (((pos as i64) >> 26) & 0xFFF) as i32, + ((pos as i64) << 38 >> 38) as i32 + )) + } + fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + let pos = (((self.x as u64) & 0x3FFFFFF) << 38) + | (((self.y as u64) & 0xFFF) << 26) + | ((self.z as u64) & 0x3FFFFFF); + try!(buf.write_u64::(pos)); + Result::Ok(()) + } +} + + /// Direction is used to define whether packets are going to the /// server or the client. #[derive(Clone, Copy)] diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index c49bfab..50ce85e 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -59,7 +59,7 @@ state_packets!( text: String =, assume_command: bool =, has_target: bool =, - target: Option = when(|p: &TabComplete| p.has_target), + target: Option = when(|p: &TabComplete| p.has_target), } // ChatMessage is sent by the client when it sends a chat message or // executes a command (prefixed by '/'). @@ -177,7 +177,7 @@ state_packets!( // It also can be sent for droppping items and eating/shooting. PlayerDigging { status: VarInt =, - location: types::Position =, + location: Position =, face: u8 =, } // PlayerAction is sent when a player preforms various actions. @@ -212,7 +212,7 @@ state_packets!( } // SetSign sets the text on a sign after placing it. SetSign { - location: types::Position =, + location: Position =, line1: String =, line2: String =, line3: String =, @@ -229,7 +229,7 @@ state_packets!( } // PlayerBlockPlacement is sent when the client tries to place a block. PlayerBlockPlacement { - location: types::Position =, + location: Position =, face: VarInt =, hand: VarInt =, cursor_x: u8 =, @@ -300,7 +300,7 @@ state_packets!( entity_id: VarInt =, uuid: UUID =, title: String =, - location: types::Position =, + location: Position =, direction: u8 =, } // SpawnPlayer is used to spawn a player when they are in range of the client. @@ -329,26 +329,26 @@ state_packets!( // animation played when a player starts digging a block. BlockBreakAnimation { entity_id: VarInt =, - location: types::Position =, + location: Position =, stage: i8 =, } // UpdateBlockEntity updates the nbt tag of a block entity in the // world. UpdateBlockEntity { - location: types::Position =, + location: Position =, action: u8 =, nbt: Option =, } // BlockAction triggers different actions depending on the target block. BlockAction { - location: types::Position =, + location: Position =, byte1: u8 =, byte2: u8 =, block_type: VarInt =, } // BlockChange is used to update a single block on the client. BlockChange { - location: types::Position =, + location: Position =, block_id: VarInt =, } // BossBar displays and/or changes a boss bar that is displayed on the @@ -505,7 +505,7 @@ state_packets!( // DisableRelative is set to true. Effect { effect_id: i32 =, - location: types::Position =, + location: Position =, data: i32 =, disable_relative: bool =, } @@ -596,7 +596,7 @@ state_packets!( // SignEditorOpen causes the client to open the editor for a sign so that // it can write to it. Only sent in vanilla when the player places a sign. SignEditorOpen { - location: types::Position =, + location: Position =, } // PlayerAbilities is used to modify the players current abilities. Flying, // creative, god mode etc. @@ -634,7 +634,7 @@ state_packets!( // EntityUsedBed is sent by the server when a player goes to bed. EntityUsedBed { entity_id: VarInt =, - location: types::Position =, + location: Position =, } // EntityDestroy destroys the entities with the ids in the provided slice. EntityDestroy { @@ -765,7 +765,7 @@ state_packets!( // SpawnPosition is sent to change the player's current spawn point. Currently // only used by the client for the compass. SpawnPosition { - location: types::Position =, + location: Position =, } // TimeUpdate is sent to sync the world's time to the client, the client // will manually tick the time itself so this doesn't need to sent repeatedly @@ -786,7 +786,7 @@ state_packets!( } // UpdateSign sets or changes the text on a sign. UpdateSign { - location: types::Position =, + location: Position =, line1: format::Component =, line2: format::Component =, line3: format::Component =, From 5f6e41f7007328d945eff33d9c722b5b4d1aed89 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Mon, 4 Apr 2016 12:37:21 +0100 Subject: [PATCH 065/160] Optimize chunk loading --- protocol/src/types/bit/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/types/bit/map.rs b/protocol/src/types/bit/map.rs index 3695df8..11979fe 100644 --- a/protocol/src/types/bit/map.rs +++ b/protocol/src/types/bit/map.rs @@ -61,7 +61,7 @@ impl Map { } pub fn from_raw(bits: Vec, size: usize) -> Map { Map { - length: (bits.len()*64 + 63) / size, + length: (bits.len()*64 + (size-1)) / size, bit_size: size, bits: bits, } From d0704b2a674f36bd553a63f18c1b9034d886f070 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Mon, 4 Apr 2016 22:08:24 +0100 Subject: [PATCH 066/160] Block entity support, implement signs --- protocol/src/types/bit/set.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/protocol/src/types/bit/set.rs b/protocol/src/types/bit/set.rs index 0a620ad..8c9c221 100644 --- a/protocol/src/types/bit/set.rs +++ b/protocol/src/types/bit/set.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Set { data: Vec, } @@ -65,4 +65,10 @@ impl Set { } true } + + pub fn or(&mut self, other: &Set) { + for (a, b) in self.data.iter_mut().zip(&other.data) { + *a = (*a) | *b; + } + } } From 24bdeb7d8fa5a33a0e82b313d7588a60e5268ae9 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Mon, 4 Apr 2016 23:50:27 +0100 Subject: [PATCH 067/160] Allow documentation on packets to be actually be considered documentation by rustdoc --- protocol/src/protocol/mod.rs | 12 +- protocol/src/protocol/packet.rs | 1726 +++++++++++++++---------------- 2 files changed, 870 insertions(+), 868 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 1a9aaee..44e73bc 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -43,9 +43,11 @@ macro_rules! state_packets { ($($state:ident $stateName:ident { $($dir:ident $dirName:ident { $( - $name:ident { - $($field:ident: $field_type:ty = $(when ($cond:expr))*, )+ - })* + $(#[$attr:meta])* + packet $name:ident { + $($(#[$fattr:meta])*field $field:ident: $field_type:ty = $(when ($cond:expr))*, )+ + } + )* })+ })+) => { use protocol::*; @@ -83,8 +85,8 @@ macro_rules! state_packets { $( #[derive(Default, Debug)] - pub struct $name { - $(pub $field: $field_type),+, + $(#[$attr])* pub struct $name { + $($(#[$fattr])* pub $field: $field_type),+, } impl PacketType for $name { diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 50ce85e..c6a3303 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -17,30 +17,30 @@ use format; state_packets!( handshake Handshaking { serverbound Serverbound { - // Handshake is the first packet sent in the protocol. - // Its used for deciding if the request is a client - // is requesting status information about the server - // (MOTD, players etc) or trying to login to the server. - // - // The host and port fields are not used by the vanilla - // server but are there for virtual server hosting to - // be able to redirect a client to a target server with - // a single address + port. - // - // Some modified servers/proxies use the handshake field - // differently, packing information into the field other - // than the hostname due to the protocol not providing - // any system for custom information to be transfered - // by the client to the server until after login. - Handshake { - // The protocol version of the connecting client - protocol_version: VarInt =, - // The hostname the client connected to - host: String =, - // The port the client connected to - port: u16 =, - // The next protocol state the client wants - next: VarInt =, + /// Handshake is the first packet sent in the protocol. + /// Its used for deciding if the request is a client + /// is requesting status information about the server + /// (MOTD, players etc) or trying to login to the server. + /// + /// The host and port fields are not used by the vanilla + /// server but are there for virtual server hosting to + /// be able to redirect a client to a target server with + /// a single address + port. + /// + /// Some modified servers/proxies use the handshake field + /// differently, packing information into the field other + /// than the hostname due to the protocol not providing + /// any system for custom information to be transfered + /// by the client to the server until after login. + packet Handshake { + /// The protocol version of the connecting client + field protocol_version: VarInt =, + /// The hostname the client connected to + field host: String =, + /// The port the client connected to + field port: u16 =, + /// The next protocol state the client wants + field next: VarInt =, } } clientbound Clientbound { @@ -48,900 +48,900 @@ state_packets!( } play Play { serverbound Serverbound { - // TeleportConfirm is sent by the client as a reply to a telport from - // the server. - TeleportConfirm { - teleport_id: VarInt =, + /// TeleportConfirm is sent by the client as a reply to a telport from + /// the server. + packet TeleportConfirm { + field teleport_id: VarInt =, } - // TabComplete is sent by the client when the client presses tab in - // the chat box. - TabComplete { - text: String =, - assume_command: bool =, - has_target: bool =, - target: Option = when(|p: &TabComplete| p.has_target), + /// TabComplete is sent by the client when the client presses tab in + /// the chat box. + packet TabComplete { + field text: String =, + field assume_command: bool =, + field has_target: bool =, + field target: Option = when(|p: &TabComplete| p.has_target), } - // ChatMessage is sent by the client when it sends a chat message or - // executes a command (prefixed by '/'). - ChatMessage { - message: String =, + /// ChatMessage is sent by the client when it sends a chat message or + /// executes a command (prefixed by '/'). + packet ChatMessage { + field message: String =, } - // ClientStatus is sent to update the client's status - ClientStatus { - action_id: VarInt =, + /// ClientStatus is sent to update the client's status + packet ClientStatus { + field action_id: VarInt =, } - // ClientSettings is sent by the client to update its current settings. - ClientSettings { - locale: String =, - view_distance: u8 =, - chat_mode: VarInt =, - chat_colors: bool =, - displayed_skin_parts: u8 =, - main_hand: VarInt =, + /// ClientSettings is sent by the client to update its current settings. + packet ClientSettings { + field locale: String =, + field view_distance: u8 =, + field chat_mode: VarInt =, + field chat_colors: bool =, + field displayed_skin_parts: u8 =, + field main_hand: VarInt =, } - // ConfirmTransactionServerbound is a reply to ConfirmTransaction. - ConfirmTransactionServerbound { - id: u8 =, - action_number: i16 =, - accepted: bool =, + /// ConfirmTransactionServerbound is a reply to ConfirmTransaction. + packet ConfirmTransactionServerbound { + field id: u8 =, + field action_number: i16 =, + field accepted: bool =, } - // EnchantItem is sent when the client enchants an item. - EnchantItem { - id: u8 =, - enchantment: u8 =, + /// EnchantItem is sent when the client enchants an item. + packet EnchantItem { + field id: u8 =, + field enchantment: u8 =, } - // ClickWindow is sent when the client clicks in a window. - ClickWindow { - id: u8 =, - slot: i16 =, - button: u8 =, - action_number: u16 =, - mode: VarInt =, - clicked_item: Option =, + /// ClickWindow is sent when the client clicks in a window. + packet ClickWindow { + field id: u8 =, + field slot: i16 =, + field button: u8 =, + field action_number: u16 =, + field mode: VarInt =, + field clicked_item: Option =, } - // CloseWindow is sent when the client closes a window. - CloseWindow { - id: u8 =, + /// CloseWindow is sent when the client closes a window. + packet CloseWindow { + field id: u8 =, } - // PluginMessageServerbound is used for custom messages between the client - // and server. This is mainly for plugins/mods but vanilla has a few channels - // registered too. - PluginMessageServerbound { - channel: String =, - data: Vec =, + /// PluginMessageServerbound is used for custom messages between the client + /// and server. This is mainly for plugins/mods but vanilla has a few channels + /// registered too. + packet PluginMessageServerbound { + field channel: String =, + field data: Vec =, } - // UseEntity is sent when the user interacts (right clicks) or attacks - // (left clicks) an entity. - UseEntity { - target_id: VarInt =, - ty: VarInt =, - target_x: f32 = when(|p: &UseEntity| p.ty.0 == 2), - target_y: f32 = when(|p: &UseEntity| p.ty.0 == 2), - target_z: f32 = when(|p: &UseEntity| p.ty.0 == 2), - hand: VarInt = when(|p: &UseEntity| p.ty.0 == 0 || p.ty.0 == 2), + /// UseEntity is sent when the user interacts (right clicks) or attacks + /// (left clicks) an entity. + packet UseEntity { + field target_id: VarInt =, + field ty: VarInt =, + field target_x: f32 = when(|p: &UseEntity| p.ty.0 == 2), + field target_y: f32 = when(|p: &UseEntity| p.ty.0 == 2), + 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), } - // KeepAliveServerbound is sent by a client as a response to a - // KeepAliveClientbound. If the client doesn't reply the server - // may disconnect the client. - KeepAliveServerbound { - id: VarInt =, + /// KeepAliveServerbound is sent by a client as a response to a + /// KeepAliveClientbound. If the client doesn't reply the server + /// may disconnect the client. + packet KeepAliveServerbound { + field id: VarInt =, } - // PlayerPosition is used to update the player's position. - PlayerPosition { - x: f64 =, - y: f64 =, - z: f64 =, - on_ground: bool =, + /// PlayerPosition is used to update the player's position. + packet PlayerPosition { + field x: f64 =, + field y: f64 =, + field z: f64 =, + field on_ground: bool =, } - // PlayerPositionLook is a combination of PlayerPosition and - // PlayerLook. - PlayerPositionLook { - x: f64 =, - y: f64 =, - z: f64 =, - yaw: f32 =, - pitch: f32 =, - on_ground: bool =, + /// PlayerPositionLook is a combination of PlayerPosition and + /// PlayerLook. + packet PlayerPositionLook { + field x: f64 =, + field y: f64 =, + field z: f64 =, + field yaw: f32 =, + field pitch: f32 =, + field on_ground: bool =, } - // PlayerLook is used to update the player's rotation. - PlayerLook { - yaw: f32 =, - pitch: f32 =, - on_ground: bool =, + /// PlayerLook is used to update the player's rotation. + packet PlayerLook { + field yaw: f32 =, + field pitch: f32 =, + field on_ground: bool =, } - // Player is used to update whether the player is on the ground or not. - Player { - on_ground: bool =, + /// Player is used to update whether the player is on the ground or not. + packet Player { + field on_ground: bool =, } - // Sent by the client when in a vehicle instead of the normal move packet. - VehicleMove { - x: f64 =, - y: f64 =, - z: f64 =, - yaw: f32 =, - pitch: f32 =, + /// Sent by the client when in a vehicle instead of the normal move packet. + packet VehicleMove { + field x: f64 =, + field y: f64 =, + field z: f64 =, + field yaw: f32 =, + field pitch: f32 =, } - // TODO: Document - SteerBoat { - unknown: bool =, - unknown2: bool =, + /// TODO: Document + packet SteerBoat { + field unknown: bool =, + field unknown2: bool =, } - // ClientAbilities is used to modify the players current abilities. - // Currently flying is the only one - ClientAbilities { - flags: u8 =, - flying_speed: f32 =, - walking_speed: f32 =, + /// ClientAbilities is used to modify the players current abilities. + /// Currently flying is the only one + packet ClientAbilities { + field flags: u8 =, + field flying_speed: f32 =, + field walking_speed: f32 =, } - // PlayerDigging is sent when the client starts/stops digging a block. - // It also can be sent for droppping items and eating/shooting. - PlayerDigging { - status: VarInt =, - location: Position =, - face: u8 =, + /// PlayerDigging is sent when the client starts/stops digging a block. + /// It also can be sent for droppping items and eating/shooting. + packet PlayerDigging { + field status: VarInt =, + field location: Position =, + field face: u8 =, } - // PlayerAction is sent when a player preforms various actions. - PlayerAction { - entity_id: VarInt =, - action_id: VarInt =, - jump_boost: VarInt =, + /// PlayerAction is sent when a player preforms various actions. + packet PlayerAction { + field entity_id: VarInt =, + field action_id: VarInt =, + field jump_boost: VarInt =, } - // SteerVehicle is sent by the client when steers or preforms an action - // on a vehicle. - SteerVehicle { - sideways: f32 =, - forward: f32 =, - flags: u8 =, + /// SteerVehicle is sent by the client when steers or preforms an action + /// on a vehicle. + packet SteerVehicle { + field sideways: f32 =, + field forward: f32 =, + field flags: u8 =, } - // ResourcePackStatus informs the server of the client's current progress - // in activating the requested resource pack - ResourcePackStatus { - hash: String =, - result: VarInt =, + /// ResourcePackStatus informs the server of the client's current progress + /// in activating the requested resource pack + packet ResourcePackStatus { + field hash: String =, + field result: VarInt =, } - // HeldItemChange is sent when the player changes the currently active - // hotbar slot. - HeldItemChange { - slot: i16 =, + /// HeldItemChange is sent when the player changes the currently active + /// hotbar slot. + packet HeldItemChange { + field slot: i16 =, } - // CreativeInventoryAction is sent when the client clicks in the creative - // inventory. This is used to spawn items in creative. - CreativeInventoryAction { - slot: i16 =, - clicked_item: Option =, + /// 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 =, } - // SetSign sets the text on a sign after placing it. - SetSign { - location: Position =, - line1: String =, - line2: String =, - line3: String =, - line4: String =, + /// SetSign sets the text on a sign after placing it. + packet SetSign { + field location: Position =, + field line1: String =, + field line2: String =, + field line3: String =, + field line4: String =, } - // ArmSwing is sent by the client when the player left clicks (to swing their - // arm). - ArmSwing { - hand: VarInt =, + /// ArmSwing is sent by the client when the player left clicks (to swing their + /// arm). + packet ArmSwing { + field hand: VarInt =, } - // SpectateTeleport is sent by clients in spectator mode to teleport to a player. - SpectateTeleport { - target: UUID =, + /// SpectateTeleport is sent by clients in spectator mode to teleport to a player. + packet SpectateTeleport { + field target: UUID =, } - // PlayerBlockPlacement is sent when the client tries to place a block. - PlayerBlockPlacement { - location: Position =, - face: VarInt =, - hand: VarInt =, - cursor_x: u8 =, - cursor_y: u8 =, - cursor_z: u8 =, + /// PlayerBlockPlacement is sent when the client tries to place a block. + packet PlayerBlockPlacement { + field location: Position =, + field face: VarInt =, + field hand: VarInt =, + field cursor_x: u8 =, + field cursor_y: u8 =, + field cursor_z: u8 =, } - // UseItem is sent when the client tries to use an item. - UseItem { - hand: VarInt =, + /// UseItem is sent when the client tries to use an item. + packet UseItem { + field hand: VarInt =, } } clientbound Clientbound { - // SpawnObject is used to spawn an object or vehicle into the world when it - // is in range of the client. - SpawnObject { - entity_id: VarInt =, - uuid: UUID =, - ty: u8 =, - x: f64 =, - y: f64 =, - z: f64 =, - pitch: i8 =, - yaw: i8 =, - data: i32 =, - velocity_x: i16 =, - velocity_y: i16 =, - velocity_z: i16 =, - } - // 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. - SpawnExperienceOrb { - entity_id: VarInt =, - x: f64 =, - y: f64 =, - z: f64 =, - count: i16 =, - } - // SpawnGlobalEntity spawns an entity which is visible from anywhere in the - // world. Currently only used for lightning. - SpawnGlobalEntity { - entity_id: VarInt =, - ty: u8 =, - x: f64 =, - y: f64 =, - z: f64 =, - } - // SpawnMob is used to spawn a living entity into the world when it is in - // range of the client. - SpawnMob { - entity_id: VarInt =, - uuid: UUID =, - ty: u8 =, - x: f64 =, - y: f64 =, - z: f64 =, - yaw: i8 =, - pitch: i8 =, - head_pitch: i8 =, - velocity_x: i16 =, - velocity_y: i16 =, - velocity_z: i16 =, - metadata: types::Metadata =, - } - // 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. - SpawnPainting { - entity_id: VarInt =, - uuid: UUID =, - title: String =, - location: Position =, - direction: u8 =, - } - // SpawnPlayer is used to spawn a player when they are in range of the client. - // This packet alone isn't enough to display the player as the skin and username - // information is in the player information packet. - SpawnPlayer { - entity_id: VarInt =, - uuid: UUID =, - x: f64 =, - y: f64 =, - z: f64 =, - yaw: i8 =, - pitch: i8 =, - metadata: types::Metadata =, - } - // Animation is sent by the server to play an animation on a specific entity. - Animation { - entity_id: VarInt =, - animation_id: u8 =, - } - // Statistics is used to update the statistics screen for the client. - Statistics { - statistices: LenPrefixed =, - } - // BlockBreakAnimation is used to create and update the block breaking - // animation played when a player starts digging a block. - BlockBreakAnimation { - entity_id: VarInt =, - location: Position =, - stage: i8 =, - } - // UpdateBlockEntity updates the nbt tag of a block entity in the - // world. - UpdateBlockEntity { - location: Position =, - action: u8 =, - nbt: Option =, - } - // BlockAction triggers different actions depending on the target block. - BlockAction { - location: Position =, - byte1: u8 =, - byte2: u8 =, - block_type: VarInt =, - } - // BlockChange is used to update a single block on the client. - BlockChange { - location: Position =, - block_id: VarInt =, - } - // BossBar displays and/or changes a boss bar that is displayed on the - // top of the client's screen. This is normally used for bosses such as - // the ender dragon or the wither. - BossBar { - uuid: UUID =, - action: VarInt =, - title: format::Component = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 3), - health: f32 = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 2), - color: VarInt = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 4), - style: VarInt = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 4), - flags: u8 = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 5), - } - // ServerDifficulty changes the displayed difficulty in the client's menu - // as well as some ui changes for hardcore. - ServerDifficulty { - difficulty: u8 =, - } - // TabCompleteReply is sent as a reply to a tab completion request. - // The matches should be possible completions for the command/chat the - // player sent. - TabCompleteReply { - matches: LenPrefixed =, - } - // 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. - ServerMessage { - message: format::Component =, - // 0 - Chat message, 1 - System message, 2 - Action bar message - position: u8 =, - } - // MultiBlockChange is used to update a batch of blocks in a single packet. - MultiBlockChange { - chunk_x: i32 =, - chunk_z: i32 =, - records: LenPrefixed =, - } - // ConfirmTransaction notifies the client whether a transaction was successful - // or failed (e.g. due to lag). - ConfirmTransaction { - id: u8 =, - action_number: i16 =, - accepted: bool =, - } - // WindowClose forces the client to close the window with the given id, - // e.g. a chest getting destroyed. - WindowClose { - id: u8 =, - } - // WindowOpen tells the client to open the inventory window of the given - // type. The ID is used to reference the instance of the window in - // other packets. - WindowOpen { - id: u8 =, - ty: String =, - title: format::Component =, - slot_count: u8 =, - entity_id: i32 = when(|p: &WindowOpen| p.ty == "EntityHorse"), - } - // WindowItems sets every item in a window. - WindowItems { - id: u8 =, - items: LenPrefixed> =, - } - // WindowProperty changes the value of a property of a window. Properties - // vary depending on the window type. - WindowProperty { - id: u8 =, - property: i16 =, - value: i16 =, - } - // WindowSetSlot changes an itemstack in one of the slots in a window. - WindowSetSlot { - id: u8 =, - property: i16 =, - item: Option =, - } - // SetCooldown disables a set item (by id) for the set number of ticks - SetCooldown { - item_id: VarInt =, - ticks: VarInt =, - } - // PluginMessageClientbound is used for custom messages between the client - // and server. This is mainly for plugins/mods but vanilla has a few channels - // registered too. - PluginMessageClientbound { - channel: String =, - data: Vec =, - } - // Plays a sound by name on the client - NamedSoundEffect { - name: String =, - category: VarInt =, - x: i32 =, - y: i32 =, - z: i32 =, - volume: f32 =, - pitch: u8 =, - } - // Disconnect causes the client to disconnect displaying the passed reason. - Disconnect { - reason: format::Component =, - } - // EntityAction causes an entity to preform an action based on the passed - // id. - EntityAction { - entity_id: i32 =, - action_id: u8 =, - } - // Explosion is sent when an explosion is triggered (tnt, creeper etc). - // This plays the effect and removes the effected blocks. - Explosion { - x: f32 =, - y: f32 =, - z: f32 =, - radius: f32 =, - records: LenPrefixed =, - velocity_x: f32 =, - velocity_y: f32 =, - velocity_z: f32 =, - } - // ChunkUnload tells the client to unload the chunk at the specified - // position. - ChunkUnload { - x: i32 =, - z: i32 =, - } - // ChangeGameState is used to modify the game's state like gamemode or - // weather. - ChangeGameState { - reason: u8 =, - value: f32 =, - } - // KeepAliveClientbound is sent by a server to check if the - // client is still responding and keep the connection open. - // The client should reply with the KeepAliveServerbound - // packet setting ID to the same as this one. - KeepAliveClientbound { - id: VarInt =, - } - // ChunkData sends or updates a single chunk on the client. If New is set - // then biome data should be sent too. - ChunkData { - chunk_x: i32 =, - chunk_z: i32 =, - new: bool =, - bitmask: VarInt =, - data: LenPrefixedBytes =, - } - // 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. - Effect { - effect_id: i32 =, - location: Position =, - data: i32 =, - disable_relative: bool =, - } - // Particle spawns particles at the target location with the various - // modifiers. - Particle { - particle_id: i32 =, - long_distance: bool =, - x: f32 =, - y: f32 =, - z: f32 =, - offset_x: f32 =, - offset_y: f32 =, - offset_z: f32 =, - speed: f32 =, - count: i32 =, - data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38), - data2: VarInt = when(|p: &Particle| p.particle_id == 36), - } - // JoinGame is sent after completing the login process. This - // sets the initial state for the client. - JoinGame { - // The entity id the client will be referenced by - entity_id: i32 =, - // The starting gamemode of the client - gamemode: u8 =, - // The dimension the client is starting in - dimension: i32 =, - // The difficuilty setting for the server - difficulty: u8 =, - // The max number of players on the server - max_players: u8 =, - // The level type of the server - level_type: String =, - // Whether the client should reduce the amount of debug - // information it displays in F3 mode - reduced_debug_info: bool =, - } - // Maps updates a single map's contents - Maps { - item_damage: VarInt =, - scale: i8 =, - tracking_position: bool =, - icons: LenPrefixed =, - columns: u8 =, - rows: Option = when(|p: &Maps| p.columns > 0), - x: Option = when(|p: &Maps| p.columns > 0), - z: Option = when(|p: &Maps| p.columns > 0), - data: Option> = when(|p: &Maps| p.columns > 0), - } - // EntityMove moves the entity with the id by the offsets provided. - EntityMove { - entity_id: VarInt =, - delta_x: i16 =, - delta_y: i16 =, - delta_z: i16 =, - on_ground: bool =, - } - // EntityLookAndMove is a combination of EntityMove and EntityLook. - EntityLookAndMove { - entity_id: VarInt =, - delta_x: i16 =, - delta_y: i16 =, - delta_z: i16 =, - yaw: i8 =, - pitch: i8 =, - on_ground: bool =, - } - // EntityLook rotates the entity to the new angles provided. - EntityLook { - entity_id: VarInt =, - yaw: i8 =, - pitch: i8 =, - on_ground: bool =, - } - // Entity does nothing. It is a result of subclassing used in Minecraft. - Entity { - entity_id: VarInt =, - } - // Teleports the player's vehicle - VehicleTeleport { - x: f64 =, - y: f64 =, - z: f64 =, - yaw: f32 =, - pitch: f32 =, - } - // SignEditorOpen causes the client to open the editor for a sign so that - // it can write to it. Only sent in vanilla when the player places a sign. - SignEditorOpen { - location: Position =, - } - // PlayerAbilities is used to modify the players current abilities. Flying, - // creative, god mode etc. - PlayerAbilities { - flags: u8 =, - flying_speed: f32 =, - walking_speed: f32 =, - } - // CombatEvent is used for... you know, I never checked. I have no - // clue. - CombatEvent { - event: VarInt =, - direction: Option = when(|p: &CombatEvent| p.event.0 == 1), - player_id: Option = when(|p: &CombatEvent| p.event.0 == 2), - entity_id: Option = when(|p: &CombatEvent| p.event.0 == 1 || p.event.0 == 2), - message: Option = when(|p: &CombatEvent| p.event.0 == 2), - } - // PlayerInfo is sent by the server for every player connected to the server - // to provide skin and username information as well as ping and gamemode info. - PlayerInfo { - inner: packet::PlayerInfoData =, // Ew but watcha gonna do? - } - // 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. - TeleportPlayer { - x: f64 =, - y: f64 =, - z: f64 =, - yaw: f32 =, - pitch: f32 =, - flags: u8 =, - teleport_id: VarInt =, - } - // EntityUsedBed is sent by the server when a player goes to bed. - EntityUsedBed { - entity_id: VarInt =, - location: Position =, - } - // EntityDestroy destroys the entities with the ids in the provided slice. - EntityDestroy { - entity_ids: LenPrefixed =, - } - // EntityRemoveEffect removes an effect from an entity. - EntityRemoveEffect { - entity_id: VarInt =, - effect_id: i8 =, - } - // ResourcePackSend causes the client to check its cache for the requested - // resource packet and download it if its missing. Once the resource pack - // is obtained the client will use it. - ResourcePackSend { - url: String =, - hash: String =, - } - // Respawn is sent to respawn the player after death or when they move worlds. - Respawn { - dimension: i32 =, - difficulty: u8 =, - gamemode: u8 =, - level_type: String =, - } - // EntityHeadLook rotates an entity's head to the new angle. - EntityHeadLook { - entity_id: VarInt =, - head_yaw: i8 =, - } - // WorldBorder configures the world's border. - WorldBorder { - action: VarInt =, - old_radius: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1), - new_radius: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1 || p.action.0 == 0), - speed: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1), - x: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 2), - z: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 2), - portal_boundary: Option = when(|p: &WorldBorder| p.action.0 == 3), - warning_time: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 4), - warning_blocks: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 5), - } - // Camera causes the client to spectate the entity with the passed id. - // Use the player's id to de-spectate. - Camera { - target_id: VarInt =, - } - // SetCurrentHotbarSlot changes the player's currently selected hotbar item. - SetCurrentHotbarSlot { - slot: u8 =, - } - // ScoreboardDisplay is used to set the display position of a scoreboard. - ScoreboardDisplay { - position: u8 =, - name: String =, - } - // EntityMetadata updates the metadata for an entity. - EntityMetadata { - entity_id: VarInt =, - metadata: types::Metadata =, - } - // EntityAttach attaches to entities together, either by mounting or leashing. - // -1 can be used at the EntityID to deattach. - EntityAttach { - entity_id: i32 =, - vehicle: i32 =, - } - // EntityVelocity sets the velocity of an entity in 1/8000 of a block - // per a tick. - EntityVelocity { - entity_id: VarInt =, - velocity_x: i16 =, - velocity_y: i16 =, - velocity_z: i16 =, - } - // EntityEquipment is sent to display an item on an entity, like a sword - // or armor. Slot 0 is the held item and slots 1 to 4 are boots, leggings - // chestplate and helmet respectively. - EntityEquipment { - entity_id: VarInt =, - slot: VarInt =, - item: Option =, - } - // SetExperience updates the experience bar on the client. - SetExperience { - experience_bar: f32 =, - level: VarInt =, - total_experience: VarInt =, - } - // UpdateHealth is sent by the server to update the player's health and food. - UpdateHealth { - health: f32 =, - food: VarInt =, - food_saturation: f32 =, - } - // ScoreboardObjective creates/updates a scoreboard objective. - ScoreboardObjective { - name: String =, - mode: u8 =, - value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), - ty: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), - } - // SetPassengers mounts entities to an entity - SetPassengers { - entity_id: VarInt =, - passengers: LenPrefixed =, - } - // Teams creates and updates teams - Teams { - name: String =, - mode: u8 =, - display_name: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - prefix: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - suffix: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - flags: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - name_tag_visibility: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - collision_rule: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - color: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - players: Option> = when(|p: &Teams| p.mode == 0 || p.mode == 3 || p.mode == 4), - } - // UpdateScore is used to update or remove an item from a scoreboard - // objective. - UpdateScore { - name: String =, - action: u8 =, - object_name: String =, - value: Option = when(|p: &UpdateScore| p.action != 1), - } - // SpawnPosition is sent to change the player's current spawn point. Currently - // only used by the client for the compass. - SpawnPosition { - location: Position =, - } - // TimeUpdate is sent to sync the world's time to the client, the client - // will manually tick the time itself so this doesn't need to sent repeatedly - // but if the server or client has issues keeping up this can fall out of sync - // so it is a good idea to send this now and again - TimeUpdate { - world_age: i64 =, - time_of_day: i64 =, - } - // Title configures an on-screen title. - Title { - action: VarInt =, - title: Option = when(|p: &Title| p.action.0 == 0), - sub_title: Option = when(|p: &Title| p.action.0 == 1), - fade_in: Option = when(|p: &Title| p.action.0 == 2), - fade_stay: Option = when(|p: &Title| p.action.0 == 2), - fade_out: Option = when(|p: &Title| p.action.0 == 2), - } - // UpdateSign sets or changes the text on a sign. - UpdateSign { - location: Position =, - line1: format::Component =, - line2: format::Component =, - line3: format::Component =, - line4: format::Component =, - } - // SoundEffect plays the named sound at the target location. - SoundEffect { - name: VarInt =, - category: VarInt =, - x: i32 =, - y: i32 =, - z: i32 =, - volume: f32 =, - pitch: u8 =, - } - // PlayerListHeaderFooter updates the header/footer of the player list. - PlayerListHeaderFooter { - header: format::Component =, - footer: format::Component =, - } - // CollectItem causes the collected item to fly towards the collector. This - // does not destroy the entity. - CollectItem { - collected_entity_id: VarInt =, - collector_entity_id: VarInt =, - } - // EntityTeleport teleports the entity to the target location. This is - // sent if the entity moves further than EntityMove allows. - EntityTeleport { - entity_id: VarInt =, - x: f64 =, - y: f64 =, - z: f64 =, - yaw: i8 =, - pitch: i8 =, - on_ground: bool =, - } - // EntityProperties updates the properties for an entity. - EntityProperties { - entity_id: VarInt =, - properties: LenPrefixed =, - } - // EntityEffect applies a status effect to an entity for a given duration. - EntityEffect { - entity_id: VarInt =, - effect_id: i8 =, - amplifier: i8 =, - duration: VarInt =, - hide_particles: bool =, + /// SpawnObject is used to spawn an object or vehicle into the world when it + /// is in range of the client. + packet SpawnObject { + field entity_id: VarInt =, + field uuid: UUID =, + field ty: u8 =, + field x: f64 =, + field y: f64 =, + field z: f64 =, + field pitch: i8 =, + field yaw: i8 =, + field data: i32 =, + field velocity_x: i16 =, + field velocity_y: i16 =, + field velocity_z: i16 =, + } + /// 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. + packet SpawnExperienceOrb { + field entity_id: VarInt =, + field x: f64 =, + field y: f64 =, + field z: f64 =, + field count: i16 =, + } + /// SpawnGlobalEntity spawns an entity which is visible from anywhere in the + /// world. Currently only used for lightning. + packet SpawnGlobalEntity { + field entity_id: VarInt =, + field ty: u8 =, + field x: f64 =, + field y: f64 =, + field z: f64 =, + } + /// SpawnMob is used to spawn a living entity into the world when it is in + /// range of the client. + packet SpawnMob { + field entity_id: VarInt =, + field uuid: UUID =, + field ty: u8 =, + field x: f64 =, + field y: f64 =, + field z: f64 =, + 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::Metadata =, + } + /// 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. + packet SpawnPainting { + field entity_id: VarInt =, + field uuid: UUID =, + field title: String =, + field location: Position =, + field direction: u8 =, + } + /// SpawnPlayer is used to spawn a player when they are in range of the client. + /// This packet alone isn't enough to display the player as the skin and username + /// information is in the player information packet. + packet SpawnPlayer { + field entity_id: VarInt =, + field uuid: UUID =, + field x: f64 =, + field y: f64 =, + field z: f64 =, + field yaw: i8 =, + field pitch: i8 =, + field metadata: types::Metadata =, + } + /// Animation is sent by the server to play an animation on a specific entity. + packet Animation { + field entity_id: VarInt =, + field animation_id: u8 =, + } + /// Statistics is used to update the statistics screen for the client. + packet Statistics { + field statistices: LenPrefixed =, + } + /// BlockBreakAnimation is used to create and update the block breaking + /// animation played when a player starts digging a block. + packet BlockBreakAnimation { + field entity_id: VarInt =, + field location: Position =, + field stage: i8 =, + } + /// UpdateBlockEntity updates the nbt tag of a block entity in the + /// world. + packet UpdateBlockEntity { + field location: Position =, + field action: u8 =, + field nbt: Option =, + } + /// BlockAction triggers different actions depending on the target block. + packet BlockAction { + field location: Position =, + field byte1: u8 =, + field byte2: u8 =, + field block_type: VarInt =, + } + /// BlockChange is used to update a single block on the client. + packet BlockChange { + field location: Position =, + field block_id: VarInt =, + } + /// BossBar displays and/or changes a boss bar that is displayed on the + /// top of the client's screen. This is normally used for bosses such as + /// the ender dragon or the wither. + packet BossBar { + field uuid: UUID =, + field action: VarInt =, + field title: format::Component = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 3), + field health: f32 = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 2), + field color: VarInt = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 4), + field style: VarInt = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 4), + field flags: u8 = when(|p: &BossBar| p.action.0 == 0 || p.action.0 == 5), + } + /// ServerDifficulty changes the displayed difficulty in the client's menu + /// as well as some ui changes for hardcore. + packet ServerDifficulty { + field difficulty: u8 =, + } + /// TabCompleteReply is sent as a reply to a tab completion request. + /// The matches should be possible completions for the command/chat the + /// player sent. + packet TabCompleteReply { + field matches: LenPrefixed =, + } + /// 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. + packet ServerMessage { + field message: format::Component =, + /// 0 - Chat message, 1 - System message, 2 - Action bar message + field position: u8 =, + } + /// MultiBlockChange is used to update a batch of blocks in a single packet. + packet MultiBlockChange { + field chunk_x: i32 =, + field chunk_z: i32 =, + field records: LenPrefixed =, + } + /// ConfirmTransaction notifies the client whether a transaction was successful + /// or failed (e.g. due to lag). + packet ConfirmTransaction { + field id: u8 =, + field action_number: i16 =, + field accepted: bool =, + } + /// WindowClose forces the client to close the window with the given id, + /// e.g. a chest getting destroyed. + packet WindowClose { + field id: u8 =, + } + /// WindowOpen tells the client to open the inventory window of the given + /// type. The ID is used to reference the instance of the window in + /// other packets. + packet WindowOpen { + field id: u8 =, + field ty: String =, + field title: format::Component =, + field slot_count: u8 =, + field entity_id: i32 = when(|p: &WindowOpen| p.ty == "EntityHorse"), + } + /// WindowItems sets every item in a window. + packet WindowItems { + field id: u8 =, + field items: LenPrefixed> =, + } + /// WindowProperty changes the value of a property of a window. Properties + /// vary depending on the window type. + packet WindowProperty { + field id: u8 =, + field property: i16 =, + field value: i16 =, + } + /// WindowSetSlot changes an itemstack in one of the slots in a window. + packet WindowSetSlot { + field id: u8 =, + field property: i16 =, + field item: Option =, + } + /// SetCooldown disables a set item (by id) for the set number of ticks + packet SetCooldown { + field item_id: VarInt =, + field ticks: VarInt =, + } + /// PluginMessageClientbound is used for custom messages between the client + /// and server. This is mainly for plugins/mods but vanilla has a few channels + /// registered too. + packet PluginMessageClientbound { + field channel: String =, + field data: Vec =, + } + /// Plays a sound by name on the client + packet NamedSoundEffect { + field name: String =, + field category: VarInt =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field volume: f32 =, + field pitch: u8 =, + } + /// Disconnect causes the client to disconnect displaying the passed reason. + packet Disconnect { + field reason: format::Component =, + } + /// EntityAction causes an entity to preform an action based on the passed + /// id. + packet EntityAction { + field entity_id: i32 =, + field action_id: u8 =, + } + /// Explosion is sent when an explosion is triggered (tnt, creeper etc). + /// This plays the effect and removes the effected blocks. + packet Explosion { + field x: f32 =, + field y: f32 =, + field z: f32 =, + field radius: f32 =, + field records: LenPrefixed =, + field velocity_x: f32 =, + field velocity_y: f32 =, + field velocity_z: f32 =, + } + /// ChunkUnload tells the client to unload the chunk at the specified + /// position. + packet ChunkUnload { + field x: i32 =, + field z: i32 =, + } + /// ChangeGameState is used to modify the game's state like gamemode or + /// weather. + packet ChangeGameState { + field reason: u8 =, + field value: f32 =, + } + /// KeepAliveClientbound is sent by a server to check if the + /// client is still responding and keep the connection open. + /// The client should reply with the KeepAliveServerbound + /// packet setting ID to the same as this one. + packet KeepAliveClientbound { + field id: VarInt =, + } + /// ChunkData sends or updates a single chunk on the client. If New is set + /// then biome data should be sent too. + packet ChunkData { + field chunk_x: i32 =, + field chunk_z: i32 =, + field new: bool =, + field bitmask: VarInt =, + field data: LenPrefixedBytes =, + } + /// 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. + packet Effect { + field effect_id: i32 =, + field location: Position =, + field data: i32 =, + field disable_relative: bool =, + } + /// Particle spawns particles at the target location with the various + /// modifiers. + packet Particle { + field particle_id: i32 =, + field long_distance: bool =, + field x: f32 =, + field y: f32 =, + field z: f32 =, + field offset_x: f32 =, + field offset_y: f32 =, + field offset_z: f32 =, + field speed: f32 =, + field count: i32 =, + field data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38), + field data2: VarInt = when(|p: &Particle| p.particle_id == 36), + } + /// JoinGame is sent after completing the login process. This + /// sets the initial state for the client. + packet JoinGame { + /// The entity id the client will be referenced by + field entity_id: i32 =, + /// The starting gamemode of the client + field gamemode: u8 =, + /// The dimension the client is starting in + field dimension: i32 =, + /// The difficuilty setting for the server + field difficulty: u8 =, + /// The max number of players on the server + field max_players: u8 =, + /// The level type of the server + field level_type: String =, + /// Whether the client should reduce the amount of debug + /// information it displays in F3 mode + field reduced_debug_info: bool =, + } + /// Maps updates a single map's contents + packet Maps { + field item_damage: VarInt =, + field scale: i8 =, + field tracking_position: bool =, + field icons: LenPrefixed =, + field columns: u8 =, + field rows: Option = when(|p: &Maps| p.columns > 0), + field x: Option = when(|p: &Maps| p.columns > 0), + field z: Option = when(|p: &Maps| p.columns > 0), + field data: Option> = when(|p: &Maps| p.columns > 0), + } + /// EntityMove moves the entity with the id by the offsets provided. + packet EntityMove { + field entity_id: VarInt =, + field delta_x: i16 =, + field delta_y: i16 =, + field delta_z: i16 =, + field on_ground: bool =, + } + /// EntityLookAndMove is a combination of EntityMove and EntityLook. + packet EntityLookAndMove { + field entity_id: VarInt =, + field delta_x: i16 =, + field delta_y: i16 =, + field delta_z: i16 =, + field yaw: i8 =, + field pitch: i8 =, + field on_ground: bool =, + } + /// EntityLook rotates the entity to the new angles provided. + packet EntityLook { + field entity_id: VarInt =, + field yaw: i8 =, + field pitch: i8 =, + field on_ground: bool =, + } + /// Entity does nothing. It is a result of subclassing used in Minecraft. + packet Entity { + field entity_id: VarInt =, + } + /// Teleports the player's vehicle + packet VehicleTeleport { + field x: f64 =, + field y: f64 =, + field z: f64 =, + field yaw: f32 =, + field pitch: f32 =, + } + /// SignEditorOpen causes the client to open the editor for a sign so that + /// it can write to it. Only sent in vanilla when the player places a sign. + packet SignEditorOpen { + field location: Position =, + } + /// PlayerAbilities is used to modify the players current abilities. Flying, + /// creative, god mode etc. + packet PlayerAbilities { + field flags: u8 =, + field flying_speed: f32 =, + field walking_speed: f32 =, + } + /// CombatEvent is used for... you know, I never checked. I have no + /// clue. + packet CombatEvent { + field event: VarInt =, + field direction: Option = when(|p: &CombatEvent| p.event.0 == 1), + field player_id: Option = when(|p: &CombatEvent| p.event.0 == 2), + field entity_id: Option = when(|p: &CombatEvent| p.event.0 == 1 || p.event.0 == 2), + field message: Option = when(|p: &CombatEvent| p.event.0 == 2), + } + /// PlayerInfo is sent by the server for every player connected to the server + /// to provide skin and username information as well as ping and gamemode info. + packet PlayerInfo { + field inner: packet::PlayerInfoData =, + } + /// 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. + packet TeleportPlayer { + field x: f64 =, + field y: f64 =, + field z: f64 =, + field yaw: f32 =, + field pitch: f32 =, + field flags: u8 =, + field teleport_id: VarInt =, + } + /// EntityUsedBed is sent by the server when a player goes to bed. + packet EntityUsedBed { + field entity_id: VarInt =, + field location: Position =, + } + /// EntityDestroy destroys the entities with the ids in the provided slice. + packet EntityDestroy { + field entity_ids: LenPrefixed =, + } + /// EntityRemoveEffect removes an effect from an entity. + packet EntityRemoveEffect { + field entity_id: VarInt =, + field effect_id: i8 =, + } + /// ResourcePackSend causes the client to check its cache for the requested + /// resource packet and download it if its missing. Once the resource pack + /// is obtained the client will use it. + packet ResourcePackSend { + field url: String =, + field hash: String =, + } + /// Respawn is sent to respawn the player after death or when they move worlds. + packet Respawn { + field dimension: i32 =, + field difficulty: u8 =, + field gamemode: u8 =, + field level_type: String =, + } + /// EntityHeadLook rotates an entity's head to the new angle. + packet EntityHeadLook { + field entity_id: VarInt =, + field head_yaw: i8 =, + } + /// WorldBorder configures the world's border. + packet WorldBorder { + field action: VarInt =, + field old_radius: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1), + field new_radius: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1 || p.action.0 == 0), + field speed: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 1), + field x: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 2), + field z: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 2), + field portal_boundary: Option = when(|p: &WorldBorder| p.action.0 == 3), + field warning_time: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 4), + field warning_blocks: Option = when(|p: &WorldBorder| p.action.0 == 3 || p.action.0 == 5), + } + /// Camera causes the client to spectate the entity with the passed id. + /// Use the player's id to de-spectate. + packet Camera { + field target_id: VarInt =, + } + /// SetCurrentHotbarSlot changes the player's currently selected hotbar item. + packet SetCurrentHotbarSlot { + field slot: u8 =, + } + /// ScoreboardDisplay is used to set the display position of a scoreboard. + packet ScoreboardDisplay { + field position: u8 =, + field name: String =, + } + /// EntityMetadata updates the metadata for an entity. + packet EntityMetadata { + field entity_id: VarInt =, + field metadata: types::Metadata =, + } + /// EntityAttach attaches to entities together, either by mounting or leashing. + /// -1 can be used at the EntityID to deattach. + packet EntityAttach { + field entity_id: i32 =, + field vehicle: i32 =, + } + /// EntityVelocity sets the velocity of an entity in 1/8000 of a block + /// per a tick. + packet EntityVelocity { + field entity_id: VarInt =, + field velocity_x: i16 =, + field velocity_y: i16 =, + field velocity_z: i16 =, + } + /// EntityEquipment is sent to display an item on an entity, like a sword + /// or armor. Slot 0 is the held item and slots 1 to 4 are boots, leggings + /// chestplate and helmet respectively. + packet EntityEquipment { + field entity_id: VarInt =, + field slot: VarInt =, + field item: Option =, + } + /// SetExperience updates the experience bar on the client. + packet SetExperience { + field experience_bar: f32 =, + field level: VarInt =, + field total_experience: VarInt =, + } + /// UpdateHealth is sent by the server to update the player's health and food. + packet UpdateHealth { + field health: f32 =, + field food: VarInt =, + field food_saturation: f32 =, + } + /// ScoreboardObjective creates/updates a scoreboard objective. + packet ScoreboardObjective { + field name: String =, + field mode: u8 =, + field value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), + field ty: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), + } + /// SetPassengers mounts entities to an entity + packet SetPassengers { + field entity_id: VarInt =, + field passengers: LenPrefixed =, + } + /// Teams creates and updates teams + packet Teams { + field name: String =, + field mode: u8 =, + field display_name: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + field prefix: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + field suffix: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + field flags: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + field name_tag_visibility: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + field collision_rule: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + field color: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + field players: Option> = when(|p: &Teams| p.mode == 0 || p.mode == 3 || p.mode == 4), + } + /// UpdateScore is used to update or remove an item from a scoreboard + /// objective. + packet UpdateScore { + field name: String =, + field action: u8 =, + field object_name: String =, + field value: Option = when(|p: &UpdateScore| p.action != 1), + } + /// SpawnPosition is sent to change the player's current spawn point. Currently + /// only used by the client for the compass. + packet SpawnPosition { + field location: Position =, + } + /// TimeUpdate is sent to sync the world's time to the client, the client + /// will manually tick the time itself so this doesn't need to sent repeatedly + /// but if the server or client has issues keeping up this can fall out of sync + /// so it is a good idea to send this now and again + packet TimeUpdate { + field world_age: i64 =, + field time_of_day: i64 =, + } + /// Title configures an on-screen title. + packet Title { + field action: VarInt =, + field title: Option = when(|p: &Title| p.action.0 == 0), + field sub_title: Option = when(|p: &Title| p.action.0 == 1), + field fade_in: Option = when(|p: &Title| p.action.0 == 2), + field fade_stay: Option = when(|p: &Title| p.action.0 == 2), + field fade_out: Option = when(|p: &Title| p.action.0 == 2), + } + /// UpdateSign sets or changes the text on a sign. + packet UpdateSign { + field location: Position =, + field line1: format::Component =, + field line2: format::Component =, + field line3: format::Component =, + field line4: format::Component =, + } + /// SoundEffect plays the named sound at the target location. + packet SoundEffect { + field name: VarInt =, + field category: VarInt =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field volume: f32 =, + field pitch: u8 =, + } + /// PlayerListHeaderFooter updates the header/footer of the player list. + packet PlayerListHeaderFooter { + field header: format::Component =, + field footer: format::Component =, + } + /// CollectItem causes the collected item to fly towards the collector. This + /// does not destroy the entity. + packet CollectItem { + field collected_entity_id: VarInt =, + field collector_entity_id: VarInt =, + } + /// EntityTeleport teleports the entity to the target location. This is + /// sent if the entity moves further than EntityMove allows. + packet EntityTeleport { + field entity_id: VarInt =, + field x: f64 =, + field y: f64 =, + field z: f64 =, + field yaw: i8 =, + field pitch: i8 =, + field on_ground: bool =, + } + /// EntityProperties updates the properties for an entity. + packet EntityProperties { + field entity_id: VarInt =, + field properties: LenPrefixed =, + } + /// EntityEffect applies a status effect to an entity for a given duration. + packet EntityEffect { + field entity_id: VarInt =, + field effect_id: i8 =, + field amplifier: i8 =, + field duration: VarInt =, + field hide_particles: bool =, } } } login Login { serverbound Serverbound { - // LoginStart is sent immeditately after switching into the login - // state. The passed username is used by the server to authenticate - // the player in online mode. - LoginStart { - username: String =, + /// LoginStart is sent immeditately after switching into the login + /// state. The passed username is used by the server to authenticate + /// the player in online mode. + packet LoginStart { + field username: String =, } - // EncryptionResponse is sent as a reply to EncryptionRequest. All - // packets following this one must be encrypted with AES/CFB8 - // encryption. - EncryptionResponse { - // The key for the AES/CFB8 cipher encrypted with the - // public key - shared_secret: LenPrefixedBytes =, - // The verify token from the request encrypted with the - // public key - verify_token: LenPrefixedBytes =, + /// EncryptionResponse is sent as a reply to EncryptionRequest. All + /// packets following this one must be encrypted with AES/CFB8 + /// encryption. + packet EncryptionResponse { + /// The key for the AES/CFB8 cipher encrypted with the + /// public key + field shared_secret: LenPrefixedBytes =, + /// The verify token from the request encrypted with the + /// public key + field verify_token: LenPrefixedBytes =, } } clientbound Clientbound { - // LoginDisconnect is sent by the server if there was any issues - // authenticating the player during login or the general server - // issues (e.g. too many players). - LoginDisconnect { - reason: format::Component =, + /// LoginDisconnect is sent by the server if there was any issues + /// authenticating the player during login or the general server + /// issues (e.g. too many players). + packet LoginDisconnect { + field reason: format::Component =, } - // EncryptionRequest is sent by the server if the server is in - // online mode. If it is not sent then its assumed the server is - // in offline mode. - EncryptionRequest { - // Generally empty, left in from legacy auth - // but is still used by the client if provided - server_id: String =, - // A RSA Public key serialized in x.509 PRIX format - public_key: LenPrefixedBytes =, - // Token used by the server to verify encryption is working - // correctly - verify_token: LenPrefixedBytes =, + /// EncryptionRequest is sent by the server if the server is in + /// online mode. If it is not sent then its assumed the server is + /// in offline mode. + packet EncryptionRequest { + /// Generally empty, left in from legacy auth + /// but is still used by the client if provided + field server_id: String =, + /// A RSA Public key serialized in x.509 PRIX format + field public_key: LenPrefixedBytes =, + /// Token used by the server to verify encryption is working + /// correctly + field verify_token: LenPrefixedBytes =, } - // LoginSuccess is sent by the server if the player successfully - // authenicates with the session servers (online mode) or straight - // after LoginStart (offline mode). - LoginSuccess { - // String encoding of a uuid (with hyphens) - uuid: String =, - username: String =, + /// LoginSuccess is sent by the server if the player successfully + /// authenicates with the session servers (online mode) or straight + /// after LoginStart (offline mode). + packet LoginSuccess { + /// String encoding of a uuid (with hyphens) + field uuid: String =, + field username: String =, } - // SetInitialCompression sets the compression threshold during the - // login state. - SetInitialCompression { - // Threshold where a packet should be sent compressed - threshold: VarInt =, + /// SetInitialCompression sets the compression threshold during the + /// login state. + packet SetInitialCompression { + /// Threshold where a packet should be sent compressed + field threshold: VarInt =, } } } status Status { serverbound Serverbound { - // StatusRequest is sent by the client instantly after - // switching to the Status protocol state and is used - // to signal the server to send a StatusResponse to the - // client - StatusRequest { - empty: () =, + /// StatusRequest is sent by the client instantly after + /// switching to the Status protocol state and is used + /// to signal the server to send a StatusResponse to the + /// client + packet StatusRequest { + field empty: () =, } - // StatusPing is sent by the client after recieving a - // StatusResponse. The client uses the time from sending - // the ping until the time of recieving a pong to measure - // the latency between the client and the server. - StatusPing { - ping: i64 =, + /// StatusPing is sent by the client after recieving a + /// StatusResponse. The client uses the time from sending + /// the ping until the time of recieving a pong to measure + /// the latency between the client and the server. + packet StatusPing { + field ping: i64 =, } } clientbound Clientbound { - // StatusResponse is sent as a reply to a StatusRequest. - // The Status should contain a json encoded structure with - // version information, a player sample, a description/MOTD - // and optionally a favicon. + /// StatusResponse is sent as a reply to a StatusRequest. + /// The Status should contain a json encoded structure with + /// version information, a player sample, a description/MOTD + /// and optionally a favicon. // - // The structure is as follows - // { - // "version": { - // "name": "1.8.3", - // "protocol": 47, - // }, - // "players": { - // "max": 20, - // "online": 1, - // "sample": [ - // {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"} - // ] - // }, - // "description": "Hello world", - // "favicon": "data:image/png;base64," - // } - StatusResponse { - status: String =, + /// The structure is as follows + /// { + /// "version": { + /// "name": "1.8.3", + /// "protocol": 47, + /// }, + /// "players": { + /// "max": 20, + /// "online": 1, + /// "sample": [ + /// packet {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"} + /// ] + /// }, + /// "description": "Hello world", + /// "favicon": "data:image/png;base64," + /// } + packet StatusResponse { + field status: String =, } - // StatusPong is sent as a reply to a StatusPing. - // The Time field should be exactly the same as the - // one sent by the client. - StatusPong { - ping: i64 =, + /// StatusPong is sent as a reply to a StatusPing. + /// The Time field should be exactly the same as the + /// one sent by the client. + packet StatusPong { + field ping: i64 =, } } } From 844d78ac8e7c672e50c2060d5e8a6381e5466592 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Mon, 4 Apr 2016 23:58:40 +0100 Subject: [PATCH 068/160] Fix the formatting of StatusResponse's example --- protocol/src/protocol/packet.rs | 35 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index c6a3303..c931a8d 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -617,7 +617,7 @@ state_packets!( /// PlayerInfo is sent by the server for every player connected to the server /// to provide skin and username information as well as ping and gamemode info. packet PlayerInfo { - field inner: packet::PlayerInfoData =, + field inner: packet::PlayerInfoData =, } /// 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 @@ -919,21 +919,24 @@ state_packets!( /// and optionally a favicon. // /// The structure is as follows - /// { - /// "version": { - /// "name": "1.8.3", - /// "protocol": 47, - /// }, - /// "players": { - /// "max": 20, - /// "online": 1, - /// "sample": [ - /// packet {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"} - /// ] - /// }, - /// "description": "Hello world", - /// "favicon": "data:image/png;base64," - /// } + /// + /// ```json + /// { + /// "version": { + /// "name": "1.8.3", + /// "protocol": 47, + /// }, + /// "players": { + /// "max": 20, + /// "online": 1, + /// "sample": [ + /// packet {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"} + /// ] + /// }, + /// "description": "Hello world", + /// "favicon": "data:image/png;base64," + /// } + /// ``` packet StatusResponse { field status: String =, } From 5614fa07b55ea99a89a1bd69ef055496e17c9427 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Tue, 5 Apr 2016 18:50:53 +0100 Subject: [PATCH 069/160] Track player list information --- protocol/src/protocol/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 44e73bc..330b9ae 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -343,7 +343,7 @@ impl Serializable for f64 { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct UUID(u64, u64); impl Default for UUID { From d1b3180e9a6b98c041c935f2c7c3bfd6e99d0ec0 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Tue, 5 Apr 2016 19:35:58 +0100 Subject: [PATCH 070/160] Drop steven_openssl in favor of using the openssl crate (Closes #31) --- protocol/src/protocol/mod.rs | 15 ++++++++++----- protocol/src/protocol/mojang.rs | 13 +++++++------ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 330b9ae..f05fe35 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -14,7 +14,7 @@ #![allow(dead_code)] -use openssl; +use openssl::crypto::symm; use serde_json; use hyper; @@ -720,13 +720,16 @@ pub struct Conn { direction: Direction, pub state: State, - cipher: Option, + cipher: Option, compression_threshold: i32, compression_read: Option>>>, compression_write: Option>>>, } +// Needed because symm::Crypter isn't send +unsafe impl Send for Conn {} + impl Conn { pub fn new(target: &str) -> Result { // TODO SRV record support @@ -832,7 +835,9 @@ impl Conn { } pub fn enable_encyption(&mut self, key: &[u8], decrypt: bool) { - self.cipher = Option::Some(openssl::EVPCipher::new(key, key, decrypt)); + let cipher = symm::Crypter::new(symm::Type::AES_128_CFB8); + cipher.init(if decrypt { symm::Mode::Decrypt } else { symm::Mode::Encrypt }, key, key); + self.cipher = Option::Some(cipher); } pub fn set_compresssion(&mut self, threshold: i32) { @@ -940,7 +945,7 @@ impl Read for Conn { Option::None => self.stream.read(buf), Option::Some(cipher) => { let ret = try!(self.stream.read(buf)); - let data = cipher.decrypt(&buf[..ret]); + let data = cipher.update(&buf[..ret]); for i in 0..ret { buf[i] = data[i]; } @@ -955,7 +960,7 @@ impl Write for Conn { match self.cipher.as_mut() { Option::None => self.stream.write(buf), Option::Some(cipher) => { - let data = cipher.encrypt(buf); + let data = cipher.update(buf); try!(self.stream.write_all(&data[..])); Ok(buf.len()) } diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 45997f5..ee081e8 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use openssl; +use openssl::crypto::hash; use serde_json; use serde_json::builder::ObjectBuilder; use hyper; @@ -101,11 +101,12 @@ impl Profile { } pub fn join_server(&self, server_id: &str, shared_key: &[u8], public_key: &[u8]) -> Result<(), super::Error> { - let mut sha1 = openssl::SHA1::new(); - sha1.update(server_id.as_bytes()); - sha1.update(shared_key); - sha1.update(public_key); - let mut hash = sha1.bytes(); + use std::io::Write; + let mut sha1 = hash::Hasher::new(hash::Type::SHA1); + sha1.write_all(server_id.as_bytes()).unwrap(); + sha1.write_all(shared_key).unwrap(); + sha1.write_all(public_key).unwrap(); + let mut hash = sha1.finish(); // Mojang uses a hex method which allows for // negatives so we have to account for that. From 4d8e3db793dc98f1f13b09d2701cea05223ba400 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Wed, 6 Apr 2016 22:50:31 +0100 Subject: [PATCH 071/160] Store the client's UUID and username when logging in --- protocol/src/protocol/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index f05fe35..347c8c4 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -346,6 +346,28 @@ impl Serializable for f64 { #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct UUID(u64, u64); +impl UUID { + pub fn from_str(s: &str) -> UUID { + use rustc_serialize::hex::FromHex; + // TODO: Panics aren't the best idea here + if s.len() != 36 { + panic!("Invalid UUID format"); + } + let mut parts = s[..8].from_hex().unwrap(); + parts.extend_from_slice(&s[9..13].from_hex().unwrap()); + parts.extend_from_slice(&s[14..18].from_hex().unwrap()); + parts.extend_from_slice(&s[19..23].from_hex().unwrap()); + parts.extend_from_slice(&s[24..36].from_hex().unwrap()); + let mut high = 0u64; + let mut low = 0u64; + for i in 0 .. 8 { + high |= (parts[i] as u64) << (56 - i*8); + low |= (parts[i + 8] as u64) << (56 - i*8); + } + UUID(high, low) + } +} + impl Default for UUID { fn default() -> Self { UUID(0, 0) From 8f976b301496cfb69f1d28890013ce910492e563 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Fri, 8 Apr 2016 18:46:07 +0100 Subject: [PATCH 072/160] Clean up the protocol implementation to use generics instead of trait objects --- protocol/src/protocol/mod.rs | 108 ++++++++++++++++---------------- protocol/src/protocol/packet.rs | 28 ++++----- 2 files changed, 67 insertions(+), 69 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 347c8c4..fd86e6d 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -93,7 +93,7 @@ macro_rules! state_packets { fn packet_id(&self) -> i32 { internal_ids::$name } - fn write(self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write(self, buf: &mut W) -> Result<(), Error> { $( if true $(&& ($cond(&self)))* { try!(self.$field.write_to(buf)); @@ -111,7 +111,7 @@ macro_rules! state_packets { /// Returns the packet for the given state, direction and id after parsing the fields /// from the buffer. - pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut io::Read) -> Result, io::Error> { + pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut R) -> Result, Error> { match state { $( State::$stateName => { @@ -146,24 +146,24 @@ macro_rules! state_packets { pub mod packet; pub trait Serializable: Sized { - fn read_from(buf: &mut io::Read) -> Result; - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error>; + fn read_from(buf: &mut R) -> Result; + fn write_to(&self, buf: &mut W) -> Result<(), Error>; } impl Serializable for Vec { - fn read_from(buf: &mut io::Read) -> Result, io::Error> { + fn read_from(buf: &mut R) -> Result, Error> { let mut v = Vec::new(); try!(buf.read_to_end(&mut v)); Ok(v) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { - buf.write_all(&self[..]) + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + buf.write_all(&self[..]).map_err(|v| v.into()) } } impl Serializable for Option{ - fn read_from(buf: &mut io::Read) -> Result, io::Error> { + fn read_from(buf: &mut R) -> Result, Error> { let ty = try!(buf.read_u8()); if ty == 0 { Result::Ok(None) @@ -173,7 +173,7 @@ impl Serializable for Option{ Result::Ok(Some(nbt::NamedTag(name, tag))) } } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { match *self { Some(ref val) => { try!(buf.write_u8(10)); @@ -187,10 +187,10 @@ impl Serializable for Option{ } impl Serializable for Option where T : Serializable { - fn read_from(buf: &mut io::Read) -> Result, io::Error> { + fn read_from(buf: &mut R) -> Result, Error> { Result::Ok(Some(try!(T::read_from(buf)))) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { if self.is_some() { try!(self.as_ref().unwrap().write_to(buf)); } @@ -199,13 +199,13 @@ impl Serializable for Option where T : Serializable { } impl Serializable for String { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { let len = try!(VarInt::read_from(buf)).0; let mut ret = String::new(); try!(buf.take(len as u64).read_to_string(&mut ret)); Result::Ok(ret) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { let bytes = self.as_bytes(); try!(VarInt(bytes.len() as i32).write_to(buf)); try!(buf.write_all(bytes)); @@ -214,14 +214,14 @@ impl Serializable for String { } impl Serializable for format::Component { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { let len = try!(VarInt::read_from(buf)).0; let mut ret = String::new(); try!(buf.take(len as u64).read_to_string(&mut ret)); let val: serde_json::Value = serde_json::from_str(&ret[..]).unwrap(); Result::Ok(Self::from_value(&val)) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { let val = serde_json::to_string(&self.to_value()).unwrap(); let bytes = val.as_bytes(); try!(VarInt(bytes.len() as i32).write_to(buf)); @@ -231,19 +231,19 @@ impl Serializable for format::Component { } impl Serializable for () { - fn read_from(_: &mut io::Read) -> Result<(), io::Error> { + fn read_from(_: &mut R) -> Result<(), Error> { Result::Ok(()) } - fn write_to(&self, _: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { Result::Ok(()) } } impl Serializable for bool { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(try!(buf.read_u8()) != 0) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_u8(if *self { 1 } else { @@ -254,90 +254,90 @@ impl Serializable for bool { } impl Serializable for i8 { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(try!(buf.read_i8())) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_i8(*self)); Result::Ok(()) } } impl Serializable for i16 { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(try!(buf.read_i16::())) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_i16::(*self)); Result::Ok(()) } } impl Serializable for i32 { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(try!(buf.read_i32::())) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_i32::(*self)); Result::Ok(()) } } impl Serializable for i64 { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(try!(buf.read_i64::())) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_i64::(*self)); Result::Ok(()) } } impl Serializable for u8 { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(try!(buf.read_u8())) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_u8(*self)); Result::Ok(()) } } impl Serializable for u16 { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(try!(buf.read_u16::())) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_u16::(*self)); Result::Ok(()) } } impl Serializable for u64 { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(try!(buf.read_u64::())) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_u64::(*self)); Result::Ok(()) } } impl Serializable for f32 { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(try!(buf.read_f32::())) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_f32::(*self)); Result::Ok(()) } } impl Serializable for f64 { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(try!(buf.read_f64::())) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_f64::(*self)); Result::Ok(()) } @@ -375,11 +375,11 @@ impl Default for UUID { } impl Serializable for UUID { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Result::Ok(UUID(try!(buf.read_u64::()), try!(buf.read_u64::()))) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(buf.write_u64::(self.0)); try!(buf.write_u64::(self.1)); Result::Ok(()) @@ -407,7 +407,7 @@ impl LenPrefixed { } impl Serializable for LenPrefixed { - fn read_from(buf: &mut io::Read) -> Result, io::Error> { + fn read_from(buf: &mut R) -> Result, Error> { let len_data: L = try!(Serializable::read_from(buf)); let len: usize = len_data.into(); let mut data: Vec = Vec::with_capacity(len); @@ -420,7 +420,7 @@ impl Serializable for LenPrefixed { }) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { let len_data: L = L::from(self.data.len()); try!(len_data.write_to(buf)); let data = &self.data; @@ -463,7 +463,7 @@ impl LenPrefixedBytes { } impl Serializable for LenPrefixedBytes { - fn read_from(buf: &mut io::Read) -> Result, io::Error> { + fn read_from(buf: &mut R) -> Result, Error> { let len_data: L = try!(Serializable::read_from(buf)); let len: usize = len_data.into(); let mut data: Vec = Vec::with_capacity(len); @@ -474,7 +474,7 @@ impl Serializable for LenPrefixedBytes { }) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { let len_data: L = L::from(self.data.len()); try!(len_data.write_to(buf)); try!(buf.write_all(&self.data[..])); @@ -535,7 +535,7 @@ impl Lengthable for VarInt { impl Serializable for VarInt { /// Decodes a `VarInt` from the Reader - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { const PART : u32 = 0x7F; let mut size = 0; let mut val = 0u32; @@ -544,8 +544,7 @@ impl Serializable for VarInt { val |= (b & PART) << (size * 7); size += 1; if size > 5 { - return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, - Error::Err("VarInt too big".to_owned()))) + return Result::Err(Error::Err("VarInt too big".to_owned())); } if (b & 0x80) == 0 { break @@ -556,7 +555,7 @@ impl Serializable for VarInt { } /// Encodes a `VarInt` into the Writer - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { const PART : u32 = 0x7F; let mut val = self.0 as u32; loop { @@ -599,7 +598,7 @@ impl Lengthable for VarLong { impl Serializable for VarLong { /// Decodes a `VarLong` from the Reader - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { const PART : u64 = 0x7F; let mut size = 0; let mut val = 0u64; @@ -608,8 +607,7 @@ impl Serializable for VarLong { val |= (b & PART) << (size * 7); size += 1; if size > 10 { - return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, - Error::Err("VarLong too big".to_owned()))) + return Result::Err(Error::Err("VarLong too big".to_owned())); } if (b & 0x80) == 0 { break @@ -620,7 +618,7 @@ impl Serializable for VarLong { } /// Encodes a `VarLong` into the Writer - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { const PART : u64 = 0x7F; let mut val = self.0 as u64; loop { @@ -647,7 +645,7 @@ impl fmt::Debug for VarLong { } impl Serializable for Position { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { let pos = try!(buf.read_u64::()); Ok(Position::new( ((pos as i64) >> 38) as i32, @@ -655,7 +653,7 @@ impl Serializable for Position { ((pos as i64) << 38 >> 38) as i32 )) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { let pos = (((self.x as u64) & 0x3FFFFFF) << 38) | (((self.y as u64) & 0xFFF) << 26) | ((self.z as u64) & 0x3FFFFFF); @@ -1010,8 +1008,8 @@ impl Clone for Conn { } } -pub trait PacketType: Sized { +pub trait PacketType { fn packet_id(&self) -> i32; - fn write(self, buf: &mut io::Write) -> Result<(), io::Error>; + fn write(self, buf: &mut W) -> Result<(), Error>; } diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index c931a8d..5dc47c6 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -957,14 +957,14 @@ pub struct Statistic { } impl Serializable for Statistic { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Ok(Statistic { name: try!(Serializable::read_from(buf)), value: try!(Serializable::read_from(buf)), }) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(self.name.write_to(buf)); self.value.write_to(buf) } @@ -978,7 +978,7 @@ pub struct BlockChangeRecord { } impl Serializable for BlockChangeRecord { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Ok(BlockChangeRecord { xz: try!(Serializable::read_from(buf)), y: try!(Serializable::read_from(buf)), @@ -986,7 +986,7 @@ impl Serializable for BlockChangeRecord { }) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(self.xz.write_to(buf)); try!(self.y.write_to(buf)); self.block_id.write_to(buf) @@ -1001,7 +1001,7 @@ pub struct ExplosionRecord { } impl Serializable for ExplosionRecord { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Ok(ExplosionRecord { x: try!(Serializable::read_from(buf)), y: try!(Serializable::read_from(buf)), @@ -1009,7 +1009,7 @@ impl Serializable for ExplosionRecord { }) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(self.x.write_to(buf)); try!(self.y.write_to(buf)); self.z.write_to(buf) @@ -1024,7 +1024,7 @@ pub struct MapIcon { } impl Serializable for MapIcon { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Ok(MapIcon { direction_type: try!(Serializable::read_from(buf)), x: try!(Serializable::read_from(buf)), @@ -1032,7 +1032,7 @@ impl Serializable for MapIcon { }) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(self.direction_type.write_to(buf)); try!(self.x.write_to(buf)); self.z.write_to(buf) @@ -1057,7 +1057,7 @@ pub struct EntityProperty { } impl Serializable for EntityProperty { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Ok(EntityProperty { key: try!(Serializable::read_from(buf)), value: try!(Serializable::read_from(buf)), @@ -1065,7 +1065,7 @@ impl Serializable for EntityProperty { }) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(self.key.write_to(buf)); try!(self.value.write_to(buf)); self.modifiers.write_to(buf) @@ -1080,7 +1080,7 @@ pub struct PropertyModifier { } impl Serializable for PropertyModifier { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Ok(PropertyModifier { uuid: try!(Serializable::read_from(buf)), amount: try!(Serializable::read_from(buf)), @@ -1088,7 +1088,7 @@ impl Serializable for PropertyModifier { }) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), Error> { try!(self.uuid.write_to(buf)); try!(self.amount.write_to(buf)); self.operation.write_to(buf) @@ -1102,7 +1102,7 @@ pub struct PlayerInfoData { } impl Serializable for PlayerInfoData { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { let mut m = PlayerInfoData { action: try!(Serializable::read_from(buf)), players: Vec::new(), @@ -1175,7 +1175,7 @@ impl Serializable for PlayerInfoData { Ok(m) } - fn write_to(&self, _: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, _: &mut W) -> Result<(), Error> { unimplemented!() // I'm lazy } } From 862cf9733139293875e6dc1c1dbd4812dadfc563 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Fri, 8 Apr 2016 18:46:07 +0100 Subject: [PATCH 073/160] Clean up the protocol implementation to use generics instead of trait objects --- protocol/src/item.rs | 6 +++--- protocol/src/nbt/mod.rs | 15 +++++++-------- protocol/src/types/metadata.rs | 8 +++----- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/protocol/src/item.rs b/protocol/src/item.rs index eeeeee3..45e4017 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -13,7 +13,7 @@ // limitations under the License. use nbt; -use protocol::Serializable; +use protocol::{self, Serializable}; use std::io; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; @@ -38,7 +38,7 @@ impl Default for Stack { } impl Serializable for Option { - fn read_from(buf: &mut io::Read) -> Result, io::Error> { + fn read_from(buf: &mut R) -> Result, protocol::Error> { let id = try!(buf.read_i16::()); if id == -1 { return Ok(None); @@ -50,7 +50,7 @@ impl Serializable for Option { tag: try!(Serializable::read_from(buf)), })) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { match *self { Some(ref val) => { try!(buf.write_i16::(val.id as i16)); diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index 4ea53bf..5883c9e 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -172,7 +172,7 @@ impl Tag { } } - fn read_type(id: u8, buf: &mut io::Read) -> Result { + fn read_type(id: u8, buf: &mut R) -> Result { match id { 0 => unreachable!(), 1 => Ok(Tag::Byte(try!(buf.read_i8()))), @@ -217,18 +217,17 @@ impl Tag { } data })), - _ => Err(io::Error::new(io::ErrorKind::InvalidData, - protocol::Error::Err("invalid tag".to_owned()))), + _ => Err(protocol::Error::Err("invalid tag".to_owned())), } } } impl Serializable for Tag { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { Tag::read_type(10, buf) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { match *self { Tag::End => {} Tag::Byte(val) => try!(buf.write_i8(val)), @@ -273,13 +272,13 @@ impl Serializable for Tag { } } -pub fn write_string(buf: &mut io::Write, s: &str) -> io::Result<()> { +pub fn write_string(buf: &mut W, s: &str) -> Result<(), protocol::Error> { let data = s.as_bytes(); try!((data.len() as i16).write_to(buf)); - buf.write_all(data) + buf.write_all(data).map_err(|v| v.into()) } -pub fn read_string(buf: &mut io::Read) -> io::Result { +pub fn read_string(buf: &mut R) -> Result { let len: i16 = try!(buf.read_i16::()); let mut ret = String::new(); try!(buf.take(len as u64).read_to_string(&mut ret)); diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index eab44a6..e5c8040 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -62,7 +62,7 @@ impl Metadata { impl Serializable for Metadata { - fn read_from(buf: &mut io::Read) -> Result { + fn read_from(buf: &mut R) -> Result { let mut m = Metadata::new(); loop { let index = try!(u8::read_from(buf)) as i32; @@ -99,15 +99,13 @@ impl Serializable for Metadata { } } 12 => m.put_raw(index, try!(protocol::VarInt::read_from(buf)).0 as u16), - _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, - protocol::Error::Err("unknown metadata type" - .to_owned()))), + _ => return Err(protocol::Error::Err("unknown metadata type".to_owned())), } } Ok(m) } - fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> { + fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { for (k, v) in &self.map { try!((*k as u8).write_to(buf)); match *v { From 03e6af8cb925bd7c9da8c849ad9998eee71c80b3 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sat, 16 Apr 2016 21:43:41 +0100 Subject: [PATCH 074/160] Fix a large number of warnings --- protocol/src/protocol/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index fd86e6d..c831552 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -234,7 +234,7 @@ impl Serializable for () { fn read_from(_: &mut R) -> Result<(), Error> { Result::Ok(()) } - fn write_to(&self, buf: &mut W) -> Result<(), Error> { + fn write_to(&self, _: &mut W) -> Result<(), Error> { Result::Ok(()) } } From 49b1ae1dbc1de775983d5e8933fa5cb3735d8c6d Mon Sep 17 00:00:00 2001 From: Techcable Date: Sun, 10 Jul 2016 04:23:59 -0700 Subject: [PATCH 075/160] Update to minecraft 1.10.2 --- protocol/src/format.rs | 14 ++++++++++++++ protocol/src/nbt/mod.rs | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 6b4ba38..1b1bad9 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -22,6 +22,20 @@ pub enum Component { } impl Component { + + pub fn from_string(str: &str) -> Self { + let mut component; + match serde_json::from_str::(str) { + Ok(value) => component = Component::from_value(&value), + // Sometimes mojang sends a literal string, so we should interpret it literally + Err(_) => { + component = Component::Text(TextComponent::new(str)); + convert_legacy(&mut component); + }, + } + return component; + } + pub fn from_value(v: &serde_json::Value) -> Self { let mut modifier = Modifier::from_value(v); if let Some(val) = v.as_string() { diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index 5883c9e..ecd9495 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -20,7 +20,7 @@ use super::protocol::Serializable; use super::protocol; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Tag { End, Byte(i8), @@ -36,7 +36,7 @@ pub enum Tag { IntArray(Vec), } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct NamedTag(pub String, pub Tag); impl Tag { From 40f146ac65b3335f59bacd41dcdab7ea697bca87 Mon Sep 17 00:00:00 2001 From: Techcable Date: Sun, 10 Jul 2016 04:23:59 -0700 Subject: [PATCH 076/160] Update to minecraft 1.10.2 --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index c831552..d1b3d02 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -34,7 +34,7 @@ use flate2; use time; use shared::Position; -pub const SUPPORTED_PROTOCOL: i32 = 109; +pub const SUPPORTED_PROTOCOL: i32 = 210; /// Helper macro for defining packets diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 5dc47c6..2b5f8aa 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -196,7 +196,6 @@ state_packets!( /// ResourcePackStatus informs the server of the client's current progress /// in activating the requested resource pack packet ResourcePackStatus { - field hash: String =, field result: VarInt =, } /// HeldItemChange is sent when the player changes the currently active @@ -448,7 +447,7 @@ state_packets!( field y: i32 =, field z: i32 =, field volume: f32 =, - field pitch: u8 =, + field pitch: f32 =, } /// Disconnect causes the client to disconnect displaying the passed reason. packet Disconnect { @@ -499,6 +498,7 @@ state_packets!( field new: bool =, field bitmask: VarInt =, field data: LenPrefixedBytes =, + field block_entities: LenPrefixed> =, } /// Effect plays a sound effect or particle at the target location with the /// volume (of sounds) being relative to the player's position unless @@ -784,14 +784,6 @@ state_packets!( field fade_stay: Option = when(|p: &Title| p.action.0 == 2), field fade_out: Option = when(|p: &Title| p.action.0 == 2), } - /// UpdateSign sets or changes the text on a sign. - packet UpdateSign { - field location: Position =, - field line1: format::Component =, - field line2: format::Component =, - field line3: format::Component =, - field line4: format::Component =, - } /// SoundEffect plays the named sound at the target location. packet SoundEffect { field name: VarInt =, @@ -800,7 +792,7 @@ state_packets!( field y: i32 =, field z: i32 =, field volume: f32 =, - field pitch: u8 =, + field pitch: f32 =, } /// PlayerListHeaderFooter updates the header/footer of the player list. packet PlayerListHeaderFooter { From 0f41b0effee35599980c79d4d34b8b8dde185cca Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 15 Sep 2016 16:15:52 +0200 Subject: [PATCH 077/160] Fixed another batch of clippy warnings Those are mostly readability-related. Also did a cargo update. --- protocol/src/format.rs | 2 +- protocol/src/nbt/mod.rs | 2 +- protocol/src/types/bit/set.rs | 2 +- protocol/src/types/metadata.rs | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 1b1bad9..e75f3ec 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -33,7 +33,7 @@ impl Component { convert_legacy(&mut component); }, } - return component; + component } pub fn from_value(v: &serde_json::Value) -> Self { diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index ecd9495..0ded16d 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -14,7 +14,7 @@ use std::collections::HashMap; use std::io; -use std::io::{Read, Write}; +use std::io::Read; use super::protocol::Serializable; use super::protocol; diff --git a/protocol/src/types/bit/set.rs b/protocol/src/types/bit/set.rs index 8c9c221..894d936 100644 --- a/protocol/src/types/bit/set.rs +++ b/protocol/src/types/bit/set.rs @@ -68,7 +68,7 @@ impl Set { pub fn or(&mut self, other: &Set) { for (a, b) in self.data.iter_mut().zip(&other.data) { - *a = (*a) | *b; + *a |= *b; } } } diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index e5c8040..0cf30be 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -15,7 +15,6 @@ use std::collections::HashMap; use std::marker::PhantomData; use std::io; -use std::io::Write; use std::fmt; use protocol; use protocol::Serializable; From 6fcf1212415abad9d49347bf319e80e0f751c279 Mon Sep 17 00:00:00 2001 From: Techcable Date: Fri, 9 Dec 2016 07:32:02 -0700 Subject: [PATCH 078/160] Update to Minecraft 1.11 (Fixes #63) --- protocol/src/protocol/mod.rs | 6 ++++-- protocol/src/protocol/packet.rs | 16 +++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index d1b3d02..6bc14c8 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -34,7 +34,7 @@ use flate2; use time; use shared::Position; -pub const SUPPORTED_PROTOCOL: i32 = 210; +pub const SUPPORTED_PROTOCOL: i32 = 315; /// Helper macro for defining packets @@ -200,7 +200,9 @@ impl Serializable for Option where T : Serializable { impl Serializable for String { fn read_from(buf: &mut R) -> Result { - let len = try!(VarInt::read_from(buf)).0; + let len = VarInt::read_from(buf)?.0; + debug_assert!(len >= 0, "Negative string length: {}", len); + debug_assert!(len <= 65536, "String length too big: {}", len); let mut ret = String::new(); try!(buf.take(len as u64).read_to_string(&mut ret)); Result::Ok(ret) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 2b5f8aa..fb4613f 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -231,9 +231,9 @@ state_packets!( field location: Position =, field face: VarInt =, field hand: VarInt =, - field cursor_x: u8 =, - field cursor_y: u8 =, - field cursor_z: u8 =, + field cursor_x: f32 =, + field cursor_y: f32 =, + field cursor_z: f32 =, } /// UseItem is sent when the client tries to use an item. packet UseItem { @@ -281,7 +281,7 @@ state_packets!( packet SpawnMob { field entity_id: VarInt =, field uuid: UUID =, - field ty: u8 =, + field ty: VarInt =, field x: f64 =, field y: f64 =, field z: f64 =, @@ -780,9 +780,10 @@ state_packets!( field action: VarInt =, field title: Option = when(|p: &Title| p.action.0 == 0), field sub_title: Option = when(|p: &Title| p.action.0 == 1), - field fade_in: Option = when(|p: &Title| p.action.0 == 2), - field fade_stay: Option = when(|p: &Title| p.action.0 == 2), - field fade_out: Option = when(|p: &Title| p.action.0 == 2), + field action_bar_text: Option = when(|p: &Title| p.action.0 == 2), + field fade_in: Option = when(|p: &Title| p.action.0 == 3), + field fade_stay: Option = when(|p: &Title| p.action.0 == 3), + field fade_out: Option = when(|p: &Title| p.action.0 == 3), } /// SoundEffect plays the named sound at the target location. packet SoundEffect { @@ -804,6 +805,7 @@ state_packets!( packet CollectItem { field collected_entity_id: VarInt =, field collector_entity_id: VarInt =, + field number_of_items: VarInt =, } /// EntityTeleport teleports the entity to the target location. This is /// sent if the entity moves further than EntityMove allows. From b7326badd61e0f08354d6b342e6aee63712bd8fe Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 29 Sep 2018 18:42:13 -0700 Subject: [PATCH 079/160] Fix warning: variable does not need to be mutable, in nightly-2017-08-31 https://github.com/iceiix/steven/issues/3 --- protocol/src/protocol/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 6bc14c8..ba6da37 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -794,7 +794,7 @@ impl Conn { let uncompressed_size = buf.len(); let mut new = Vec::new(); try!(VarInt(uncompressed_size as i32).write_to(&mut new)); - let mut write = self.compression_write.as_mut().unwrap(); + let write = self.compression_write.as_mut().unwrap(); write.reset(io::Cursor::new(buf)); try!(write.read_to_end(&mut new)); buf = new; @@ -824,7 +824,7 @@ impl Conn { if uncompressed_size != 0 { let mut new = Vec::with_capacity(uncompressed_size as usize); { - let mut reader = self.compression_read.as_mut().unwrap(); + let reader = self.compression_read.as_mut().unwrap(); reader.reset(buf); try!(reader.read_to_end(&mut new)); } From 04ca22729e79684efaf3337fdd81fbe750e66b56 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 29 Sep 2018 22:23:48 -0700 Subject: [PATCH 080/160] Use sha1 module for hashing instead of openssl, part of https://github.com/iceiix/steven/issues/2 --- protocol/src/protocol/mojang.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index ee081e8..edf0a91 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use openssl::crypto::hash; +use sha1; use serde_json; use serde_json::builder::ObjectBuilder; use hyper; @@ -101,12 +101,11 @@ impl Profile { } pub fn join_server(&self, server_id: &str, shared_key: &[u8], public_key: &[u8]) -> Result<(), super::Error> { - use std::io::Write; - let mut sha1 = hash::Hasher::new(hash::Type::SHA1); - sha1.write_all(server_id.as_bytes()).unwrap(); - sha1.write_all(shared_key).unwrap(); - sha1.write_all(public_key).unwrap(); - let mut hash = sha1.finish(); + let mut sha1 = sha1::Sha1::new(); + sha1.update(server_id.as_bytes()); + sha1.update(shared_key); + sha1.update(public_key); + let mut hash = sha1.digest().bytes(); // Mojang uses a hex method which allows for // negatives so we have to account for that. @@ -147,7 +146,7 @@ impl Profile { } } -fn twos_compliment(data: &mut Vec) { +fn twos_compliment(data: &mut [u8]) { let mut carry = true; for i in (0..data.len()).rev() { data[i] = !data[i]; From ecbc7abc58f1fe431d8a3187ba9db72c65ff76a5 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 29 Sep 2018 22:57:55 -0700 Subject: [PATCH 081/160] Use std::time for server ping, getting closer to eliminating use of 'time' crate, https://github.com/iceiix/steven/issues/3 --- protocol/src/protocol/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index ba6da37..4ada321 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -31,7 +31,7 @@ use std::convert; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; use flate2::read::{ZlibDecoder, ZlibEncoder}; use flate2; -use time; +use std::time::{Instant, Duration}; use shared::Position; pub const SUPPORTED_PROTOCOL: i32 = 315; @@ -866,7 +866,7 @@ impl Conn { self.compression_threshold = threshold; } - pub fn do_status(mut self) -> Result<(Status, time::Duration), Error> { + pub fn do_status(mut self) -> Result<(Status, Duration), Error> { use serde_json::Value; use self::packet::status::serverbound::*; use self::packet::handshake::serverbound::Handshake; @@ -889,7 +889,7 @@ impl Conn { return Err(Error::Err("Wrong packet".to_owned())); }; - let start = time::now(); + let start = Instant::now(); try!(self.write_packet(StatusPing { ping: 42 })); if let Packet::StatusPong(_) = try!(self.read_packet()) { @@ -897,7 +897,7 @@ impl Conn { return Err(Error::Err("Wrong packet".to_owned())); }; - let ping = time::now() - start; + let ping = start.elapsed(); let val: Value = match serde_json::from_str(&status) { Ok(val) => val, From c3e4824a046dbb27ca8a71bdf770a4cc154e2587 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 30 Sep 2018 16:19:24 -0700 Subject: [PATCH 082/160] Update to flate2 1.0.2 https://github.com/iceiix/steven/issues/4 --- protocol/src/protocol/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 4ada321..2ae6e3f 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -30,7 +30,7 @@ use std::io::{Write, Read}; use std::convert; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; use flate2::read::{ZlibDecoder, ZlibEncoder}; -use flate2; +use flate2::Compression; use std::time::{Instant, Duration}; use shared::Position; @@ -788,7 +788,7 @@ impl Conn { }; if self.compression_threshold >= 0 && buf.len() as i32 > self.compression_threshold { if self.compression_write.is_none() { - self.compression_write = Some(ZlibEncoder::new(io::Cursor::new(Vec::new()), flate2::Compression::Default)); + self.compression_write = Some(ZlibEncoder::new(io::Cursor::new(Vec::new()), Compression::default())); } extra = 0; let uncompressed_size = buf.len(); From ce8d17cd8da486ed04e630edee621cc57f82383a Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 30 Sep 2018 18:14:36 -0700 Subject: [PATCH 083/160] Use hex module for hex decoding, removing deprecated rustc-serialize for https://github.com/iceiix/steven/issues/4 --- protocol/src/protocol/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 2ae6e3f..6625da4 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -350,16 +350,16 @@ pub struct UUID(u64, u64); impl UUID { pub fn from_str(s: &str) -> UUID { - use rustc_serialize::hex::FromHex; + use hex; // TODO: Panics aren't the best idea here if s.len() != 36 { panic!("Invalid UUID format"); } - let mut parts = s[..8].from_hex().unwrap(); - parts.extend_from_slice(&s[9..13].from_hex().unwrap()); - parts.extend_from_slice(&s[14..18].from_hex().unwrap()); - parts.extend_from_slice(&s[19..23].from_hex().unwrap()); - parts.extend_from_slice(&s[24..36].from_hex().unwrap()); + let mut parts = hex::decode(&s[..8]).unwrap(); + parts.extend_from_slice(&hex::decode(&s[9..13]).unwrap()); + parts.extend_from_slice(&hex::decode(&s[14..18]).unwrap()); + parts.extend_from_slice(&hex::decode(&s[19..23]).unwrap()); + parts.extend_from_slice(&hex::decode(&s[24..36]).unwrap()); let mut high = 0u64; let mut low = 0u64; for i in 0 .. 8 { From ca79b0c735a35988e4b16a926150bf6de02db5c8 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Wed, 3 Oct 2018 18:28:05 -0700 Subject: [PATCH 084/160] Use the RustCrypto sha-1 crate instead of sha1 For https://github.com/iceiix/steven/issues/2#issuecomment-425769562 --- protocol/src/protocol/mojang.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index edf0a91..f1506b6 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use sha1; +use sha1::{self, Digest}; use serde_json; use serde_json::builder::ObjectBuilder; use hyper; @@ -101,11 +101,11 @@ impl Profile { } pub fn join_server(&self, server_id: &str, shared_key: &[u8], public_key: &[u8]) -> Result<(), super::Error> { - let mut sha1 = sha1::Sha1::new(); - sha1.update(server_id.as_bytes()); - sha1.update(shared_key); - sha1.update(public_key); - let mut hash = sha1.digest().bytes(); + let mut hasher = sha1::Sha1::new(); + hasher.input(server_id.as_bytes()); + hasher.input(shared_key); + hasher.input(public_key); + let mut hash = hasher.result(); // Mojang uses a hex method which allows for // negatives so we have to account for that. From b2b1ec00ed0f11ae2116ff1862d3909231d92763 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Tue, 23 Oct 2018 18:47:21 -0700 Subject: [PATCH 085/160] Update to serde_json 1.0 (#6) * Replace find() with get() * Update for renamed as_string->as_str and as_boolean->as_bool https://github.com/serde-rs/json/releases/tag/v0.8.0 Value::as_string() has been renamed to as_str() and Value::as_boolean() has been renamed to as_bool() to improve consistency https://github.com/serde-rs/json/issues/126 * No serde_json::Value::I64/U64/F64 anymore, only Number * Update from lookup() to pointer(), using JSON pointer syntax https://github.com/iceiix/steven/pull/6#issuecomment-432472123 * Remove unused and removed ObjectBuilder import * Use into_iter().collect() to convert BTreeMap to serde_json::Map * Change parse_rules to accept serde_json::Map instead of BTreeMap * Remove unused serde_json macro_use * Update Cargo.lock --- protocol/src/protocol/mod.rs | 16 +++++----- protocol/src/protocol/mojang.rs | 56 ++++++++++++++++----------------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 6625da4..7faf197 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -906,29 +906,29 @@ impl Conn { let invalid_status = || Error::Err("Invalid status".to_owned()); - let version = try!(val.find("version").ok_or(invalid_status())); - let players = try!(val.find("players").ok_or(invalid_status())); + let version = try!(val.get("version").ok_or(invalid_status())); + let players = try!(val.get("players").ok_or(invalid_status())); Ok((Status { version: StatusVersion { - name: try!(version.find("name").and_then(Value::as_string).ok_or(invalid_status())) + name: try!(version.get("name").and_then(Value::as_str).ok_or(invalid_status())) .to_owned(), - protocol: try!(version.find("protocol") + protocol: try!(version.get("protocol") .and_then(Value::as_i64) .ok_or(invalid_status())) as i32, }, players: StatusPlayers { - max: try!(players.find("max") + max: try!(players.get("max") .and_then(Value::as_i64) .ok_or(invalid_status())) as i32, - online: try!(players.find("online") + online: try!(players.get("online") .and_then(Value::as_i64) .ok_or(invalid_status())) as i32, sample: Vec::new(), /* TODO */ }, - description: format::Component::from_value(try!(val.find("description") + description: format::Component::from_value(try!(val.get("description") .ok_or(invalid_status()))), - favicon: val.find("favicon").and_then(Value::as_string).map(|v| v.to_owned()), + favicon: val.get("favicon").and_then(Value::as_str).map(|v| v.to_owned()), }, ping)) } diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index f1506b6..d18610e 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -14,7 +14,6 @@ use sha1::{self, Digest}; use serde_json; -use serde_json::builder::ObjectBuilder; use hyper; #[derive(Clone, Debug)] @@ -31,15 +30,14 @@ const VALIDATE_URL: &'static str = "https://authserver.mojang.com/validate"; impl Profile { pub fn login(username: &str, password: &str, token: &str) -> Result { - let req_msg = ObjectBuilder::new() - .insert("username", username) - .insert("password", password) - .insert("clientToken", token) - .insert_object("agent", |b| b - .insert("name", "Minecraft") - .insert("version", 1) - ) - .unwrap(); + let req_msg = json!({ + "username": username, + "password": password, + "clientToken": token, + "agent": { + "name": "Minecraft", + "version": 1 + }}); let req = try!(serde_json::to_string(&req_msg)); let client = hyper::Client::new(); @@ -49,25 +47,25 @@ impl Profile { .send()); let ret: serde_json::Value = try!(serde_json::from_reader(res)); - if let Some(error) = ret.find("error").and_then(|v| v.as_string()) { + if let Some(error) = ret.get("error").and_then(|v| v.as_str()) { return Err(super::Error::Err(format!( "{}: {}", error, - ret.find("errorMessage").and_then(|v| v.as_string()).unwrap()) + ret.get("errorMessage").and_then(|v| v.as_str()).unwrap()) )); } Ok(Profile { - username: ret.lookup("selectedProfile.name").and_then(|v| v.as_string()).unwrap().to_owned(), - id: ret.lookup("selectedProfile.id").and_then(|v| v.as_string()).unwrap().to_owned(), - access_token: ret.find("accessToken").and_then(|v| v.as_string()).unwrap().to_owned(), + username: ret.pointer("/selectedProfile/name").and_then(|v| v.as_str()).unwrap().to_owned(), + id: ret.pointer("/selectedProfile/id").and_then(|v| v.as_str()).unwrap().to_owned(), + access_token: ret.get("accessToken").and_then(|v| v.as_str()).unwrap().to_owned(), }) } pub fn refresh(self, token: &str) -> Result { - let req_msg = ObjectBuilder::new() - .insert("accessToken", self.access_token.clone()) - .insert("clientToken", token) - .unwrap(); + let req_msg = json!({ + "accessToken": self.access_token.clone(), + "clientToken": token + }); let req = try!(serde_json::to_string(&req_msg)); let client = hyper::Client::new(); @@ -84,17 +82,17 @@ impl Profile { .send()); let ret: serde_json::Value = try!(serde_json::from_reader(res)); - if let Some(error) = ret.find("error").and_then(|v| v.as_string()) { + if let Some(error) = ret.get("error").and_then(|v| v.as_str()) { return Err(super::Error::Err(format!( "{}: {}", error, - ret.find("errorMessage").and_then(|v| v.as_string()).unwrap()) + ret.get("errorMessage").and_then(|v| v.as_str()).unwrap()) )); } return Ok(Profile { - username: ret.lookup("selectedProfile.name").and_then(|v| v.as_string()).unwrap().to_owned(), - id: ret.lookup("selectedProfile.id").and_then(|v| v.as_string()).unwrap().to_owned(), - access_token: ret.find("accessToken").and_then(|v| v.as_string()).unwrap().to_owned(), + username: ret.pointer("/selectedProfile/name").and_then(|v| v.as_str()).unwrap().to_owned(), + id: ret.pointer("/selectedProfile/id").and_then(|v| v.as_str()).unwrap().to_owned(), + access_token: ret.get("accessToken").and_then(|v| v.as_str()).unwrap().to_owned(), }); } Ok(self) @@ -121,11 +119,11 @@ impl Profile { hash_val.to_owned() }; - let join_msg = ObjectBuilder::new() - .insert("accessToken", &self.access_token) - .insert("selectedProfile", &self.id) - .insert("serverId", hash_str) - .unwrap(); + let join_msg = json!({ + "accessToken": &self.access_token, + "selectedProfile": &self.id, + "serverId": hash_str + }); let join = serde_json::to_string(&join_msg).unwrap(); let client = hyper::Client::new(); From 7e5bb999e4571497745f1c3c4d5011b423329f5e Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Tue, 23 Oct 2018 18:47:21 -0700 Subject: [PATCH 086/160] Update to serde_json 1.0 (#6) * Replace find() with get() * Update for renamed as_string->as_str and as_boolean->as_bool https://github.com/serde-rs/json/releases/tag/v0.8.0 Value::as_string() has been renamed to as_str() and Value::as_boolean() has been renamed to as_bool() to improve consistency https://github.com/serde-rs/json/issues/126 * No serde_json::Value::I64/U64/F64 anymore, only Number * Update from lookup() to pointer(), using JSON pointer syntax https://github.com/iceiix/steven/pull/6#issuecomment-432472123 * Remove unused and removed ObjectBuilder import * Use into_iter().collect() to convert BTreeMap to serde_json::Map * Change parse_rules to accept serde_json::Map instead of BTreeMap * Remove unused serde_json macro_use * Update Cargo.lock --- protocol/src/format.rs | 22 +++++++++++----------- protocol/src/nbt/mod.rs | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index e75f3ec..9eb6ceb 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -38,12 +38,12 @@ impl Component { pub fn from_value(v: &serde_json::Value) -> Self { let mut modifier = Modifier::from_value(v); - if let Some(val) = v.as_string() { + if let Some(val) = v.as_str() { Component::Text(TextComponent { text: val.to_owned(), modifier: modifier, }) - } else if v.find("text").is_some() { + } else if v.get("text").is_some() { Component::Text(TextComponent::from_value(v, modifier)) } else { modifier.color = Some(Color::RGB(255, 0, 0)); @@ -92,17 +92,17 @@ pub struct Modifier { impl Modifier { pub fn from_value(v: &serde_json::Value) -> Self { let mut m = Modifier { - bold: v.find("bold").map_or(Option::None, |v| v.as_boolean()), - italic: v.find("italic").map_or(Option::None, |v| v.as_boolean()), - underlined: v.find("underlined").map_or(Option::None, |v| v.as_boolean()), - strikethrough: v.find("strikethrough").map_or(Option::None, |v| v.as_boolean()), - obfuscated: v.find("obfuscated").map_or(Option::None, |v| v.as_boolean()), - color: v.find("color") - .map_or(Option::None, |v| v.as_string()) + bold: v.get("bold").map_or(Option::None, |v| v.as_bool()), + italic: v.get("italic").map_or(Option::None, |v| v.as_bool()), + underlined: v.get("underlined").map_or(Option::None, |v| v.as_bool()), + strikethrough: v.get("strikethrough").map_or(Option::None, |v| v.as_bool()), + obfuscated: v.get("obfuscated").map_or(Option::None, |v| v.as_bool()), + color: v.get("color") + .map_or(Option::None, |v| v.as_str()) .map(|v| Color::from_string(&v.to_owned())), extra: Option::None, }; - if let Some(extra) = v.find("extra") { + if let Some(extra) = v.get("extra") { if let Some(data) = extra.as_array() { let mut ex = Vec::new(); for e in data { @@ -135,7 +135,7 @@ impl TextComponent { pub fn from_value(v: &serde_json::Value, modifier: Modifier) -> Self { TextComponent { - text: v.find("text").unwrap().as_string().unwrap_or("").to_owned(), + text: v.get("text").unwrap().as_str().unwrap_or("").to_owned(), modifier: modifier, } } diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index 0ded16d..db4d302 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -127,7 +127,7 @@ impl Tag { } } - pub fn as_string(&self) -> Option<&str> { + pub fn as_str(&self) -> Option<&str> { match *self { Tag::String(ref val) => Some(&val[..]), _ => None, From 88563ba894ebc80ac54f1272a81716949e5954ff Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sat, 27 Oct 2018 17:03:34 -0700 Subject: [PATCH 087/160] Replace hyper with reqwest (#7) An old version of hyper was used before (0.8.0), in the process of updating to hyper 0.12.11, found this higher-level replacement/wrapper, reqwest 0.9.4 which is simpler to use than the latest hyper and serves the purpose of a simple HTTP client well * Begin updating to hyper 0.12.11 https://github.com/iceiix/steven/issues/4#issuecomment-425759778 * Use type variables for hyper::Client * Fix setting header syntax, Content-Type: application/json, 17->13 * Parse strings into URLs with url.parse::().unwrap() https://github.com/hyperium/hyper/blob/b20971cb4e5f158844aec5829eea1854e5b7d4b6/examples/client.rs#L25 * Use hyper::Request::post() then client.request() since client.post() removed * wait() on the ResponseFuture to get the Result * try! to unwrap the Result * status() is now a method * Concatenate body chunks unwrap into bytes, then parse JSON from byte slice, instead of from_reader which didn't compile * Replace send() with wait() on ResponseFuture * Parse HeaderValue to u64 * Slices implement std::io::Read trait * Read into_bytes() instead of read_to_end() * Disable boxed logger for now to workaround 'expected function, found macro' * Remove unnecessary mutability, warnings * Hack to parse twice to avoid double move * Use hyper-rustls pure Rust implementation for TLS for HTTPS in hyper * Start converting to reqwest: add Protocol::Error and reqwest::Error conversion * Use reqwest, replacing hyper, in protocol * Convert resources to use reqwest instead of hyper * Convert skin download to reqwest, instead of hyper * Remove hyper * Revert unnecessary variable name change req/body to reduce diff * Revert unnecessary whitespace change to reduce diff, align indentation on . * Fix authenticating to server, wrong method and join URL * Update Cargo.lock --- protocol/src/protocol/mod.rs | 14 +++++----- protocol/src/protocol/mojang.rs | 45 +++++++++++++++++---------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 7faf197..93f6e89 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -16,7 +16,7 @@ use openssl::crypto::symm; use serde_json; -use hyper; +use reqwest; pub mod mojang; @@ -690,7 +690,7 @@ pub enum Error { Disconnect(format::Component), IOError(io::Error), Json(serde_json::Error), - Hyper(hyper::Error), + Reqwest(reqwest::Error), } impl convert::From for Error { @@ -705,9 +705,9 @@ impl convert::From for Error { } } -impl convert::From for Error { - fn from(e: hyper::Error) -> Error { - Error::Hyper(e) +impl convert::From for Error { + fn from(e: reqwest::Error) -> Error { + Error::Reqwest(e) } } @@ -718,7 +718,7 @@ impl ::std::error::Error for Error { Error::Disconnect(_) => "Disconnect", Error::IOError(ref e) => e.description(), Error::Json(ref e) => e.description(), - Error::Hyper(ref e) => e.description(), + Error::Reqwest(ref e) => e.description(), } } } @@ -730,7 +730,7 @@ impl ::std::fmt::Display for Error { Error::Disconnect(ref val) => write!(f, "{}", val), Error::IOError(ref e) => e.fmt(f), Error::Json(ref e) => e.fmt(f), - Error::Hyper(ref e) => e.fmt(f), + Error::Reqwest(ref e) => e.fmt(f), } } } diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index d18610e..d546363 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -14,7 +14,7 @@ use sha1::{self, Digest}; use serde_json; -use hyper; +use reqwest; #[derive(Clone, Debug)] pub struct Profile { @@ -40,11 +40,11 @@ impl Profile { }}); let req = try!(serde_json::to_string(&req_msg)); - let client = hyper::Client::new(); - let res = try!(client.post(LOGIN_URL) - .body(&req) - .header(hyper::header::ContentType("application/json".parse().unwrap())) - .send()); + let client = reqwest::Client::new(); + let res = client.post(LOGIN_URL) + .header(reqwest::header::CONTENT_TYPE, "application/json") + .body(req) + .send()?; let ret: serde_json::Value = try!(serde_json::from_reader(res)); if let Some(error) = ret.get("error").and_then(|v| v.as_str()) { @@ -68,18 +68,19 @@ impl Profile { }); let req = try!(serde_json::to_string(&req_msg)); - let client = hyper::Client::new(); - let res = try!(client.post(VALIDATE_URL) - .body(&req) - .header(hyper::header::ContentType("application/json".parse().unwrap())) - .send()); + let client = reqwest::Client::new(); + let res = client.post(VALIDATE_URL) + .header(reqwest::header::CONTENT_TYPE, "application/json") + .body(req) + .send()?; - if res.status != hyper::status::StatusCode::NoContent { + if res.status() != reqwest::StatusCode::NO_CONTENT { + let req = try!(serde_json::to_string(&req_msg)); // TODO: fix parsing twice to avoid move // Refresh needed - let res = try!(client.post(REFRESH_URL) - .body(&req) - .header(hyper::header::ContentType("application/json".parse().unwrap())) - .send()); + let res = client.post(REFRESH_URL) + .header(reqwest::header::CONTENT_TYPE, "application/json") + .body(req) + .send()?; let ret: serde_json::Value = try!(serde_json::from_reader(res)); if let Some(error) = ret.get("error").and_then(|v| v.as_str()) { @@ -126,13 +127,13 @@ impl Profile { }); let join = serde_json::to_string(&join_msg).unwrap(); - let client = hyper::Client::new(); - let res = try!(client.post(JOIN_URL) - .body(&join) - .header(hyper::header::ContentType("application/json".parse().unwrap())) - .send()); + let client = reqwest::Client::new(); + let res = client.post(JOIN_URL) + .header(reqwest::header::CONTENT_TYPE, "application/json") + .body(join) + .send()?; - if res.status == hyper::status::StatusCode::NoContent { + if res.status() == reqwest::StatusCode::NO_CONTENT { Ok(()) } else { Err(super::Error::Err("Failed to auth with server".to_owned())) From 9840a012622bc695f60ec5559b711b9cf061b72c Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 27 Oct 2018 19:56:34 -0700 Subject: [PATCH 088/160] Update to rust-openssl 0.10.15 (from 0.7.8) Major API change, the last of the outdated dependencies Closes https://github.com/iceiix/steven/issues/4 Note: would still like to replace the last usages of the OpenSSL crate https://github.com/iceiix/steven/issues/2 but it is needed for CFB8 until a replacement is available (maybe https://github.com/RustCrypto/stream-ciphers/issues/4) --- protocol/src/protocol/mod.rs | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 93f6e89..9785873 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -14,9 +14,10 @@ #![allow(dead_code)] -use openssl::crypto::symm; +use openssl::symm; use serde_json; use reqwest; +use openssl; pub mod mojang; @@ -691,6 +692,7 @@ pub enum Error { IOError(io::Error), Json(serde_json::Error), Reqwest(reqwest::Error), + OpenSSL(openssl::error::ErrorStack), } impl convert::From for Error { @@ -711,6 +713,12 @@ impl convert::From for Error { } } +impl convert::From for Error { + fn from(e: openssl::error::ErrorStack) -> Error { + Error::OpenSSL(e) + } +} + impl ::std::error::Error for Error { fn description(&self) -> &str { match *self { @@ -719,6 +727,7 @@ impl ::std::error::Error for Error { Error::IOError(ref e) => e.description(), Error::Json(ref e) => e.description(), Error::Reqwest(ref e) => e.description(), + Error::OpenSSL(ref e) => e.description(), } } } @@ -731,6 +740,7 @@ impl ::std::fmt::Display for Error { Error::IOError(ref e) => e.fmt(f), Error::Json(ref e) => e.fmt(f), Error::Reqwest(ref e) => e.fmt(f), + Error::OpenSSL(ref e) => e.fmt(f), } } } @@ -857,8 +867,10 @@ impl Conn { } pub fn enable_encyption(&mut self, key: &[u8], decrypt: bool) { - let cipher = symm::Crypter::new(symm::Type::AES_128_CFB8); - cipher.init(if decrypt { symm::Mode::Decrypt } else { symm::Mode::Encrypt }, key, key); + let cipher = symm::Crypter::new(symm::Cipher::aes_128_cfb8(), + if decrypt { symm::Mode::Decrypt } else { symm::Mode::Encrypt }, + key, + Some(key)).unwrap(); self.cipher = Option::Some(cipher); } @@ -967,8 +979,9 @@ impl Read for Conn { Option::None => self.stream.read(buf), Option::Some(cipher) => { let ret = try!(self.stream.read(buf)); - let data = cipher.update(&buf[..ret]); - for i in 0..ret { + let mut data = vec![0; ret + symm::Cipher::aes_128_cfb8().block_size()]; + let count = cipher.update(&buf[..ret], &mut data).unwrap(); + for i in 0..count { buf[i] = data[i]; } Ok(ret) @@ -982,8 +995,9 @@ impl Write for Conn { match self.cipher.as_mut() { Option::None => self.stream.write(buf), Option::Some(cipher) => { - let data = cipher.update(buf); - try!(self.stream.write_all(&data[..])); + let mut data = vec![0; buf.len() + symm::Cipher::aes_128_cfb8().block_size()]; + let count = cipher.update(buf, &mut data).unwrap(); + try!(self.stream.write_all(&data[..count])); Ok(buf.len()) } } From 38543feae7a9e7d6380eb28af6fb5ed780b078c3 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Thu, 1 Nov 2018 20:45:40 -0700 Subject: [PATCH 089/160] Switch to RustCrypto for Cfb8 symmetric crypto, instead of OpenSSL (#10) (#2) * Encrypt with both RustCrypto cfb8 and OpenSSL * Switch to RustCrypto for decrypting * Show encryption for both RustCrypto and OpenSSL, for comparison... * Correct off-by-one error in encryption, cfb8 doesn't need extra byte * Remove OpenSSL for symmetric crypto * Update Cargo.lock --- protocol/src/protocol/mod.rs | 37 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 9785873..dddab91 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -14,7 +14,9 @@ #![allow(dead_code)] -use openssl::symm; +use aes::Aes128; +use cfb8::Cfb8; +use cfb8::stream_cipher::{NewStreamCipher, StreamCipher}; use serde_json; use reqwest; use openssl; @@ -745,6 +747,8 @@ impl ::std::fmt::Display for Error { } } +type Aes128Cfb = Cfb8; + pub struct Conn { stream: TcpStream, pub host: String, @@ -752,16 +756,13 @@ pub struct Conn { direction: Direction, pub state: State, - cipher: Option, + cipher: Option, compression_threshold: i32, compression_read: Option>>>, compression_write: Option>>>, } -// Needed because symm::Crypter isn't send -unsafe impl Send for Conn {} - impl Conn { pub fn new(target: &str) -> Result { // TODO SRV record support @@ -866,11 +867,8 @@ impl Conn { } } - pub fn enable_encyption(&mut self, key: &[u8], decrypt: bool) { - let cipher = symm::Crypter::new(symm::Cipher::aes_128_cfb8(), - if decrypt { symm::Mode::Decrypt } else { symm::Mode::Encrypt }, - key, - Some(key)).unwrap(); + pub fn enable_encyption(&mut self, key: &[u8], _decrypt: bool) { + let cipher = Aes128Cfb::new_var(key, key).unwrap(); self.cipher = Option::Some(cipher); } @@ -979,11 +977,8 @@ impl Read for Conn { Option::None => self.stream.read(buf), Option::Some(cipher) => { let ret = try!(self.stream.read(buf)); - let mut data = vec![0; ret + symm::Cipher::aes_128_cfb8().block_size()]; - let count = cipher.update(&buf[..ret], &mut data).unwrap(); - for i in 0..count { - buf[i] = data[i]; - } + cipher.decrypt(&mut buf[..ret]); + Ok(ret) } } @@ -995,9 +990,15 @@ impl Write for Conn { match self.cipher.as_mut() { Option::None => self.stream.write(buf), Option::Some(cipher) => { - let mut data = vec![0; buf.len() + symm::Cipher::aes_128_cfb8().block_size()]; - let count = cipher.update(buf, &mut data).unwrap(); - try!(self.stream.write_all(&data[..count])); + // TODO: avoid copying, but trait requires non-mutable buf + let mut data = vec![0; buf.len()]; + for i in 0..buf.len() { + data[i] = buf[i]; + } + + cipher.encrypt(&mut data); + + try!(self.stream.write_all(&data)); Ok(buf.len()) } } From d95b99c175955b24d7181d7b409f38c15c9f5b06 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Fri, 2 Nov 2018 16:57:23 -0700 Subject: [PATCH 090/160] Support beta Rust release. Closes #8 (#11) * Remove seemingly unneeded const on MetadataKey new * Change biome temperature/moisture to integer, x100 to remove floating-point so can use within stable 'const fn' * Remove unstable const_fn feature, now using stable const fn: see https://www.reddit.com/r/rust/comments/9msqfn/const_fn_soon_on_stable_rust/ * Test on Rust beta (awaiting 1.31 release for stable) * Update readme for beta Rust support --- protocol/src/types/metadata.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 0cf30be..59ea64d 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -29,7 +29,7 @@ pub struct MetadataKey { impl MetadataKey { #[allow(dead_code)] - const fn new(index: i32) -> MetadataKey { + fn new(index: i32) -> MetadataKey { MetadataKey { index: index, ty: PhantomData, From 2be7a2ba6b9de4b984014c8f0e53d574ad34866a Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 4 Nov 2018 09:40:51 -0800 Subject: [PATCH 091/160] Remove use of OpenSSL for RSA PKCS1 encryption (#12). Closes #2 * Add handwritten RSA PKCS1 encryption using num-bigint and simple_asn1 * Add more logging to compare OpenSSL with/without side-by-side * Log message and ciphertext in hex * Print N and e as hexadecimal integers * Fix bad encryption caused by zeros in PKCS1 padding PS field in https://tools.ietf.org/html/rfc8017#section-7.2.1 Must be nonzero * Use rand fill instead of rand_bytes * Remove OpenSSL! * Update CI scripts and docs to not install OpenSSL * Remove copying OpenSSL DLLs (libeay and ssleay) in AppVeyor script * Change rsa_public_encrypt_pkcs1 to return a Result, String> * Add error checking, returning Err on failure; RFC comments * Add the required message representative range checking * Use expect() instead of unwrap() on from_der * Map the ASN.1 error to a String to return it from rsa_public_encrypt_pkcs1() instead of panicking * Move RSA to a new crate, rsa_public_encrypt_pkcs1 https://github.com/iceiix/rsa_public_encrypt_pkcs1 * Update to rsa_public_encrypt_pkcs1 with simple_asn 0.1.0 https://github.com/iceiix/rsa_public_encrypt_pkcs1/issues/1 * Update to published version of rsa_public_encrypt_pkcs1, 0.1.0 * Remove unnecessarily added blank line * Remove libssl-dev from .travis.yml --- protocol/src/protocol/mod.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index dddab91..333ea19 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -19,7 +19,6 @@ use cfb8::Cfb8; use cfb8::stream_cipher::{NewStreamCipher, StreamCipher}; use serde_json; use reqwest; -use openssl; pub mod mojang; @@ -694,7 +693,6 @@ pub enum Error { IOError(io::Error), Json(serde_json::Error), Reqwest(reqwest::Error), - OpenSSL(openssl::error::ErrorStack), } impl convert::From for Error { @@ -715,12 +713,6 @@ impl convert::From for Error { } } -impl convert::From for Error { - fn from(e: openssl::error::ErrorStack) -> Error { - Error::OpenSSL(e) - } -} - impl ::std::error::Error for Error { fn description(&self) -> &str { match *self { @@ -729,7 +721,6 @@ impl ::std::error::Error for Error { Error::IOError(ref e) => e.description(), Error::Json(ref e) => e.description(), Error::Reqwest(ref e) => e.description(), - Error::OpenSSL(ref e) => e.description(), } } } @@ -742,7 +733,6 @@ impl ::std::fmt::Display for Error { Error::IOError(ref e) => e.fmt(f), Error::Json(ref e) => e.fmt(f), Error::Reqwest(ref e) => e.fmt(f), - Error::OpenSSL(ref e) => e.fmt(f), } } } From 5bedf46353a0abc6de3fe2c1028be78bf05b047e Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 4 Nov 2018 11:33:23 -0800 Subject: [PATCH 092/160] Remove anonymous trait parameters, name _ for Rust 2018 https://rust-lang-nursery.github.io/edition-guide/rust-2018/trait-system/no-anon-params.html --- protocol/src/protocol/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 333ea19..53bc638 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -393,7 +393,7 @@ impl Serializable for UUID { pub trait Lengthable : Serializable + Copy + Default { fn into(self) -> usize; - fn from(usize) -> Self; + fn from(_: usize) -> Self; } pub struct LenPrefixed { From d31a58b3eb4c4e855674582cd6b1f18a6cc789d3 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 4 Nov 2018 11:33:23 -0800 Subject: [PATCH 093/160] Remove anonymous trait parameters, name _ for Rust 2018 https://rust-lang-nursery.github.io/edition-guide/rust-2018/trait-system/no-anon-params.html --- protocol/src/types/metadata.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 59ea64d..7616266 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -205,7 +205,7 @@ pub enum Value { } pub trait MetaValue { - fn unwrap(&Value) -> &Self; + fn unwrap(_: &Value) -> &Self; fn wrap(self) -> Value; } From d2f256e19fc75aeb14c189e06ae55cbc73f725ee Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 4 Nov 2018 11:34:53 -0800 Subject: [PATCH 094/160] Update try!() to new ? syntax for Rust 2018 edition Not automatically updated, see https://users.rust-lang.org/t/why-does-cargo-fix-replace-try-with-r-try-instead-of/21972/3 There are other tools to replace it, btu this is what I used: find src -name '*.rs' -exec perl -MRegexp::Common -0777 -pe'$bp=$RE{balanced}{-parens=>"()"}; s/try\!($bp)/substr($1, 1, length($1) - 2) . "?"/ges' -i {} \; --- protocol/src/protocol/mod.rs | 186 ++++++++++++++++---------------- protocol/src/protocol/mojang.rs | 10 +- protocol/src/protocol/packet.rs | 90 ++++++++-------- 3 files changed, 143 insertions(+), 143 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 53bc638..e565712 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -98,7 +98,7 @@ macro_rules! state_packets { fn write(self, buf: &mut W) -> Result<(), Error> { $( if true $(&& ($cond(&self)))* { - try!(self.$field.write_to(buf)); + self.$field.write_to(buf)?; } )+ @@ -127,7 +127,7 @@ macro_rules! state_packets { let mut packet : $name = $name::default(); $( if true $(&& ($cond(&packet)))* { - packet.$field = try!(Serializable::read_from(&mut buf)); + packet.$field = Serializable::read_from(&mut buf)?; } )+ Result::Ok(Option::Some(Packet::$name(packet))) @@ -155,7 +155,7 @@ pub trait Serializable: Sized { impl Serializable for Vec { fn read_from(buf: &mut R) -> Result, Error> { let mut v = Vec::new(); - try!(buf.read_to_end(&mut v)); + buf.read_to_end(&mut v)?; Ok(v) } @@ -166,23 +166,23 @@ impl Serializable for Vec { impl Serializable for Option{ fn read_from(buf: &mut R) -> Result, Error> { - let ty = try!(buf.read_u8()); + let ty = buf.read_u8()?; if ty == 0 { Result::Ok(None) } else { - let name = try!(nbt::read_string(buf)); - let tag = try!(nbt::Tag::read_from(buf)); + let name = nbt::read_string(buf)?; + let tag = nbt::Tag::read_from(buf)?; Result::Ok(Some(nbt::NamedTag(name, tag))) } } fn write_to(&self, buf: &mut W) -> Result<(), Error> { match *self { Some(ref val) => { - try!(buf.write_u8(10)); - try!(nbt::write_string(buf, &val.0)); - try!(val.1.write_to(buf)); + buf.write_u8(10)?; + nbt::write_string(buf, &val.0)?; + val.1.write_to(buf)?; } - None => try!(buf.write_u8(0)), + None => buf.write_u8(0)?, } Result::Ok(()) } @@ -190,11 +190,11 @@ impl Serializable for Option{ impl Serializable for Option where T : Serializable { fn read_from(buf: &mut R) -> Result, Error> { - Result::Ok(Some(try!(T::read_from(buf)))) + Result::Ok(Some(T::read_from(buf)?)) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { if self.is_some() { - try!(self.as_ref().unwrap().write_to(buf)); + self.as_ref().unwrap().write_to(buf)?; } Result::Ok(()) } @@ -206,30 +206,30 @@ impl Serializable for String { debug_assert!(len >= 0, "Negative string length: {}", len); debug_assert!(len <= 65536, "String length too big: {}", len); let mut ret = String::new(); - try!(buf.take(len as u64).read_to_string(&mut ret)); + buf.take(len as u64).read_to_string(&mut ret)?; Result::Ok(ret) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { let bytes = self.as_bytes(); - try!(VarInt(bytes.len() as i32).write_to(buf)); - try!(buf.write_all(bytes)); + VarInt(bytes.len() as i32).write_to(buf)?; + buf.write_all(bytes)?; Result::Ok(()) } } impl Serializable for format::Component { fn read_from(buf: &mut R) -> Result { - let len = try!(VarInt::read_from(buf)).0; + let len = VarInt::read_from(buf)?.0; let mut ret = String::new(); - try!(buf.take(len as u64).read_to_string(&mut ret)); + buf.take(len as u64).read_to_string(&mut ret)?; let val: serde_json::Value = serde_json::from_str(&ret[..]).unwrap(); Result::Ok(Self::from_value(&val)) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { let val = serde_json::to_string(&self.to_value()).unwrap(); let bytes = val.as_bytes(); - try!(VarInt(bytes.len() as i32).write_to(buf)); - try!(buf.write_all(bytes)); + VarInt(bytes.len() as i32).write_to(buf)?; + buf.write_all(bytes)?; Result::Ok(()) } } @@ -245,104 +245,104 @@ impl Serializable for () { impl Serializable for bool { fn read_from(buf: &mut R) -> Result { - Result::Ok(try!(buf.read_u8()) != 0) + Result::Ok(buf.read_u8()? != 0) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_u8(if *self { + buf.write_u8(if *self { 1 } else { 0 - })); + })?; Result::Ok(()) } } impl Serializable for i8 { fn read_from(buf: &mut R) -> Result { - Result::Ok(try!(buf.read_i8())) + Result::Ok(buf.read_i8()?) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_i8(*self)); + buf.write_i8(*self)?; Result::Ok(()) } } impl Serializable for i16 { fn read_from(buf: &mut R) -> Result { - Result::Ok(try!(buf.read_i16::())) + Result::Ok(buf.read_i16::()?) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_i16::(*self)); + buf.write_i16::(*self)?; Result::Ok(()) } } impl Serializable for i32 { fn read_from(buf: &mut R) -> Result { - Result::Ok(try!(buf.read_i32::())) + Result::Ok(buf.read_i32::()?) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_i32::(*self)); + buf.write_i32::(*self)?; Result::Ok(()) } } impl Serializable for i64 { fn read_from(buf: &mut R) -> Result { - Result::Ok(try!(buf.read_i64::())) + Result::Ok(buf.read_i64::()?) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_i64::(*self)); + buf.write_i64::(*self)?; Result::Ok(()) } } impl Serializable for u8 { fn read_from(buf: &mut R) -> Result { - Result::Ok(try!(buf.read_u8())) + Result::Ok(buf.read_u8()?) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_u8(*self)); + buf.write_u8(*self)?; Result::Ok(()) } } impl Serializable for u16 { fn read_from(buf: &mut R) -> Result { - Result::Ok(try!(buf.read_u16::())) + Result::Ok(buf.read_u16::()?) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_u16::(*self)); + buf.write_u16::(*self)?; Result::Ok(()) } } impl Serializable for u64 { fn read_from(buf: &mut R) -> Result { - Result::Ok(try!(buf.read_u64::())) + Result::Ok(buf.read_u64::()?) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_u64::(*self)); + buf.write_u64::(*self)?; Result::Ok(()) } } impl Serializable for f32 { fn read_from(buf: &mut R) -> Result { - Result::Ok(try!(buf.read_f32::())) + Result::Ok(buf.read_f32::()?) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_f32::(*self)); + buf.write_f32::(*self)?; Result::Ok(()) } } impl Serializable for f64 { fn read_from(buf: &mut R) -> Result { - Result::Ok(try!(buf.read_f64::())) + Result::Ok(buf.read_f64::()?) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_f64::(*self)); + buf.write_f64::(*self)?; Result::Ok(()) } } @@ -380,12 +380,12 @@ impl Default for UUID { impl Serializable for UUID { fn read_from(buf: &mut R) -> Result { - Result::Ok(UUID(try!(buf.read_u64::()), - try!(buf.read_u64::()))) + Result::Ok(UUID(buf.read_u64::()?, + buf.read_u64::()?)) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(buf.write_u64::(self.0)); - try!(buf.write_u64::(self.1)); + buf.write_u64::(self.0)?; + buf.write_u64::(self.1)?; Result::Ok(()) } } @@ -412,11 +412,11 @@ impl LenPrefixed { impl Serializable for LenPrefixed { fn read_from(buf: &mut R) -> Result, Error> { - let len_data: L = try!(Serializable::read_from(buf)); + let len_data: L = Serializable::read_from(buf)?; let len: usize = len_data.into(); let mut data: Vec = Vec::with_capacity(len); for _ in 0..len { - data.push(try!(Serializable::read_from(buf))); + data.push(Serializable::read_from(buf)?); } Result::Ok(LenPrefixed { len: len_data, @@ -426,10 +426,10 @@ impl Serializable for LenPrefixed { fn write_to(&self, buf: &mut W) -> Result<(), Error> { let len_data: L = L::from(self.data.len()); - try!(len_data.write_to(buf)); + len_data.write_to(buf)?; let data = &self.data; for val in data { - try!(val.write_to(buf)); + val.write_to(buf)?; } Result::Ok(()) } @@ -468,10 +468,10 @@ impl LenPrefixedBytes { impl Serializable for LenPrefixedBytes { fn read_from(buf: &mut R) -> Result, Error> { - let len_data: L = try!(Serializable::read_from(buf)); + let len_data: L = Serializable::read_from(buf)?; let len: usize = len_data.into(); let mut data: Vec = Vec::with_capacity(len); - try!(buf.take(len as u64).read_to_end(&mut data)); + buf.take(len as u64).read_to_end(&mut data)?; Result::Ok(LenPrefixedBytes { len: len_data, data: data, @@ -480,8 +480,8 @@ impl Serializable for LenPrefixedBytes { fn write_to(&self, buf: &mut W) -> Result<(), Error> { let len_data: L = L::from(self.data.len()); - try!(len_data.write_to(buf)); - try!(buf.write_all(&self.data[..])); + len_data.write_to(buf)?; + buf.write_all(&self.data[..])?; Result::Ok(()) } } @@ -544,7 +544,7 @@ impl Serializable for VarInt { let mut size = 0; let mut val = 0u32; loop { - let b = try!(buf.read_u8()) as u32; + let b = buf.read_u8()? as u32; val |= (b & PART) << (size * 7); size += 1; if size > 5 { @@ -564,10 +564,10 @@ impl Serializable for VarInt { let mut val = self.0 as u32; loop { if (val & !PART) == 0 { - try!(buf.write_u8(val as u8)); + buf.write_u8(val as u8)?; return Result::Ok(()); } - try!(buf.write_u8(((val & PART) | 0x80) as u8)); + buf.write_u8(((val & PART) | 0x80) as u8)?; val >>= 7; } } @@ -607,7 +607,7 @@ impl Serializable for VarLong { let mut size = 0; let mut val = 0u64; loop { - let b = try!(buf.read_u8()) as u64; + let b = buf.read_u8()? as u64; val |= (b & PART) << (size * 7); size += 1; if size > 10 { @@ -627,10 +627,10 @@ impl Serializable for VarLong { let mut val = self.0 as u64; loop { if (val & !PART) == 0 { - try!(buf.write_u8(val as u8)); + buf.write_u8(val as u8)?; return Result::Ok(()); } - try!(buf.write_u8(((val & PART) | 0x80) as u8)); + buf.write_u8(((val & PART) | 0x80) as u8)?; val >>= 7; } } @@ -650,7 +650,7 @@ impl fmt::Debug for VarLong { impl Serializable for Position { fn read_from(buf: &mut R) -> Result { - let pos = try!(buf.read_u64::()); + let pos = buf.read_u64::()?; Ok(Position::new( ((pos as i64) >> 38) as i32, (((pos as i64) >> 26) & 0xFFF) as i32, @@ -661,7 +661,7 @@ impl Serializable for Position { let pos = (((self.x as u64) & 0x3FFFFFF) << 38) | (((self.y as u64) & 0xFFF) << 26) | ((self.z as u64) & 0x3FFFFFF); - try!(buf.write_u64::(pos)); + buf.write_u64::(pos)?; Result::Ok(()) } } @@ -763,7 +763,7 @@ impl Conn { } else { format!("{}:{}", parts[0], parts[1]) }; - let stream = try!(TcpStream::connect(&*address)); + let stream = TcpStream::connect(&*address)?; Result::Ok(Conn { stream: stream, host: parts[0].to_owned(), @@ -779,8 +779,8 @@ impl Conn { pub fn write_packet(&mut self, packet: T) -> Result<(), Error> { let mut buf = Vec::new(); - try!(VarInt(packet.packet_id()).write_to(&mut buf)); - try!(packet.write(&mut buf)); + VarInt(packet.packet_id()).write_to(&mut buf)?; + packet.write(&mut buf)?; let mut extra = if self.compression_threshold >= 0 { 1 @@ -794,26 +794,26 @@ impl Conn { extra = 0; let uncompressed_size = buf.len(); let mut new = Vec::new(); - try!(VarInt(uncompressed_size as i32).write_to(&mut new)); + VarInt(uncompressed_size as i32).write_to(&mut new)?; let write = self.compression_write.as_mut().unwrap(); write.reset(io::Cursor::new(buf)); - try!(write.read_to_end(&mut new)); + write.read_to_end(&mut new)?; buf = new; } - try!(VarInt(buf.len() as i32 + extra).write_to(self)); + VarInt(buf.len() as i32 + extra).write_to(self)?; if self.compression_threshold >= 0 && extra == 1 { - try!(VarInt(0).write_to(self)); + VarInt(0).write_to(self)?; } - try!(self.write_all(&buf)); + self.write_all(&buf)?; Result::Ok(()) } pub fn read_packet(&mut self) -> Result { - let len = try!(VarInt::read_from(self)).0 as usize; + let len = VarInt::read_from(self)?.0 as usize; let mut ibuf = vec![0; len]; - try!(self.read_exact(&mut ibuf)); + self.read_exact(&mut ibuf)?; let mut buf = io::Cursor::new(ibuf); @@ -821,25 +821,25 @@ impl Conn { if self.compression_read.is_none() { self.compression_read = Some(ZlibDecoder::new(io::Cursor::new(Vec::new()))); } - let uncompressed_size = try!(VarInt::read_from(&mut buf)).0; + let uncompressed_size = VarInt::read_from(&mut buf)?.0; if uncompressed_size != 0 { let mut new = Vec::with_capacity(uncompressed_size as usize); { let reader = self.compression_read.as_mut().unwrap(); reader.reset(buf); - try!(reader.read_to_end(&mut new)); + reader.read_to_end(&mut new)?; } buf = io::Cursor::new(new); } } - let id = try!(VarInt::read_from(&mut buf)).0; + let id = VarInt::read_from(&mut buf)?.0; let dir = match self.direction { Direction::Clientbound => Direction::Serverbound, Direction::Serverbound => Direction::Clientbound, }; - let packet = try!(packet::packet_by_id(self.state, dir, id, &mut buf)); + let packet = packet::packet_by_id(self.state, dir, id, &mut buf)?; match packet { Some(val) => { @@ -873,26 +873,26 @@ impl Conn { use self::packet::Packet; let host = self.host.clone(); let port = self.port; - try!(self.write_packet(Handshake { + self.write_packet(Handshake { protocol_version: VarInt(SUPPORTED_PROTOCOL), host: host, port: port, next: VarInt(1), - })); + })?; self.state = State::Status; - try!(self.write_packet(StatusRequest { empty: () })); + self.write_packet(StatusRequest { empty: () })?; - let status = if let Packet::StatusResponse(res) = try!(self.read_packet()) { + let status = if let Packet::StatusResponse(res) = self.read_packet()? { res.status } else { return Err(Error::Err("Wrong packet".to_owned())); }; let start = Instant::now(); - try!(self.write_packet(StatusPing { ping: 42 })); + self.write_packet(StatusPing { ping: 42 })?; - if let Packet::StatusPong(_) = try!(self.read_packet()) { + if let Packet::StatusPong(_) = self.read_packet()? { } else { return Err(Error::Err("Wrong packet".to_owned())); }; @@ -906,28 +906,28 @@ impl Conn { let invalid_status = || Error::Err("Invalid status".to_owned()); - let version = try!(val.get("version").ok_or(invalid_status())); - let players = try!(val.get("players").ok_or(invalid_status())); + let version = val.get("version").ok_or(invalid_status())?; + let players = val.get("players").ok_or(invalid_status())?; Ok((Status { version: StatusVersion { - name: try!(version.get("name").and_then(Value::as_str).ok_or(invalid_status())) + name: version.get("name").and_then(Value::as_str).ok_or(invalid_status())? .to_owned(), - protocol: try!(version.get("protocol") + protocol: version.get("protocol") .and_then(Value::as_i64) - .ok_or(invalid_status())) as i32, + .ok_or(invalid_status())? as i32, }, players: StatusPlayers { - max: try!(players.get("max") + max: players.get("max") .and_then(Value::as_i64) - .ok_or(invalid_status())) as i32, - online: try!(players.get("online") + .ok_or(invalid_status())? as i32, + online: players.get("online") .and_then(Value::as_i64) - .ok_or(invalid_status())) as i32, + .ok_or(invalid_status())? as i32, sample: Vec::new(), /* TODO */ }, - description: format::Component::from_value(try!(val.get("description") - .ok_or(invalid_status()))), + description: format::Component::from_value(val.get("description") + .ok_or(invalid_status())?), favicon: val.get("favicon").and_then(Value::as_str).map(|v| v.to_owned()), }, ping)) @@ -966,7 +966,7 @@ impl Read for Conn { match self.cipher.as_mut() { Option::None => self.stream.read(buf), Option::Some(cipher) => { - let ret = try!(self.stream.read(buf)); + let ret = self.stream.read(buf)?; cipher.decrypt(&mut buf[..ret]); Ok(ret) @@ -988,7 +988,7 @@ impl Write for Conn { cipher.encrypt(&mut data); - try!(self.stream.write_all(&data)); + self.stream.write_all(&data)?; Ok(buf.len()) } } diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index d546363..a0c7704 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -38,7 +38,7 @@ impl Profile { "name": "Minecraft", "version": 1 }}); - let req = try!(serde_json::to_string(&req_msg)); + let req = serde_json::to_string(&req_msg)?; let client = reqwest::Client::new(); let res = client.post(LOGIN_URL) @@ -46,7 +46,7 @@ impl Profile { .body(req) .send()?; - let ret: serde_json::Value = try!(serde_json::from_reader(res)); + let ret: serde_json::Value = serde_json::from_reader(res)?; if let Some(error) = ret.get("error").and_then(|v| v.as_str()) { return Err(super::Error::Err(format!( "{}: {}", @@ -66,7 +66,7 @@ impl Profile { "accessToken": self.access_token.clone(), "clientToken": token }); - let req = try!(serde_json::to_string(&req_msg)); + let req = serde_json::to_string(&req_msg)?; let client = reqwest::Client::new(); let res = client.post(VALIDATE_URL) @@ -75,14 +75,14 @@ impl Profile { .send()?; if res.status() != reqwest::StatusCode::NO_CONTENT { - let req = try!(serde_json::to_string(&req_msg)); // TODO: fix parsing twice to avoid move + let req = serde_json::to_string(&req_msg)?; // TODO: fix parsing twice to avoid move // Refresh needed let res = client.post(REFRESH_URL) .header(reqwest::header::CONTENT_TYPE, "application/json") .body(req) .send()?; - let ret: serde_json::Value = try!(serde_json::from_reader(res)); + let ret: serde_json::Value = serde_json::from_reader(res)?; if let Some(error) = ret.get("error").and_then(|v| v.as_str()) { return Err(super::Error::Err(format!( "{}: {}", diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index fb4613f..6e1a943 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -953,13 +953,13 @@ pub struct Statistic { impl Serializable for Statistic { fn read_from(buf: &mut R) -> Result { Ok(Statistic { - name: try!(Serializable::read_from(buf)), - value: try!(Serializable::read_from(buf)), + name: Serializable::read_from(buf)?, + value: Serializable::read_from(buf)?, }) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(self.name.write_to(buf)); + self.name.write_to(buf)?; self.value.write_to(buf) } } @@ -974,15 +974,15 @@ pub struct BlockChangeRecord { impl Serializable for BlockChangeRecord { fn read_from(buf: &mut R) -> Result { Ok(BlockChangeRecord { - xz: try!(Serializable::read_from(buf)), - y: try!(Serializable::read_from(buf)), - block_id: try!(Serializable::read_from(buf)), + xz: Serializable::read_from(buf)?, + y: Serializable::read_from(buf)?, + block_id: Serializable::read_from(buf)?, }) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(self.xz.write_to(buf)); - try!(self.y.write_to(buf)); + self.xz.write_to(buf)?; + self.y.write_to(buf)?; self.block_id.write_to(buf) } } @@ -997,15 +997,15 @@ pub struct ExplosionRecord { impl Serializable for ExplosionRecord { fn read_from(buf: &mut R) -> Result { Ok(ExplosionRecord { - x: try!(Serializable::read_from(buf)), - y: try!(Serializable::read_from(buf)), - z: try!(Serializable::read_from(buf)), + x: Serializable::read_from(buf)?, + y: Serializable::read_from(buf)?, + z: Serializable::read_from(buf)?, }) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(self.x.write_to(buf)); - try!(self.y.write_to(buf)); + self.x.write_to(buf)?; + self.y.write_to(buf)?; self.z.write_to(buf) } } @@ -1020,15 +1020,15 @@ pub struct MapIcon { impl Serializable for MapIcon { fn read_from(buf: &mut R) -> Result { Ok(MapIcon { - direction_type: try!(Serializable::read_from(buf)), - x: try!(Serializable::read_from(buf)), - z: try!(Serializable::read_from(buf)), + direction_type: Serializable::read_from(buf)?, + x: Serializable::read_from(buf)?, + z: Serializable::read_from(buf)?, }) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(self.direction_type.write_to(buf)); - try!(self.x.write_to(buf)); + self.direction_type.write_to(buf)?; + self.x.write_to(buf)?; self.z.write_to(buf) } } @@ -1053,15 +1053,15 @@ pub struct EntityProperty { impl Serializable for EntityProperty { fn read_from(buf: &mut R) -> Result { Ok(EntityProperty { - key: try!(Serializable::read_from(buf)), - value: try!(Serializable::read_from(buf)), - modifiers: try!(Serializable::read_from(buf)), + key: Serializable::read_from(buf)?, + value: Serializable::read_from(buf)?, + modifiers: Serializable::read_from(buf)?, }) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(self.key.write_to(buf)); - try!(self.value.write_to(buf)); + self.key.write_to(buf)?; + self.value.write_to(buf)?; self.modifiers.write_to(buf) } } @@ -1076,15 +1076,15 @@ pub struct PropertyModifier { impl Serializable for PropertyModifier { fn read_from(buf: &mut R) -> Result { Ok(PropertyModifier { - uuid: try!(Serializable::read_from(buf)), - amount: try!(Serializable::read_from(buf)), - operation: try!(Serializable::read_from(buf)), + uuid: Serializable::read_from(buf)?, + amount: Serializable::read_from(buf)?, + operation: Serializable::read_from(buf)?, }) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - try!(self.uuid.write_to(buf)); - try!(self.amount.write_to(buf)); + self.uuid.write_to(buf)?; + self.amount.write_to(buf)?; self.operation.write_to(buf) } } @@ -1098,25 +1098,25 @@ pub struct PlayerInfoData { impl Serializable for PlayerInfoData { fn read_from(buf: &mut R) -> Result { let mut m = PlayerInfoData { - action: try!(Serializable::read_from(buf)), + action: Serializable::read_from(buf)?, players: Vec::new(), }; - let len = try!(VarInt::read_from(buf)); + let len = VarInt::read_from(buf)?; for _ in 0..len.0 { - let uuid = try!(UUID::read_from(buf)); + let uuid = UUID::read_from(buf)?; match m.action.0 { 0 => { - let name = try!(String::read_from(buf)); + let name = String::read_from(buf)?; let mut props = Vec::new(); - let plen = try!(VarInt::read_from(buf)).0; + let plen = VarInt::read_from(buf)?.0; for _ in 0..plen { let mut prop = PlayerProperty { - name: try!(String::read_from(buf)), - value: try!(String::read_from(buf)), + name: String::read_from(buf)?, + value: String::read_from(buf)?, signature: Default::default(), }; - if try!(bool::read_from(buf)) { - prop.signature = Some(try!(String::read_from(buf))); + if bool::read_from(buf)? { + prop.signature = Some(String::read_from(buf)?); } props.push(prop); } @@ -1124,11 +1124,11 @@ impl Serializable for PlayerInfoData { uuid: uuid, name: name, properties: props, - gamemode: try!(Serializable::read_from(buf)), - ping: try!(Serializable::read_from(buf)), + gamemode: Serializable::read_from(buf)?, + ping: Serializable::read_from(buf)?, display: { - if try!(bool::read_from(buf)) { - Some(try!(Serializable::read_from(buf))) + if bool::read_from(buf)? { + Some(Serializable::read_from(buf)?) } else { None } @@ -1139,21 +1139,21 @@ impl Serializable for PlayerInfoData { 1 => { m.players.push(PlayerDetail::UpdateGamemode { uuid: uuid, - gamemode: try!(Serializable::read_from(buf)), + gamemode: Serializable::read_from(buf)?, }) } 2 => { m.players.push(PlayerDetail::UpdateLatency { uuid: uuid, - ping: try!(Serializable::read_from(buf)), + ping: Serializable::read_from(buf)?, }) } 3 => { m.players.push(PlayerDetail::UpdateDisplayName { uuid: uuid, display: { - if try!(bool::read_from(buf)) { - Some(try!(Serializable::read_from(buf))) + if bool::read_from(buf)? { + Some(Serializable::read_from(buf)?) } else { None } From 099e10195b068c5788377f52e9b2408440b96834 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 4 Nov 2018 11:34:53 -0800 Subject: [PATCH 095/160] Update try!() to new ? syntax for Rust 2018 edition Not automatically updated, see https://users.rust-lang.org/t/why-does-cargo-fix-replace-try-with-r-try-instead-of/21972/3 There are other tools to replace it, btu this is what I used: find src -name '*.rs' -exec perl -MRegexp::Common -0777 -pe'$bp=$RE{balanced}{-parens=>"()"}; s/try\!($bp)/substr($1, 1, length($1) - 2) . "?"/ges' -i {} \; --- protocol/src/format.rs | 4 +- protocol/src/item.rs | 18 +++--- protocol/src/nbt/mod.rs | 80 ++++++++++++------------- protocol/src/types/metadata.rs | 106 ++++++++++++++++----------------- 4 files changed, 104 insertions(+), 104 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 9eb6ceb..88dba9c 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -147,10 +147,10 @@ impl TextComponent { impl fmt::Display for TextComponent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}", self.text)); + write!(f, "{}", self.text)?; if let Some(ref extra) = self.modifier.extra { for c in extra { - try!(write!(f, "{}", c)); + write!(f, "{}", c)?; } } Result::Ok(()) diff --git a/protocol/src/item.rs b/protocol/src/item.rs index 45e4017..dfea1c6 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -39,26 +39,26 @@ impl Default for Stack { impl Serializable for Option { fn read_from(buf: &mut R) -> Result, protocol::Error> { - let id = try!(buf.read_i16::()); + let id = buf.read_i16::()?; if id == -1 { return Ok(None); } Ok(Some(Stack { id: id as isize, - count: try!(buf.read_u8()) as isize, - damage: try!(buf.read_i16::()) as isize, - tag: try!(Serializable::read_from(buf)), + count: buf.read_u8()? as isize, + damage: buf.read_i16::()? as isize, + tag: Serializable::read_from(buf)?, })) } fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { match *self { Some(ref val) => { - try!(buf.write_i16::(val.id as i16)); - try!(buf.write_u8(val.count as u8)); - try!(buf.write_i16::(val.damage as i16)); - try!(val.tag.write_to(buf)); + buf.write_i16::(val.id as i16)?; + buf.write_u8(val.count as u8)?; + buf.write_i16::(val.damage as i16)?; + val.tag.write_to(buf)?; } - None => try!(buf.write_i16::(-1)), + None => buf.write_i16::(-1)?, } Result::Ok(()) } diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index db4d302..3e79578 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -175,45 +175,45 @@ impl Tag { fn read_type(id: u8, buf: &mut R) -> Result { match id { 0 => unreachable!(), - 1 => Ok(Tag::Byte(try!(buf.read_i8()))), - 2 => Ok(Tag::Short(try!(buf.read_i16::()))), - 3 => Ok(Tag::Int(try!(buf.read_i32::()))), - 4 => Ok(Tag::Long(try!(buf.read_i64::()))), - 5 => Ok(Tag::Float(try!(buf.read_f32::()))), - 6 => Ok(Tag::Double(try!(buf.read_f64::()))), + 1 => Ok(Tag::Byte(buf.read_i8()?)), + 2 => Ok(Tag::Short(buf.read_i16::()?)), + 3 => Ok(Tag::Int(buf.read_i32::()?)), + 4 => Ok(Tag::Long(buf.read_i64::()?)), + 5 => Ok(Tag::Float(buf.read_f32::()?)), + 6 => Ok(Tag::Double(buf.read_f64::()?)), 7 => Ok(Tag::ByteArray({ - let len: i32 = try!(Serializable::read_from(buf)); + let len: i32 = Serializable::read_from(buf)?; let mut data = Vec::with_capacity(len as usize); - try!(buf.take(len as u64).read_to_end(&mut data)); + buf.take(len as u64).read_to_end(&mut data)?; data })), - 8 => Ok(Tag::String(try!(read_string(buf)))), + 8 => Ok(Tag::String(read_string(buf)?)), 9 => { let mut l = Vec::new(); - let ty = try!(buf.read_u8()); - let len: i32 = try!(Serializable::read_from(buf)); + let ty = buf.read_u8()?; + let len: i32 = Serializable::read_from(buf)?; for _ in 0..len { - l.push(try!(Tag::read_type(ty, buf))); + l.push(Tag::read_type(ty, buf)?); } Ok(Tag::List(l)) } 10 => { let mut c = Tag::new_compound(); loop { - let ty = try!(buf.read_u8()); + let ty = buf.read_u8()?; if ty == 0 { break; } - let name: String = try!(read_string(buf)); - c.put(&name[..], try!(Tag::read_type(ty, buf))); + let name: String = read_string(buf)?; + c.put(&name[..], Tag::read_type(ty, buf)?); } Ok(c) } 11 => Ok(Tag::IntArray({ - let len: i32 = try!(Serializable::read_from(buf)); + let len: i32 = Serializable::read_from(buf)?; let mut data = Vec::with_capacity(len as usize); for _ in 0..len { - data.push(try!(buf.read_i32::())); + data.push(buf.read_i32::()?); } data })), @@ -230,41 +230,41 @@ impl Serializable for Tag { fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { match *self { Tag::End => {} - Tag::Byte(val) => try!(buf.write_i8(val)), - Tag::Short(val) => try!(buf.write_i16::(val)), - Tag::Int(val) => try!(buf.write_i32::(val)), - Tag::Long(val) => try!(buf.write_i64::(val)), - Tag::Float(val) => try!(buf.write_f32::(val)), - Tag::Double(val) => try!(buf.write_f64::(val)), + Tag::Byte(val) => buf.write_i8(val)?, + Tag::Short(val) => buf.write_i16::(val)?, + Tag::Int(val) => buf.write_i32::(val)?, + Tag::Long(val) => buf.write_i64::(val)?, + Tag::Float(val) => buf.write_f32::(val)?, + Tag::Double(val) => buf.write_f64::(val)?, Tag::ByteArray(ref val) => { - try!((val.len() as i32).write_to(buf)); - try!(buf.write_all(val)); + (val.len() as i32).write_to(buf)?; + buf.write_all(val)?; } - Tag::String(ref val) => try!(write_string(buf, val)), + Tag::String(ref val) => write_string(buf, val)?, Tag::List(ref val) => { if val.is_empty() { - try!(buf.write_u8(0)); - try!(buf.write_i32::(0)); + buf.write_u8(0)?; + buf.write_i32::(0)?; } else { - try!(buf.write_u8(val[0].internal_id())); - try!(buf.write_i32::(val.len() as i32)); + buf.write_u8(val[0].internal_id())?; + buf.write_i32::(val.len() as i32)?; for e in val { - try!(e.write_to(buf)); + e.write_to(buf)?; } } } Tag::Compound(ref val) => { for (k, v) in val { - try!(v.internal_id().write_to(buf)); - try!(write_string(buf, k)); - try!(v.write_to(buf)); + v.internal_id().write_to(buf)?; + write_string(buf, k)?; + v.write_to(buf)?; } - try!(buf.write_u8(0)); + buf.write_u8(0)?; } Tag::IntArray(ref val) => { - try!((val.len() as i32).write_to(buf)); + (val.len() as i32).write_to(buf)?; for v in val { - try!(v.write_to(buf)); + v.write_to(buf)?; } } } @@ -274,13 +274,13 @@ impl Serializable for Tag { pub fn write_string(buf: &mut W, s: &str) -> Result<(), protocol::Error> { let data = s.as_bytes(); - try!((data.len() as i16).write_to(buf)); + (data.len() as i16).write_to(buf)?; buf.write_all(data).map_err(|v| v.into()) } pub fn read_string(buf: &mut R) -> Result { - let len: i16 = try!(buf.read_i16::()); + let len: i16 = buf.read_i16::()?; let mut ret = String::new(); - try!(buf.take(len as u64).read_to_string(&mut ret)); + buf.take(len as u64).read_to_string(&mut ret)?; Result::Ok(ret) } diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 7616266..d59ba51 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -64,40 +64,40 @@ impl Serializable for Metadata { fn read_from(buf: &mut R) -> Result { let mut m = Metadata::new(); loop { - let index = try!(u8::read_from(buf)) as i32; + let index = u8::read_from(buf)? as i32; if index == 0xFF { break; } - let ty = try!(u8::read_from(buf)); + let ty = u8::read_from(buf)?; match ty { - 0 => m.put_raw(index, try!(i8::read_from(buf))), - 1 => m.put_raw(index, try!(protocol::VarInt::read_from(buf)).0), - 2 => m.put_raw(index, try!(f32::read_from(buf))), - 3 => m.put_raw(index, try!(String::read_from(buf))), - 4 => m.put_raw(index, try!(format::Component::read_from(buf))), - 5 => m.put_raw(index, try!(Option::::read_from(buf))), - 6 => m.put_raw(index, try!(bool::read_from(buf))), + 0 => m.put_raw(index, i8::read_from(buf)?), + 1 => m.put_raw(index, protocol::VarInt::read_from(buf)?.0), + 2 => m.put_raw(index, f32::read_from(buf)?), + 3 => m.put_raw(index, String::read_from(buf)?), + 4 => m.put_raw(index, format::Component::read_from(buf)?), + 5 => m.put_raw(index, Option::::read_from(buf)?), + 6 => m.put_raw(index, bool::read_from(buf)?), 7 => m.put_raw(index, - [try!(f32::read_from(buf)), - try!(f32::read_from(buf)), - try!(f32::read_from(buf))]), - 8 => m.put_raw(index, try!(Position::read_from(buf))), + [f32::read_from(buf)?, + f32::read_from(buf)?, + f32::read_from(buf)?]), + 8 => m.put_raw(index, Position::read_from(buf)?), 9 => { - if try!(bool::read_from(buf)) { - m.put_raw(index, try!(Option::::read_from(buf))); + if bool::read_from(buf)? { + m.put_raw(index, Option::::read_from(buf)?); } else { m.put_raw::>(index, None); } } - 10 => m.put_raw(index, try!(protocol::VarInt::read_from(buf))), + 10 => m.put_raw(index, protocol::VarInt::read_from(buf)?), 11 => { - if try!(bool::read_from(buf)) { - m.put_raw(index, try!(Option::::read_from(buf))); + if bool::read_from(buf)? { + m.put_raw(index, Option::::read_from(buf)?); } else { m.put_raw::>(index, None); } } - 12 => m.put_raw(index, try!(protocol::VarInt::read_from(buf)).0 as u16), + 12 => m.put_raw(index, protocol::VarInt::read_from(buf)?.0 as u16), _ => return Err(protocol::Error::Err("unknown metadata type".to_owned())), } } @@ -106,76 +106,76 @@ impl Serializable for Metadata { fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { for (k, v) in &self.map { - try!((*k as u8).write_to(buf)); + (*k as u8).write_to(buf)?; match *v { Value::Byte(ref val) => { - try!(u8::write_to(&0, buf)); - try!(val.write_to(buf)); + u8::write_to(&0, buf)?; + val.write_to(buf)?; } Value::Int(ref val) => { - try!(u8::write_to(&1, buf)); - try!(protocol::VarInt(*val).write_to(buf)); + u8::write_to(&1, buf)?; + protocol::VarInt(*val).write_to(buf)?; } Value::Float(ref val) => { - try!(u8::write_to(&2, buf)); - try!(val.write_to(buf)); + u8::write_to(&2, buf)?; + val.write_to(buf)?; } Value::String(ref val) => { - try!(u8::write_to(&3, buf)); - try!(val.write_to(buf)); + u8::write_to(&3, buf)?; + val.write_to(buf)?; } Value::FormatComponent(ref val) => { - try!(u8::write_to(&4, buf)); - try!(val.write_to(buf)); + u8::write_to(&4, buf)?; + val.write_to(buf)?; } Value::OptionalItemStack(ref val) => { - try!(u8::write_to(&5, buf)); - try!(val.write_to(buf)); + u8::write_to(&5, buf)?; + val.write_to(buf)?; } Value::Bool(ref val) => { - try!(u8::write_to(&6, buf)); - try!(val.write_to(buf)); + u8::write_to(&6, buf)?; + val.write_to(buf)?; } Value::Vector(ref val) => { - try!(u8::write_to(&7, buf)); - try!(val[0].write_to(buf)); - try!(val[1].write_to(buf)); - try!(val[2].write_to(buf)); + u8::write_to(&7, buf)?; + val[0].write_to(buf)?; + val[1].write_to(buf)?; + val[2].write_to(buf)?; } Value::Position(ref val) => { - try!(u8::write_to(&8, buf)); - try!(val.write_to(buf)); + u8::write_to(&8, buf)?; + val.write_to(buf)?; } Value::OptionalPosition(ref val) => { - try!(u8::write_to(&9, buf)); - try!(val.is_some().write_to(buf)); - try!(val.write_to(buf)); + u8::write_to(&9, buf)?; + val.is_some().write_to(buf)?; + val.write_to(buf)?; } Value::Direction(ref val) => { - try!(u8::write_to(&10, buf)); - try!(val.write_to(buf)); + u8::write_to(&10, buf)?; + val.write_to(buf)?; } Value::OptionalUUID(ref val) => { - try!(u8::write_to(&11, buf)); - try!(val.is_some().write_to(buf)); - try!(val.write_to(buf)); + u8::write_to(&11, buf)?; + val.is_some().write_to(buf)?; + val.write_to(buf)?; } Value::Block(ref val) => { - try!(u8::write_to(&11, buf)); - try!(protocol::VarInt(*val as i32).write_to(buf)); + u8::write_to(&11, buf)?; + protocol::VarInt(*val as i32).write_to(buf)?; } } } - try!(u8::write_to(&0xFF, buf)); + u8::write_to(&0xFF, buf)?; Ok(()) } } impl fmt::Debug for Metadata { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "Metadata[ ")); + write!(f, "Metadata[ ")?; for (k, v) in &self.map { - try!(write!(f, "{:?}={:?}, ", k, v)); + write!(f, "{:?}={:?}, ", k, v)?; } write!(f, "]") } From 4b59bce512659b61498683a02aef860a68d338ad Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 4 Nov 2018 11:48:03 -0800 Subject: [PATCH 096/160] Update to use crate:: for current crate, for Rust 2018 edition From `cargo fix --edition`, see https://rust-lang-nursery.github.io/edition-guide/print.html#the-crate-keyword-refers-to-the-current-crate --- protocol/src/protocol/mod.rs | 20 ++++++++++---------- protocol/src/protocol/packet.rs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index e565712..ba2d9f8 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -22,8 +22,8 @@ use reqwest; pub mod mojang; -use nbt; -use format; +use crate::nbt; +use crate::format; use std::fmt; use std::default; use std::net::TcpStream; @@ -34,7 +34,7 @@ use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; use flate2::read::{ZlibDecoder, ZlibEncoder}; use flate2::Compression; use std::time::{Instant, Duration}; -use shared::Position; +use crate::shared::Position; pub const SUPPORTED_PROTOCOL: i32 = 315; @@ -52,7 +52,7 @@ macro_rules! state_packets { )* })+ })+) => { - use protocol::*; + use crate::protocol::*; use std::io; #[derive(Debug)] @@ -72,13 +72,13 @@ macro_rules! state_packets { $( pub mod $dir { #![allow(unused_imports)] - use protocol::*; + use crate::protocol::*; use std::io; - use format; - use nbt; - use types; - use item; - use shared::Position; + use crate::format; + use crate::nbt; + use crate::types; + use crate::item; + use crate::shared::Position; pub mod internal_ids { diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 6e1a943..ea97def 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use format; +use crate::format; state_packets!( handshake Handshaking { From cb9cf3ef708f20a01cba355b033be720fa3ca5ea Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 4 Nov 2018 11:48:03 -0800 Subject: [PATCH 097/160] Update to use crate:: for current crate, for Rust 2018 edition From `cargo fix --edition`, see https://rust-lang-nursery.github.io/edition-guide/print.html#the-crate-keyword-refers-to-the-current-crate --- protocol/src/item.rs | 4 ++-- protocol/src/types/metadata.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/protocol/src/item.rs b/protocol/src/item.rs index dfea1c6..19fa87c 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use nbt; -use protocol::{self, Serializable}; +use crate::nbt; +use crate::protocol::{self, Serializable}; use std::io; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index d59ba51..5af4fcc 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -16,11 +16,11 @@ use std::collections::HashMap; use std::marker::PhantomData; use std::io; use std::fmt; -use protocol; -use protocol::Serializable; -use format; -use item; -use shared::Position; +use crate::protocol; +use crate::protocol::Serializable; +use crate::format; +use crate::item; +use crate::shared::Position; pub struct MetadataKey { index: i32, From f0fd3eabf4b44a7a828a1a51ca5cab47637db2ed Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 4 Nov 2018 12:35:06 -0800 Subject: [PATCH 098/160] Remove unnecessary 'extern crate's in Rust 2018 edition, import macros https://rust-lang-nursery.github.io/edition-guide/print.html#no-more-extern-crate https://rust-lang-nursery.github.io/edition-guide/rust-2018/macros/macro-changes.html https://github.com/iceiix/steven/pull/13#issuecomment-435702507 --- protocol/src/protocol/mojang.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index a0c7704..3a5f527 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -13,7 +13,7 @@ // limitations under the License. use sha1::{self, Digest}; -use serde_json; +use serde_json::json; use reqwest; #[derive(Clone, Debug)] From d8b2c17f834501d4a47a6adfc114692f54b2bbc1 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 4 Nov 2018 13:37:57 -0800 Subject: [PATCH 099/160] Remove inferred 'static lifetime on const, Rust 1.17+ No longer needed since it is inferred automatically: https://rust-lang-nursery.github.io/edition-guide/rust-2018/ownership-and-lifetimes/simpler-lifetimes-in-static-and-const.html --- protocol/src/protocol/mojang.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 3a5f527..d554b04 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -23,10 +23,10 @@ pub struct Profile { pub access_token: String, } -const JOIN_URL: &'static str = "https://sessionserver.mojang.com/session/minecraft/join"; -const LOGIN_URL: &'static str = "https://authserver.mojang.com/authenticate"; -const REFRESH_URL: &'static str = "https://authserver.mojang.com/refresh"; -const VALIDATE_URL: &'static str = "https://authserver.mojang.com/validate"; +const JOIN_URL: &str = "https://sessionserver.mojang.com/session/minecraft/join"; +const LOGIN_URL: &str = "https://authserver.mojang.com/authenticate"; +const REFRESH_URL: &str = "https://authserver.mojang.com/refresh"; +const VALIDATE_URL: &str = "https://authserver.mojang.com/validate"; impl Profile { pub fn login(username: &str, password: &str, token: &str) -> Result { From da04367669f38e30bf69590c9d84a63c0f29354a Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 4 Nov 2018 13:43:30 -0800 Subject: [PATCH 100/160] Use field init shorthand, instead of x:x, just x, https://rust-lang-nursery.github.io/edition-guide/rust-2018/data-types/field-init-shorthand.html find src -name '*.rs' -exec perl -pe 's/\b(\w+): \1,/$1,/g' -i {} \; --- protocol/src/protocol/mod.rs | 14 +++++++------- protocol/src/protocol/packet.rs | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index ba2d9f8..1e16cc5 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -405,7 +405,7 @@ impl LenPrefixed { pub fn new(data: Vec) -> LenPrefixed { LenPrefixed { len: Default::default(), - data: data, + data, } } } @@ -420,7 +420,7 @@ impl Serializable for LenPrefixed { } Result::Ok(LenPrefixed { len: len_data, - data: data, + data, }) } @@ -461,7 +461,7 @@ impl LenPrefixedBytes { pub fn new(data: Vec) -> LenPrefixedBytes { LenPrefixedBytes { len: Default::default(), - data: data, + data, } } } @@ -474,7 +474,7 @@ impl Serializable for LenPrefixedBytes { buf.take(len as u64).read_to_end(&mut data)?; Result::Ok(LenPrefixedBytes { len: len_data, - data: data, + data, }) } @@ -765,7 +765,7 @@ impl Conn { }; let stream = TcpStream::connect(&*address)?; Result::Ok(Conn { - stream: stream, + stream, host: parts[0].to_owned(), port: parts[1].parse().unwrap(), direction: Direction::Serverbound, @@ -875,8 +875,8 @@ impl Conn { let port = self.port; self.write_packet(Handshake { protocol_version: VarInt(SUPPORTED_PROTOCOL), - host: host, - port: port, + host, + port, next: VarInt(1), })?; self.state = State::Status; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index ea97def..b491cd0 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1121,8 +1121,8 @@ impl Serializable for PlayerInfoData { props.push(prop); } let p = PlayerDetail::Add { - uuid: uuid, - name: name, + uuid, + name, properties: props, gamemode: Serializable::read_from(buf)?, ping: Serializable::read_from(buf)?, @@ -1138,19 +1138,19 @@ impl Serializable for PlayerInfoData { } 1 => { m.players.push(PlayerDetail::UpdateGamemode { - uuid: uuid, + uuid, gamemode: Serializable::read_from(buf)?, }) } 2 => { m.players.push(PlayerDetail::UpdateLatency { - uuid: uuid, + uuid, ping: Serializable::read_from(buf)?, }) } 3 => { m.players.push(PlayerDetail::UpdateDisplayName { - uuid: uuid, + uuid, display: { if bool::read_from(buf)? { Some(Serializable::read_from(buf)?) From 901e54772eacc0f53f536ee89c826d6d18752261 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 4 Nov 2018 13:43:30 -0800 Subject: [PATCH 101/160] Use field init shorthand, instead of x:x, just x, https://rust-lang-nursery.github.io/edition-guide/rust-2018/data-types/field-init-shorthand.html find src -name '*.rs' -exec perl -pe 's/\b(\w+): \1,/$1,/g' -i {} \; --- protocol/src/format.rs | 6 +++--- protocol/src/types/bit/map.rs | 2 +- protocol/src/types/metadata.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 88dba9c..79248d4 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -41,7 +41,7 @@ impl Component { if let Some(val) = v.as_str() { Component::Text(TextComponent { text: val.to_owned(), - modifier: modifier, + modifier, }) } else if v.get("text").is_some() { Component::Text(TextComponent::from_value(v, modifier)) @@ -49,7 +49,7 @@ impl Component { modifier.color = Some(Color::RGB(255, 0, 0)); Component::Text(TextComponent { text: "UNHANDLED".to_owned(), - modifier: modifier, + modifier, }) } } @@ -136,7 +136,7 @@ impl TextComponent { pub fn from_value(v: &serde_json::Value, modifier: Modifier) -> Self { TextComponent { text: v.get("text").unwrap().as_str().unwrap_or("").to_owned(), - modifier: modifier, + modifier, } } diff --git a/protocol/src/types/bit/map.rs b/protocol/src/types/bit/map.rs index 11979fe..2f86979 100644 --- a/protocol/src/types/bit/map.rs +++ b/protocol/src/types/bit/map.rs @@ -63,7 +63,7 @@ impl Map { Map { length: (bits.len()*64 + (size-1)) / size, bit_size: size, - bits: bits, + bits, } } diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 5af4fcc..228a034 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -31,7 +31,7 @@ impl MetadataKey { #[allow(dead_code)] fn new(index: i32) -> MetadataKey { MetadataKey { - index: index, + index, ty: PhantomData, } } From 3b0502bfd4cbadaa724648020a0e47ac6b856b30 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Fri, 30 Nov 2018 17:02:26 -0800 Subject: [PATCH 102/160] Explicitly assign packet IDs (not implicitly from ordering in source code) (#19) The packet IDs were assigned by the `create_ids!` macro, in the order they are in the source code. This reduces flexibility for adopting new protocols, since the packets can be reordered arbitrarily. Part of https://github.com/iceiix/steven/issues/18 --- protocol/src/protocol/mod.rs | 119 ++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 1 deletion(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 1e16cc5..65add2c 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -81,8 +81,125 @@ macro_rules! state_packets { use crate::shared::Position; + #[allow(non_upper_case_globals)] pub mod internal_ids { - create_ids!(i32, $($name),*); +pub const Handshake: i32 = 0x00; +pub const TeleportConfirm: i32 = 0x00; +pub const TabComplete: i32 = 0x01; +pub const ChatMessage: i32 = 0x02; +pub const ClientStatus: i32 = 0x03; +pub const ClientSettings: i32 = 0x04; +pub const ConfirmTransactionServerbound: i32 = 0x05; +pub const EnchantItem: i32 = 0x06; +pub const ClickWindow: i32 = 0x07; +pub const CloseWindow: i32 = 0x08; +pub const PluginMessageServerbound: i32 = 0x09; +pub const UseEntity: i32 = 0x0a; +pub const KeepAliveServerbound: i32 = 0x0b; +pub const PlayerPosition: i32 = 0x0c; +pub const PlayerPositionLook: i32 = 0x0d; +pub const PlayerLook: i32 = 0x0e; +pub const Player: i32 = 0x0f; +pub const VehicleMove: i32 = 0x10; +pub const SteerBoat: i32 = 0x11; +pub const ClientAbilities: i32 = 0x12; +pub const PlayerDigging: i32 = 0x13; +pub const PlayerAction: i32 = 0x14; +pub const SteerVehicle: i32 = 0x15; +pub const ResourcePackStatus: i32 = 0x16; +pub const HeldItemChange: i32 = 0x17; +pub const CreativeInventoryAction: i32 = 0x18; +pub const SetSign: i32 = 0x19; +pub const ArmSwing: i32 = 0x1a; +pub const SpectateTeleport: i32 = 0x1b; +pub const PlayerBlockPlacement: i32 = 0x1c; +pub const UseItem: i32 = 0x1d; +pub const SpawnObject: i32 = 0x00; +pub const SpawnExperienceOrb: i32 = 0x01; +pub const SpawnGlobalEntity: i32 = 0x02; +pub const SpawnMob: i32 = 0x03; +pub const SpawnPainting: i32 = 0x04; +pub const SpawnPlayer: i32 = 0x05; +pub const Animation: i32 = 0x06; +pub const Statistics: i32 = 0x07; +pub const BlockBreakAnimation: i32 = 0x08; +pub const UpdateBlockEntity: i32 = 0x09; +pub const BlockAction: i32 = 0x0a; +pub const BlockChange: i32 = 0x0b; +pub const BossBar: i32 = 0x0c; +pub const ServerDifficulty: i32 = 0x0d; +pub const TabCompleteReply: i32 = 0x0e; +pub const ServerMessage: i32 = 0x0f; +pub const MultiBlockChange: i32 = 0x10; +pub const ConfirmTransaction: i32 = 0x11; +pub const WindowClose: i32 = 0x12; +pub const WindowOpen: i32 = 0x13; +pub const WindowItems: i32 = 0x14; +pub const WindowProperty: i32 = 0x15; +pub const WindowSetSlot: i32 = 0x16; +pub const SetCooldown: i32 = 0x17; +pub const PluginMessageClientbound: i32 = 0x18; +pub const NamedSoundEffect: i32 = 0x19; +pub const Disconnect: i32 = 0x1a; +pub const EntityAction: i32 = 0x1b; +pub const Explosion: i32 = 0x1c; +pub const ChunkUnload: i32 = 0x1d; +pub const ChangeGameState: i32 = 0x1e; +pub const KeepAliveClientbound: i32 = 0x1f; +pub const ChunkData: i32 = 0x20; +pub const Effect: i32 = 0x21; +pub const Particle: i32 = 0x22; +pub const JoinGame: i32 = 0x23; +pub const Maps: i32 = 0x24; +pub const EntityMove: i32 = 0x25; +pub const EntityLookAndMove: i32 = 0x26; +pub const EntityLook: i32 = 0x27; +pub const Entity: i32 = 0x28; +pub const VehicleTeleport: i32 = 0x29; +pub const SignEditorOpen: i32 = 0x2a; +pub const PlayerAbilities: i32 = 0x2b; +pub const CombatEvent: i32 = 0x2c; +pub const PlayerInfo: i32 = 0x2d; +pub const TeleportPlayer: i32 = 0x2e; +pub const EntityUsedBed: i32 = 0x2f; +pub const EntityDestroy: i32 = 0x30; +pub const EntityRemoveEffect: i32 = 0x31; +pub const ResourcePackSend: i32 = 0x32; +pub const Respawn: i32 = 0x33; +pub const EntityHeadLook: i32 = 0x34; +pub const WorldBorder: i32 = 0x35; +pub const Camera: i32 = 0x36; +pub const SetCurrentHotbarSlot: i32 = 0x37; +pub const ScoreboardDisplay: i32 = 0x38; +pub const EntityMetadata: i32 = 0x39; +pub const EntityAttach: i32 = 0x3a; +pub const EntityVelocity: i32 = 0x3b; +pub const EntityEquipment: i32 = 0x3c; +pub const SetExperience: i32 = 0x3d; +pub const UpdateHealth: i32 = 0x3e; +pub const ScoreboardObjective: i32 = 0x3f; +pub const SetPassengers: i32 = 0x40; +pub const Teams: i32 = 0x41; +pub const UpdateScore: i32 = 0x42; +pub const SpawnPosition: i32 = 0x43; +pub const TimeUpdate: i32 = 0x44; +pub const Title: i32 = 0x45; +pub const SoundEffect: i32 = 0x46; +pub const PlayerListHeaderFooter: i32 = 0x47; +pub const CollectItem: i32 = 0x48; +pub const EntityTeleport: i32 = 0x49; +pub const EntityProperties: i32 = 0x4a; +pub const EntityEffect: i32 = 0x4b; +pub const LoginStart: i32 = 0x00; +pub const EncryptionResponse: i32 = 0x01; +pub const LoginDisconnect: i32 = 0x00; +pub const EncryptionRequest: i32 = 0x01; +pub const LoginSuccess: i32 = 0x02; +pub const SetInitialCompression: i32 = 0x03; +pub const StatusRequest: i32 = 0x00; +pub const StatusPing: i32 = 0x01; +pub const StatusResponse: i32 = 0x00; +pub const StatusPong: i32 = 0x01; } $( From 400cf2c18b6d6611c7df8e6fa2934f73b67ae063 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sat, 1 Dec 2018 10:10:48 -0800 Subject: [PATCH 103/160] 1.11.2 (316) protocol update (#38) Only a minor update, -1 now indicates no color, so changed u8 to i8: https://wiki.vg/Protocol_History#16w50a https://wiki.vg/index.php?title=Protocol&diff=8543&oldid=8405 https://wiki.vg/index.php?title=Protocol&oldid=8543 and updated version numbers. 1.11.2 uses the same 1.11 assets, which can be found by looking up 1.11.2 in: https://launchermeta.mojang.com/mc/game/version_manifest.json https://launchermeta.mojang.com/v1/packages/6bd228727ed48bd7ac7bdc0088587dad0fb7c02b/1.11.2.json 1.11.2/1.11 is compatible except for the version number, which is now sent matching the server (#20), so no backwards-compatible branch for 1.11 (315) is needed. https://github.com/iceiix/steven/issues/18 Enhance protocol support --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 65add2c..7a61b26 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -36,7 +36,7 @@ use flate2::Compression; use std::time::{Instant, Duration}; use crate::shared::Position; -pub const SUPPORTED_PROTOCOL: i32 = 315; +pub const SUPPORTED_PROTOCOL: i32 = 316; /// Helper macro for defining packets diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index b491cd0..dc6dcca 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -751,7 +751,7 @@ state_packets!( field flags: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), field name_tag_visibility: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), field collision_rule: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - field color: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), + field color: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), field players: Option> = when(|p: &Teams| p.mode == 0 || p.mode == 3 || p.mode == 4), } /// UpdateScore is used to update or remove an item from a scoreboard From 9504be550f92535fc267d761ad853efcf5cc58bc Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 2 Dec 2018 12:29:05 -0800 Subject: [PATCH 104/160] Recognize translate text components, instead of showing "UNHANDLED" For example, if a server connection times out and you are kicked, Steven will now show disconnect.reason timeout, versus a non-descriptive UNHANDLED message. This text is supposed to be looked up in a translation table for localization, not yet supported, but showing the translation identifier is more informative than nothing. --- protocol/src/format.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index 79248d4..29776bd 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -45,6 +45,9 @@ impl Component { }) } else if v.get("text").is_some() { Component::Text(TextComponent::from_value(v, modifier)) + } else if v.get("translate").is_some() { + // TODO: translations + Component::Text(TextComponent::new(v.get("translate").unwrap().as_str().unwrap())) } else { modifier.color = Some(Color::RGB(255, 0, 0)); Component::Text(TextComponent { From b554dbb98b0a3cfc57710005279276b020066e04 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 2 Dec 2018 19:32:52 -0800 Subject: [PATCH 105/160] 1.12.2 protocol support (340) (#40) * Add new 1.12.2 packets and shift IDs CraftRecipeResponse AdvancementTab SelectAdvancementTab Advancements CraftingRecipeRequest UnlockRecipes CraftingBookData * Fix unlock recipes packet, add length-prefixed arrays https://wiki.vg/index.php?title=Protocol&oldid=14204#Unlock_Recipes * Update resources to 1.12.2 * Handle NBTTag metadata (value 13), parsed as nbt::NamedTag https://wiki.vg/index.php?title=Entity_metadata&oldid=14048#Entity_Metadata_Format https://github.com/iceiix/steven/pull/40#issuecomment-443454757 * Fix entity packet IDs, 0x25 now is Entity https://wiki.vg/index.php?title=Protocol&oldid=14204#Entity * Add NBT long array (type 12) support https://wiki.vg/NBT#Specification * Entity metadata type is now a VarInt, not u8: https://wiki.vg/index.php?title=Entity_metadata&oldid=14048#Entity_Metadata_Format * Keep alives changed to longs, no longer VarInts https://wiki.vg/index.php?title=Protocol&oldid=14204#Keep_Alive_.28serverbound.29 * Parse CraftRecipeResponse (0x2b) * Add structs for advancements data * Implement Serializable trait for Advancement and AdvancementDisplay * Implement advancement progress parsing; advancement packet works * Particle packet adds fallingdust (46) with length 1 https://wiki.vg/index.php?title=Protocol&oldid=14204#Particle_2 --- protocol/src/protocol/mod.rs | 115 +++++++++--------- protocol/src/protocol/packet.rs | 204 +++++++++++++++++++++++++++++++- 2 files changed, 262 insertions(+), 57 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 7a61b26..c0f7a6c 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -36,7 +36,7 @@ use flate2::Compression; use std::time::{Instant, Duration}; use crate::shared::Position; -pub const SUPPORTED_PROTOCOL: i32 = 316; +pub const SUPPORTED_PROTOCOL: i32 = 340; /// Helper macro for defining packets @@ -96,24 +96,27 @@ pub const CloseWindow: i32 = 0x08; pub const PluginMessageServerbound: i32 = 0x09; pub const UseEntity: i32 = 0x0a; pub const KeepAliveServerbound: i32 = 0x0b; -pub const PlayerPosition: i32 = 0x0c; -pub const PlayerPositionLook: i32 = 0x0d; -pub const PlayerLook: i32 = 0x0e; -pub const Player: i32 = 0x0f; +pub const Player: i32 = 0x0c; +pub const PlayerPosition: i32 = 0x0d; +pub const PlayerPositionLook: i32 = 0x0e; +pub const PlayerLook: i32 = 0x0f; pub const VehicleMove: i32 = 0x10; pub const SteerBoat: i32 = 0x11; -pub const ClientAbilities: i32 = 0x12; -pub const PlayerDigging: i32 = 0x13; -pub const PlayerAction: i32 = 0x14; -pub const SteerVehicle: i32 = 0x15; -pub const ResourcePackStatus: i32 = 0x16; -pub const HeldItemChange: i32 = 0x17; -pub const CreativeInventoryAction: i32 = 0x18; -pub const SetSign: i32 = 0x19; -pub const ArmSwing: i32 = 0x1a; -pub const SpectateTeleport: i32 = 0x1b; -pub const PlayerBlockPlacement: i32 = 0x1c; -pub const UseItem: i32 = 0x1d; +pub const CraftRecipeRequest: i32 = 0x12; +pub const ClientAbilities: i32 = 0x13; +pub const PlayerDigging: i32 = 0x14; +pub const PlayerAction: i32 = 0x15; +pub const SteerVehicle: i32 = 0x16; +pub const CraftingBookData: i32 = 0x17; +pub const ResourcePackStatus: i32 = 0x18; +pub const AdvancementTab: i32 = 0x19; +pub const HeldItemChange: i32 = 0x1a; +pub const CreativeInventoryAction: i32 = 0x1b; +pub const SetSign: i32 = 0x1c; +pub const ArmSwing: i32 = 0x1d; +pub const SpectateTeleport: i32 = 0x1e; +pub const PlayerBlockPlacement: i32 = 0x1f; +pub const UseItem: i32 = 0x20; pub const SpawnObject: i32 = 0x00; pub const SpawnExperienceOrb: i32 = 0x01; pub const SpawnGlobalEntity: i32 = 0x02; @@ -151,45 +154,49 @@ pub const Effect: i32 = 0x21; pub const Particle: i32 = 0x22; pub const JoinGame: i32 = 0x23; pub const Maps: i32 = 0x24; -pub const EntityMove: i32 = 0x25; -pub const EntityLookAndMove: i32 = 0x26; -pub const EntityLook: i32 = 0x27; -pub const Entity: i32 = 0x28; +pub const Entity: i32 = 0x25; +pub const EntityMove: i32 = 0x26; +pub const EntityLookAndMove: i32 = 0x27; +pub const EntityLook: i32 = 0x28; pub const VehicleTeleport: i32 = 0x29; pub const SignEditorOpen: i32 = 0x2a; -pub const PlayerAbilities: i32 = 0x2b; -pub const CombatEvent: i32 = 0x2c; -pub const PlayerInfo: i32 = 0x2d; -pub const TeleportPlayer: i32 = 0x2e; -pub const EntityUsedBed: i32 = 0x2f; -pub const EntityDestroy: i32 = 0x30; -pub const EntityRemoveEffect: i32 = 0x31; -pub const ResourcePackSend: i32 = 0x32; -pub const Respawn: i32 = 0x33; -pub const EntityHeadLook: i32 = 0x34; -pub const WorldBorder: i32 = 0x35; -pub const Camera: i32 = 0x36; -pub const SetCurrentHotbarSlot: i32 = 0x37; -pub const ScoreboardDisplay: i32 = 0x38; -pub const EntityMetadata: i32 = 0x39; -pub const EntityAttach: i32 = 0x3a; -pub const EntityVelocity: i32 = 0x3b; -pub const EntityEquipment: i32 = 0x3c; -pub const SetExperience: i32 = 0x3d; -pub const UpdateHealth: i32 = 0x3e; -pub const ScoreboardObjective: i32 = 0x3f; -pub const SetPassengers: i32 = 0x40; -pub const Teams: i32 = 0x41; -pub const UpdateScore: i32 = 0x42; -pub const SpawnPosition: i32 = 0x43; -pub const TimeUpdate: i32 = 0x44; -pub const Title: i32 = 0x45; -pub const SoundEffect: i32 = 0x46; -pub const PlayerListHeaderFooter: i32 = 0x47; -pub const CollectItem: i32 = 0x48; -pub const EntityTeleport: i32 = 0x49; -pub const EntityProperties: i32 = 0x4a; -pub const EntityEffect: i32 = 0x4b; +pub const CraftRecipeResponse: i32 = 0x2b; +pub const PlayerAbilities: i32 = 0x2c; +pub const CombatEvent: i32 = 0x2d; +pub const PlayerInfo: i32 = 0x2e; +pub const TeleportPlayer: i32 = 0x2f; +pub const EntityUsedBed: i32 = 0x30; +pub const UnlockRecipes: i32 = 0x31; +pub const EntityDestroy: i32 = 0x32; +pub const EntityRemoveEffect: i32 = 0x33; +pub const ResourcePackSend: i32 = 0x34; +pub const Respawn: i32 = 0x35; +pub const EntityHeadLook: i32 = 0x36; +pub const SelectAdvancementTab: i32 = 0x37; +pub const WorldBorder: i32 = 0x38; +pub const Camera: i32 = 0x39; +pub const SetCurrentHotbarSlot: i32 = 0x3a; +pub const ScoreboardDisplay: i32 = 0x3b; +pub const EntityMetadata: i32 = 0x3c; +pub const EntityAttach: i32 = 0x3d; +pub const EntityVelocity: i32 = 0x3e; +pub const EntityEquipment: i32 = 0x3f; +pub const SetExperience: i32 = 0x40; +pub const UpdateHealth: i32 = 0x41; +pub const ScoreboardObjective: i32 = 0x42; +pub const SetPassengers: i32 = 0x43; +pub const Teams: i32 = 0x44; +pub const UpdateScore: i32 = 0x45; +pub const SpawnPosition: i32 = 0x46; +pub const TimeUpdate: i32 = 0x47; +pub const Title: i32 = 0x48; +pub const SoundEffect: i32 = 0x49; +pub const PlayerListHeaderFooter: i32 = 0x4a; +pub const CollectItem: i32 = 0x4b; +pub const EntityTeleport: i32 = 0x4c; +pub const Advancements: i32 = 0x4d; +pub const EntityProperties: i32 = 0x4e; +pub const EntityEffect: i32 = 0x4f; pub const LoginStart: i32 = 0x00; pub const EncryptionResponse: i32 = 0x01; pub const LoginDisconnect: i32 = 0x00; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index dc6dcca..f23e353 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -124,7 +124,7 @@ state_packets!( /// KeepAliveClientbound. If the client doesn't reply the server /// may disconnect the client. packet KeepAliveServerbound { - field id: VarInt =, + field id: i64 =, } /// PlayerPosition is used to update the player's position. packet PlayerPosition { @@ -166,6 +166,12 @@ state_packets!( field unknown: bool =, field unknown2: bool =, } + /// CraftRecipeRequest is sent when player clicks a recipe in the crafting book. + packet CraftRecipeRequest { + field window_id: u8 =, + field recipe: VarInt =, + field make_all: bool =, + } /// ClientAbilities is used to modify the players current abilities. /// Currently flying is the only one packet ClientAbilities { @@ -193,11 +199,23 @@ state_packets!( field forward: f32 =, field flags: u8 =, } + /// CraftingBookData is sent when the player interacts with the crafting book. + packet CraftingBookData { + field action: VarInt =, + field recipe_id: i32 = when(|p: &CraftingBookData| p.action.0 == 0), + field crafting_book_open: bool = when(|p: &CraftingBookData| p.action.0 == 1), + field crafting_filter: bool = when(|p: &CraftingBookData| p.action.0 == 1), + } /// ResourcePackStatus informs the server of the client's current progress /// in activating the requested resource pack packet ResourcePackStatus { field result: VarInt =, } + // TODO: Document + packet AdvancementTab { + field action: VarInt =, + field tab_id: String = when(|p: &AdvancementTab| p.action.0 == 0), + } /// HeldItemChange is sent when the player changes the currently active /// hotbar slot. packet HeldItemChange { @@ -488,7 +506,7 @@ state_packets!( /// The client should reply with the KeepAliveServerbound /// packet setting ID to the same as this one. packet KeepAliveClientbound { - field id: VarInt =, + field id: i64 =, } /// ChunkData sends or updates a single chunk on the client. If New is set /// then biome data should be sent too. @@ -522,7 +540,7 @@ state_packets!( field offset_z: f32 =, field speed: f32 =, field count: i32 =, - field data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38), + field data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38 || p.particle_id == 46), field data2: VarInt = when(|p: &Particle| p.particle_id == 36), } /// JoinGame is sent after completing the login process. This @@ -598,6 +616,11 @@ state_packets!( packet SignEditorOpen { field location: Position =, } + /// CraftRecipeResponse is a response to CraftRecipeRequest, notifies the UI. + packet CraftRecipeResponse { + field window_id: u8 =, + field recipe: VarInt =, + } /// PlayerAbilities is used to modify the players current abilities. Flying, /// creative, god mode etc. packet PlayerAbilities { @@ -636,6 +659,13 @@ state_packets!( field entity_id: VarInt =, field location: Position =, } + packet UnlockRecipes { + 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), + } /// EntityDestroy destroys the entities with the ids in the provided slice. packet EntityDestroy { field entity_ids: LenPrefixed =, @@ -664,6 +694,11 @@ state_packets!( field entity_id: VarInt =, field head_yaw: i8 =, } + /// SelectAdvancementTab indicates the client should switch the advancement tab. + packet SelectAdvancementTab { + field has_id: bool =, + field tab_id: String = when(|p: &SelectAdvancementTab| p.has_id), + } /// WorldBorder configures the world's border. packet WorldBorder { field action: VarInt =, @@ -818,6 +853,12 @@ state_packets!( field pitch: i8 =, field on_ground: bool =, } + packet Advancements { + field reset_clear: bool =, + field mapping: LenPrefixed =, + field identifiers: LenPrefixed =, + field progress: LenPrefixed =, + } /// EntityProperties updates the properties for an entity. packet EntityProperties { field entity_id: VarInt =, @@ -1043,6 +1084,163 @@ impl Default for MapIcon { } } +#[derive(Debug, Default)] +pub struct Advancement { + pub id: String, + pub parent_id: Option, + pub display_data: Option, + pub criteria: LenPrefixed, + pub requirements: LenPrefixed>, +} + +impl Serializable for Advancement { + fn read_from(buf: &mut R) -> Result { + let id: String = Serializable::read_from(buf)?; + let parent_id = { + let has_parent: u8 = Serializable::read_from(buf)?; + if has_parent != 0 { + let parent_id: String = Serializable::read_from(buf)?; + Some(parent_id) + } else { + None + } + }; + + let has_display: u8 = Serializable::read_from(buf)?; + let display_data = { + if has_display != 0 { + let display_data: AdvancementDisplay = Serializable::read_from(buf)?; + Some(display_data) + } else { + None + } + }; + + let criteria: LenPrefixed = Serializable::read_from(buf)?; + let requirements: LenPrefixed> = Serializable::read_from(buf)?; + Ok(Advancement { + id, + parent_id, + display_data, + criteria, + requirements, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.id.write_to(buf)?; + self.parent_id.write_to(buf)?; + self.display_data.write_to(buf)?; + self.criteria.write_to(buf)?; + self.requirements.write_to(buf) + } + +} + +#[derive(Debug, Default)] +pub struct AdvancementDisplay { + pub title: String, + pub description: String, + pub icon: Option, + pub frame_type: VarInt, + pub flags: i32, + pub background_texture: Option, + pub x_coord: f32, + pub y_coord: f32, +} + +impl Serializable for AdvancementDisplay { + fn read_from(buf: &mut R) -> Result { + let title: String = Serializable::read_from(buf)?; + let description: String = Serializable::read_from(buf)?; + let icon: Option = Serializable::read_from(buf)?; + let frame_type: VarInt = Serializable::read_from(buf)?; + let flags: i32 = Serializable::read_from(buf)?; + let background_texture: Option = if flags & 1 != 0 { + Serializable::read_from(buf)? + } else { + None + }; + let x_coord: f32 = Serializable::read_from(buf)?; + let y_coord: f32 = Serializable::read_from(buf)?; + + Ok(AdvancementDisplay { + title, + description, + icon, + frame_type, + flags, + background_texture, + x_coord, + y_coord, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.title.write_to(buf)?; + self.description.write_to(buf)?; + self.icon.write_to(buf)?; + self.frame_type.write_to(buf)?; + self.flags.write_to(buf)?; + if self.flags & 1 != 0 { + self.background_texture.write_to(buf)?; + } + self.x_coord.write_to(buf)?; + self.y_coord.write_to(buf) + } + +} + +#[derive(Debug, Default)] +pub struct AdvancementProgress { + pub id: String, + pub criteria: LenPrefixed, +} + +impl Serializable for AdvancementProgress { + fn read_from(buf: &mut R) -> Result { + Ok(AdvancementProgress { + id: Serializable::read_from(buf)?, + criteria: Serializable::read_from(buf)?, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.id.write_to(buf)?; + self.criteria.write_to(buf) + } +} + +#[derive(Debug, Default)] +pub struct CriterionProgress { + pub id: String, + pub date_of_achieving: Option, +} + +impl Serializable for CriterionProgress { + fn read_from(buf: &mut R) -> Result { + let id = Serializable::read_from(buf)?; + let achieved: u8 = Serializable::read_from(buf)?; + let date_of_achieving: Option = if achieved != 0 { + Serializable::read_from(buf)? + } else { + None + }; + + Ok(CriterionProgress { + id, + date_of_achieving, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.id.write_to(buf)?; + self.date_of_achieving.write_to(buf) + } +} + + + #[derive(Debug, Default)] pub struct EntityProperty { pub key: String, From ad8bcf6abafcb8e237ab2bc34447923b3d24acc8 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 2 Dec 2018 19:32:52 -0800 Subject: [PATCH 106/160] 1.12.2 protocol support (340) (#40) * Add new 1.12.2 packets and shift IDs CraftRecipeResponse AdvancementTab SelectAdvancementTab Advancements CraftingRecipeRequest UnlockRecipes CraftingBookData * Fix unlock recipes packet, add length-prefixed arrays https://wiki.vg/index.php?title=Protocol&oldid=14204#Unlock_Recipes * Update resources to 1.12.2 * Handle NBTTag metadata (value 13), parsed as nbt::NamedTag https://wiki.vg/index.php?title=Entity_metadata&oldid=14048#Entity_Metadata_Format https://github.com/iceiix/steven/pull/40#issuecomment-443454757 * Fix entity packet IDs, 0x25 now is Entity https://wiki.vg/index.php?title=Protocol&oldid=14204#Entity * Add NBT long array (type 12) support https://wiki.vg/NBT#Specification * Entity metadata type is now a VarInt, not u8: https://wiki.vg/index.php?title=Entity_metadata&oldid=14048#Entity_Metadata_Format * Keep alives changed to longs, no longer VarInts https://wiki.vg/index.php?title=Protocol&oldid=14204#Keep_Alive_.28serverbound.29 * Parse CraftRecipeResponse (0x2b) * Add structs for advancements data * Implement Serializable trait for Advancement and AdvancementDisplay * Implement advancement progress parsing; advancement packet works * Particle packet adds fallingdust (46) with length 1 https://wiki.vg/index.php?title=Protocol&oldid=14204#Particle_2 --- protocol/src/nbt/mod.rs | 24 ++++++++++++++++++++++++ protocol/src/types/metadata.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index 3e79578..e72fad6 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -34,6 +34,7 @@ pub enum Tag { List(Vec), Compound(HashMap), IntArray(Vec), + LongArray(Vec), } #[derive(Debug, Clone)] @@ -155,6 +156,14 @@ impl Tag { } } + pub fn as_long_array(&self) -> Option<&[i64]> { + match *self { + Tag::LongArray(ref val) => Some(&val[..]), + _ => None, + } + } + + fn internal_id(&self) -> u8 { match *self { Tag::End => 0, @@ -169,6 +178,7 @@ impl Tag { Tag::List(_) => 9, Tag::Compound(_) => 10, Tag::IntArray(_) => 11, + Tag::LongArray(_) => 12, } } @@ -217,6 +227,14 @@ impl Tag { } data })), + 12 => Ok(Tag::LongArray({ + let len: i32 = Serializable::read_from(buf)?; + let mut data = Vec::with_capacity(len as usize); + for _ in 0..len { + data.push(buf.read_i64::()?); + } + data + })), _ => Err(protocol::Error::Err("invalid tag".to_owned())), } } @@ -267,6 +285,12 @@ impl Serializable for Tag { v.write_to(buf)?; } } + Tag::LongArray(ref val) => { + (val.len() as i32).write_to(buf)?; + for v in val { + v.write_to(buf)?; + } + } } Result::Ok(()) } diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 228a034..396b983 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -21,6 +21,7 @@ use crate::protocol::Serializable; use crate::format; use crate::item; use crate::shared::Position; +use crate::nbt; pub struct MetadataKey { index: i32, @@ -68,7 +69,7 @@ impl Serializable for Metadata { if index == 0xFF { break; } - let ty = u8::read_from(buf)?; + let ty = protocol::VarInt::read_from(buf)?.0; match ty { 0 => m.put_raw(index, i8::read_from(buf)?), 1 => m.put_raw(index, protocol::VarInt::read_from(buf)?.0), @@ -98,6 +99,15 @@ impl Serializable for Metadata { } } 12 => m.put_raw(index, protocol::VarInt::read_from(buf)?.0 as u16), + 13 => { + let ty = u8::read_from(buf)?; + if ty != 0 { + let name = nbt::read_string(buf)?; + let tag = nbt::Tag::read_from(buf)?; + + m.put_raw(index, nbt::NamedTag(name, tag)); + } + } _ => return Err(protocol::Error::Err("unknown metadata type".to_owned())), } } @@ -164,6 +174,11 @@ impl Serializable for Metadata { u8::write_to(&11, buf)?; protocol::VarInt(*val as i32).write_to(buf)?; } + Value::NBTTag(ref _val) => { + u8::write_to(&13, buf)?; + // TODO: write NBT tags metadata + //nbt::Tag(*val).write_to(buf)?; + } } } u8::write_to(&0xFF, buf)?; @@ -202,6 +217,7 @@ pub enum Value { Direction(protocol::VarInt), // TODO: Proper type OptionalUUID(Option), Block(u16), // TODO: Proper type + NBTTag(nbt::NamedTag), } pub trait MetaValue { @@ -365,6 +381,18 @@ impl MetaValue for u16 { } } +impl MetaValue for nbt::NamedTag { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::NBTTag(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::NBTTag(self) + } +} + #[cfg(test)] mod test { use super::*; From c64304b98f7b31d840126204a23af8e3ab0603a4 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Mon, 3 Dec 2018 14:22:47 -0800 Subject: [PATCH 107/160] Multiprotocol support: 1.12.2 and 1.11.2 (#54) Adds support for connecting to both 1.12.2 and 1.11.2 (protocols 340 and 316) servers https://github.com/iceiix/steven/issues/18 Enhance protocol support Closes https://github.com/iceiix/steven/pull/48 1.11.2 protocol support (316) * Restore create_ids!() macro in packet identifiers * Add translate_packet_id() function to map external 1.12.2 packet ids to internal sequential ids * Implement translate_internal_packet_id() from a new protocol_packet_ids! macro * Move packet IDs to separate file, v1_12_2.rs * Change supported protocols constant to an array * Add v1_11_2 protocol packet IDs (from https://github.com/iceiix/steven/pull/48) * Add keep alive packet variants: _i64 (>=1.12.2) and _VarInt (<=1.11.2) * Abstract protocol versions, can now connect to both 1.12.2 and 1.11.2 * Send protocol version in handshake packet * Restore 1.11 (315) protocol support as in original (https://github.com/thinkofname/steven) Steven --- protocol/src/protocol/mod.rs | 196 +++++++--------------- protocol/src/protocol/packet.rs | 10 +- protocol/src/protocol/versions.rs | 21 +++ protocol/src/protocol/versions/v1_11_2.rs | 145 ++++++++++++++++ protocol/src/protocol/versions/v1_12_2.rs | 152 +++++++++++++++++ 5 files changed, 388 insertions(+), 136 deletions(-) create mode 100644 protocol/src/protocol/versions.rs create mode 100644 protocol/src/protocol/versions/v1_11_2.rs create mode 100644 protocol/src/protocol/versions/v1_12_2.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index c0f7a6c..4a31c74 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -13,6 +13,7 @@ // limitations under the License. #![allow(dead_code)] +#![allow(non_camel_case_types)] use aes::Aes128; use cfb8::Cfb8; @@ -36,7 +37,7 @@ use flate2::Compression; use std::time::{Instant, Duration}; use crate::shared::Position; -pub const SUPPORTED_PROTOCOL: i32 = 340; +pub const SUPPORTED_PROTOCOLS: [i32; 3] = [340, 316, 315]; /// Helper macro for defining packets @@ -83,130 +84,7 @@ macro_rules! state_packets { #[allow(non_upper_case_globals)] pub mod internal_ids { -pub const Handshake: i32 = 0x00; -pub const TeleportConfirm: i32 = 0x00; -pub const TabComplete: i32 = 0x01; -pub const ChatMessage: i32 = 0x02; -pub const ClientStatus: i32 = 0x03; -pub const ClientSettings: i32 = 0x04; -pub const ConfirmTransactionServerbound: i32 = 0x05; -pub const EnchantItem: i32 = 0x06; -pub const ClickWindow: i32 = 0x07; -pub const CloseWindow: i32 = 0x08; -pub const PluginMessageServerbound: i32 = 0x09; -pub const UseEntity: i32 = 0x0a; -pub const KeepAliveServerbound: i32 = 0x0b; -pub const Player: i32 = 0x0c; -pub const PlayerPosition: i32 = 0x0d; -pub const PlayerPositionLook: i32 = 0x0e; -pub const PlayerLook: i32 = 0x0f; -pub const VehicleMove: i32 = 0x10; -pub const SteerBoat: i32 = 0x11; -pub const CraftRecipeRequest: i32 = 0x12; -pub const ClientAbilities: i32 = 0x13; -pub const PlayerDigging: i32 = 0x14; -pub const PlayerAction: i32 = 0x15; -pub const SteerVehicle: i32 = 0x16; -pub const CraftingBookData: i32 = 0x17; -pub const ResourcePackStatus: i32 = 0x18; -pub const AdvancementTab: i32 = 0x19; -pub const HeldItemChange: i32 = 0x1a; -pub const CreativeInventoryAction: i32 = 0x1b; -pub const SetSign: i32 = 0x1c; -pub const ArmSwing: i32 = 0x1d; -pub const SpectateTeleport: i32 = 0x1e; -pub const PlayerBlockPlacement: i32 = 0x1f; -pub const UseItem: i32 = 0x20; -pub const SpawnObject: i32 = 0x00; -pub const SpawnExperienceOrb: i32 = 0x01; -pub const SpawnGlobalEntity: i32 = 0x02; -pub const SpawnMob: i32 = 0x03; -pub const SpawnPainting: i32 = 0x04; -pub const SpawnPlayer: i32 = 0x05; -pub const Animation: i32 = 0x06; -pub const Statistics: i32 = 0x07; -pub const BlockBreakAnimation: i32 = 0x08; -pub const UpdateBlockEntity: i32 = 0x09; -pub const BlockAction: i32 = 0x0a; -pub const BlockChange: i32 = 0x0b; -pub const BossBar: i32 = 0x0c; -pub const ServerDifficulty: i32 = 0x0d; -pub const TabCompleteReply: i32 = 0x0e; -pub const ServerMessage: i32 = 0x0f; -pub const MultiBlockChange: i32 = 0x10; -pub const ConfirmTransaction: i32 = 0x11; -pub const WindowClose: i32 = 0x12; -pub const WindowOpen: i32 = 0x13; -pub const WindowItems: i32 = 0x14; -pub const WindowProperty: i32 = 0x15; -pub const WindowSetSlot: i32 = 0x16; -pub const SetCooldown: i32 = 0x17; -pub const PluginMessageClientbound: i32 = 0x18; -pub const NamedSoundEffect: i32 = 0x19; -pub const Disconnect: i32 = 0x1a; -pub const EntityAction: i32 = 0x1b; -pub const Explosion: i32 = 0x1c; -pub const ChunkUnload: i32 = 0x1d; -pub const ChangeGameState: i32 = 0x1e; -pub const KeepAliveClientbound: i32 = 0x1f; -pub const ChunkData: i32 = 0x20; -pub const Effect: i32 = 0x21; -pub const Particle: i32 = 0x22; -pub const JoinGame: i32 = 0x23; -pub const Maps: i32 = 0x24; -pub const Entity: i32 = 0x25; -pub const EntityMove: i32 = 0x26; -pub const EntityLookAndMove: i32 = 0x27; -pub const EntityLook: i32 = 0x28; -pub const VehicleTeleport: i32 = 0x29; -pub const SignEditorOpen: i32 = 0x2a; -pub const CraftRecipeResponse: i32 = 0x2b; -pub const PlayerAbilities: i32 = 0x2c; -pub const CombatEvent: i32 = 0x2d; -pub const PlayerInfo: i32 = 0x2e; -pub const TeleportPlayer: i32 = 0x2f; -pub const EntityUsedBed: i32 = 0x30; -pub const UnlockRecipes: i32 = 0x31; -pub const EntityDestroy: i32 = 0x32; -pub const EntityRemoveEffect: i32 = 0x33; -pub const ResourcePackSend: i32 = 0x34; -pub const Respawn: i32 = 0x35; -pub const EntityHeadLook: i32 = 0x36; -pub const SelectAdvancementTab: i32 = 0x37; -pub const WorldBorder: i32 = 0x38; -pub const Camera: i32 = 0x39; -pub const SetCurrentHotbarSlot: i32 = 0x3a; -pub const ScoreboardDisplay: i32 = 0x3b; -pub const EntityMetadata: i32 = 0x3c; -pub const EntityAttach: i32 = 0x3d; -pub const EntityVelocity: i32 = 0x3e; -pub const EntityEquipment: i32 = 0x3f; -pub const SetExperience: i32 = 0x40; -pub const UpdateHealth: i32 = 0x41; -pub const ScoreboardObjective: i32 = 0x42; -pub const SetPassengers: i32 = 0x43; -pub const Teams: i32 = 0x44; -pub const UpdateScore: i32 = 0x45; -pub const SpawnPosition: i32 = 0x46; -pub const TimeUpdate: i32 = 0x47; -pub const Title: i32 = 0x48; -pub const SoundEffect: i32 = 0x49; -pub const PlayerListHeaderFooter: i32 = 0x4a; -pub const CollectItem: i32 = 0x4b; -pub const EntityTeleport: i32 = 0x4c; -pub const Advancements: i32 = 0x4d; -pub const EntityProperties: i32 = 0x4e; -pub const EntityEffect: i32 = 0x4f; -pub const LoginStart: i32 = 0x00; -pub const EncryptionResponse: i32 = 0x01; -pub const LoginDisconnect: i32 = 0x00; -pub const EncryptionRequest: i32 = 0x01; -pub const LoginSuccess: i32 = 0x02; -pub const SetInitialCompression: i32 = 0x03; -pub const StatusRequest: i32 = 0x00; -pub const StatusPing: i32 = 0x01; -pub const StatusResponse: i32 = 0x00; -pub const StatusPong: i32 = 0x01; + create_ids!(i32, $($name),*); } $( @@ -217,7 +95,9 @@ pub const StatusPong: i32 = 0x01; impl PacketType for $name { - fn packet_id(&self) -> i32 { internal_ids::$name } + fn packet_id(&self, version: i32) -> i32 { + packet::versions::translate_internal_packet_id_for_version(version, State::$stateName, Direction::$dirName, internal_ids::$name, false) + } fn write(self, buf: &mut W) -> Result<(), Error> { $( @@ -237,14 +117,15 @@ pub const StatusPong: i32 = 0x01; /// Returns the packet for the given state, direction and id after parsing the fields /// from the buffer. - pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut R) -> Result, Error> { + pub fn packet_by_id(version: i32, state: State, dir: Direction, id: i32, mut buf: &mut R) -> Result, Error> { match state { $( State::$stateName => { match dir { $( Direction::$dirName => { - match id { + let internal_id = packet::versions::translate_internal_packet_id_for_version(version, state, dir, id, true); + match internal_id { $( self::$state::$dir::internal_ids::$name => { use self::$state::$dir::$name; @@ -269,8 +150,52 @@ pub const StatusPong: i32 = 0x01; } } -pub mod packet; +#[macro_export] +macro_rules! protocol_packet_ids { + ($($state:ident $stateName:ident { + $($dir:ident $dirName:ident { + $( + $(#[$attr:meta])* + $id:expr => $name:ident + )* + })+ + })+) => { + use crate::protocol::*; + pub fn translate_internal_packet_id(state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { + match state { + $( + State::$stateName => { + match dir { + $( + Direction::$dirName => { + if to_internal { + match id { + $( + $id => crate::protocol::packet::$state::$dir::internal_ids::$name, + )* + _ => panic!("bad packet id $id in $dir $state"), + } + } else { + match id { + $( + crate::protocol::packet::$state::$dir::internal_ids::$name => $id, + )* + _ => panic!("bad packet internal id $id in $dir $state"), + } + } + } + )* + } + } + )* + } + } + } +} + +pub mod packet; +pub mod versions; pub trait Serializable: Sized { fn read_from(buf: &mut R) -> Result; fn write_to(&self, buf: &mut W) -> Result<(), Error>; @@ -868,6 +793,7 @@ pub struct Conn { pub host: String, pub port: u16, direction: Direction, + pub protocol_version: i32, pub state: State, cipher: Option, @@ -878,7 +804,7 @@ pub struct Conn { } impl Conn { - pub fn new(target: &str) -> Result { + pub fn new(target: &str, protocol_version: i32) -> Result { // TODO SRV record support let mut parts = target.split(':').collect::>(); let address = if parts.len() == 1 { @@ -894,6 +820,7 @@ impl Conn { port: parts[1].parse().unwrap(), direction: Direction::Serverbound, state: State::Handshaking, + protocol_version, cipher: Option::None, compression_threshold: -1, compression_read: Option::None, @@ -903,7 +830,7 @@ impl Conn { pub fn write_packet(&mut self, packet: T) -> Result<(), Error> { let mut buf = Vec::new(); - VarInt(packet.packet_id()).write_to(&mut buf)?; + VarInt(packet.packet_id(self.protocol_version)).write_to(&mut buf)?; packet.write(&mut buf)?; let mut extra = if self.compression_threshold >= 0 { @@ -963,7 +890,7 @@ impl Conn { Direction::Serverbound => Direction::Clientbound, }; - let packet = packet::packet_by_id(self.state, dir, id, &mut buf)?; + let packet = packet::packet_by_id(self.protocol_version, self.state, dir, id, &mut buf)?; match packet { Some(val) => { @@ -998,7 +925,7 @@ impl Conn { let host = self.host.clone(); let port = self.port; self.write_packet(Handshake { - protocol_version: VarInt(SUPPORTED_PROTOCOL), + protocol_version: VarInt(self.protocol_version), host, port, next: VarInt(1), @@ -1131,6 +1058,7 @@ impl Clone for Conn { port: self.port, direction: self.direction, state: self.state, + protocol_version: self.protocol_version, cipher: Option::None, compression_threshold: self.compression_threshold, compression_read: Option::None, @@ -1140,7 +1068,7 @@ impl Clone for Conn { } pub trait PacketType { - fn packet_id(&self) -> i32; + fn packet_id(&self, protocol_version: i32) -> i32; fn write(self, buf: &mut W) -> Result<(), Error>; } diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index f23e353..94a60e2 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -123,9 +123,12 @@ state_packets!( /// KeepAliveServerbound is sent by a client as a response to a /// KeepAliveClientbound. If the client doesn't reply the server /// may disconnect the client. - packet KeepAliveServerbound { + packet KeepAliveServerbound_i64 { field id: i64 =, } + packet KeepAliveServerbound_VarInt { + field id: VarInt =, + } /// PlayerPosition is used to update the player's position. packet PlayerPosition { field x: f64 =, @@ -505,9 +508,12 @@ state_packets!( /// client is still responding and keep the connection open. /// The client should reply with the KeepAliveServerbound /// packet setting ID to the same as this one. - packet KeepAliveClientbound { + packet KeepAliveClientbound_i64 { field id: i64 =, } + packet KeepAliveClientbound_VarInt { + field id: VarInt =, + } /// ChunkData sends or updates a single chunk on the client. If New is set /// then biome data should be sent too. packet ChunkData { diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs new file mode 100644 index 0000000..ce355d6 --- /dev/null +++ b/protocol/src/protocol/versions.rs @@ -0,0 +1,21 @@ +use crate::protocol::*; + +mod v1_12_2; +mod v1_11_2; + +pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { + match version { + // https://wiki.vg/Protocol_History + // https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite + // 1.12.2 + 340 => v1_12_2::translate_internal_packet_id(state, dir, id, to_internal), + + // 1.11.2 + 316 => v1_11_2::translate_internal_packet_id(state, dir, id, to_internal), + + // 1.11 + 315 => v1_11_2::translate_internal_packet_id(state, dir, id, to_internal), + + _ => panic!("unsupported protocol version"), + } +} diff --git a/protocol/src/protocol/versions/v1_11_2.rs b/protocol/src/protocol/versions/v1_11_2.rs new file mode 100644 index 0000000..02dc201 --- /dev/null +++ b/protocol/src/protocol/versions/v1_11_2.rs @@ -0,0 +1,145 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => TabComplete + 0x02 => ChatMessage + 0x03 => ClientStatus + 0x04 => ClientSettings + 0x05 => ConfirmTransactionServerbound + 0x06 => EnchantItem + 0x07 => ClickWindow + 0x08 => CloseWindow + 0x09 => PluginMessageServerbound + 0x0a => UseEntity + 0x0b => KeepAliveServerbound_VarInt + 0x0c => PlayerPosition + 0x0d => PlayerPositionLook + 0x0e => PlayerLook + 0x0f => Player + 0x10 => VehicleMove + 0x11 => SteerBoat + 0x12 => ClientAbilities + 0x13 => PlayerDigging + 0x14 => PlayerAction + 0x15 => SteerVehicle + 0x16 => ResourcePackStatus + 0x17 => HeldItemChange + 0x18 => CreativeInventoryAction + 0x19 => SetSign + 0x1a => ArmSwing + 0x1b => SpectateTeleport + 0x1c => PlayerBlockPlacement + 0x1d => UseItem + } + clientbound Clientbound { + 0x00 => SpawnObject + 0x01 => SpawnExperienceOrb + 0x02 => SpawnGlobalEntity + 0x03 => SpawnMob + 0x04 => SpawnPainting + 0x05 => SpawnPlayer + 0x06 => Animation + 0x07 => Statistics + 0x08 => BlockBreakAnimation + 0x09 => UpdateBlockEntity + 0x0a => BlockAction + 0x0b => BlockChange + 0x0c => BossBar + 0x0d => ServerDifficulty + 0x0e => TabCompleteReply + 0x0f => ServerMessage + 0x10 => MultiBlockChange + 0x11 => ConfirmTransaction + 0x12 => WindowClose + 0x13 => WindowOpen + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => NamedSoundEffect + 0x1a => Disconnect + 0x1b => EntityAction + 0x1c => Explosion + 0x1d => ChunkUnload + 0x1e => ChangeGameState + 0x1f => KeepAliveClientbound_VarInt + 0x20 => ChunkData + 0x21 => Effect + 0x22 => Particle + 0x23 => JoinGame + 0x24 => Maps + 0x25 => EntityMove + 0x26 => EntityLookAndMove + 0x27 => EntityLook + 0x28 => Entity + 0x29 => VehicleTeleport + 0x2a => SignEditorOpen + 0x2b => PlayerAbilities + 0x2c => CombatEvent + 0x2d => PlayerInfo + 0x2e => TeleportPlayer + 0x2f => EntityUsedBed + 0x30 => EntityDestroy + 0x31 => EntityRemoveEffect + 0x32 => ResourcePackSend + 0x33 => Respawn + 0x34 => EntityHeadLook + 0x35 => WorldBorder + 0x36 => Camera + 0x37 => SetCurrentHotbarSlot + 0x38 => ScoreboardDisplay + 0x39 => EntityMetadata + 0x3a => EntityAttach + 0x3b => EntityVelocity + 0x3c => EntityEquipment + 0x3d => SetExperience + 0x3e => UpdateHealth + 0x3f => ScoreboardObjective + 0x40 => SetPassengers + 0x41 => Teams + 0x42 => UpdateScore + 0x43 => SpawnPosition + 0x44 => TimeUpdate + 0x45 => Title + 0x46 => SoundEffect + 0x47 => PlayerListHeaderFooter + 0x48 => CollectItem + 0x49 => EntityTeleport + 0x4a => EntityProperties + 0x4b => EntityEffect + } + } + 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 + } + } +); + + diff --git a/protocol/src/protocol/versions/v1_12_2.rs b/protocol/src/protocol/versions/v1_12_2.rs new file mode 100644 index 0000000..d4ceb8d --- /dev/null +++ b/protocol/src/protocol/versions/v1_12_2.rs @@ -0,0 +1,152 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => TabComplete + 0x02 => ChatMessage + 0x03 => ClientStatus + 0x04 => ClientSettings + 0x05 => ConfirmTransactionServerbound + 0x06 => EnchantItem + 0x07 => ClickWindow + 0x08 => CloseWindow + 0x09 => PluginMessageServerbound + 0x0a => UseEntity + 0x0b => KeepAliveServerbound_i64 + 0x0c => Player + 0x0d => PlayerPosition + 0x0e => PlayerPositionLook + 0x0f => PlayerLook + 0x10 => VehicleMove + 0x11 => SteerBoat + 0x12 => CraftRecipeRequest + 0x13 => ClientAbilities + 0x14 => PlayerDigging + 0x15 => PlayerAction + 0x16 => SteerVehicle + 0x17 => CraftingBookData + 0x18 => ResourcePackStatus + 0x19 => AdvancementTab + 0x1a => HeldItemChange + 0x1b => CreativeInventoryAction + 0x1c => SetSign + 0x1d => ArmSwing + 0x1e => SpectateTeleport + 0x1f => PlayerBlockPlacement + 0x20 => UseItem + } + clientbound Clientbound { + 0x00 => SpawnObject + 0x01 => SpawnExperienceOrb + 0x02 => SpawnGlobalEntity + 0x03 => SpawnMob + 0x04 => SpawnPainting + 0x05 => SpawnPlayer + 0x06 => Animation + 0x07 => Statistics + 0x08 => BlockBreakAnimation + 0x09 => UpdateBlockEntity + 0x0a => BlockAction + 0x0b => BlockChange + 0x0c => BossBar + 0x0d => ServerDifficulty + 0x0e => TabCompleteReply + 0x0f => ServerMessage + 0x10 => MultiBlockChange + 0x11 => ConfirmTransaction + 0x12 => WindowClose + 0x13 => WindowOpen + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => NamedSoundEffect + 0x1a => Disconnect + 0x1b => EntityAction + 0x1c => Explosion + 0x1d => ChunkUnload + 0x1e => ChangeGameState + 0x1f => KeepAliveClientbound_i64 + 0x20 => ChunkData + 0x21 => Effect + 0x22 => Particle + 0x23 => JoinGame + 0x24 => Maps + 0x25 => Entity + 0x26 => EntityMove + 0x27 => EntityLookAndMove + 0x28 => EntityLook + 0x29 => VehicleTeleport + 0x2a => SignEditorOpen + 0x2b => CraftRecipeResponse + 0x2c => PlayerAbilities + 0x2d => CombatEvent + 0x2e => PlayerInfo + 0x2f => TeleportPlayer + 0x30 => EntityUsedBed + 0x31 => UnlockRecipes + 0x32 => EntityDestroy + 0x33 => EntityRemoveEffect + 0x34 => ResourcePackSend + 0x35 => Respawn + 0x36 => EntityHeadLook + 0x37 => SelectAdvancementTab + 0x38 => WorldBorder + 0x39 => Camera + 0x3a => SetCurrentHotbarSlot + 0x3b => ScoreboardDisplay + 0x3c => EntityMetadata + 0x3d => EntityAttach + 0x3e => EntityVelocity + 0x3f => EntityEquipment + 0x40 => SetExperience + 0x41 => UpdateHealth + 0x42 => ScoreboardObjective + 0x43 => SetPassengers + 0x44 => Teams + 0x45 => UpdateScore + 0x46 => SpawnPosition + 0x47 => TimeUpdate + 0x48 => Title + 0x49 => SoundEffect + 0x4a => PlayerListHeaderFooter + 0x4b => CollectItem + 0x4c => EntityTeleport + 0x4d => Advancements + 0x4e => EntityProperties + 0x4f => EntityEffect + } + } + 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 + } + } +); + + From 556ea1849f6de6429c98beac5e2cd3ab865b0705 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Mon, 3 Dec 2018 15:04:39 -0800 Subject: [PATCH 108/160] Add 1.10.2 (210) multiprotocol support Closes https://github.com/iceiix/steven/pull/14 Enhances https://github.com/iceiix/steven/issues/18 --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 37 +++++- protocol/src/protocol/versions.rs | 4 + protocol/src/protocol/versions/v1_10_2.rs | 145 ++++++++++++++++++++++ protocol/src/protocol/versions/v1_11_2.rs | 2 +- protocol/src/protocol/versions/v1_12_2.rs | 2 +- 6 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 protocol/src/protocol/versions/v1_10_2.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 4a31c74..8103dec 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; 3] = [340, 316, 315]; +pub const SUPPORTED_PROTOCOLS: [i32; 4] = [340, 316, 315, 210]; /// Helper macro for defining packets diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 94a60e2..42ea33d 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -248,7 +248,7 @@ state_packets!( field target: UUID =, } /// PlayerBlockPlacement is sent when the client tries to place a block. - packet PlayerBlockPlacement { + packet PlayerBlockPlacement_f32 { field location: Position =, field face: VarInt =, field hand: VarInt =, @@ -256,6 +256,14 @@ state_packets!( field cursor_y: f32 =, field cursor_z: f32 =, } + packet PlayerBlockPlacement_u8 { + field location: Position =, + field face: VarInt =, + field hand: VarInt =, + 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 =, @@ -314,6 +322,21 @@ state_packets!( field velocity_z: i16 =, field metadata: types::Metadata =, } + packet SpawnMob_u8 { + field entity_id: VarInt =, + field uuid: UUID =, + field ty: u8 =, + field x: f64 =, + field y: f64 =, + field z: f64 =, + 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::Metadata =, + } /// 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. packet SpawnPainting { @@ -826,6 +849,14 @@ state_packets!( field fade_stay: Option = when(|p: &Title| p.action.0 == 3), field fade_out: Option = when(|p: &Title| p.action.0 == 3), } + packet Title_notext { + field action: VarInt =, + field title: Option = when(|p: &Title_notext| p.action.0 == 0), + field sub_title: Option = when(|p: &Title_notext| p.action.0 == 1), + field fade_in: Option = when(|p: &Title_notext| p.action.0 == 2), + field fade_stay: Option = when(|p: &Title_notext| p.action.0 == 2), + field fade_out: Option = when(|p: &Title_notext| p.action.0 == 2), + } /// SoundEffect plays the named sound at the target location. packet SoundEffect { field name: VarInt =, @@ -848,6 +879,10 @@ state_packets!( field collector_entity_id: VarInt =, field number_of_items: VarInt =, } + packet CollectItem_nocount { + field collected_entity_id: VarInt =, + field collector_entity_id: VarInt =, + } /// EntityTeleport teleports the entity to the target location. This is /// sent if the entity moves further than EntityMove allows. packet EntityTeleport { diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index ce355d6..63bb6c1 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -2,6 +2,7 @@ use crate::protocol::*; mod v1_12_2; mod v1_11_2; +mod v1_10_2; pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { @@ -16,6 +17,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: // 1.11 315 => v1_11_2::translate_internal_packet_id(state, dir, id, to_internal), + // 1.10.2 + 210 => v1_10_2::translate_internal_packet_id(state, dir, id, to_internal), + _ => panic!("unsupported protocol version"), } } diff --git a/protocol/src/protocol/versions/v1_10_2.rs b/protocol/src/protocol/versions/v1_10_2.rs new file mode 100644 index 0000000..add7478 --- /dev/null +++ b/protocol/src/protocol/versions/v1_10_2.rs @@ -0,0 +1,145 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => TabComplete + 0x02 => ChatMessage + 0x03 => ClientStatus + 0x04 => ClientSettings + 0x05 => ConfirmTransactionServerbound + 0x06 => EnchantItem + 0x07 => ClickWindow + 0x08 => CloseWindow + 0x09 => PluginMessageServerbound + 0x0a => UseEntity + 0x0b => KeepAliveServerbound_VarInt + 0x0c => PlayerPosition + 0x0d => PlayerPositionLook + 0x0e => PlayerLook + 0x0f => Player + 0x10 => VehicleMove + 0x11 => SteerBoat + 0x12 => ClientAbilities + 0x13 => PlayerDigging + 0x14 => PlayerAction + 0x15 => SteerVehicle + 0x16 => ResourcePackStatus + 0x17 => HeldItemChange + 0x18 => CreativeInventoryAction + 0x19 => SetSign + 0x1a => ArmSwing + 0x1b => SpectateTeleport + 0x1c => PlayerBlockPlacement_u8 + 0x1d => UseItem + } + clientbound Clientbound { + 0x00 => SpawnObject + 0x01 => SpawnExperienceOrb + 0x02 => SpawnGlobalEntity + 0x03 => SpawnMob_u8 + 0x04 => SpawnPainting + 0x05 => SpawnPlayer + 0x06 => Animation + 0x07 => Statistics + 0x08 => BlockBreakAnimation + 0x09 => UpdateBlockEntity + 0x0a => BlockAction + 0x0b => BlockChange + 0x0c => BossBar + 0x0d => ServerDifficulty + 0x0e => TabCompleteReply + 0x0f => ServerMessage + 0x10 => MultiBlockChange + 0x11 => ConfirmTransaction + 0x12 => WindowClose + 0x13 => WindowOpen + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => NamedSoundEffect + 0x1a => Disconnect + 0x1b => EntityAction + 0x1c => Explosion + 0x1d => ChunkUnload + 0x1e => ChangeGameState + 0x1f => KeepAliveClientbound_VarInt + 0x20 => ChunkData + 0x21 => Effect + 0x22 => Particle + 0x23 => JoinGame + 0x24 => Maps + 0x25 => EntityMove + 0x26 => EntityLookAndMove + 0x27 => EntityLook + 0x28 => Entity + 0x29 => VehicleTeleport + 0x2a => SignEditorOpen + 0x2b => PlayerAbilities + 0x2c => CombatEvent + 0x2d => PlayerInfo + 0x2e => TeleportPlayer + 0x2f => EntityUsedBed + 0x30 => EntityDestroy + 0x31 => EntityRemoveEffect + 0x32 => ResourcePackSend + 0x33 => Respawn + 0x34 => EntityHeadLook + 0x35 => WorldBorder + 0x36 => Camera + 0x37 => SetCurrentHotbarSlot + 0x38 => ScoreboardDisplay + 0x39 => EntityMetadata + 0x3a => EntityAttach + 0x3b => EntityVelocity + 0x3c => EntityEquipment + 0x3d => SetExperience + 0x3e => UpdateHealth + 0x3f => ScoreboardObjective + 0x40 => SetPassengers + 0x41 => Teams + 0x42 => UpdateScore + 0x43 => SpawnPosition + 0x44 => TimeUpdate + 0x45 => Title_notext + 0x46 => SoundEffect + 0x47 => PlayerListHeaderFooter + 0x48 => CollectItem_nocount + 0x49 => EntityTeleport + 0x4a => EntityProperties + 0x4b => EntityEffect + } + } + 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 + } + } +); + + diff --git a/protocol/src/protocol/versions/v1_11_2.rs b/protocol/src/protocol/versions/v1_11_2.rs index 02dc201..2bd0271 100644 --- a/protocol/src/protocol/versions/v1_11_2.rs +++ b/protocol/src/protocol/versions/v1_11_2.rs @@ -36,7 +36,7 @@ protocol_packet_ids!( 0x19 => SetSign 0x1a => ArmSwing 0x1b => SpectateTeleport - 0x1c => PlayerBlockPlacement + 0x1c => PlayerBlockPlacement_f32 0x1d => UseItem } clientbound Clientbound { diff --git a/protocol/src/protocol/versions/v1_12_2.rs b/protocol/src/protocol/versions/v1_12_2.rs index d4ceb8d..39a7539 100644 --- a/protocol/src/protocol/versions/v1_12_2.rs +++ b/protocol/src/protocol/versions/v1_12_2.rs @@ -39,7 +39,7 @@ protocol_packet_ids!( 0x1c => SetSign 0x1d => ArmSwing 0x1e => SpectateTeleport - 0x1f => PlayerBlockPlacement + 0x1f => PlayerBlockPlacement_f32 0x20 => UseItem } clientbound Clientbound { From ed0985a665e7536f9842e9ce8bc065a44ec6692f Mon Sep 17 00:00:00 2001 From: ice_iix Date: Mon, 3 Dec 2018 15:40:57 -0800 Subject: [PATCH 109/160] Add 1.9.2 (109) multiprotocol support Closes https://github.com/iceiix/steven/pull/15 Enhances https://github.com/iceiix/steven/issues/18 --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 37 ++++++ protocol/src/protocol/versions.rs | 4 + protocol/src/protocol/versions/v1_9_2.rs | 146 +++++++++++++++++++++++ 4 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 protocol/src/protocol/versions/v1_9_2.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 8103dec..9e77afc 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; 4] = [340, 316, 315, 210]; +pub const SUPPORTED_PROTOCOLS: [i32; 5] = [340, 316, 315, 210, 109]; /// Helper macro for defining packets diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 42ea33d..8cdac1a 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -214,6 +214,10 @@ state_packets!( packet ResourcePackStatus { field result: VarInt =, } + packet ResourcePackStatus_hash { + field hash: String =, + field result: VarInt =, + } // TODO: Document packet AdvancementTab { field action: VarInt =, @@ -493,6 +497,15 @@ state_packets!( field volume: f32 =, field pitch: f32 =, } + packet NamedSoundEffect_u8 { + field name: String =, + field category: VarInt =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field volume: f32 =, + field pitch: u8 =, + } /// Disconnect causes the client to disconnect displaying the passed reason. packet Disconnect { field reason: format::Component =, @@ -547,6 +560,13 @@ state_packets!( field data: LenPrefixedBytes =, field block_entities: LenPrefixed> =, } + packet ChunkData_NoEntities { + field chunk_x: i32 =, + field chunk_z: i32 =, + field new: bool =, + field bitmask: VarInt =, + field data: LenPrefixedBytes =, + } /// 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. @@ -857,6 +877,14 @@ state_packets!( field fade_stay: Option = when(|p: &Title_notext| p.action.0 == 2), field fade_out: Option = when(|p: &Title_notext| p.action.0 == 2), } + /// UpdateSign sets or changes the text on a sign. + packet UpdateSign { + field location: Position =, + field line1: format::Component =, + field line2: format::Component =, + field line3: format::Component =, + field line4: format::Component =, + } /// SoundEffect plays the named sound at the target location. packet SoundEffect { field name: VarInt =, @@ -867,6 +895,15 @@ state_packets!( field volume: f32 =, field pitch: f32 =, } + packet SoundEffect_u8 { + field name: VarInt =, + field category: VarInt =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field volume: f32 =, + field pitch: u8 =, + } /// PlayerListHeaderFooter updates the header/footer of the player list. packet PlayerListHeaderFooter { field header: format::Component =, diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index 63bb6c1..c2f276a 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -3,6 +3,7 @@ use crate::protocol::*; mod v1_12_2; mod v1_11_2; mod v1_10_2; +mod v1_9_2; pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { @@ -20,6 +21,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: // 1.10.2 210 => v1_10_2::translate_internal_packet_id(state, dir, id, to_internal), + // 1.9.2 + 109 => v1_9_2::translate_internal_packet_id(state, dir, id, to_internal), + _ => panic!("unsupported protocol version"), } } diff --git a/protocol/src/protocol/versions/v1_9_2.rs b/protocol/src/protocol/versions/v1_9_2.rs new file mode 100644 index 0000000..faaaca9 --- /dev/null +++ b/protocol/src/protocol/versions/v1_9_2.rs @@ -0,0 +1,146 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => TabComplete + 0x02 => ChatMessage + 0x03 => ClientStatus + 0x04 => ClientSettings + 0x05 => ConfirmTransactionServerbound + 0x06 => EnchantItem + 0x07 => ClickWindow + 0x08 => CloseWindow + 0x09 => PluginMessageServerbound + 0x0a => UseEntity + 0x0b => KeepAliveServerbound_VarInt + 0x0c => PlayerPosition + 0x0d => PlayerPositionLook + 0x0e => PlayerLook + 0x0f => Player + 0x10 => VehicleMove + 0x11 => SteerBoat + 0x12 => ClientAbilities + 0x13 => PlayerDigging + 0x14 => PlayerAction + 0x15 => SteerVehicle + 0x16 => ResourcePackStatus_hash + 0x17 => HeldItemChange + 0x18 => CreativeInventoryAction + 0x19 => SetSign + 0x1a => ArmSwing + 0x1b => SpectateTeleport + 0x1c => PlayerBlockPlacement_u8 + 0x1d => UseItem + } + clientbound Clientbound { + 0x00 => SpawnObject + 0x01 => SpawnExperienceOrb + 0x02 => SpawnGlobalEntity + 0x03 => SpawnMob_u8 + 0x04 => SpawnPainting + 0x05 => SpawnPlayer + 0x06 => Animation + 0x07 => Statistics + 0x08 => BlockBreakAnimation + 0x09 => UpdateBlockEntity + 0x0a => BlockAction + 0x0b => BlockChange + 0x0c => BossBar + 0x0d => ServerDifficulty + 0x0e => TabCompleteReply + 0x0f => ServerMessage + 0x10 => MultiBlockChange + 0x11 => ConfirmTransaction + 0x12 => WindowClose + 0x13 => WindowOpen + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => NamedSoundEffect_u8 + 0x1a => Disconnect + 0x1b => EntityAction + 0x1c => Explosion + 0x1d => ChunkUnload + 0x1e => ChangeGameState + 0x1f => KeepAliveClientbound_VarInt + 0x20 => ChunkData_NoEntities + 0x21 => Effect + 0x22 => Particle + 0x23 => JoinGame + 0x24 => Maps + 0x25 => EntityMove + 0x26 => EntityLookAndMove + 0x27 => EntityLook + 0x28 => Entity + 0x29 => VehicleTeleport + 0x2a => SignEditorOpen + 0x2b => PlayerAbilities + 0x2c => CombatEvent + 0x2d => PlayerInfo + 0x2e => TeleportPlayer + 0x2f => EntityUsedBed + 0x30 => EntityDestroy + 0x31 => EntityRemoveEffect + 0x32 => ResourcePackSend + 0x33 => Respawn + 0x34 => EntityHeadLook + 0x35 => WorldBorder + 0x36 => Camera + 0x37 => SetCurrentHotbarSlot + 0x38 => ScoreboardDisplay + 0x39 => EntityMetadata + 0x3a => EntityAttach + 0x3b => EntityVelocity + 0x3c => EntityEquipment + 0x3d => SetExperience + 0x3e => UpdateHealth + 0x3f => ScoreboardObjective + 0x40 => SetPassengers + 0x41 => Teams + 0x42 => UpdateScore + 0x43 => SpawnPosition + 0x44 => TimeUpdate + 0x45 => Title_notext + 0x46 => UpdateSign + 0x47 => SoundEffect_u8 + 0x48 => PlayerListHeaderFooter + 0x49 => CollectItem_nocount + 0x4a => EntityTeleport + 0x4b => EntityProperties + 0x4c => EntityEffect + } + } + 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 + } + } +); + + From 9a15a90af845f1a3853d7bf2ad1fc3283f005ac1 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Mon, 3 Dec 2018 16:06:20 -0800 Subject: [PATCH 110/160] Add 1.9 (107) multiprotocol support Closes https://github.com/iceiix/steven/pull/16 Enhances https://github.com/iceiix/steven/issues/18 --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 19 ++- protocol/src/protocol/versions.rs | 4 + protocol/src/protocol/versions/v1_10_2.rs | 2 +- protocol/src/protocol/versions/v1_11_2.rs | 2 +- protocol/src/protocol/versions/v1_12_2.rs | 2 +- protocol/src/protocol/versions/v1_9.rs | 146 ++++++++++++++++++++++ protocol/src/protocol/versions/v1_9_2.rs | 2 +- 8 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 protocol/src/protocol/versions/v1_9.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 9e77afc..48f12db 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; 5] = [340, 316, 315, 210, 109]; +pub const SUPPORTED_PROTOCOLS: [i32; 6] = [340, 316, 315, 210, 109, 107]; /// Helper macro for defining packets diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 8cdac1a..23c234b 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -594,7 +594,7 @@ state_packets!( } /// JoinGame is sent after completing the login process. This /// sets the initial state for the client. - packet JoinGame { + packet JoinGame_i32 { /// The entity id the client will be referenced by field entity_id: i32 =, /// The starting gamemode of the client @@ -611,6 +611,23 @@ state_packets!( /// information it displays in F3 mode field reduced_debug_info: bool =, } + packet JoinGame_i8 { + /// The entity id the client will be referenced by + field entity_id: i32 =, + /// The starting gamemode of the client + field gamemode: u8 =, + /// The dimension the client is starting in + field dimension: i8 =, + /// The difficuilty setting for the server + field difficulty: u8 =, + /// The max number of players on the server + field max_players: u8 =, + /// The level type of the server + field level_type: String =, + /// Whether the client should reduce the amount of debug + /// information it displays in F3 mode + field reduced_debug_info: bool =, + } /// Maps updates a single map's contents packet Maps { field item_damage: VarInt =, diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index c2f276a..ea8d955 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -4,6 +4,7 @@ mod v1_12_2; mod v1_11_2; mod v1_10_2; mod v1_9_2; +mod v1_9; pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { @@ -24,6 +25,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: // 1.9.2 109 => v1_9_2::translate_internal_packet_id(state, dir, id, to_internal), + // 1.9 + 107 => v1_9::translate_internal_packet_id(state, dir, id, to_internal), + _ => panic!("unsupported protocol version"), } } diff --git a/protocol/src/protocol/versions/v1_10_2.rs b/protocol/src/protocol/versions/v1_10_2.rs index add7478..7cbfed5 100644 --- a/protocol/src/protocol/versions/v1_10_2.rs +++ b/protocol/src/protocol/versions/v1_10_2.rs @@ -75,7 +75,7 @@ protocol_packet_ids!( 0x20 => ChunkData 0x21 => Effect 0x22 => Particle - 0x23 => JoinGame + 0x23 => JoinGame_i32 0x24 => Maps 0x25 => EntityMove 0x26 => EntityLookAndMove diff --git a/protocol/src/protocol/versions/v1_11_2.rs b/protocol/src/protocol/versions/v1_11_2.rs index 2bd0271..5f6cc60 100644 --- a/protocol/src/protocol/versions/v1_11_2.rs +++ b/protocol/src/protocol/versions/v1_11_2.rs @@ -75,7 +75,7 @@ protocol_packet_ids!( 0x20 => ChunkData 0x21 => Effect 0x22 => Particle - 0x23 => JoinGame + 0x23 => JoinGame_i32 0x24 => Maps 0x25 => EntityMove 0x26 => EntityLookAndMove diff --git a/protocol/src/protocol/versions/v1_12_2.rs b/protocol/src/protocol/versions/v1_12_2.rs index 39a7539..897447d 100644 --- a/protocol/src/protocol/versions/v1_12_2.rs +++ b/protocol/src/protocol/versions/v1_12_2.rs @@ -78,7 +78,7 @@ protocol_packet_ids!( 0x20 => ChunkData 0x21 => Effect 0x22 => Particle - 0x23 => JoinGame + 0x23 => JoinGame_i32 0x24 => Maps 0x25 => Entity 0x26 => EntityMove diff --git a/protocol/src/protocol/versions/v1_9.rs b/protocol/src/protocol/versions/v1_9.rs new file mode 100644 index 0000000..74b8982 --- /dev/null +++ b/protocol/src/protocol/versions/v1_9.rs @@ -0,0 +1,146 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => TabComplete + 0x02 => ChatMessage + 0x03 => ClientStatus + 0x04 => ClientSettings + 0x05 => ConfirmTransactionServerbound + 0x06 => EnchantItem + 0x07 => ClickWindow + 0x08 => CloseWindow + 0x09 => PluginMessageServerbound + 0x0a => UseEntity + 0x0b => KeepAliveServerbound_VarInt + 0x0c => PlayerPosition + 0x0d => PlayerPositionLook + 0x0e => PlayerLook + 0x0f => Player + 0x10 => VehicleMove + 0x11 => SteerBoat + 0x12 => ClientAbilities + 0x13 => PlayerDigging + 0x14 => PlayerAction + 0x15 => SteerVehicle + 0x16 => ResourcePackStatus_hash + 0x17 => HeldItemChange + 0x18 => CreativeInventoryAction + 0x19 => SetSign + 0x1a => ArmSwing + 0x1b => SpectateTeleport + 0x1c => PlayerBlockPlacement_u8 + 0x1d => UseItem + } + clientbound Clientbound { + 0x00 => SpawnObject + 0x01 => SpawnExperienceOrb + 0x02 => SpawnGlobalEntity + 0x03 => SpawnMob_u8 + 0x04 => SpawnPainting + 0x05 => SpawnPlayer + 0x06 => Animation + 0x07 => Statistics + 0x08 => BlockBreakAnimation + 0x09 => UpdateBlockEntity + 0x0a => BlockAction + 0x0b => BlockChange + 0x0c => BossBar + 0x0d => ServerDifficulty + 0x0e => TabCompleteReply + 0x0f => ServerMessage + 0x10 => MultiBlockChange + 0x11 => ConfirmTransaction + 0x12 => WindowClose + 0x13 => WindowOpen + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => NamedSoundEffect_u8 + 0x1a => Disconnect + 0x1b => EntityAction + 0x1c => Explosion + 0x1d => ChunkUnload + 0x1e => ChangeGameState + 0x1f => KeepAliveClientbound_VarInt + 0x20 => ChunkData_NoEntities + 0x21 => Effect + 0x22 => Particle + 0x23 => JoinGame_i8 + 0x24 => Maps + 0x25 => EntityMove + 0x26 => EntityLookAndMove + 0x27 => EntityLook + 0x28 => Entity + 0x29 => VehicleTeleport + 0x2a => SignEditorOpen + 0x2b => PlayerAbilities + 0x2c => CombatEvent + 0x2d => PlayerInfo + 0x2e => TeleportPlayer + 0x2f => EntityUsedBed + 0x30 => EntityDestroy + 0x31 => EntityRemoveEffect + 0x32 => ResourcePackSend + 0x33 => Respawn + 0x34 => EntityHeadLook + 0x35 => WorldBorder + 0x36 => Camera + 0x37 => SetCurrentHotbarSlot + 0x38 => ScoreboardDisplay + 0x39 => EntityMetadata + 0x3a => EntityAttach + 0x3b => EntityVelocity + 0x3c => EntityEquipment + 0x3d => SetExperience + 0x3e => UpdateHealth + 0x3f => ScoreboardObjective + 0x40 => SetPassengers + 0x41 => Teams + 0x42 => UpdateScore + 0x43 => SpawnPosition + 0x44 => TimeUpdate + 0x45 => Title_notext + 0x46 => UpdateSign + 0x47 => SoundEffect_u8 + 0x48 => PlayerListHeaderFooter + 0x49 => CollectItem_nocount + 0x4a => EntityTeleport + 0x4b => EntityProperties + 0x4c => EntityEffect + } + } + 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 + } + } +); + + diff --git a/protocol/src/protocol/versions/v1_9_2.rs b/protocol/src/protocol/versions/v1_9_2.rs index faaaca9..2b9f603 100644 --- a/protocol/src/protocol/versions/v1_9_2.rs +++ b/protocol/src/protocol/versions/v1_9_2.rs @@ -75,7 +75,7 @@ protocol_packet_ids!( 0x20 => ChunkData_NoEntities 0x21 => Effect 0x22 => Particle - 0x23 => JoinGame + 0x23 => JoinGame_i32 0x24 => Maps 0x25 => EntityMove 0x26 => EntityLookAndMove From 7a8f9b23752f42d42ebbce69fc1fc07a3d703419 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Mon, 3 Dec 2018 19:29:02 -0800 Subject: [PATCH 111/160] Add 15w39c (74) multiprotocol support (#56) Closes https://github.com/iceiix/steven/pull/17 This is the biggest multi-protocol change yet, adding many new packet variants and implementing the most, necessitating a respectable amount of refactoring. The last of the "easy" protocols (already implemented, and cribbed from Steven commit history). For https://github.com/iceiix/steven/issues/18 Enhance protocol support * Add 15w39c packet IDs * Add 15w39c packet changes * Implement EntityMove i16 and i8 packet variants * Implement EntityLookAndMove i16 and i8 packet variants * Implement TeleportPlayer no confirm / with confirm packet variants * Implement EntityTeleport f64 and i32 packet variants * Implement SpawnPlayer f64 and i32 packet variants --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 153 +++++++++++++++++++++- protocol/src/protocol/versions.rs | 4 + protocol/src/protocol/versions/v15w39c.rs | 141 ++++++++++++++++++++ protocol/src/protocol/versions/v1_10_2.rs | 10 +- protocol/src/protocol/versions/v1_11_2.rs | 10 +- protocol/src/protocol/versions/v1_12_2.rs | 10 +- protocol/src/protocol/versions/v1_9.rs | 10 +- protocol/src/protocol/versions/v1_9_2.rs | 10 +- 9 files changed, 319 insertions(+), 31 deletions(-) create mode 100644 protocol/src/protocol/versions/v15w39c.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 48f12db..5c337fb 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; 6] = [340, 316, 315, 210, 109, 107]; +pub const SUPPORTED_PROTOCOLS: [i32; 7] = [340, 316, 315, 210, 109, 107, 74]; /// Helper macro for defining packets diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 23c234b..aea455f 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -61,6 +61,11 @@ state_packets!( field has_target: bool =, field target: Option = when(|p: &TabComplete| p.has_target), } + packet TabComplete_NoAssume { + field text: String =, + field has_target: bool =, + field target: Option = when(|p: &TabComplete_NoAssume| p.has_target), + } /// ChatMessage is sent by the client when it sends a chat message or /// executes a command (prefixed by '/'). packet ChatMessage { @@ -79,6 +84,14 @@ state_packets!( field displayed_skin_parts: u8 =, field main_hand: VarInt =, } + packet ClientSettings_u8 { + field locale: String =, + field view_distance: u8 =, + field chat_mode: u8 =, + field chat_colors: bool =, + field displayed_skin_parts: u8 =, + field main_hand: VarInt =, + } /// ConfirmTransactionServerbound is a reply to ConfirmTransaction. packet ConfirmTransactionServerbound { field id: u8 =, @@ -99,6 +112,14 @@ state_packets!( field mode: VarInt =, field clicked_item: Option =, } + packet ClickWindow_u8 { + field id: u8 =, + field slot: i16 =, + field button: u8 =, + field action_number: u16 =, + field mode: u8 =, + field clicked_item: Option =, + } /// CloseWindow is sent when the client closes a window. packet CloseWindow { field id: u8 =, @@ -189,6 +210,11 @@ state_packets!( field location: Position =, field face: u8 =, } + packet PlayerDigging_u8 { + field status: u8 =, + field location: Position =, + field face: u8 =, + } /// PlayerAction is sent when a player preforms various actions. packet PlayerAction { field entity_id: VarInt =, @@ -290,6 +316,20 @@ state_packets!( field velocity_y: i16 =, field velocity_z: i16 =, } + packet SpawnObject_i32 { + field entity_id: VarInt =, + field uuid: UUID =, + 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 =, + field velocity_y: i16 =, + field velocity_z: i16 =, + } /// 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. @@ -300,6 +340,13 @@ state_packets!( field z: f64 =, field count: i16 =, } + packet SpawnExperienceOrb_i32 { + field entity_id: VarInt =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field count: i16 =, + } /// SpawnGlobalEntity spawns an entity which is visible from anywhere in the /// world. Currently only used for lightning. packet SpawnGlobalEntity { @@ -309,6 +356,13 @@ state_packets!( field y: f64 =, field z: f64 =, } + packet SpawnGlobalEntity_i32 { + field entity_id: VarInt =, + field ty: u8 =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + } /// SpawnMob is used to spawn a living entity into the world when it is in /// range of the client. packet SpawnMob { @@ -341,6 +395,21 @@ state_packets!( field velocity_z: i16 =, field metadata: types::Metadata =, } + packet SpawnMob_u8_i32 { + field entity_id: VarInt =, + field uuid: UUID =, + 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::Metadata =, + } /// 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. packet SpawnPainting { @@ -350,10 +419,16 @@ state_packets!( field location: Position =, field direction: u8 =, } + packet SpawnPainting_NoUUID { + field entity_id: VarInt =, + field title: String =, + field location: Position =, + field direction: u8 =, + } /// SpawnPlayer is used to spawn a player when they are in range of the client. /// This packet alone isn't enough to display the player as the skin and username /// information is in the player information packet. - packet SpawnPlayer { + packet SpawnPlayer_f64 { field entity_id: VarInt =, field uuid: UUID =, field x: f64 =, @@ -363,6 +438,16 @@ state_packets!( field pitch: i8 =, field metadata: types::Metadata =, } + packet SpawnPlayer_i32 { + field entity_id: VarInt =, + field uuid: UUID =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field yaw: i8 =, + field pitch: i8 =, + field metadata: types::Metadata =, + } /// Animation is sent by the server to play an animation on a specific entity. packet Animation { field entity_id: VarInt =, @@ -506,6 +591,14 @@ state_packets!( field volume: f32 =, field pitch: u8 =, } + packet NamedSoundEffect_u8_NoCategory { + field name: String =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field volume: f32 =, + field pitch: u8 =, + } /// Disconnect causes the client to disconnect displaying the passed reason. packet Disconnect { field reason: format::Component =, @@ -534,6 +627,10 @@ state_packets!( field x: i32 =, field z: i32 =, } + /// SetCompression updates the compression threshold. + packet SetCompression { + field threshold: VarInt =, + } /// ChangeGameState is used to modify the game's state like gamemode or /// weather. packet ChangeGameState { @@ -641,15 +738,22 @@ state_packets!( field data: Option> = when(|p: &Maps| p.columns > 0), } /// EntityMove moves the entity with the id by the offsets provided. - packet EntityMove { + packet EntityMove_i16 { field entity_id: VarInt =, field delta_x: i16 =, field delta_y: i16 =, field delta_z: i16 =, field on_ground: bool =, } + packet EntityMove_i8 { + field entity_id: VarInt =, + field delta_x: i8 =, + field delta_y: i8 =, + field delta_z: i8 =, + field on_ground: bool =, + } /// EntityLookAndMove is a combination of EntityMove and EntityLook. - packet EntityLookAndMove { + packet EntityLookAndMove_i16 { field entity_id: VarInt =, field delta_x: i16 =, field delta_y: i16 =, @@ -658,6 +762,15 @@ state_packets!( field pitch: i8 =, field on_ground: bool =, } + packet EntityLookAndMove_i8 { + field entity_id: VarInt =, + field delta_x: i8 =, + field delta_y: i8 =, + field delta_z: i8 =, + field yaw: i8 =, + field pitch: i8 =, + field on_ground: bool =, + } /// EntityLook rotates the entity to the new angles provided. packet EntityLook { field entity_id: VarInt =, @@ -711,7 +824,7 @@ state_packets!( /// 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. - packet TeleportPlayer { + packet TeleportPlayer_WithConfirm { field x: f64 =, field y: f64 =, field z: f64 =, @@ -720,6 +833,14 @@ state_packets!( field flags: u8 =, field teleport_id: VarInt =, } + packet TeleportPlayer_NoConfirm { + field x: f64 =, + field y: f64 =, + field z: f64 =, + field yaw: f32 =, + field pitch: f32 =, + field flags: u8 =, + } /// EntityUsedBed is sent by the server when a player goes to bed. packet EntityUsedBed { field entity_id: VarInt =, @@ -802,6 +923,11 @@ state_packets!( field entity_id: i32 =, field vehicle: i32 =, } + packet EntityAttach_leashed { + field entity_id: i32 =, + field vehicle: i32 =, + field leash: bool =, + } /// EntityVelocity sets the velocity of an entity in 1/8000 of a block /// per a tick. packet EntityVelocity { @@ -894,6 +1020,14 @@ state_packets!( field fade_stay: Option = when(|p: &Title_notext| p.action.0 == 2), field fade_out: Option = when(|p: &Title_notext| p.action.0 == 2), } + packet Title_notext_component { + field action: VarInt =, + field title: Option = when(|p: &Title_notext_component| p.action.0 == 0), + field sub_title: Option = when(|p: &Title_notext_component| p.action.0 == 1), + field fade_in: Option = when(|p: &Title_notext_component| p.action.0 == 2), + field fade_stay: Option = when(|p: &Title_notext_component| p.action.0 == 2), + field fade_out: Option = when(|p: &Title_notext_component| p.action.0 == 2), + } /// UpdateSign sets or changes the text on a sign. packet UpdateSign { field location: Position =, @@ -939,7 +1073,7 @@ state_packets!( } /// EntityTeleport teleports the entity to the target location. This is /// sent if the entity moves further than EntityMove allows. - packet EntityTeleport { + packet EntityTeleport_f64 { field entity_id: VarInt =, field x: f64 =, field y: f64 =, @@ -948,6 +1082,15 @@ state_packets!( field pitch: i8 =, field on_ground: bool =, } + packet EntityTeleport_i32 { + field entity_id: VarInt =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field yaw: i8 =, + field pitch: i8 =, + field on_ground: bool =, + } packet Advancements { field reset_clear: bool =, field mapping: LenPrefixed =, diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index ea8d955..259c5af 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -5,6 +5,7 @@ mod v1_11_2; mod v1_10_2; mod v1_9_2; mod v1_9; +mod v15w39c; pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { @@ -28,6 +29,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: // 1.9 107 => v1_9::translate_internal_packet_id(state, dir, id, to_internal), + // 15w39a/b/c + 74 => v15w39c::translate_internal_packet_id(state, dir, id, to_internal), + _ => panic!("unsupported protocol version"), } } diff --git a/protocol/src/protocol/versions/v15w39c.rs b/protocol/src/protocol/versions/v15w39c.rs new file mode 100644 index 0000000..35847be --- /dev/null +++ b/protocol/src/protocol/versions/v15w39c.rs @@ -0,0 +1,141 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TabComplete_NoAssume + 0x01 => ChatMessage + 0x02 => ClientStatus + 0x03 => ClientSettings_u8 + 0x04 => ConfirmTransactionServerbound + 0x05 => EnchantItem + 0x06 => ClickWindow_u8 + 0x07 => CloseWindow + 0x08 => PluginMessageServerbound + 0x09 => UseEntity + 0x0a => KeepAliveServerbound_VarInt + 0x0b => PlayerPosition + 0x0c => PlayerPositionLook + 0x0d => PlayerLook + 0x0e => Player + 0x0f => ClientAbilities + 0x10 => PlayerDigging_u8 + 0x11 => PlayerAction + 0x12 => SteerVehicle + 0x13 => ResourcePackStatus + 0x14 => HeldItemChange + 0x15 => CreativeInventoryAction + 0x16 => SetSign + 0x17 => ArmSwing + 0x18 => SpectateTeleport + 0x19 => PlayerBlockPlacement_u8 + 0x1a => UseItem + } + clientbound Clientbound { + 0x00 => SpawnObject_i32 + 0x01 => SpawnExperienceOrb_i32 + 0x02 => SpawnGlobalEntity_i32 + 0x03 => SpawnMob_u8_i32 + 0x04 => SpawnPainting_NoUUID + 0x05 => SpawnPlayer_i32 + 0x06 => Animation + 0x07 => Statistics + 0x08 => BlockBreakAnimation + 0x09 => UpdateBlockEntity + 0x0a => BlockAction + 0x0b => BlockChange + 0x0c => BossBar + 0x0d => ServerDifficulty + 0x0e => TabCompleteReply + 0x0f => ServerMessage + 0x10 => MultiBlockChange + 0x11 => ConfirmTransaction + 0x12 => WindowClose + 0x13 => WindowOpen + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => Disconnect + 0x1a => EntityAction + 0x1b => Explosion + 0x1c => ChunkUnload + 0x1d => SetCompression + 0x1e => ChangeGameState + 0x1f => KeepAliveClientbound_VarInt + 0x20 => ChunkData_NoEntities + 0x21 => Effect + 0x22 => Particle + 0x23 => NamedSoundEffect_u8_NoCategory + 0x24 => JoinGame_i8 + 0x25 => Maps + 0x26 => EntityMove_i8 + 0x27 => EntityLookAndMove_i8 + 0x28 => EntityLook + 0x29 => Entity + 0x2a => SignEditorOpen + 0x2b => PlayerAbilities + 0x2c => CombatEvent + 0x2d => PlayerInfo + 0x2e => TeleportPlayer_NoConfirm + 0x2f => EntityUsedBed + 0x30 => EntityDestroy + 0x31 => EntityRemoveEffect + 0x32 => ResourcePackSend + 0x33 => Respawn + 0x34 => EntityHeadLook + 0x35 => WorldBorder + 0x36 => Camera + 0x37 => SetCurrentHotbarSlot + 0x38 => ScoreboardDisplay + 0x39 => EntityMetadata + 0x3a => EntityAttach_leashed + 0x3b => EntityVelocity + 0x3c => EntityEquipment + 0x3d => SetExperience + 0x3e => UpdateHealth + 0x3f => ScoreboardObjective + 0x40 => Teams + 0x41 => UpdateScore + 0x42 => SpawnPosition + 0x43 => TimeUpdate + 0x44 => Title_notext_component + 0x45 => UpdateSign + 0x46 => PlayerListHeaderFooter + 0x47 => CollectItem_nocount + 0x48 => EntityTeleport_i32 + 0x49 => EntityProperties + 0x4a => EntityEffect + } + } + 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 + } + } +); + + diff --git a/protocol/src/protocol/versions/v1_10_2.rs b/protocol/src/protocol/versions/v1_10_2.rs index 7cbfed5..0be2deb 100644 --- a/protocol/src/protocol/versions/v1_10_2.rs +++ b/protocol/src/protocol/versions/v1_10_2.rs @@ -45,7 +45,7 @@ protocol_packet_ids!( 0x02 => SpawnGlobalEntity 0x03 => SpawnMob_u8 0x04 => SpawnPainting - 0x05 => SpawnPlayer + 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics 0x08 => BlockBreakAnimation @@ -77,8 +77,8 @@ protocol_packet_ids!( 0x22 => Particle 0x23 => JoinGame_i32 0x24 => Maps - 0x25 => EntityMove - 0x26 => EntityLookAndMove + 0x25 => EntityMove_i16 + 0x26 => EntityLookAndMove_i16 0x27 => EntityLook 0x28 => Entity 0x29 => VehicleTeleport @@ -86,7 +86,7 @@ protocol_packet_ids!( 0x2b => PlayerAbilities 0x2c => CombatEvent 0x2d => PlayerInfo - 0x2e => TeleportPlayer + 0x2e => TeleportPlayer_WithConfirm 0x2f => EntityUsedBed 0x30 => EntityDestroy 0x31 => EntityRemoveEffect @@ -113,7 +113,7 @@ protocol_packet_ids!( 0x46 => SoundEffect 0x47 => PlayerListHeaderFooter 0x48 => CollectItem_nocount - 0x49 => EntityTeleport + 0x49 => EntityTeleport_f64 0x4a => EntityProperties 0x4b => EntityEffect } diff --git a/protocol/src/protocol/versions/v1_11_2.rs b/protocol/src/protocol/versions/v1_11_2.rs index 5f6cc60..fed44e4 100644 --- a/protocol/src/protocol/versions/v1_11_2.rs +++ b/protocol/src/protocol/versions/v1_11_2.rs @@ -45,7 +45,7 @@ protocol_packet_ids!( 0x02 => SpawnGlobalEntity 0x03 => SpawnMob 0x04 => SpawnPainting - 0x05 => SpawnPlayer + 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics 0x08 => BlockBreakAnimation @@ -77,8 +77,8 @@ protocol_packet_ids!( 0x22 => Particle 0x23 => JoinGame_i32 0x24 => Maps - 0x25 => EntityMove - 0x26 => EntityLookAndMove + 0x25 => EntityMove_i16 + 0x26 => EntityLookAndMove_i16 0x27 => EntityLook 0x28 => Entity 0x29 => VehicleTeleport @@ -86,7 +86,7 @@ protocol_packet_ids!( 0x2b => PlayerAbilities 0x2c => CombatEvent 0x2d => PlayerInfo - 0x2e => TeleportPlayer + 0x2e => TeleportPlayer_WithConfirm 0x2f => EntityUsedBed 0x30 => EntityDestroy 0x31 => EntityRemoveEffect @@ -113,7 +113,7 @@ protocol_packet_ids!( 0x46 => SoundEffect 0x47 => PlayerListHeaderFooter 0x48 => CollectItem - 0x49 => EntityTeleport + 0x49 => EntityTeleport_f64 0x4a => EntityProperties 0x4b => EntityEffect } diff --git a/protocol/src/protocol/versions/v1_12_2.rs b/protocol/src/protocol/versions/v1_12_2.rs index 897447d..b5765eb 100644 --- a/protocol/src/protocol/versions/v1_12_2.rs +++ b/protocol/src/protocol/versions/v1_12_2.rs @@ -48,7 +48,7 @@ protocol_packet_ids!( 0x02 => SpawnGlobalEntity 0x03 => SpawnMob 0x04 => SpawnPainting - 0x05 => SpawnPlayer + 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics 0x08 => BlockBreakAnimation @@ -81,8 +81,8 @@ protocol_packet_ids!( 0x23 => JoinGame_i32 0x24 => Maps 0x25 => Entity - 0x26 => EntityMove - 0x27 => EntityLookAndMove + 0x26 => EntityMove_i16 + 0x27 => EntityLookAndMove_i16 0x28 => EntityLook 0x29 => VehicleTeleport 0x2a => SignEditorOpen @@ -90,7 +90,7 @@ protocol_packet_ids!( 0x2c => PlayerAbilities 0x2d => CombatEvent 0x2e => PlayerInfo - 0x2f => TeleportPlayer + 0x2f => TeleportPlayer_WithConfirm 0x30 => EntityUsedBed 0x31 => UnlockRecipes 0x32 => EntityDestroy @@ -119,7 +119,7 @@ protocol_packet_ids!( 0x49 => SoundEffect 0x4a => PlayerListHeaderFooter 0x4b => CollectItem - 0x4c => EntityTeleport + 0x4c => EntityTeleport_f64 0x4d => Advancements 0x4e => EntityProperties 0x4f => EntityEffect diff --git a/protocol/src/protocol/versions/v1_9.rs b/protocol/src/protocol/versions/v1_9.rs index 74b8982..07ccb44 100644 --- a/protocol/src/protocol/versions/v1_9.rs +++ b/protocol/src/protocol/versions/v1_9.rs @@ -45,7 +45,7 @@ protocol_packet_ids!( 0x02 => SpawnGlobalEntity 0x03 => SpawnMob_u8 0x04 => SpawnPainting - 0x05 => SpawnPlayer + 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics 0x08 => BlockBreakAnimation @@ -77,8 +77,8 @@ protocol_packet_ids!( 0x22 => Particle 0x23 => JoinGame_i8 0x24 => Maps - 0x25 => EntityMove - 0x26 => EntityLookAndMove + 0x25 => EntityMove_i16 + 0x26 => EntityLookAndMove_i16 0x27 => EntityLook 0x28 => Entity 0x29 => VehicleTeleport @@ -86,7 +86,7 @@ protocol_packet_ids!( 0x2b => PlayerAbilities 0x2c => CombatEvent 0x2d => PlayerInfo - 0x2e => TeleportPlayer + 0x2e => TeleportPlayer_WithConfirm 0x2f => EntityUsedBed 0x30 => EntityDestroy 0x31 => EntityRemoveEffect @@ -114,7 +114,7 @@ protocol_packet_ids!( 0x47 => SoundEffect_u8 0x48 => PlayerListHeaderFooter 0x49 => CollectItem_nocount - 0x4a => EntityTeleport + 0x4a => EntityTeleport_f64 0x4b => EntityProperties 0x4c => EntityEffect } diff --git a/protocol/src/protocol/versions/v1_9_2.rs b/protocol/src/protocol/versions/v1_9_2.rs index 2b9f603..524f42f 100644 --- a/protocol/src/protocol/versions/v1_9_2.rs +++ b/protocol/src/protocol/versions/v1_9_2.rs @@ -45,7 +45,7 @@ protocol_packet_ids!( 0x02 => SpawnGlobalEntity 0x03 => SpawnMob_u8 0x04 => SpawnPainting - 0x05 => SpawnPlayer + 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics 0x08 => BlockBreakAnimation @@ -77,8 +77,8 @@ protocol_packet_ids!( 0x22 => Particle 0x23 => JoinGame_i32 0x24 => Maps - 0x25 => EntityMove - 0x26 => EntityLookAndMove + 0x25 => EntityMove_i16 + 0x26 => EntityLookAndMove_i16 0x27 => EntityLook 0x28 => Entity 0x29 => VehicleTeleport @@ -86,7 +86,7 @@ protocol_packet_ids!( 0x2b => PlayerAbilities 0x2c => CombatEvent 0x2d => PlayerInfo - 0x2e => TeleportPlayer + 0x2e => TeleportPlayer_WithConfirm 0x2f => EntityUsedBed 0x30 => EntityDestroy 0x31 => EntityRemoveEffect @@ -114,7 +114,7 @@ protocol_packet_ids!( 0x47 => SoundEffect_u8 0x48 => PlayerListHeaderFooter 0x49 => CollectItem_nocount - 0x4a => EntityTeleport + 0x4a => EntityTeleport_f64 0x4b => EntityProperties 0x4c => EntityEffect } From 0c41b385f9d90f0e2a25ee966d9e636b4122330e Mon Sep 17 00:00:00 2001 From: ice_iix Date: Tue, 4 Dec 2018 08:03:58 -0800 Subject: [PATCH 112/160] Fix logging bad packet IDs in multiprotocol packet translation macro https://github.com/iceiix/steven/pull/57#issuecomment-443962662 --- protocol/src/protocol/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 5c337fb..c247dba 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -174,14 +174,14 @@ macro_rules! protocol_packet_ids { $( $id => crate::protocol::packet::$state::$dir::internal_ids::$name, )* - _ => panic!("bad packet id $id in $dir $state"), + _ => panic!("bad packet id 0x{:x} in {:?} {:?}", id, dir, state), } } else { match id { $( crate::protocol::packet::$state::$dir::internal_ids::$name => $id, )* - _ => panic!("bad packet internal id $id in $dir $state"), + _ => panic!("bad packet internal id 0x{:x} in {:?} {:?}", id, dir, state), } } } @@ -718,7 +718,7 @@ impl Serializable for Position { /// Direction is used to define whether packets are going to the /// server or the client. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum Direction { Serverbound, Clientbound, @@ -726,7 +726,7 @@ pub enum Direction { /// The protocol has multiple 'sub-protocols' or states which control which /// packet an id points to. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum State { Handshaking, Play, From afac49389687463f9abc6f18ce678899f014790a Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 9 Dec 2018 12:03:55 -0800 Subject: [PATCH 113/160] 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 + } + } +); + + From 7f2e2033cad5d9739c433202844c4a8f0dd1b27f Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 9 Dec 2018 12:03:55 -0800 Subject: [PATCH 114/160] 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()); From a6ea434421923b3994eb0ebdc07223b6cfb6430f Mon Sep 17 00:00:00 2001 From: ice_iix Date: Tue, 11 Dec 2018 18:18:25 -0800 Subject: [PATCH 115/160] Add protocol version global mutable to merge Metadata18/19 into one Cleans up https://github.com/iceiix/steven/pull/57 1.8.9 (47) multiprotocol support which added too much code duplication, Metadata19 vs Metadata18, and different packets for each, the only difference being how it was parsed. Instead, switch back to using only one Metadata implementation, but with parsing conditionalized on a new global mutable: SUPPORTED_PROTOCOL_VERSION. Accessing global mutable state is unsafe but it is only set when initializing the connection, and only read when deep enough in the code where it is not feasible to pass otherwise. More elegant, worth it. --- protocol/src/protocol/mod.rs | 6 ++++++ protocol/src/protocol/packet.rs | 24 ++++++++++-------------- protocol/src/protocol/versions/v1_8_9.rs | 6 +++--- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 3fcc23d..d78ac15 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -39,6 +39,8 @@ use crate::shared::Position; pub const SUPPORTED_PROTOCOLS: [i32; 8] = [340, 316, 315, 210, 109, 107, 74, 47]; +// 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]; /// Helper macro for defining packets #[macro_export] @@ -805,6 +807,10 @@ pub struct Conn { impl Conn { pub fn new(target: &str, protocol_version: i32) -> Result { + unsafe { + CURRENT_PROTOCOL_VERSION = protocol_version; + } + // TODO SRV record support let mut parts = target.split(':').collect::>(); let address = if parts.len() == 1 { diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index bf87440..5006d13 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -417,7 +417,7 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata19 =, + field metadata: types::Metadata =, } packet SpawnMob_u8 { field entity_id: VarInt =, @@ -432,7 +432,7 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata19 =, + field metadata: types::Metadata =, } packet SpawnMob_u8_i32 { field entity_id: VarInt =, @@ -447,9 +447,9 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata19 =, + field metadata: types::Metadata =, } - packet SpawnMob_u8_i32_NoUUID_18 { + packet SpawnMob_u8_i32_NoUUID { field entity_id: VarInt =, field ty: u8 =, field x: i32 =, @@ -461,7 +461,7 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata18 =, + field metadata: types::Metadata =, } /// 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. @@ -489,7 +489,7 @@ state_packets!( field z: f64 =, field yaw: i8 =, field pitch: i8 =, - field metadata: types::Metadata19 =, + field metadata: types::Metadata =, } packet SpawnPlayer_i32 { field entity_id: VarInt =, @@ -499,9 +499,9 @@ state_packets!( field z: i32 =, field yaw: i8 =, field pitch: i8 =, - field metadata: types::Metadata19 =, + field metadata: types::Metadata =, } - packet SpawnPlayer_i32_HeldItem_18 { + packet SpawnPlayer_i32_HeldItem { field entity_id: VarInt =, field uuid: UUID =, field x: i32 =, @@ -510,7 +510,7 @@ state_packets!( field yaw: i8 =, field pitch: i8 =, field current_item: u16 =, - field metadata: types::Metadata18 =, + field metadata: types::Metadata =, } /// Animation is sent by the server to play an animation on a specific entity. packet Animation { @@ -1010,11 +1010,7 @@ state_packets!( /// EntityMetadata updates the metadata for an entity. packet EntityMetadata { field entity_id: VarInt =, - field metadata: types::Metadata19 =, - } - packet EntityMetadata_18 { - field entity_id: VarInt =, - field metadata: types::Metadata18 =, + field metadata: types::Metadata =, } /// EntityAttach attaches to entities together, either by mounting or leashing. /// -1 can be used at the EntityID to deattach. diff --git a/protocol/src/protocol/versions/v1_8_9.rs b/protocol/src/protocol/versions/v1_8_9.rs index 72d55ff..362b93c 100644 --- a/protocol/src/protocol/versions/v1_8_9.rs +++ b/protocol/src/protocol/versions/v1_8_9.rs @@ -48,10 +48,10 @@ protocol_packet_ids!( 0x09 => SetCurrentHotbarSlot 0x0a => EntityUsedBed 0x0b => Animation - 0x0c => SpawnPlayer_i32_HeldItem_18 + 0x0c => SpawnPlayer_i32_HeldItem 0x0d => CollectItem_nocount 0x0e => SpawnObject_i32_NoUUID - 0x0f => SpawnMob_u8_i32_NoUUID_18 + 0x0f => SpawnMob_u8_i32_NoUUID 0x10 => SpawnPainting_NoUUID 0x11 => SpawnExperienceOrb_i32 0x12 => EntityVelocity @@ -64,7 +64,7 @@ protocol_packet_ids!( 0x19 => EntityHeadLook 0x1a => EntityStatus 0x1b => EntityAttach_leashed - 0x1c => EntityMetadata_18 + 0x1c => EntityMetadata 0x1d => EntityEffect 0x1e => EntityRemoveEffect 0x1f => SetExperience From e28946b691b241cedaf62bcb5932b6212e3ae31f Mon Sep 17 00:00:00 2001 From: ice_iix Date: Tue, 11 Dec 2018 18:18:25 -0800 Subject: [PATCH 116/160] Add protocol version global mutable to merge Metadata18/19 into one Cleans up https://github.com/iceiix/steven/pull/57 1.8.9 (47) multiprotocol support which added too much code duplication, Metadata19 vs Metadata18, and different packets for each, the only difference being how it was parsed. Instead, switch back to using only one Metadata implementation, but with parsing conditionalized on a new global mutable: SUPPORTED_PROTOCOL_VERSION. Accessing global mutable state is unsafe but it is only set when initializing the connection, and only read when deep enough in the code where it is not feasible to pass otherwise. More elegant, worth it. --- protocol/src/types/metadata.rs | 118 ++++++++++++++------------------- 1 file changed, 49 insertions(+), 69 deletions(-) diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 498cd01..44ee0a7 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -38,66 +38,28 @@ impl MetadataKey { } } -pub struct Metadata18 { +pub struct Metadata { map: HashMap, } -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) +impl Metadata { + pub fn new() -> Metadata { + Metadata { map: HashMap::new() } } - fn put(&mut self, key: &MetadataKey, val: T) { - self.map_mut().insert(key.index, val.wrap()); + 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_raw(&mut self, index: i32, val: T) { - self.map_mut().insert(index, val.wrap()); + self.map.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 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 { + fn read_from18(buf: &mut R) -> Result { let mut m = Self::new(); loop { let ty_index = u8::read_from(buf)? as i32; @@ -128,7 +90,7 @@ impl Serializable for Metadata18 { Ok(m) } - fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { + fn write_to18(&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); @@ -177,9 +139,7 @@ impl Serializable for Metadata18 { 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"); } } @@ -187,11 +147,8 @@ impl Serializable for Metadata18 { u8::write_to(&0x7f, buf)?; Ok(()) } -} -impl Serializable for Metadata19 { - - fn read_from(buf: &mut R) -> Result { + fn read_from19(buf: &mut R) -> Result { let mut m = Self::new(); loop { let index = u8::read_from(buf)? as i32; @@ -243,7 +200,7 @@ impl Serializable for Metadata19 { Ok(m) } - fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { + fn write_to19(&self, buf: &mut W) -> Result<(), protocol::Error> { for (k, v) in &self.map { (*k as u8).write_to(buf)?; match *v { @@ -316,18 +273,41 @@ impl Serializable for Metadata19 { } } -// 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 Serializable for Metadata { + fn read_from(buf: &mut R) -> Result { + let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; -impl Default for Metadata19 { - fn default() -> Self { - Self::new() + if protocol_version >= 74 { + Metadata::read_from19(buf) + } else { + Metadata::read_from18(buf) + } + } + + fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { + let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; + + if protocol_version >= 74 { + self.write_to19(buf) + } else { + self.write_to18(buf) + } } } -impl Default for Metadata18 { - fn default() -> Self { - Self::new() + +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, "]") + } +} + +impl Default for Metadata { + fn default() -> Metadata { + Metadata::new() } } @@ -561,7 +541,7 @@ mod test { #[test] fn basic() { - let mut m = Metadata19::new(); + let mut m = Metadata::new(); m.put(&TEST, "Hello world".to_owned()); From 4d127c55fe5ea80eba29015feb4f494c2ca63eb9 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sat, 15 Dec 2018 19:56:54 -0800 Subject: [PATCH 117/160] 1.7.10 (5) multiprotocol support (#64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds 1.7.10 protocol version 5 support, a major update with significant changes. Expands https://github.com/iceiix/steven/issues/18 Enhance protocol support * Add v1_7_10 protocol packet structures and IDs * EncryptionRequest/Response i16 variant in login protocol * 1.7.10 slot NBT data parsing * Support both 1.7/1.8+ item::Stack in read_from, using if protocol_verson * 1.7.10 chunk format support, ChunkDataBulk_17 and ChunkData_17 * Extract dirty_chunks_by_bitmask from load_chunks17/18/19 * Implement keepalive i32 handler * Send PlayerPositionLook_HeadY * Send PlayerBlockPlacement_u8_Item_u8y * Handle JoinGame_i8_NoDebug * Handle SpawnPlayer_i32_HeldItem_String * BlockChange_u8, MultiBlockChange_i16, UpdateBlockEntity_Data, EntityMove_i8_i32_NoGround, EntityLook_i32_NoGround, EntityLookAndMove_i8_i32_NoGround * UpdateSign_u16, PlayerInfo_String, EntityDestroy_u8, EntityTeleport_i32_i32_NoGround * Send feet_y = head_y - 1.62, fixes Illegal stance https://wiki.vg/index.php?title=Protocol&oldid=6003#Player_Position > Updates the players XYZ position on the server. If HeadY - FeetY is less than 0.1 or greater than 1.65, the stance is illegal and the client will be kicked with the message “Illegal Stance”. > Absolute feet position, normally HeadY - 1.62. Used to modify the players bounding box when going up stairs, crouching, etc… * Set on_ground = true in entity teleport, fixes bouncing * Implement block change, fix metadata/id packing, bounce _u8 through on_block_change * Implement on_multi_block_change_u16, used with explosions --- protocol/src/protocol/mod.rs | 12 +- protocol/src/protocol/packet.rs | 396 +++++++++++++++++++++- protocol/src/protocol/versions.rs | 4 + protocol/src/protocol/versions/v15w39c.rs | 6 +- protocol/src/protocol/versions/v1_10_2.rs | 6 +- protocol/src/protocol/versions/v1_11_2.rs | 6 +- protocol/src/protocol/versions/v1_12_2.rs | 6 +- protocol/src/protocol/versions/v1_7_10.rs | 127 +++++++ protocol/src/protocol/versions/v1_8_9.rs | 6 +- protocol/src/protocol/versions/v1_9.rs | 6 +- protocol/src/protocol/versions/v1_9_2.rs | 6 +- 11 files changed, 556 insertions(+), 25 deletions(-) create mode 100644 protocol/src/protocol/versions/v1_7_10.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index d78ac15..c94640c 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; 8] = [340, 316, 315, 210, 109, 107, 74, 47]; +pub const SUPPORTED_PROTOCOLS: [i32; 9] = [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,16 @@ impl fmt::Debug for LenPrefixedBytes { } } +impl Lengthable for u8 { + fn into(self) -> usize { + self as usize + } + + fn from(u: usize) -> u8 { + u as u8 + } +} + impl Lengthable for i16 { fn into(self) -> usize { self as usize diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 5006d13..4134491 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -66,6 +66,9 @@ state_packets!( field has_target: bool =, field target: Option = when(|p: &TabComplete_NoAssume| p.has_target), } + packet TabComplete_NoAssume_NoTarget { + field text: String =, + } /// ChatMessage is sent by the client when it sends a chat message or /// executes a command (prefixed by '/'). packet ChatMessage { @@ -75,6 +78,9 @@ state_packets!( packet ClientStatus { field action_id: VarInt =, } + packet ClientStatus_u8 { + field action_id: u8=, + } /// ClientSettings is sent by the client to update its current settings. packet ClientSettings { field locale: String =, @@ -99,6 +105,14 @@ state_packets!( field chat_colors: bool =, field displayed_skin_parts: u8 =, } + packet ClientSettings_u8_Handsfree_Difficulty { + field locale: String =, + field view_distance: u8 =, + field chat_mode: u8 =, + field chat_colors: bool =, + field difficulty: u8 =, + field displayed_skin_parts: u8 =, + } /// ConfirmTransactionServerbound is a reply to ConfirmTransaction. packet ConfirmTransactionServerbound { field id: u8 =, @@ -138,6 +152,10 @@ state_packets!( field channel: String =, field data: Vec =, } + packet PluginMessageServerbound_i16 { + field channel: String =, + field data: LenPrefixedBytes =, + } /// UseEntity is sent when the user interacts (right clicks) or attacks /// (left clicks) an entity. packet UseEntity { @@ -155,6 +173,10 @@ state_packets!( field target_y: f32 = when(|p: &UseEntity_Handsfree| p.ty.0 == 2), field target_z: f32 = when(|p: &UseEntity_Handsfree| p.ty.0 == 2), } + packet UseEntity_Handsfree_i32 { + field target_id: i32 =, + field ty: u8 =, + } /// KeepAliveServerbound is sent by a client as a response to a /// KeepAliveClientbound. If the client doesn't reply the server /// may disconnect the client. @@ -164,6 +186,9 @@ state_packets!( packet KeepAliveServerbound_VarInt { field id: VarInt =, } + packet KeepAliveServerbound_i32 { + field id: i32 =, + } /// PlayerPosition is used to update the player's position. packet PlayerPosition { field x: f64 =, @@ -171,6 +196,13 @@ state_packets!( field z: f64 =, field on_ground: bool =, } + packet PlayerPosition_HeadY { + field x: f64 =, + field feet_y: f64 =, + field head_y: f64 =, + field z: f64 =, + field on_ground: bool =, + } /// PlayerPositionLook is a combination of PlayerPosition and /// PlayerLook. packet PlayerPositionLook { @@ -181,6 +213,15 @@ state_packets!( field pitch: f32 =, field on_ground: bool =, } + packet PlayerPositionLook_HeadY { + field x: f64 =, + field feet_y: f64 =, + field head_y: f64 =, + field z: f64 =, + field yaw: f32 =, + field pitch: f32 =, + field on_ground: bool =, + } /// PlayerLook is used to update the player's rotation. packet PlayerLook { field yaw: f32 =, @@ -229,12 +270,24 @@ state_packets!( field location: Position =, field face: u8 =, } + packet PlayerDigging_u8_u8y { + field status: u8 =, + field x: i32 =, + field y: u8 =, + field z: i32 =, + field face: u8 =, + } /// PlayerAction is sent when a player preforms various actions. packet PlayerAction { field entity_id: VarInt =, field action_id: VarInt =, field jump_boost: VarInt =, } + packet PlayerAction_i32 { + field entity_id: i32 =, + field action_id: i8 =, + field jump_boost: i32 =, + } /// SteerVehicle is sent by the client when steers or preforms an action /// on a vehicle. packet SteerVehicle { @@ -242,6 +295,12 @@ state_packets!( field forward: f32 =, field flags: u8 =, } + packet SteerVehicle_jump_unmount { + field sideways: f32 =, + field forward: f32 =, + field jump: bool =, + field unmount: bool =, + } /// CraftingBookData is sent when the player interacts with the crafting book. packet CraftingBookData { field action: VarInt =, @@ -282,6 +341,15 @@ state_packets!( field line3: String =, field line4: String =, } + packet SetSign_i16y { + field x: i32 =, + field y: i16 =, + field z: i32 =, + field line1: String =, + field line2: String =, + field line3: String =, + field line4: String =, + } /// ArmSwing is sent by the client when the player left clicks (to swing their /// arm). packet ArmSwing { @@ -290,6 +358,10 @@ state_packets!( packet ArmSwing_Handsfree { field empty: () =, } + packet ArmSwing_Handsfree_ID { + field entity_id: i32 =, + field animation: u8 =, + } /// SpectateTeleport is sent by clients in spectator mode to teleport to a player. packet SpectateTeleport { field target: UUID =, @@ -319,6 +391,16 @@ state_packets!( field cursor_y: u8 =, field cursor_z: u8 =, } + packet PlayerBlockPlacement_u8_Item_u8y { + field x: i32 =, + field y: u8 =, + field z: i32 =, + 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 { @@ -478,6 +560,14 @@ state_packets!( field location: Position =, field direction: u8 =, } + packet SpawnPainting_NoUUID_i32 { + field entity_id: VarInt =, + field title: String =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field direction: i32 =, + } /// SpawnPlayer is used to spawn a player when they are in range of the client. /// This packet alone isn't enough to display the player as the skin and username /// information is in the player information packet. @@ -512,6 +602,20 @@ state_packets!( field current_item: u16 =, field metadata: types::Metadata =, } + packet SpawnPlayer_i32_HeldItem_String { + field entity_id: VarInt =, + field uuid: String =, + field name: String =, + field properties: LenPrefixed =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field yaw: i8 =, + field pitch: i8 =, + field current_item: u16 =, + field metadata: types::Metadata =, + } + /// Animation is sent by the server to play an animation on a specific entity. packet Animation { field entity_id: VarInt =, @@ -528,6 +632,13 @@ state_packets!( field location: Position =, field stage: i8 =, } + packet BlockBreakAnimation_i32 { + field entity_id: VarInt =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field stage: i8 =, + } /// UpdateBlockEntity updates the nbt tag of a block entity in the /// world. packet UpdateBlockEntity { @@ -535,6 +646,14 @@ state_packets!( field action: u8 =, field nbt: Option =, } + packet UpdateBlockEntity_Data { + field x: i32 =, + field y: i16 =, + field z: i32 =, + field action: u8 =, + field data_length: i16 =, + field gzipped_nbt: Vec =, + } /// BlockAction triggers different actions depending on the target block. packet BlockAction { field location: Position =, @@ -542,11 +661,26 @@ state_packets!( field byte2: u8 =, field block_type: VarInt =, } + packet BlockAction_u16 { + field x: i32 =, + field y: u16 =, + field z: i32 =, + field byte1: u8 =, + field byte2: u8 =, + field block_type: VarInt =, + } /// BlockChange is used to update a single block on the client. - packet BlockChange { + packet BlockChange_VarInt { field location: Position =, field block_id: VarInt =, } + packet BlockChange_u8 { + field x: i32 =, + field y: u8 =, + field z: i32 =, + field block_id: VarInt =, + field block_metadata: u8 =, + } /// BossBar displays and/or changes a boss bar that is displayed on the /// top of the client's screen. This is normally used for bosses such as /// the ender dragon or the wither. @@ -578,12 +712,22 @@ state_packets!( /// 0 - Chat message, 1 - System message, 2 - Action bar message field position: u8 =, } + packet ServerMessage_NoPosition { + field message: format::Component =, + } /// MultiBlockChange is used to update a batch of blocks in a single packet. - packet MultiBlockChange { + packet MultiBlockChange_VarInt { field chunk_x: i32 =, field chunk_z: i32 =, field records: LenPrefixed =, } + packet MultiBlockChange_u16 { + field chunk_x: i32 =, + field chunk_z: i32 =, + field record_count: u16 =, + field data_size: i32 =, + field data: Vec =, + } /// ConfirmTransaction notifies the client whether a transaction was successful /// or failed (e.g. due to lag). packet ConfirmTransaction { @@ -606,6 +750,14 @@ state_packets!( field slot_count: u8 =, field entity_id: i32 = when(|p: &WindowOpen| p.ty == "EntityHorse"), } + packet WindowOpen_u8 { + field id: u8 =, + field ty: u8 =, + field title: format::Component =, + field slot_count: u8 =, + field use_provided_window_title: bool =, + field entity_id: i32 = when(|p: &WindowOpen_u8| p.ty == 11), + } /// WindowItems sets every item in a window. packet WindowItems { field id: u8 =, @@ -636,6 +788,10 @@ state_packets!( field channel: String =, field data: Vec =, } + packet PluginMessageClientbound_i16 { + field channel: String =, + field data: LenPrefixedBytes =, + } /// Plays a sound by name on the client packet NamedSoundEffect { field name: String =, @@ -711,6 +867,9 @@ state_packets!( packet KeepAliveClientbound_VarInt { field id: VarInt =, } + packet KeepAliveClientbound_i32 { + field id: i32 =, + } /// ChunkData sends or updates a single chunk on the client. If New is set /// then biome data should be sent too. packet ChunkData { @@ -735,11 +894,25 @@ state_packets!( field bitmask: u16 =, field data: LenPrefixedBytes =, } + packet ChunkData_17 { + field chunk_x: i32 =, + field chunk_z: i32 =, + field new: bool =, + field bitmask: u16 =, + field add_bitmask: u16 =, + field compressed_data: LenPrefixedBytes =, + } packet ChunkDataBulk { field skylight: bool =, field chunk_meta: LenPrefixed =, field chunk_data: Vec =, } + packet ChunkDataBulk_17 { + field chunk_column_count: u16 =, + field data_length: i32 =, + field skylight: bool =, + field chunk_data_and_meta: 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. @@ -749,6 +922,14 @@ state_packets!( field data: i32 =, field disable_relative: bool =, } + packet Effect_u8y { + field effect_id: i32 =, + field x: i32 =, + field y: u8 =, + field z: i32 =, + field data: i32 =, + field disable_relative: bool =, + } /// Particle spawns particles at the target location with the various /// modifiers. packet Particle { @@ -765,6 +946,17 @@ state_packets!( field data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38 || p.particle_id == 46), field data2: VarInt = when(|p: &Particle| p.particle_id == 36), } + packet Particle_Named { + field particle_id: String =, + field x: f32 =, + field y: f32 =, + field z: f32 =, + field offset_x: f32 =, + field offset_y: f32 =, + field offset_z: f32 =, + field speed: f32 =, + field count: i32 =, + } /// JoinGame is sent after completing the login process. This /// sets the initial state for the client. packet JoinGame_i32 { @@ -801,6 +993,14 @@ state_packets!( /// information it displays in F3 mode field reduced_debug_info: bool =, } + packet JoinGame_i8_NoDebug { + field entity_id: i32 =, + field gamemode: u8 =, + field dimension: i8 =, + field difficulty: u8 =, + field max_players: u8 =, + field level_type: String =, + } /// Maps updates a single map's contents packet Maps { field item_damage: VarInt =, @@ -823,6 +1023,10 @@ state_packets!( field z: Option = when(|p: &Maps_NoTracking| p.columns > 0), field data: Option> = when(|p: &Maps_NoTracking| p.columns > 0), } + packet Maps_NoTracking_Data { + field item_damage: VarInt =, + field data: LenPrefixedBytes =, + } /// EntityMove moves the entity with the id by the offsets provided. packet EntityMove_i16 { field entity_id: VarInt =, @@ -838,6 +1042,12 @@ state_packets!( field delta_z: i8 =, field on_ground: bool =, } + packet EntityMove_i8_i32_NoGround { + field entity_id: i32 =, + field delta_x: i8 =, + field delta_y: i8 =, + field delta_z: i8 =, + } /// EntityLookAndMove is a combination of EntityMove and EntityLook. packet EntityLookAndMove_i16 { field entity_id: VarInt =, @@ -857,17 +1067,33 @@ state_packets!( field pitch: i8 =, field on_ground: bool =, } + packet EntityLookAndMove_i8_i32_NoGround { + field entity_id: i32 =, + field delta_x: i8 =, + field delta_y: i8 =, + field delta_z: i8 =, + field yaw: i8 =, + field pitch: i8 =, + } /// EntityLook rotates the entity to the new angles provided. - packet EntityLook { + packet EntityLook_VarInt { field entity_id: VarInt =, field yaw: i8 =, field pitch: i8 =, field on_ground: bool =, } + packet EntityLook_i32_NoGround { + field entity_id: i32 =, + field yaw: i8 =, + field pitch: i8 =, + } /// Entity does nothing. It is a result of subclassing used in Minecraft. packet Entity { field entity_id: VarInt =, } + packet Entity_i32 { + field entity_id: i32 =, + } /// EntityUpdateNBT updates the entity named binary tag. packet EntityUpdateNBT { field entity_id: VarInt =, @@ -886,6 +1112,11 @@ state_packets!( packet SignEditorOpen { field location: Position =, } + packet SignEditorOpen_i32 { + field x: i32 =, + field y: i32 =, + field z: i32 =, + } /// CraftRecipeResponse is a response to CraftRecipeRequest, notifies the UI. packet CraftRecipeResponse { field window_id: u8 =, @@ -912,6 +1143,11 @@ state_packets!( packet PlayerInfo { field inner: packet::PlayerInfoData =, } + packet PlayerInfo_String { + field name: String =, + field online: bool =, + field ping: u16 =, + } /// 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. @@ -937,6 +1173,12 @@ state_packets!( field entity_id: VarInt =, field location: Position =, } + packet EntityUsedBed_i32 { + field entity_id: i32 =, + field x: i32 =, + field y: u8 =, + field z: i32 =, + } packet UnlockRecipes { field action: VarInt =, field crafting_book_open: bool =, @@ -948,11 +1190,18 @@ state_packets!( packet EntityDestroy { field entity_ids: LenPrefixed =, } + packet EntityDestroy_u8 { + field entity_ids: LenPrefixed =, + } /// EntityRemoveEffect removes an effect from an entity. packet EntityRemoveEffect { field entity_id: VarInt =, field effect_id: i8 =, } + packet EntityRemoveEffect_i32 { + field entity_id: i32 =, + field effect_id: i8 =, + } /// ResourcePackSend causes the client to check its cache for the requested /// resource packet and download it if its missing. Once the resource pack /// is obtained the client will use it. @@ -972,6 +1221,10 @@ state_packets!( field entity_id: VarInt =, field head_yaw: i8 =, } + packet EntityHeadLook_i32 { + field entity_id: i32 =, + field head_yaw: i8 =, + } packet EntityStatus { field entity_id: i32 =, field entity_status: i8 =, @@ -1012,6 +1265,10 @@ state_packets!( field entity_id: VarInt =, field metadata: types::Metadata =, } + packet EntityMetadata_i32 { + field entity_id: i32 =, + field metadata: types::Metadata =, + } /// EntityAttach attaches to entities together, either by mounting or leashing. /// -1 can be used at the EntityID to deattach. packet EntityAttach { @@ -1031,6 +1288,12 @@ state_packets!( field velocity_y: i16 =, field velocity_z: i16 =, } + packet EntityVelocity_i32 { + field entity_id: i32 =, + field velocity_x: i16 =, + field velocity_y: i16 =, + field velocity_z: i16 =, + } /// EntityEquipment is sent to display an item on an entity, like a sword /// or armor. Slot 0 is the held item and slots 1 to 4 are boots, leggings /// chestplate and helmet respectively. @@ -1044,18 +1307,33 @@ state_packets!( field slot: u16 =, field item: Option =, } + packet EntityEquipment_u16_i32 { + field entity_id: i32 =, + field slot: u16 =, + field item: Option =, + } /// SetExperience updates the experience bar on the client. packet SetExperience { field experience_bar: f32 =, field level: VarInt =, field total_experience: VarInt =, } + packet SetExperience_i16 { + field experience_bar: f32 =, + field level: i16 =, + field total_experience: i16 =, + } /// UpdateHealth is sent by the server to update the player's health and food. packet UpdateHealth { field health: f32 =, field food: VarInt =, field food_saturation: f32 =, } + packet UpdateHealth_u16 { + field health: f32 =, + field food: u16 =, + field food_saturation: f32 =, + } /// ScoreboardObjective creates/updates a scoreboard objective. packet ScoreboardObjective { field name: String =, @@ -1063,6 +1341,11 @@ state_packets!( field value: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), field ty: String = when(|p: &ScoreboardObjective| p.mode == 0 || p.mode == 2), } + packet ScoreboardObjective_NoMode { + field name: String =, + field value: String =, + field ty: u8 =, + } /// SetPassengers mounts entities to an entity packet SetPassengers { field entity_id: VarInt =, @@ -1081,6 +1364,15 @@ state_packets!( field color: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), field players: Option> = when(|p: &Teams| p.mode == 0 || p.mode == 3 || p.mode == 4), } + packet Teams_NoVisColor { + field name: String =, + field mode: u8 =, + field display_name: Option = when(|p: &Teams_NoVisColor| p.mode == 0 || p.mode == 2), + field prefix: Option = when(|p: &Teams_NoVisColor| p.mode == 0 || p.mode == 2), + field suffix: Option = when(|p: &Teams_NoVisColor| p.mode == 0 || p.mode == 2), + field flags: Option = when(|p: &Teams_NoVisColor| p.mode == 0 || p.mode == 2), + field players: Option> = when(|p: &Teams_NoVisColor| p.mode == 0 || p.mode == 3 || p.mode == 4), + } /// UpdateScore is used to update or remove an item from a scoreboard /// objective. packet UpdateScore { @@ -1089,11 +1381,22 @@ state_packets!( field object_name: String =, field value: Option = when(|p: &UpdateScore| p.action != 1), } + packet UpdateScore_i32 { + field name: String =, + field action: u8 =, + field object_name: String =, + field value: Option = when(|p: &UpdateScore_i32| p.action != 1), + } /// SpawnPosition is sent to change the player's current spawn point. Currently /// only used by the client for the compass. packet SpawnPosition { field location: Position =, } + packet SpawnPosition_i32 { + field x: i32 =, + field y: i32 =, + field z: i32 =, + } /// TimeUpdate is sent to sync the world's time to the client, the client /// will manually tick the time itself so this doesn't need to sent repeatedly /// but if the server or client has issues keeping up this can fall out of sync @@ -1136,6 +1439,15 @@ state_packets!( field line3: format::Component =, field line4: format::Component =, } + packet UpdateSign_u16 { + field x: i32 =, + field y: u16 =, + field z: i32 =, + field line1: format::Component =, + field line2: format::Component =, + field line3: format::Component =, + field line4: format::Component =, + } /// SoundEffect plays the named sound at the target location. packet SoundEffect { field name: VarInt =, @@ -1171,6 +1483,10 @@ state_packets!( field collected_entity_id: VarInt =, field collector_entity_id: VarInt =, } + packet CollectItem_nocount_i32 { + field collected_entity_id: i32 =, + field collector_entity_id: i32 =, + } /// EntityTeleport teleports the entity to the target location. This is /// sent if the entity moves further than EntityMove allows. packet EntityTeleport_f64 { @@ -1191,6 +1507,14 @@ state_packets!( field pitch: i8 =, field on_ground: bool =, } + packet EntityTeleport_i32_i32_NoGround { + field entity_id: i32 =, + field x: i32 =, + field y: i32 =, + field z: i32 =, + field yaw: i8 =, + field pitch: i8 =, + } packet Advancements { field reset_clear: bool =, field mapping: LenPrefixed =, @@ -1202,6 +1526,10 @@ state_packets!( field entity_id: VarInt =, field properties: LenPrefixed =, } + packet EntityProperties_i32 { + field entity_id: i32 =, + field properties: LenPrefixed =, + } /// EntityEffect applies a status effect to an entity for a given duration. packet EntityEffect { field entity_id: VarInt =, @@ -1210,6 +1538,12 @@ state_packets!( field duration: VarInt =, field hide_particles: bool =, } + packet EntityEffect_i32 { + field entity_id: i32 =, + field effect_id: i8 =, + field amplifier: i8 =, + field duration: i16 =, + } } } login Login { @@ -1231,6 +1565,10 @@ state_packets!( /// public key field verify_token: LenPrefixedBytes =, } + packet EncryptionResponse_i16 { + field shared_secret: LenPrefixedBytes =, + field verify_token: LenPrefixedBytes =, + } } clientbound Clientbound { /// LoginDisconnect is sent by the server if there was any issues @@ -1252,6 +1590,11 @@ state_packets!( /// correctly field verify_token: LenPrefixedBytes =, } + packet EncryptionRequest_i16 { + field server_id: String =, + field public_key: LenPrefixedBytes =, + field verify_token: LenPrefixedBytes =, + } /// LoginSuccess is sent by the server if the player successfully /// authenicates with the session servers (online mode) or straight /// after LoginStart (offline mode). @@ -1323,6 +1666,29 @@ state_packets!( } ); +#[derive(Debug, Default)] +pub struct SpawnProperty { + pub name: String, + pub value: String, + pub signature: String, +} + +impl Serializable for SpawnProperty { + fn read_from(buf: &mut R) -> Result { + Ok(SpawnProperty { + name: Serializable::read_from(buf)?, + value: Serializable::read_from(buf)?, + signature: Serializable::read_from(buf)?, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.name.write_to(buf)?; + self.value.write_to(buf)?; + self.signature.write_to(buf) + } +} + #[derive(Debug, Default)] pub struct Statistic { pub name: String, @@ -1625,6 +1991,30 @@ impl Serializable for EntityProperty { } } +#[derive(Debug, Default)] +pub struct EntityProperty_i16 { + pub key: String, + pub value: f64, + pub modifiers: LenPrefixed, +} + +impl Serializable for EntityProperty_i16 { + fn read_from(buf: &mut R) -> Result { + Ok(EntityProperty_i16 { + key: Serializable::read_from(buf)?, + value: Serializable::read_from(buf)?, + modifiers: Serializable::read_from(buf)?, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.key.write_to(buf)?; + self.value.write_to(buf)?; + self.modifiers.write_to(buf) + } +} + + #[derive(Debug, Default)] pub struct PropertyModifier { pub uuid: UUID, diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index 3e71b28..2d022fc 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -7,6 +7,7 @@ mod v1_9_2; mod v1_9; mod v15w39c; mod v1_8_9; +mod v1_7_10; pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { @@ -36,6 +37,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: // 1.8.9 - 1.8 47 => v1_8_9::translate_internal_packet_id(state, dir, id, to_internal), + // 1.7.10 - 1.7.6 + 5 => v1_7_10::translate_internal_packet_id(state, dir, id, to_internal), + _ => panic!("unsupported protocol version"), } } diff --git a/protocol/src/protocol/versions/v15w39c.rs b/protocol/src/protocol/versions/v15w39c.rs index 35847be..edf5c71 100644 --- a/protocol/src/protocol/versions/v15w39c.rs +++ b/protocol/src/protocol/versions/v15w39c.rs @@ -48,12 +48,12 @@ protocol_packet_ids!( 0x08 => BlockBreakAnimation 0x09 => UpdateBlockEntity 0x0a => BlockAction - 0x0b => BlockChange + 0x0b => BlockChange_VarInt 0x0c => BossBar 0x0d => ServerDifficulty 0x0e => TabCompleteReply 0x0f => ServerMessage - 0x10 => MultiBlockChange + 0x10 => MultiBlockChange_VarInt 0x11 => ConfirmTransaction 0x12 => WindowClose 0x13 => WindowOpen @@ -77,7 +77,7 @@ protocol_packet_ids!( 0x25 => Maps 0x26 => EntityMove_i8 0x27 => EntityLookAndMove_i8 - 0x28 => EntityLook + 0x28 => EntityLook_VarInt 0x29 => Entity 0x2a => SignEditorOpen 0x2b => PlayerAbilities diff --git a/protocol/src/protocol/versions/v1_10_2.rs b/protocol/src/protocol/versions/v1_10_2.rs index 0be2deb..e5dc3fd 100644 --- a/protocol/src/protocol/versions/v1_10_2.rs +++ b/protocol/src/protocol/versions/v1_10_2.rs @@ -51,12 +51,12 @@ protocol_packet_ids!( 0x08 => BlockBreakAnimation 0x09 => UpdateBlockEntity 0x0a => BlockAction - 0x0b => BlockChange + 0x0b => BlockChange_VarInt 0x0c => BossBar 0x0d => ServerDifficulty 0x0e => TabCompleteReply 0x0f => ServerMessage - 0x10 => MultiBlockChange + 0x10 => MultiBlockChange_VarInt 0x11 => ConfirmTransaction 0x12 => WindowClose 0x13 => WindowOpen @@ -79,7 +79,7 @@ protocol_packet_ids!( 0x24 => Maps 0x25 => EntityMove_i16 0x26 => EntityLookAndMove_i16 - 0x27 => EntityLook + 0x27 => EntityLook_VarInt 0x28 => Entity 0x29 => VehicleTeleport 0x2a => SignEditorOpen diff --git a/protocol/src/protocol/versions/v1_11_2.rs b/protocol/src/protocol/versions/v1_11_2.rs index fed44e4..1f09b13 100644 --- a/protocol/src/protocol/versions/v1_11_2.rs +++ b/protocol/src/protocol/versions/v1_11_2.rs @@ -51,12 +51,12 @@ protocol_packet_ids!( 0x08 => BlockBreakAnimation 0x09 => UpdateBlockEntity 0x0a => BlockAction - 0x0b => BlockChange + 0x0b => BlockChange_VarInt 0x0c => BossBar 0x0d => ServerDifficulty 0x0e => TabCompleteReply 0x0f => ServerMessage - 0x10 => MultiBlockChange + 0x10 => MultiBlockChange_VarInt 0x11 => ConfirmTransaction 0x12 => WindowClose 0x13 => WindowOpen @@ -79,7 +79,7 @@ protocol_packet_ids!( 0x24 => Maps 0x25 => EntityMove_i16 0x26 => EntityLookAndMove_i16 - 0x27 => EntityLook + 0x27 => EntityLook_VarInt 0x28 => Entity 0x29 => VehicleTeleport 0x2a => SignEditorOpen diff --git a/protocol/src/protocol/versions/v1_12_2.rs b/protocol/src/protocol/versions/v1_12_2.rs index b5765eb..c609790 100644 --- a/protocol/src/protocol/versions/v1_12_2.rs +++ b/protocol/src/protocol/versions/v1_12_2.rs @@ -54,12 +54,12 @@ protocol_packet_ids!( 0x08 => BlockBreakAnimation 0x09 => UpdateBlockEntity 0x0a => BlockAction - 0x0b => BlockChange + 0x0b => BlockChange_VarInt 0x0c => BossBar 0x0d => ServerDifficulty 0x0e => TabCompleteReply 0x0f => ServerMessage - 0x10 => MultiBlockChange + 0x10 => MultiBlockChange_VarInt 0x11 => ConfirmTransaction 0x12 => WindowClose 0x13 => WindowOpen @@ -83,7 +83,7 @@ protocol_packet_ids!( 0x25 => Entity 0x26 => EntityMove_i16 0x27 => EntityLookAndMove_i16 - 0x28 => EntityLook + 0x28 => EntityLook_VarInt 0x29 => VehicleTeleport 0x2a => SignEditorOpen 0x2b => CraftRecipeResponse diff --git a/protocol/src/protocol/versions/v1_7_10.rs b/protocol/src/protocol/versions/v1_7_10.rs new file mode 100644 index 0000000..5ca41d9 --- /dev/null +++ b/protocol/src/protocol/versions/v1_7_10.rs @@ -0,0 +1,127 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => KeepAliveServerbound_i32 + 0x01 => ChatMessage + 0x02 => UseEntity_Handsfree_i32 + 0x03 => Player + 0x04 => PlayerPosition_HeadY + 0x05 => PlayerLook + 0x06 => PlayerPositionLook_HeadY + 0x07 => PlayerDigging_u8_u8y + 0x08 => PlayerBlockPlacement_u8_Item_u8y + 0x09 => HeldItemChange + 0x0a => ArmSwing_Handsfree_ID + 0x0b => PlayerAction_i32 + 0x0c => SteerVehicle_jump_unmount + 0x0d => CloseWindow + 0x0e => ClickWindow_u8 + 0x0f => ConfirmTransactionServerbound + 0x10 => CreativeInventoryAction + 0x11 => EnchantItem + 0x12 => SetSign_i16y + 0x13 => ClientAbilities + 0x14 => TabComplete_NoAssume_NoTarget + 0x15 => ClientSettings_u8_Handsfree_Difficulty + 0x16 => ClientStatus_u8 + 0x17 => PluginMessageServerbound_i16 + } + clientbound Clientbound { + 0x00 => KeepAliveClientbound_i32 + 0x01 => JoinGame_i8_NoDebug + 0x02 => ServerMessage_NoPosition + 0x03 => TimeUpdate + 0x04 => EntityEquipment_u16_i32 + 0x05 => SpawnPosition_i32 + 0x06 => UpdateHealth_u16 + 0x07 => Respawn + 0x08 => TeleportPlayer_NoConfirm + 0x09 => SetCurrentHotbarSlot + 0x0a => EntityUsedBed_i32 + 0x0b => Animation + 0x0c => SpawnPlayer_i32_HeldItem_String + 0x0d => CollectItem_nocount_i32 + 0x0e => SpawnObject_i32_NoUUID + 0x0f => SpawnMob_u8_i32_NoUUID + 0x10 => SpawnPainting_NoUUID_i32 + 0x11 => SpawnExperienceOrb_i32 + 0x12 => EntityVelocity_i32 + 0x13 => EntityDestroy_u8 + 0x14 => Entity_i32 + 0x15 => EntityMove_i8_i32_NoGround + 0x16 => EntityLook_i32_NoGround + 0x17 => EntityLookAndMove_i8_i32_NoGround + 0x18 => EntityTeleport_i32_i32_NoGround + 0x19 => EntityHeadLook_i32 + 0x1a => EntityStatus + 0x1b => EntityAttach_leashed + 0x1c => EntityMetadata_i32 + 0x1d => EntityEffect_i32 + 0x1e => EntityRemoveEffect_i32 + 0x1f => SetExperience_i16 + 0x20 => EntityProperties_i32 + 0x21 => ChunkData_17 + 0x22 => MultiBlockChange_u16 + 0x23 => BlockChange_u8 + 0x24 => BlockAction_u16 + 0x25 => BlockBreakAnimation_i32 + 0x26 => ChunkDataBulk_17 + 0x27 => Explosion + 0x28 => Effect_u8y + 0x29 => NamedSoundEffect_u8_NoCategory + 0x2a => Particle_Named + 0x2b => ChangeGameState + 0x2c => SpawnGlobalEntity_i32 + 0x2d => WindowOpen_u8 + 0x2e => WindowClose + 0x2f => WindowSetSlot + 0x30 => WindowItems + 0x31 => WindowProperty + 0x32 => ConfirmTransaction + 0x33 => UpdateSign_u16 + 0x34 => Maps_NoTracking_Data + 0x35 => UpdateBlockEntity_Data + 0x36 => SignEditorOpen_i32 + 0x37 => Statistics + 0x38 => PlayerInfo_String + 0x39 => PlayerAbilities + 0x3a => TabCompleteReply + 0x3b => ScoreboardObjective_NoMode + 0x3c => UpdateScore_i32 + 0x3d => ScoreboardDisplay + 0x3e => Teams_NoVisColor + 0x3f => PluginMessageClientbound_i16 + 0x40 => Disconnect + } + } + login Login { + serverbound Serverbound { + 0x00 => LoginStart + 0x01 => EncryptionResponse_i16 + } + clientbound Clientbound { + 0x00 => LoginDisconnect + 0x01 => EncryptionRequest_i16 + 0x02 => LoginSuccess + } + } + status Status { + serverbound Serverbound { + 0x00 => StatusRequest + 0x01 => StatusPing + } + clientbound Clientbound { + 0x00 => StatusResponse + 0x01 => StatusPong + } + } +); + + diff --git a/protocol/src/protocol/versions/v1_8_9.rs b/protocol/src/protocol/versions/v1_8_9.rs index 362b93c..b8e531e 100644 --- a/protocol/src/protocol/versions/v1_8_9.rs +++ b/protocol/src/protocol/versions/v1_8_9.rs @@ -58,7 +58,7 @@ protocol_packet_ids!( 0x13 => EntityDestroy 0x14 => Entity 0x15 => EntityMove_i8 - 0x16 => EntityLook + 0x16 => EntityLook_VarInt 0x17 => EntityLookAndMove_i8 0x18 => EntityTeleport_i32 0x19 => EntityHeadLook @@ -70,8 +70,8 @@ protocol_packet_ids!( 0x1f => SetExperience 0x20 => EntityProperties 0x21 => ChunkData_NoEntities_u16 - 0x22 => MultiBlockChange - 0x23 => BlockChange + 0x22 => MultiBlockChange_VarInt + 0x23 => BlockChange_VarInt 0x24 => BlockAction 0x25 => BlockBreakAnimation 0x26 => ChunkDataBulk diff --git a/protocol/src/protocol/versions/v1_9.rs b/protocol/src/protocol/versions/v1_9.rs index 07ccb44..2b598b3 100644 --- a/protocol/src/protocol/versions/v1_9.rs +++ b/protocol/src/protocol/versions/v1_9.rs @@ -51,12 +51,12 @@ protocol_packet_ids!( 0x08 => BlockBreakAnimation 0x09 => UpdateBlockEntity 0x0a => BlockAction - 0x0b => BlockChange + 0x0b => BlockChange_VarInt 0x0c => BossBar 0x0d => ServerDifficulty 0x0e => TabCompleteReply 0x0f => ServerMessage - 0x10 => MultiBlockChange + 0x10 => MultiBlockChange_VarInt 0x11 => ConfirmTransaction 0x12 => WindowClose 0x13 => WindowOpen @@ -79,7 +79,7 @@ protocol_packet_ids!( 0x24 => Maps 0x25 => EntityMove_i16 0x26 => EntityLookAndMove_i16 - 0x27 => EntityLook + 0x27 => EntityLook_VarInt 0x28 => Entity 0x29 => VehicleTeleport 0x2a => SignEditorOpen diff --git a/protocol/src/protocol/versions/v1_9_2.rs b/protocol/src/protocol/versions/v1_9_2.rs index 524f42f..6fc799d 100644 --- a/protocol/src/protocol/versions/v1_9_2.rs +++ b/protocol/src/protocol/versions/v1_9_2.rs @@ -51,12 +51,12 @@ protocol_packet_ids!( 0x08 => BlockBreakAnimation 0x09 => UpdateBlockEntity 0x0a => BlockAction - 0x0b => BlockChange + 0x0b => BlockChange_VarInt 0x0c => BossBar 0x0d => ServerDifficulty 0x0e => TabCompleteReply 0x0f => ServerMessage - 0x10 => MultiBlockChange + 0x10 => MultiBlockChange_VarInt 0x11 => ConfirmTransaction 0x12 => WindowClose 0x13 => WindowOpen @@ -79,7 +79,7 @@ protocol_packet_ids!( 0x24 => Maps 0x25 => EntityMove_i16 0x26 => EntityLookAndMove_i16 - 0x27 => EntityLook + 0x27 => EntityLook_VarInt 0x28 => Entity 0x29 => VehicleTeleport 0x2a => SignEditorOpen From 37e6d962ec974dddf07285c8397aee38633785c3 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sat, 15 Dec 2018 19:56:54 -0800 Subject: [PATCH 118/160] 1.7.10 (5) multiprotocol support (#64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds 1.7.10 protocol version 5 support, a major update with significant changes. Expands https://github.com/iceiix/steven/issues/18 Enhance protocol support * Add v1_7_10 protocol packet structures and IDs * EncryptionRequest/Response i16 variant in login protocol * 1.7.10 slot NBT data parsing * Support both 1.7/1.8+ item::Stack in read_from, using if protocol_verson * 1.7.10 chunk format support, ChunkDataBulk_17 and ChunkData_17 * Extract dirty_chunks_by_bitmask from load_chunks17/18/19 * Implement keepalive i32 handler * Send PlayerPositionLook_HeadY * Send PlayerBlockPlacement_u8_Item_u8y * Handle JoinGame_i8_NoDebug * Handle SpawnPlayer_i32_HeldItem_String * BlockChange_u8, MultiBlockChange_i16, UpdateBlockEntity_Data, EntityMove_i8_i32_NoGround, EntityLook_i32_NoGround, EntityLookAndMove_i8_i32_NoGround * UpdateSign_u16, PlayerInfo_String, EntityDestroy_u8, EntityTeleport_i32_i32_NoGround * Send feet_y = head_y - 1.62, fixes Illegal stance https://wiki.vg/index.php?title=Protocol&oldid=6003#Player_Position > Updates the players XYZ position on the server. If HeadY - FeetY is less than 0.1 or greater than 1.65, the stance is illegal and the client will be kicked with the message “Illegal Stance”. > Absolute feet position, normally HeadY - 1.62. Used to modify the players bounding box when going up stairs, crouching, etc… * Set on_ground = true in entity teleport, fixes bouncing * Implement block change, fix metadata/id packing, bounce _u8 through on_block_change * Implement on_multi_block_change_u16, used with explosions --- protocol/src/item.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/protocol/src/item.rs b/protocol/src/item.rs index 19fa87c..7ed18d6 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -43,11 +43,32 @@ impl Serializable for Option { if id == -1 { return Ok(None); } + let count = buf.read_u8()? as isize; + let damage = buf.read_i16::()? as isize; + + let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; + + let tag: Option = if protocol_version >= 47 { + Serializable::read_from(buf)? + } else { + // 1.7 uses a different slot data format described on https://wiki.vg/index.php?title=Slot_Data&diff=6056&oldid=4753 + let tag_size = buf.read_i16::()?; + if tag_size != -1 { + for _ in 0..tag_size { + let _ = buf.read_u8()?; + } + // TODO: decompress zlib NBT for 1.7 + None + } else { + None + } + }; + Ok(Some(Stack { id: id as isize, - count: buf.read_u8()? as isize, - damage: buf.read_i16::()? as isize, - tag: Serializable::read_from(buf)?, + count, + damage, + tag, })) } fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { @@ -56,6 +77,7 @@ impl Serializable for Option { buf.write_i16::(val.id as i16)?; buf.write_u8(val.count as u8)?; buf.write_i16::(val.damage as i16)?; + // TODO: compress zlib NBT if 1.7 val.tag.write_to(buf)?; } None => buf.write_i16::(-1)?, 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 119/160] 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 + } + } +); + + From 68441320e8fcb6c0cfe2b8337e831d6692ab0c43 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 120/160] 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/item.rs | 32 +++- protocol/src/types/metadata.rs | 299 ++++++++++++++++++++++++++++++++- 2 files changed, 322 insertions(+), 9 deletions(-) diff --git a/protocol/src/item.rs b/protocol/src/item.rs index 7ed18d6..9d791e9 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -21,7 +21,7 @@ use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; pub struct Stack { id: isize, count: isize, - damage: isize, + damage: Option, tag: Option, } @@ -31,7 +31,7 @@ impl Default for Stack { Stack { id: -1, count: 0, - damage: 0, + damage: None, tag: None, } } @@ -39,14 +39,31 @@ impl Default for Stack { impl Serializable for Option { fn read_from(buf: &mut R) -> Result, protocol::Error> { - let id = buf.read_i16::()?; + let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; + + if protocol_version >= 404 { + let present = buf.read_u8()? != 0; + if !present { + return Ok(None) + } + } + + let id = if protocol_version >= 404 { + protocol::VarInt::read_from(buf)?.0 as isize + } else { + buf.read_i16::()? as isize + }; + if id == -1 { return Ok(None); } let count = buf.read_u8()? as isize; - let damage = buf.read_i16::()? as isize; - - let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; + let damage = if protocol_version >= 404 { + // 1.13.2+ stores damage in the NBT + None + } else { + Some(buf.read_i16::()? as isize) + }; let tag: Option = if protocol_version >= 47 { Serializable::read_from(buf)? @@ -74,9 +91,10 @@ impl Serializable for Option { fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { match *self { Some(ref val) => { + // TODO: if protocol_version >= 404, send present and id varint, no damage, for 1.13.2 buf.write_i16::(val.id as i16)?; buf.write_u8(val.count as u8)?; - buf.write_i16::(val.damage as i16)?; + buf.write_i16::(val.damage.unwrap_or(0) as i16)?; // TODO: compress zlib NBT if 1.7 val.tag.write_to(buf)?; } diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index 44ee0a7..a5e8a3e 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -18,6 +18,7 @@ use std::io; use std::fmt; use crate::protocol; use crate::protocol::Serializable; +use crate::protocol::LenPrefixed; use crate::format; use crate::item; use crate::shared::Position; @@ -271,13 +272,151 @@ impl Metadata { u8::write_to(&0xFF, buf)?; Ok(()) } + + fn read_from113(buf: &mut R) -> Result { + let mut m = Self::new(); + loop { + let index = u8::read_from(buf)? as i32; + if index == 0xFF { + break; + } + let ty = protocol::VarInt::read_from(buf)?.0; + match ty { + 0 => m.put_raw(index, i8::read_from(buf)?), + 1 => m.put_raw(index, protocol::VarInt::read_from(buf)?.0), + 2 => m.put_raw(index, f32::read_from(buf)?), + 3 => m.put_raw(index, String::read_from(buf)?), + 4 => m.put_raw(index, format::Component::read_from(buf)?), + 5 => m.put_raw(index, LenPrefixed::::read_from(buf)?), + 6 => m.put_raw(index, Option::::read_from(buf)?), + 7 => m.put_raw(index, bool::read_from(buf)?), + 8 => m.put_raw(index, + [f32::read_from(buf)?, + f32::read_from(buf)?, + f32::read_from(buf)?]), + 9 => m.put_raw(index, Position::read_from(buf)?), + 10 => { + if bool::read_from(buf)? { + m.put_raw(index, Option::::read_from(buf)?); + } else { + m.put_raw::>(index, None); + } + } + 11 => m.put_raw(index, protocol::VarInt::read_from(buf)?), + 12 => { + if bool::read_from(buf)? { + m.put_raw(index, Option::::read_from(buf)?); + } else { + m.put_raw::>(index, None); + } + } + 13 => m.put_raw(index, protocol::VarInt::read_from(buf)?.0 as u16), + 14 => { + let ty = u8::read_from(buf)?; + if ty != 0 { + let name = nbt::read_string(buf)?; + let tag = nbt::Tag::read_from(buf)?; + + m.put_raw(index, nbt::NamedTag(name, tag)); + } + } + 15 => panic!("TODO: particle"), + _ => return Err(protocol::Error::Err("unknown metadata type".to_owned())), + } + } + Ok(m) + } + + fn write_to113(&self, buf: &mut W) -> Result<(), protocol::Error> { + for (k, v) in &self.map { + (*k as u8).write_to(buf)?; + match *v { + Value::Byte(ref val) => { + u8::write_to(&0, buf)?; + val.write_to(buf)?; + } + Value::Int(ref val) => { + u8::write_to(&1, buf)?; + protocol::VarInt(*val).write_to(buf)?; + } + Value::Float(ref val) => { + u8::write_to(&2, buf)?; + val.write_to(buf)?; + } + Value::String(ref val) => { + u8::write_to(&3, buf)?; + val.write_to(buf)?; + } + Value::FormatComponent(ref val) => { + u8::write_to(&4, buf)?; + val.write_to(buf)?; + } + Value::OptionalFormatComponent(ref val) => { + u8::write_to(&5, buf)?; + val.write_to(buf)?; + } + Value::OptionalItemStack(ref val) => { + u8::write_to(&6, buf)?; + val.write_to(buf)?; + } + Value::Bool(ref val) => { + u8::write_to(&7, buf)?; + val.write_to(buf)?; + } + Value::Vector(ref val) => { + u8::write_to(&8, buf)?; + val[0].write_to(buf)?; + val[1].write_to(buf)?; + val[2].write_to(buf)?; + } + Value::Position(ref val) => { + u8::write_to(&9, buf)?; + val.write_to(buf)?; + } + Value::OptionalPosition(ref val) => { + u8::write_to(&10, buf)?; + val.is_some().write_to(buf)?; + val.write_to(buf)?; + } + Value::Direction(ref val) => { + u8::write_to(&11, buf)?; + val.write_to(buf)?; + } + Value::OptionalUUID(ref val) => { + u8::write_to(&12, buf)?; + val.is_some().write_to(buf)?; + val.write_to(buf)?; + } + Value::Block(ref val) => { + u8::write_to(&13, buf)?; + protocol::VarInt(*val as i32).write_to(buf)?; + } + Value::NBTTag(ref _val) => { + u8::write_to(&14, buf)?; + // TODO: write NBT tags metadata + //nbt::Tag(*val).write_to(buf)?; + } + Value::Particle(ref val) => { + u8::write_to(&15, buf)?; + val.write_to(buf)?; + } + _ => panic!("unexpected metadata"), + } + } + u8::write_to(&0xFF, buf)?; + Ok(()) + } + + } impl Serializable for Metadata { fn read_from(buf: &mut R) -> Result { let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; - if protocol_version >= 74 { + if protocol_version >= 404 { + Metadata::read_from113(buf) + } else if protocol_version >= 74 { Metadata::read_from19(buf) } else { Metadata::read_from18(buf) @@ -287,7 +426,9 @@ impl Serializable for Metadata { fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; - if protocol_version >= 74 { + if protocol_version >= 404 { + self.write_to113(buf) + } else if protocol_version >= 74 { self.write_to19(buf) } else { self.write_to18(buf) @@ -319,6 +460,7 @@ pub enum Value { Float(f32), String(String), FormatComponent(format::Component), + OptionalFormatComponent(LenPrefixed), OptionalItemStack(Option), Bool(bool), Vector([f32; 3]), @@ -329,6 +471,146 @@ pub enum Value { OptionalUUID(Option), Block(u16), // TODO: Proper type NBTTag(nbt::NamedTag), + Particle(ParticleData), +} + +#[derive(Debug)] +pub enum ParticleData { + AmbientEntityEffect, + AngryVillager, + Barrier, + Block { + block_state: protocol::VarInt, + }, + Bubble, + Cloud, + Crit, + DamageIndicator, + DragonBreath, + DrippingLava, + DrippingWater, + Dust { + red: f32, + green: f32, + blue: f32, + scale: f32, + }, + Effect, + ElderGuardian, + EnchantedHit, + Enchant, + EndRod, + EntityEffect, + ExplosionEmitter, + Explosion, + FallingDust { + block_state: protocol::VarInt, + }, + Firework, + Fishing, + Flame, + HappyVillager, + Heart, + InstantEffect, + Item { + item: Option, + }, + ItemSlime, + ItemSnowball, + LargeSmoke, + Lava, + Mycelium, + Note, + Poof, + Portal, + Rain, + Smoke, + Spit, + SquidInk, + SweepAttack, + TotemOfUndying, + Underwater, + Splash, + Witch, + BubblePop, + CurrentDown, + BubbleColumnUp, + Nautilus, + Dolphin, +} + +impl Serializable for ParticleData { + fn read_from(buf: &mut R) -> Result { + let id = protocol::VarInt::read_from(buf)?.0; + Ok(match id { + 0 => ParticleData::AmbientEntityEffect, + 1 => ParticleData::AngryVillager, + 2 => ParticleData::Barrier, + 3 => ParticleData::Block { + block_state: Serializable::read_from(buf)? + }, + 4 => ParticleData::Bubble, + 5 => ParticleData::Cloud, + 6 => ParticleData::Crit, + 7 => ParticleData::DamageIndicator, + 8 => ParticleData::DragonBreath, + 9 => ParticleData::DrippingLava, + 10 => ParticleData::DrippingWater, + 11 => ParticleData::Dust { + red: Serializable::read_from(buf)?, + green: Serializable::read_from(buf)?, + blue: Serializable::read_from(buf)?, + scale: Serializable::read_from(buf)?, + }, + 12 => ParticleData::Effect, + 13 => ParticleData::ElderGuardian, + 14 => ParticleData::EnchantedHit, + 15 => ParticleData::Enchant, + 16 => ParticleData::EndRod, + 17 => ParticleData::EntityEffect, + 18 => ParticleData::ExplosionEmitter, + 19 => ParticleData::Explosion, + 20 => ParticleData::FallingDust { + block_state: Serializable::read_from(buf)?, + }, + 21 => ParticleData::Firework, + 22 => ParticleData::Fishing, + 23 => ParticleData::Flame, + 24 => ParticleData::HappyVillager, + 25 => ParticleData::Heart, + 26 => ParticleData::InstantEffect, + 27 => ParticleData::Item { + item: Serializable::read_from(buf)?, + }, + 28 => ParticleData::ItemSlime, + 29 => ParticleData::ItemSnowball, + 30 => ParticleData::LargeSmoke, + 31 => ParticleData::Lava, + 32 => ParticleData::Mycelium, + 33 => ParticleData::Note, + 34 => ParticleData::Poof, + 35 => ParticleData::Portal, + 36 => ParticleData::Rain, + 37 => ParticleData::Smoke, + 38 => ParticleData::Spit, + 39 => ParticleData::SquidInk, + 40 => ParticleData::SweepAttack, + 41 => ParticleData::TotemOfUndying, + 42 => ParticleData::Underwater, + 43 => ParticleData::Splash, + 44 => ParticleData::Witch, + 45 => ParticleData::BubblePop, + 46 => ParticleData::CurrentDown, + 47 => ParticleData::BubbleColumnUp, + 48 => ParticleData::Nautilus, + 49 => ParticleData::Dolphin, + _ => panic!("unrecognized particle data id {}", id), + }) + } + + fn write_to(&self, _buf: &mut W) -> Result<(), protocol::Error> { + unimplemented!() + } } pub trait MetaValue { @@ -408,6 +690,19 @@ impl MetaValue for format::Component { } } +impl MetaValue for LenPrefixed { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::OptionalFormatComponent(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::OptionalFormatComponent(self) + } +} + + impl MetaValue for Option { fn unwrap(value: &Value) -> &Self { match *value { From 3075de291b21b73731c020d29c34d8d5f4ea020d Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Thu, 10 Jan 2019 17:21:19 -0800 Subject: [PATCH 121/160] Add 18w50a (451) multiprotocol support (#79) Adds 18w50a (451) multiprotocol support, last snapshot of 2018 Reference: https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14491 * Use v18w50a module for protocol * Add blasting, smoking, and suspicious stew recipe types * Add entity tags to tags packet * Add chunk data packet variant with height map * Add update light packet * Add chunk format parsing with block_count, without skylights, conditionalize on protocol_version >= 451 * Add villager data entity metadata type parsing https://wiki.vg/Pre-release_protocol#Entity_Metadata * Add open book and entity sound effect packets --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 65 ++++++++ protocol/src/protocol/versions.rs | 5 + protocol/src/protocol/versions/v18w50a.rs | 172 ++++++++++++++++++++++ 4 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 protocol/src/protocol/versions/v18w50a.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index b54cea6..5062d12 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; 10] = [404, 340, 316, 315, 210, 109, 107, 74, 47, 5]; +pub const SUPPORTED_PROTOCOLS: [i32; 11] = [404, 451, 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]; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 9f5c2e1..5a0cf30 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -931,6 +931,15 @@ state_packets!( } /// ChunkData sends or updates a single chunk on the client. If New is set /// then biome data should be sent too. + packet ChunkData_HeightMap { + field chunk_x: i32 =, + field chunk_z: i32 =, + field new: bool =, + field bitmask: VarInt =, + field heightmaps: Option =, + field data: LenPrefixedBytes =, + field block_entities: LenPrefixed> =, + } packet ChunkData { field chunk_x: i32 =, field chunk_z: i32 =, @@ -1166,6 +1175,10 @@ state_packets!( field yaw: f32 =, field pitch: f32 =, } + /// Opens the book GUI. + packet OpenBook { + field hand: VarInt =, + } /// SignEditorOpen causes the client to open the editor for a sign so that /// it can write to it. Only sent in vanilla when the player places a sign. packet SignEditorOpen { @@ -1553,6 +1566,14 @@ state_packets!( field volume: f32 =, field pitch: u8 =, } + /// Plays a sound effect from an entity. + packet EntitySoundEffect { + field sound_id: VarInt =, + field sound_category: VarInt =, + field entity_id: VarInt =, + field volume: f32 =, + field pitch: f32 =, + } /// PlayerListHeaderFooter updates the header/footer of the player list. packet PlayerListHeaderFooter { field header: format::Component =, @@ -1638,6 +1659,20 @@ state_packets!( field item_tags: LenPrefixed =, field fluid_tags: LenPrefixed =, } + packet TagsWithEntities { + field block_tags: LenPrefixed =, + field item_tags: LenPrefixed =, + field fluid_tags: LenPrefixed =, + field entity_tags: LenPrefixed =, + } + packet UpdateLight { + field chunk_x: VarInt =, + field chunk_z: VarInt =, + field sky_light_mask: VarInt =, + field block_light_mask: VarInt =, + field empty_sky_light_mask: VarInt =, + field light_arrays: Vec =, + } } } login Login { @@ -2300,6 +2335,7 @@ pub enum RecipeData { BannerAddPattern, ShieldDecoration, ShulkerBoxColoring, + SuspiciousStew, Smelting { group: String, ingredient: RecipeIngredient, @@ -2307,6 +2343,20 @@ pub enum RecipeData { experience: f32, cooking_time: VarInt, }, + Blasting { + group: String, + ingredient: RecipeIngredient, + result: Option, + experience: f32, + cooking_time: VarInt, + }, + Smoking { + group: String, + ingredient: RecipeIngredient, + result: Option, + experience: f32, + cooking_time: VarInt, + }, } impl Default for RecipeData { @@ -2362,6 +2412,7 @@ impl Serializable for Recipe { "crafting_special_banneraddpattern" => RecipeData::BannerAddPattern, "crafting_special_shielddecoration" => RecipeData::ShieldDecoration, "crafting_special_shulkerboxcoloring" => RecipeData::ShulkerBoxColoring, + "crafting_special_suspiciousstew" => RecipeData::SuspiciousStew, "smelting" => RecipeData::Smelting { group: Serializable::read_from(buf)?, ingredient: Serializable::read_from(buf)?, @@ -2369,6 +2420,20 @@ impl Serializable for Recipe { experience: Serializable::read_from(buf)?, cooking_time: Serializable::read_from(buf)?, }, + "blasting" => RecipeData::Blasting { + 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)?, + }, + "smoking" => RecipeData::Smoking { + 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) }; diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index 5435189..1ba9dae 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -1,5 +1,6 @@ use crate::protocol::*; +mod v18w50a; mod v1_13_2; mod v1_12_2; mod v1_11_2; @@ -14,6 +15,10 @@ 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 + + // 18w50a + 451 => v18w50a::translate_internal_packet_id(state, dir, id, to_internal), + // 1.13.2 404 => v1_13_2::translate_internal_packet_id(state, dir, id, to_internal), diff --git a/protocol/src/protocol/versions/v18w50a.rs b/protocol/src/protocol/versions/v18w50a.rs new file mode 100644 index 0000000..836353e --- /dev/null +++ b/protocol/src/protocol/versions/v18w50a.rs @@ -0,0 +1,172 @@ +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_HeightMap + 0x23 => Effect + 0x24 => Particle + 0x25 => JoinGame_i32 + 0x26 => Maps + 0x27 => Entity + 0x28 => EntityMove_i16 + 0x29 => EntityLookAndMove_i16 + 0x2a => EntityLook_VarInt + 0x2b => VehicleTeleport + 0x2c => OpenBook + 0x2d => SignEditorOpen + 0x2e => CraftRecipeResponse + 0x2f => PlayerAbilities + 0x30 => CombatEvent + 0x31 => PlayerInfo + 0x32 => FacePlayer + 0x33 => TeleportPlayer_WithConfirm + 0x34 => EntityUsedBed + 0x35 => UnlockRecipes_WithSmelting + 0x36 => EntityDestroy + 0x37 => EntityRemoveEffect + 0x38 => ResourcePackSend + 0x39 => Respawn + 0x3a => EntityHeadLook + 0x3b => SelectAdvancementTab + 0x3c => WorldBorder + 0x3d => Camera + 0x3e => SetCurrentHotbarSlot + 0x3f => ScoreboardDisplay + 0x40 => EntityMetadata + 0x41 => EntityAttach + 0x42 => EntityVelocity + 0x43 => EntityEquipment + 0x44 => SetExperience + 0x45 => UpdateHealth + 0x46 => ScoreboardObjective + 0x47 => SetPassengers + 0x48 => Teams + 0x49 => UpdateScore + 0x4a => SpawnPosition + 0x4b => TimeUpdate + 0x4d => StopSound + 0x4e => SoundEffect + 0x4f => EntitySoundEffect + 0x50 => PlayerListHeaderFooter + 0x51 => CollectItem + 0x52 => EntityTeleport_f64 + 0x53 => Advancements + 0x54 => EntityProperties + 0x55 => EntityEffect + 0x56 => DeclareRecipes + 0x57 => TagsWithEntities + 0x58 => UpdateLight + } + } + 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 + } + } +); + + From 264bf6084e20cd9a753aba2973cdcee80993eed7 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Thu, 10 Jan 2019 17:21:19 -0800 Subject: [PATCH 122/160] Add 18w50a (451) multiprotocol support (#79) Adds 18w50a (451) multiprotocol support, last snapshot of 2018 Reference: https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14491 * Use v18w50a module for protocol * Add blasting, smoking, and suspicious stew recipe types * Add entity tags to tags packet * Add chunk data packet variant with height map * Add update light packet * Add chunk format parsing with block_count, without skylights, conditionalize on protocol_version >= 451 * Add villager data entity metadata type parsing https://wiki.vg/Pre-release_protocol#Entity_Metadata * Add open book and entity sound effect packets --- protocol/src/types/metadata.rs | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index a5e8a3e..cb390ff 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -321,6 +321,7 @@ impl Metadata { } } 15 => panic!("TODO: particle"), + 16 => m.put_raw(index, VillagerData::read_from(buf)?), _ => return Err(protocol::Error::Err("unknown metadata type".to_owned())), } } @@ -400,6 +401,10 @@ impl Metadata { u8::write_to(&15, buf)?; val.write_to(buf)?; } + Value::Villager(ref val) => { + u8::write_to(&16, buf)?; + val.write_to(buf)?; + } _ => panic!("unexpected metadata"), } } @@ -472,6 +477,7 @@ pub enum Value { Block(u16), // TODO: Proper type NBTTag(nbt::NamedTag), Particle(ParticleData), + Villager(VillagerData), } #[derive(Debug)] @@ -613,6 +619,27 @@ impl Serializable for ParticleData { } } +#[derive(Debug)] +pub struct VillagerData { + villager_type: protocol::VarInt, + profession: protocol::VarInt, + level: protocol::VarInt, +} + +impl Serializable for VillagerData { + fn read_from(buf: &mut R) -> Result { + let villager_type = protocol::VarInt::read_from(buf)?; + let profession = protocol::VarInt::read_from(buf)?; + let level = protocol::VarInt::read_from(buf)?; + Ok(VillagerData { villager_type, profession, level }) + } + + fn write_to(&self, _buf: &mut W) -> Result<(), protocol::Error> { + unimplemented!() + } +} + + pub trait MetaValue { fn unwrap(_: &Value) -> &Self; fn wrap(self) -> Value; @@ -823,6 +850,18 @@ impl MetaValue for nbt::NamedTag { } } +impl MetaValue for VillagerData { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Villager(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Villager(self) + } +} + #[cfg(test)] mod test { use super::*; From 19057ed2a007915aa87abd557284fc1df40c10b6 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Thu, 10 Jan 2019 17:47:07 -0800 Subject: [PATCH 123/160] Add 19w02a (452) multiprotocol support (#82) Adds support for the 19w02a (451) protocol, yesterday's snapshot. Builds on https://github.com/iceiix/steven/pull/79 18w50a Closer to https://github.com/iceiix/steven/issues/72 1.14 protocol support Updates https://github.com/iceiix/steven/issues/18 Enhance protocol support * Add 19w02a (452) protocol * Add campfire recipe type * Add trade list new packet, and window open variants --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 58 ++++++++ protocol/src/protocol/versions.rs | 4 + protocol/src/protocol/versions/v19w02a.rs | 174 ++++++++++++++++++++++ 4 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 protocol/src/protocol/versions/v19w02a.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 5062d12..2b82b0a 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; 11] = [404, 451, 340, 316, 315, 210, 109, 107, 74, 47, 5]; +pub const SUPPORTED_PROTOCOLS: [i32; 12] = [404, 451, 452, 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]; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 5a0cf30..2e5576b 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -809,6 +809,11 @@ state_packets!( field slot_count: u8 =, field entity_id: i32 = when(|p: &WindowOpen| p.ty == "EntityHorse"), } + packet WindowOpenHorse { + field window_id: u8 =, + field number_of_slots: VarInt =, + field entity_id: i32 =, + } packet WindowOpen_u8 { field id: u8 =, field ty: u8 =, @@ -817,6 +822,11 @@ state_packets!( field use_provided_window_title: bool =, field entity_id: i32 = when(|p: &WindowOpen_u8| p.ty == 11), } + packet WindowOpen_VarInt { + field id: VarInt =, + field ty: VarInt =, + field title: format::Component =, + } /// WindowItems sets every item in a window. packet WindowItems { field id: u8 =, @@ -1673,6 +1683,10 @@ state_packets!( field empty_sky_light_mask: VarInt =, field light_arrays: Vec =, } + packet TradeList { + field id: VarInt =, + field trades: LenPrefixed =, + } } } login Login { @@ -2357,6 +2371,13 @@ pub enum RecipeData { experience: f32, cooking_time: VarInt, }, + Campfire { + group: String, + ingredient: RecipeIngredient, + result: Option, + experience: f32, + cooking_time: VarInt, + }, } impl Default for RecipeData { @@ -2434,6 +2455,13 @@ impl Serializable for Recipe { experience: Serializable::read_from(buf)?, cooking_time: Serializable::read_from(buf)?, }, + "campfire" => RecipeData::Campfire { + 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) }; @@ -2464,6 +2492,36 @@ impl Serializable for Tags { } } +#[derive(Debug, Default)] +pub struct Trade { + pub input_item_1: Option, + pub output_item: Option, + pub has_second_item: bool, + pub input_item_2: Option, + pub trades_disabled: bool, + pub tool_uses: i32, + pub max_trade_uses: i32, +} + +impl Serializable for Trade { + fn read_from(buf: &mut R) -> Result { + Ok(Trade { + input_item_1: Serializable::read_from(buf)?, + output_item: Serializable::read_from(buf)?, + has_second_item: Serializable::read_from(buf)?, + input_item_2: Serializable::read_from(buf)?, + trades_disabled: Serializable::read_from(buf)?, + tool_uses: Serializable::read_from(buf)?, + max_trade_uses: Serializable::read_from(buf)?, + }) + } + + fn write_to(&self, _: &mut W) -> Result<(), Error> { + unimplemented!() + } +} + + #[derive(Debug, Default)] pub struct CommandNode { pub flags: u8, diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index 1ba9dae..d80b2a6 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -1,5 +1,6 @@ use crate::protocol::*; +mod v19w02a; mod v18w50a; mod v1_13_2; mod v1_12_2; @@ -15,6 +16,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 + + // 19w02a + 452 => v19w02a::translate_internal_packet_id(state, dir, id, to_internal), // 18w50a 451 => v18w50a::translate_internal_packet_id(state, dir, id, to_internal), diff --git a/protocol/src/protocol/versions/v19w02a.rs b/protocol/src/protocol/versions/v19w02a.rs new file mode 100644 index 0000000..edbce6a --- /dev/null +++ b/protocol/src/protocol/versions/v19w02a.rs @@ -0,0 +1,174 @@ +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 => WindowOpenHorse + 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_HeightMap + 0x23 => Effect + 0x24 => Particle + 0x25 => JoinGame_i32 + 0x26 => Maps + 0x27 => Entity + 0x28 => EntityMove_i16 + 0x29 => EntityLookAndMove_i16 + 0x2a => EntityLook_VarInt + 0x2b => VehicleTeleport + 0x2c => OpenBook + 0x2d => SignEditorOpen + 0x2e => CraftRecipeResponse + 0x2f => PlayerAbilities + 0x30 => CombatEvent + 0x31 => PlayerInfo + 0x32 => FacePlayer + 0x33 => TeleportPlayer_WithConfirm + 0x34 => EntityUsedBed + 0x35 => UnlockRecipes_WithSmelting + 0x36 => EntityDestroy + 0x37 => EntityRemoveEffect + 0x38 => ResourcePackSend + 0x39 => Respawn + 0x3a => EntityHeadLook + 0x3b => SelectAdvancementTab + 0x3c => WorldBorder + 0x3d => Camera + 0x3e => SetCurrentHotbarSlot + 0x3f => ScoreboardDisplay + 0x40 => EntityMetadata + 0x41 => EntityAttach + 0x42 => EntityVelocity + 0x43 => EntityEquipment + 0x44 => SetExperience + 0x45 => UpdateHealth + 0x46 => ScoreboardObjective + 0x47 => SetPassengers + 0x48 => Teams + 0x49 => UpdateScore + 0x4a => SpawnPosition + 0x4b => TimeUpdate + 0x4d => StopSound + 0x4e => SoundEffect + 0x4f => EntitySoundEffect + 0x50 => PlayerListHeaderFooter + 0x51 => CollectItem + 0x52 => EntityTeleport_f64 + 0x53 => Advancements + 0x54 => EntityProperties + 0x55 => EntityEffect + 0x56 => DeclareRecipes + 0x57 => TagsWithEntities + 0x58 => UpdateLight + 0x59 => WindowOpen_VarInt + 0x5a => TradeList + } + } + 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 + } + } +); + + From ed10ca43b8313df46cc69221b65002a72ef3e077 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 26 Jan 2019 13:39:25 -0800 Subject: [PATCH 124/160] Add 1.13.2+ protocol parsing Particle packet variant. Fixes #104 Pre-1.13.2 particle packet: https://wiki.vg/index.php?title=Protocol&oldid=14204#Particle_2 1.13.2 and later: https://wiki.vg/Protocol#Particle_2 + https://wiki.vg/Protocol#Particle Split into Particle_Data (1.13.2+) and Particle_VarIntArray (pre-1.13.2) Fixes crash when landing when dropping from creative flight, among other crashes when a particle packet is sent. --- protocol/src/protocol/packet.rs | 24 ++++++++++++++++++++--- protocol/src/protocol/versions/v15w39c.rs | 2 +- protocol/src/protocol/versions/v18w50a.rs | 2 +- protocol/src/protocol/versions/v19w02a.rs | 2 +- protocol/src/protocol/versions/v1_10_2.rs | 2 +- protocol/src/protocol/versions/v1_11_2.rs | 2 +- protocol/src/protocol/versions/v1_12_2.rs | 2 +- protocol/src/protocol/versions/v1_13_2.rs | 2 +- protocol/src/protocol/versions/v1_8_9.rs | 2 +- protocol/src/protocol/versions/v1_9.rs | 2 +- protocol/src/protocol/versions/v1_9_2.rs | 2 +- 11 files changed, 31 insertions(+), 13 deletions(-) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 2e5576b..90dc89f 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1010,7 +1010,7 @@ state_packets!( } /// Particle spawns particles at the target location with the various /// modifiers. - packet Particle { + packet Particle_Data { field particle_id: i32 =, field long_distance: bool =, field x: f32 =, @@ -1021,8 +1021,26 @@ state_packets!( field offset_z: f32 =, field speed: f32 =, field count: i32 =, - field data1: VarInt = when(|p: &Particle| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38 || p.particle_id == 46), - field data2: VarInt = when(|p: &Particle| p.particle_id == 36), + field block_state: VarInt = when(|p: &Particle_Data| p.particle_id == 3 || p.particle_id == 20), + field red: f32 = when(|p: &Particle_Data| p.particle_id == 11), + field green: f32 = when(|p: &Particle_Data| p.particle_id == 11), + field blue: f32 = when(|p: &Particle_Data| p.particle_id == 11), + field scale: f32 = when(|p: &Particle_Data| p.particle_id == 11), + field item: Option = when(|p: &Particle_Data| p.particle_id == 27), + } + packet Particle_VarIntArray { + field particle_id: i32 =, + field long_distance: bool =, + field x: f32 =, + field y: f32 =, + field z: f32 =, + field offset_x: f32 =, + field offset_y: f32 =, + field offset_z: f32 =, + field speed: f32 =, + field count: i32 =, + field data1: VarInt = when(|p: &Particle_VarIntArray| p.particle_id == 36 || p.particle_id == 37 || p.particle_id == 38 || p.particle_id == 46), + field data2: VarInt = when(|p: &Particle_VarIntArray| p.particle_id == 36), } packet Particle_Named { field particle_id: String =, diff --git a/protocol/src/protocol/versions/v15w39c.rs b/protocol/src/protocol/versions/v15w39c.rs index edf5c71..f78c0bd 100644 --- a/protocol/src/protocol/versions/v15w39c.rs +++ b/protocol/src/protocol/versions/v15w39c.rs @@ -71,7 +71,7 @@ protocol_packet_ids!( 0x1f => KeepAliveClientbound_VarInt 0x20 => ChunkData_NoEntities 0x21 => Effect - 0x22 => Particle + 0x22 => Particle_VarIntArray 0x23 => NamedSoundEffect_u8_NoCategory 0x24 => JoinGame_i8 0x25 => Maps diff --git a/protocol/src/protocol/versions/v18w50a.rs b/protocol/src/protocol/versions/v18w50a.rs index 836353e..93e4628 100644 --- a/protocol/src/protocol/versions/v18w50a.rs +++ b/protocol/src/protocol/versions/v18w50a.rs @@ -89,7 +89,7 @@ protocol_packet_ids!( 0x21 => KeepAliveClientbound_i64 0x22 => ChunkData_HeightMap 0x23 => Effect - 0x24 => Particle + 0x24 => Particle_VarIntArray 0x25 => JoinGame_i32 0x26 => Maps 0x27 => Entity diff --git a/protocol/src/protocol/versions/v19w02a.rs b/protocol/src/protocol/versions/v19w02a.rs index edbce6a..e8259c2 100644 --- a/protocol/src/protocol/versions/v19w02a.rs +++ b/protocol/src/protocol/versions/v19w02a.rs @@ -89,7 +89,7 @@ protocol_packet_ids!( 0x21 => KeepAliveClientbound_i64 0x22 => ChunkData_HeightMap 0x23 => Effect - 0x24 => Particle + 0x24 => Particle_Data 0x25 => JoinGame_i32 0x26 => Maps 0x27 => Entity diff --git a/protocol/src/protocol/versions/v1_10_2.rs b/protocol/src/protocol/versions/v1_10_2.rs index e5dc3fd..85dc83a 100644 --- a/protocol/src/protocol/versions/v1_10_2.rs +++ b/protocol/src/protocol/versions/v1_10_2.rs @@ -74,7 +74,7 @@ protocol_packet_ids!( 0x1f => KeepAliveClientbound_VarInt 0x20 => ChunkData 0x21 => Effect - 0x22 => Particle + 0x22 => Particle_VarIntArray 0x23 => JoinGame_i32 0x24 => Maps 0x25 => EntityMove_i16 diff --git a/protocol/src/protocol/versions/v1_11_2.rs b/protocol/src/protocol/versions/v1_11_2.rs index 1f09b13..3db3c06 100644 --- a/protocol/src/protocol/versions/v1_11_2.rs +++ b/protocol/src/protocol/versions/v1_11_2.rs @@ -74,7 +74,7 @@ protocol_packet_ids!( 0x1f => KeepAliveClientbound_VarInt 0x20 => ChunkData 0x21 => Effect - 0x22 => Particle + 0x22 => Particle_VarIntArray 0x23 => JoinGame_i32 0x24 => Maps 0x25 => EntityMove_i16 diff --git a/protocol/src/protocol/versions/v1_12_2.rs b/protocol/src/protocol/versions/v1_12_2.rs index 7dc1454..ebd23a0 100644 --- a/protocol/src/protocol/versions/v1_12_2.rs +++ b/protocol/src/protocol/versions/v1_12_2.rs @@ -77,7 +77,7 @@ protocol_packet_ids!( 0x1f => KeepAliveClientbound_i64 0x20 => ChunkData 0x21 => Effect - 0x22 => Particle + 0x22 => Particle_VarIntArray 0x23 => JoinGame_i32 0x24 => Maps 0x25 => Entity diff --git a/protocol/src/protocol/versions/v1_13_2.rs b/protocol/src/protocol/versions/v1_13_2.rs index 3f3c9da..659ed85 100644 --- a/protocol/src/protocol/versions/v1_13_2.rs +++ b/protocol/src/protocol/versions/v1_13_2.rs @@ -89,7 +89,7 @@ protocol_packet_ids!( 0x21 => KeepAliveClientbound_i64 0x22 => ChunkData 0x23 => Effect - 0x24 => Particle + 0x24 => Particle_Data 0x25 => JoinGame_i32 0x26 => Maps 0x27 => Entity diff --git a/protocol/src/protocol/versions/v1_8_9.rs b/protocol/src/protocol/versions/v1_8_9.rs index b8e531e..6dc9682 100644 --- a/protocol/src/protocol/versions/v1_8_9.rs +++ b/protocol/src/protocol/versions/v1_8_9.rs @@ -78,7 +78,7 @@ protocol_packet_ids!( 0x27 => Explosion 0x28 => Effect 0x29 => NamedSoundEffect_u8_NoCategory - 0x2a => Particle + 0x2a => Particle_VarIntArray 0x2b => ChangeGameState 0x2c => SpawnGlobalEntity_i32 0x2d => WindowOpen diff --git a/protocol/src/protocol/versions/v1_9.rs b/protocol/src/protocol/versions/v1_9.rs index 2b598b3..9ce2b03 100644 --- a/protocol/src/protocol/versions/v1_9.rs +++ b/protocol/src/protocol/versions/v1_9.rs @@ -74,7 +74,7 @@ protocol_packet_ids!( 0x1f => KeepAliveClientbound_VarInt 0x20 => ChunkData_NoEntities 0x21 => Effect - 0x22 => Particle + 0x22 => Particle_VarIntArray 0x23 => JoinGame_i8 0x24 => Maps 0x25 => EntityMove_i16 diff --git a/protocol/src/protocol/versions/v1_9_2.rs b/protocol/src/protocol/versions/v1_9_2.rs index 6fc799d..4aec3d0 100644 --- a/protocol/src/protocol/versions/v1_9_2.rs +++ b/protocol/src/protocol/versions/v1_9_2.rs @@ -74,7 +74,7 @@ protocol_packet_ids!( 0x1f => KeepAliveClientbound_VarInt 0x20 => ChunkData_NoEntities 0x21 => Effect - 0x22 => Particle + 0x22 => Particle_VarIntArray 0x23 => JoinGame_i32 0x24 => Maps 0x25 => EntityMove_i16 From 04d9fed5c5cf68f54f4c6da61d093d5206739d2d Mon Sep 17 00:00:00 2001 From: Bart Ribbers Date: Sat, 23 Feb 2019 18:02:04 +0100 Subject: [PATCH 125/160] Replace trim_left_matches() (deprecated) for trim_start_matches() (#110) --- protocol/src/protocol/mojang.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index d554b04..078488c 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -113,7 +113,7 @@ impl Profile { twos_compliment(&mut hash); } let hash_str = hash.iter().map(|b| format!("{:02x}", b)).collect::>().join(""); - let hash_val = hash_str.trim_left_matches('0'); + let hash_val = hash_str.trim_start_matches('0'); let hash_str = if negative { "-".to_owned() + &hash_val[..] } else { From 096c4c6fc68d229daf67d10c673e1d0bbf0d7afd Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 3 Mar 2019 08:32:36 -0800 Subject: [PATCH 126/160] Add support for compiling WebAssembly wasm32-unknown-unknown target (#92) Note this only is the first step in web support, although the project compiles, it doesn't run! Merging now to avoid branch divergence, until dependencies can be updated for wasm support. * Add instructions to build for wasm32-unknown-unknown with wasm-pack in www/ * Update to rust-clipboard fork to compile with emscripten https://github.com/aweinstock314/rust-clipboard/pull/62 * Exclude reqwest dependency in wasm32 * Exclude compiling clipboard pasting on wasm32 * Exclude reqwest-using code from wasm32 * Install wasm target with rustup in Travis CI * Update to collision 0.19.0 Fixes wasm incompatibility in deprecated rustc-serialize crate: https://github.com/rustgd/collision-rs/issues/106 error[E0046]: not all trait items implemented, missing: `encode` --> github.com-1ecc6299db9ec823/rustc-serialize-0.3.24/src/serialize.rs:1358:1 * Increase travis_wait time even further, try 120 minutes * Set RUST_BACKTRACE=1 in main * Remove unused unneeded bzip2 features in zip crate To fix wasm32-unknown-unknown target compile error: error[E0432]: unresolved imports `libc::c_int`, `libc::c_uint`, `libc::c_void`, `libc::c_char` --> src/github.com-1ecc6299db9ec823/bzip2-sys-0.1.7/lib.rs:5:12 | 5 | use libc::{c_int, c_uint, c_void, c_char}; | ^^^^^ ^^^^^^ ^^^^^^ ^^^^^^ no `c_char` in the root | | | | | | | no `c_void` in the root | | no `c_uint` in the root | no `c_int` in the root * flate2 use Rust backend * Add console_error_panic_hook module for wasm backtraces * Build using wasm-pack, wasm-bindgen, run with wasm-app * Update to miniz_oxide 0.2.1, remove patch for https://github.com/Frommi/miniz_oxide/issues/42 * Update to official clipboard crate since https://github.com/aweinstock314/rust-clipboard/pull/62 was merged, but git revision pending release * Update to branch of glutin attempting to build for wasm https://github.com/iceiix/glutin/pull/1 * Update winit dependency of glutin to git master https://github.com/iceiix/winit/pull/2 * Update to glutin branch with working (compiles, doesn't run) wasm_stub * Add app name in title on web page * Add wasm to Travis-CI test matrix * Update glutin to fix Windows EGL compilation on AppVeyor https://github.com/iceiix/glutin/pull/1/commits/97797352b5242436cb82d8ecfb44242b69766e4c --- protocol/src/protocol/mod.rs | 5 +++++ protocol/src/protocol/mojang.rs | 2 ++ 2 files changed, 7 insertions(+) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 2b82b0a..71f52ce 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -19,6 +19,7 @@ use aes::Aes128; use cfb8::Cfb8; use cfb8::stream_cipher::{NewStreamCipher, StreamCipher}; use serde_json; +#[cfg(not(target_arch = "wasm32"))] use reqwest; pub mod mojang; @@ -764,6 +765,7 @@ pub enum Error { Disconnect(format::Component), IOError(io::Error), Json(serde_json::Error), + #[cfg(not(target_arch = "wasm32"))] Reqwest(reqwest::Error), } @@ -779,6 +781,7 @@ impl convert::From for Error { } } +#[cfg(not(target_arch = "wasm32"))] impl convert::From for Error { fn from(e: reqwest::Error) -> Error { Error::Reqwest(e) @@ -792,6 +795,7 @@ impl ::std::error::Error for Error { Error::Disconnect(_) => "Disconnect", Error::IOError(ref e) => e.description(), Error::Json(ref e) => e.description(), + #[cfg(not(target_arch = "wasm32"))] Error::Reqwest(ref e) => e.description(), } } @@ -804,6 +808,7 @@ impl ::std::fmt::Display for Error { Error::Disconnect(ref val) => write!(f, "{}", val), Error::IOError(ref e) => e.fmt(f), Error::Json(ref e) => e.fmt(f), + #[cfg(not(target_arch = "wasm32"))] Error::Reqwest(ref e) => e.fmt(f), } } diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 078488c..8a11e75 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -14,6 +14,7 @@ use sha1::{self, Digest}; use serde_json::json; +#[cfg(not(target_arch = "wasm32"))] use reqwest; #[derive(Clone, Debug)] @@ -28,6 +29,7 @@ const LOGIN_URL: &str = "https://authserver.mojang.com/authenticate"; const REFRESH_URL: &str = "https://authserver.mojang.com/refresh"; const VALIDATE_URL: &str = "https://authserver.mojang.com/validate"; +#[cfg(not(target_arch = "wasm32"))] impl Profile { pub fn login(username: &str, password: &str, token: &str) -> Result { let req_msg = json!({ From e44511b62f40b14bbd6d50b32cd5b87966608d6f Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 17 Mar 2019 15:22:42 -0700 Subject: [PATCH 127/160] Add entity_summon and other missing CommandData nodes. Fix #119 Match to https://github.com/SpigotMC/BungeeCord/blob/4428409d41acef5495c6d41c9640c99dbb0d6468/protocol/src/main/java/net/md_5/bungee/protocol/packet/Commands.java#L514 Not all were documented on https://wiki.vg/Command_Data --- protocol/src/protocol/packet.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 90dc89f..41da4ef 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -2584,6 +2584,7 @@ pub enum CommandProperty { }, GameProfile, BlockPos, + ColumnPos, Vec3, Vec2, BlockState, @@ -2614,7 +2615,11 @@ pub enum CommandProperty { Range { decimals: bool, }, + IntRange, + FloatRange, ItemEnchantment, + EntitySummon, + Dimension, } @@ -2679,6 +2684,7 @@ impl Serializable for CommandNode { }, "minecraft:game_profile" => CommandProperty::GameProfile, "minecraft:block_pos" => CommandProperty::BlockPos, + "minecraft:column_pos" => CommandProperty::ColumnPos, "minecraft:vec3" => CommandProperty::Vec3, "minecraft:vec2" => CommandProperty::Vec2, "minecraft:block_state" => CommandProperty::BlockState, @@ -2709,7 +2715,11 @@ impl Serializable for CommandNode { "minecraft:range" => { CommandProperty::Range { decimals: Serializable::read_from(buf)? } }, + "minecraft:int_range" => CommandProperty::IntRange, + "minecraft:float_range" => CommandProperty::FloatRange, "minecraft:item_enchantment" => CommandProperty::ItemEnchantment, + "minecraft:entity_summon" => CommandProperty::EntitySummon, + "minecraft:dimension" => CommandProperty::Dimension, _ => panic!("unsupported command node parser {}", parse), }) } else { From 65ddb3b898966d2e66d25f5cd88e70195654300e Mon Sep 17 00:00:00 2001 From: ice_iix Date: Tue, 30 Apr 2019 19:20:32 -0700 Subject: [PATCH 128/160] Improve error reporting of invalid UTF-8 when deserializing strings std::io::Read read_to_string() [1] reports this uninformative error: thread 'main' panicked at 'Err: IOError(Custom { kind: InvalidData, error: StringError("stream did not contain valid UTF-8") })', src/server/mod.rs:442:33 Instead of read_to_string(), use read_to_end() to read into a buffer, then convert using String::from_utf8() and unwrap it. This gives a better error message when UTF-8 fails to decode: thread '' panicked at 'called Result::unwrap() on an Err value: FromUtf8Error { bytes: [105, 110, 101, 99, 114, 97, 102, 116, 58, 99, 114, 97, 102, 116, 105, 110, 103, 95, 115, 104, 97, 112, 101, 100, 20, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 98, 111, 110, 101, 95, 98, 108, 111, 99, 107, 3, 3, 0, 1, 1, 134, 5, 1, 0, 1, 1, 134, 5, 1, 0, 1, 1, 134, 5, 1, 0, 1, 1, 134, 5, 1, 0, 1, 1, 134, 5, 1, 0, 1, 1, 134, 5, 1, 0, 1, 1, 134, 5, 1, 0, 1, 1, 134, 5, 1, 0, 1, 1, 134, 5, 1, 0, 1, 249, 2, 1, 0, 25, 109], error: Utf8Error { valid_up_to: 50, error_len: Some(1) } }', src/libcore/result.rs:1009:5 which is helpful for tracking down protocol errors, such as updating to a new protocol (developed for GH-132 / GH-72). [1] https://doc.rust-lang.org/nightly/std/io/trait.Read.html#method.read_to_string [2] https://doc.rust-lang.org/std/string/struct.String.html#method.from_utf8 --- protocol/src/protocol/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 71f52ce..bb91423 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -257,8 +257,9 @@ impl Serializable for String { let len = VarInt::read_from(buf)?.0; debug_assert!(len >= 0, "Negative string length: {}", len); debug_assert!(len <= 65536, "String length too big: {}", len); - let mut ret = String::new(); - buf.take(len as u64).read_to_string(&mut ret)?; + let mut bytes = Vec::::new(); + buf.take(len as u64).read_to_end(&mut bytes)?; + let ret = String::from_utf8(bytes).unwrap(); Result::Ok(ret) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { From f935afdeac143de9db3206cc57ab7e07d92ae570 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 4 May 2019 16:01:28 -0700 Subject: [PATCH 129/160] 1.14 protocol support (477) (#132). Closes #72 Adds 1.14 (477) protocol support, based on: https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14723 * New packets: SetDifficulty, LockDifficulty, UpdateJigsawBlock, UpdateViewPosition, UpdateViewDistance * New metadata: Optional VarInt (17) and Pose (18) * Add new join game variant with view distance, without difficulty * Add new server difficulty variant, with locked boolean * Implement recipe parsing changes, add stonecutting recipe type --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 85 ++++++++++- protocol/src/protocol/versions.rs | 3 + protocol/src/protocol/versions/v1_14.rs | 180 ++++++++++++++++++++++++ 4 files changed, 266 insertions(+), 4 deletions(-) create mode 100644 protocol/src/protocol/versions/v1_14.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index bb91423..6ac2f39 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -38,7 +38,7 @@ use flate2::Compression; use std::time::{Instant, Duration}; use crate::shared::Position; -pub const SUPPORTED_PROTOCOLS: [i32; 12] = [404, 451, 452, 340, 316, 315, 210, 109, 107, 74, 47, 5]; +pub const SUPPORTED_PROTOCOLS: [i32; 13] = [477, 452, 451, 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]; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 41da4ef..6b75acb 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -57,6 +57,9 @@ state_packets!( field transaction_id: VarInt =, field location: Position =, } + packet SetDifficulty { + field new_difficulty: u8 =, + } /// TabComplete is sent by the client when the client presses tab in /// the chat box. packet TabComplete { @@ -202,6 +205,9 @@ state_packets!( packet KeepAliveServerbound_i32 { field id: i32 =, } + packet LockDifficulty { + field locked: bool =, + } /// PlayerPosition is used to update the player's position. packet PlayerPosition { field x: f64 =, @@ -370,6 +376,12 @@ state_packets!( field slot: i16 =, field clicked_item: Option =, } + packet UpdateJigsawBlock { + field location: Position =, + field attachment_type: String =, + field target_pool: String =, + field final_state: String =, + } packet UpdateStructureBlock { field location: Position =, field action: VarInt =, @@ -753,6 +765,10 @@ state_packets!( packet ServerDifficulty { field difficulty: u8 =, } + packet ServerDifficulty_Locked { + field difficulty: u8 =, + field locked: bool =, + } /// TabCompleteReply is sent as a reply to a tab completion request. /// The matches should be possible completions for the command/chat the /// player sent. @@ -1055,6 +1071,23 @@ state_packets!( } /// JoinGame is sent after completing the login process. This /// sets the initial state for the client. + packet JoinGame_i32_ViewDistance { + /// The entity id the client will be referenced by + field entity_id: i32 =, + /// The starting gamemode of the client + field gamemode: u8 =, + /// The dimension the client is starting in + field dimension: i32 =, + /// The max number of players on the server + field max_players: u8 =, + /// The level type of the server + field level_type: String =, + /// The render distance (2-32) + field view_distance: VarInt =, + /// Whether the client should reduce the amount of debug + /// information it displays in F3 mode + field reduced_debug_info: bool =, + } packet JoinGame_i32 { /// The entity id the client will be referenced by field entity_id: i32 =, @@ -1377,6 +1410,15 @@ state_packets!( packet SetCurrentHotbarSlot { field slot: u8 =, } + /// UpdateViewPosition is used to determine what chunks should be remain loaded. + packet UpdateViewPosition { + field chunk_x: VarInt =, + field chunk_z: VarInt =, + } + /// UpdateViewDistance is sent by the integrated server when changing render distance. + packet UpdateViewDistance { + field view_distance: VarInt =, + } /// ScoreboardDisplay is used to set the display position of a scoreboard. packet ScoreboardDisplay { field position: u8 =, @@ -2396,6 +2438,11 @@ pub enum RecipeData { experience: f32, cooking_time: VarInt, }, + Stonecutting { + group: String, + ingredient: RecipeIngredient, + result: Option, + }, } impl Default for RecipeData { @@ -2413,8 +2460,35 @@ pub struct Recipe { impl Serializable for Recipe { fn read_from(buf: &mut R) -> Result { - let id = String::read_from(buf)?; - let ty = String::read_from(buf)?; + let (id, ty, namespace) = { + let a = String::read_from(buf)?; + let b = String::read_from(buf)?; + + let protocol_version = unsafe { crate::protocol::CURRENT_PROTOCOL_VERSION }; + + // 1.14+ swaps recipe identifier and type, and adds namespace to type + if protocol_version >= 477 { + let ty = a; + let id = b; + + if let Some(at) = ty.find(':') { + let (namespace, ty) = ty.split_at(at + 1); + let ty: String = ty.into(); + let namespace: String = namespace.into(); + (id, ty, namespace) + } else { + (id, ty, "minecraft:".to_string()) + } + } else { + let ty = b; + let id = a; + (id, ty, "minecraft:".to_string()) + } + }; + + if namespace != "minecraft:" { + panic!("unrecognized recipe type namespace: {}", namespace); + } let data = match ty.as_ref() { @@ -2473,13 +2547,18 @@ impl Serializable for Recipe { experience: Serializable::read_from(buf)?, cooking_time: Serializable::read_from(buf)?, }, - "campfire" => RecipeData::Campfire { + "campfire" | "campfire_cooking" => RecipeData::Campfire { 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)?, }, + "stonecutting" => RecipeData::Stonecutting { + group: Serializable::read_from(buf)?, + ingredient: Serializable::read_from(buf)?, + result: Serializable::read_from(buf)?, + }, _ => panic!("unrecognized recipe type: {}", ty) }; diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index d80b2a6..dbc569a 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -1,5 +1,6 @@ use crate::protocol::*; +mod v1_14; mod v19w02a; mod v18w50a; mod v1_13_2; @@ -16,6 +17,8 @@ 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 + + 477 => v1_14::translate_internal_packet_id(state, dir, id, to_internal), // 19w02a 452 => v19w02a::translate_internal_packet_id(state, dir, id, to_internal), diff --git a/protocol/src/protocol/versions/v1_14.rs b/protocol/src/protocol/versions/v1_14.rs new file mode 100644 index 0000000..7949206 --- /dev/null +++ b/protocol/src/protocol/versions/v1_14.rs @@ -0,0 +1,180 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => QueryBlockNBT + 0x02 => SetDifficulty + 0x03 => ChatMessage + 0x04 => ClientStatus + 0x05 => ClientSettings + 0x06 => TabComplete + 0x07 => ConfirmTransactionServerbound + //0x08 => EnchantItem + //0x08 => ClickWindowButton + 0x09 => ClickWindow + 0x0a => CloseWindow + 0x0b => PluginMessageServerbound + 0x0c => EditBook + 0x0d => QueryEntityNBT + 0x0e => UseEntity + 0x0f => KeepAliveServerbound_i64 + 0x10 => LockDifficulty + 0x11 => PlayerPosition + 0x12 => PlayerPositionLook + 0x13 => PlayerLook + 0x14 => Player + 0x15 => VehicleMove + 0x16 => SteerBoat + 0x17 => PickItem + 0x18 => CraftRecipeRequest + 0x19 => ClientAbilities + 0x1a => PlayerDigging + 0x1b => PlayerAction + 0x1c => SteerVehicle + 0x1d => CraftingBookData + 0x1e => NameItem + 0x1f => ResourcePackStatus + 0x20 => AdvancementTab + 0x21 => SelectTrade + 0x22 => SetBeaconEffect + 0x23 => HeldItemChange + 0x24 => UpdateCommandBlock + 0x25 => UpdateCommandBlockMinecart + 0x26 => CreativeInventoryAction + 0x27 => UpdateJigsawBlock + 0x28 => UpdateStructureBlock + 0x29 => SetSign + 0x2a => ArmSwing + 0x2b => SpectateTeleport + 0x2c => PlayerBlockPlacement_f32 + 0x2d => 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_Locked + 0x0e => ServerMessage + 0x0f => MultiBlockChange_VarInt + 0x10 => TabCompleteReply + 0x11 => DeclareCommands + 0x12 => ConfirmTransaction + 0x13 => WindowClose + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => NamedSoundEffect + 0x1a => Disconnect + 0x1b => EntityAction + 0x1c => Explosion + 0x1d => ChunkUnload + 0x1e => ChangeGameState + 0x1f => WindowOpenHorse + 0x20 => KeepAliveClientbound_i64 + 0x21 => ChunkData_HeightMap + 0x22 => Effect + 0x23 => Particle_Data + 0x24 => UpdateLight + 0x25 => JoinGame_i32_ViewDistance + 0x26 => Maps + 0x27 => TradeList + 0x28 => EntityMove_i16 + 0x29 => EntityLookAndMove_i16 + 0x2a => EntityLook_VarInt + 0x2b => Entity + 0x2c => VehicleTeleport + 0x2d => OpenBook + 0x2e => WindowOpen_VarInt + 0x2f => SignEditorOpen + 0x30 => CraftRecipeResponse + 0x31 => PlayerAbilities + 0x32 => CombatEvent + 0x33 => PlayerInfo + 0x34 => FacePlayer + 0x35 => TeleportPlayer_WithConfirm + 0x36 => UnlockRecipes_WithSmelting + 0x37 => EntityDestroy + 0x38 => EntityRemoveEffect + 0x39 => ResourcePackSend + 0x3a => Respawn + 0x3b => EntityHeadLook + 0x3c => SelectAdvancementTab + 0x3d => WorldBorder + 0x3e => Camera + 0x3f => SetCurrentHotbarSlot + 0x40 => UpdateViewPosition + 0x41 => UpdateViewDistance + 0x42 => ScoreboardDisplay + 0x43 => EntityMetadata + 0x44 => EntityAttach + 0x45 => EntityVelocity + 0x46 => EntityEquipment + 0x47 => SetExperience + 0x48 => UpdateHealth + 0x49 => ScoreboardObjective + 0x4a => SetPassengers + 0x4b => Teams + 0x4c => UpdateScore + 0x4d => SpawnPosition + 0x4e => TimeUpdate + 0x4f => Title + 0x50 => EntitySoundEffect + 0x51 => SoundEffect + 0x52 => StopSound + 0x53 => PlayerListHeaderFooter + 0x54 => NBTQueryResponse + 0x55 => CollectItem + 0x56 => EntityTeleport_f64 + 0x57 => Advancements + 0x58 => EntityProperties + 0x59 => EntityEffect + 0x5a => DeclareRecipes + 0x5b => TagsWithEntities + } + } + 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 + } + } +); + + From 4f4533411e9f4f90678f45dbcff38c4b14c8ab97 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 4 May 2019 16:01:28 -0700 Subject: [PATCH 130/160] 1.14 protocol support (477) (#132). Closes #72 Adds 1.14 (477) protocol support, based on: https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14723 * New packets: SetDifficulty, LockDifficulty, UpdateJigsawBlock, UpdateViewPosition, UpdateViewDistance * New metadata: Optional VarInt (17) and Pose (18) * Add new join game variant with view distance, without difficulty * Add new server difficulty variant, with locked boolean * Implement recipe parsing changes, add stonecutting recipe type --- protocol/src/types/metadata.rs | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index cb390ff..e27e043 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -322,6 +322,14 @@ impl Metadata { } 15 => panic!("TODO: particle"), 16 => m.put_raw(index, VillagerData::read_from(buf)?), + 17 => { + if bool::read_from(buf)? { + m.put_raw(index, Option::::read_from(buf)?); + } else { + m.put_raw::>(index, None); + } + }, + 18 => m.put_raw(index, PoseData::read_from(buf)?), _ => return Err(protocol::Error::Err("unknown metadata type".to_owned())), } } @@ -405,6 +413,14 @@ impl Metadata { u8::write_to(&16, buf)?; val.write_to(buf)?; } + Value::OptionalVarInt(ref val) => { + u8::write_to(&17, buf)?; + val.write_to(buf)?; + } + Value::Pose(ref val) => { + u8::write_to(&18, buf)?; + val.write_to(buf)?; + } _ => panic!("unexpected metadata"), } } @@ -478,6 +494,8 @@ pub enum Value { NBTTag(nbt::NamedTag), Particle(ParticleData), Villager(VillagerData), + OptionalVarInt(Option), + Pose(PoseData), } #[derive(Debug)] @@ -639,6 +657,38 @@ impl Serializable for VillagerData { } } +#[derive(Debug)] +pub enum PoseData { + Standing, + FallFlying, + Sleeping, + Swimming, + SpinAttack, + Sneaking, + Dying, +} + +impl Serializable for PoseData { + fn read_from(buf: &mut R) -> Result { + let n = protocol::VarInt::read_from(buf)?; + Ok(match n.0 { + 0 => PoseData::Standing, + 1 => PoseData::FallFlying, + 2 => PoseData::Sleeping, + 3 => PoseData::Swimming, + 4 => PoseData::SpinAttack, + 5 => PoseData::Sneaking, + 6 => PoseData::Dying, + _ => panic!("unknown pose data: {}", n.0), + }) + } + + fn write_to(&self, _buf: &mut W) -> Result<(), protocol::Error> { + unimplemented!() + } +} + + pub trait MetaValue { fn unwrap(_: &Value) -> &Self; @@ -862,6 +912,31 @@ impl MetaValue for VillagerData { } } +impl MetaValue for Option { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::OptionalVarInt(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::OptionalVarInt(self) + } +} + +impl MetaValue for PoseData { + fn unwrap(value: &Value) -> &Self { + match *value { + Value::Pose(ref val) => val, + _ => panic!("incorrect key"), + } + } + fn wrap(self) -> Value { + Value::Pose(self) + } +} + + #[cfg(test)] mod test { use super::*; From 2f82d2ae7136d39c94df8ba7c484507d919411ab Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 5 May 2019 14:32:48 -0700 Subject: [PATCH 131/160] Add flag to log network packets for debugging, --network-debug Pass -n or --network-debug to print out the packets from the server as they are received, as well as write to last-packet for later analysis. Useful when debugging network protocol issues. Builds on #90 #114. --- protocol/src/protocol/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 6ac2f39..be57ae3 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -42,6 +42,7 @@ pub const SUPPORTED_PROTOCOLS: [i32; 13] = [477, 452, 451, 404, 340, 316, 315, 2 // 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]; +pub static mut NETWORK_DEBUG: bool = false; /// Helper macro for defining packets #[macro_export] @@ -923,8 +924,19 @@ impl Conn { Direction::Serverbound => Direction::Clientbound, }; + let network_debug = unsafe { NETWORK_DEBUG }; + + if network_debug { + println!("about to parse id={:x}, dir={:?} state={:?}", id, dir, self.state); + std::fs::File::create("last-packet")?.write_all(buf.get_ref())?; + } + let packet = packet::packet_by_id(self.protocol_version, self.state, dir, id, &mut buf)?; + if network_debug { + println!("packet = {:?}", packet); + } + match packet { Some(val) => { let pos = buf.position() as usize; From 1476d0628a33f36d58db7ff9db788300d1cc2b5a Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 5 May 2019 18:37:37 -0700 Subject: [PATCH 132/160] Add Forge handshake support. Closes #88 (#134) Adds support for connecting to 1.7.10 modded servers using the FML|HS protocol: https://wiki.vg/Minecraft_Forge_Handshake * Handle client-bound plugin message packets * Parse FML|HS plugin channel messages * Add ModList serialization using Mod serializable, LenPrefixed * Save forge_mods from server ping and send in FML|HS ModList packet * Show Forge mod count in server ping listing * Send acknowledgements, completing the handshake * Add VarShort to custom payload len prefix replaces i16, fixes OOM on large modded servers * Add custom CoFHLib's SendUUID packet -26 See explanation at https://github.com/SpigotMC/BungeeCord/issues/1437 This packet is defined by CoFHLib in https://github.com/CoFH/CoFHLib/blob/1.7.10/src/main/java/cofh/lib/util/helpers/SecurityHelper.java#L40 Fixes thread '' panicked at 'bad packet id 0xffffffe6 in Clientbound Play' with FTB:IE --- protocol/src/protocol/forge.rs | 180 ++++++++++++++++++++++ protocol/src/protocol/mod.rs | 85 ++++++++++ protocol/src/protocol/packet.rs | 7 +- protocol/src/protocol/versions/v1_7_10.rs | 1 + 4 files changed, 271 insertions(+), 2 deletions(-) create mode 100644 protocol/src/protocol/forge.rs diff --git a/protocol/src/protocol/forge.rs b/protocol/src/protocol/forge.rs new file mode 100644 index 0000000..21d47b3 --- /dev/null +++ b/protocol/src/protocol/forge.rs @@ -0,0 +1,180 @@ + +/// Implements https://wiki.vg/Minecraft_Forge_Handshake +use std::io; +use byteorder::WriteBytesExt; + +use crate::protocol::{Serializable, Error, LenPrefixed, VarInt}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Phase { + // Client handshake states (written) + Start, + WaitingServerData, + WaitingServerComplete, + PendingComplete, + + // Server handshake states (read) + WaitingCAck, + + // Both client and server handshake states (different values on the wire) + Complete, +} + +impl Serializable for Phase { + /// Read server handshake state from server + fn read_from(buf: &mut R) -> Result { + let phase: i8 = Serializable::read_from(buf)?; + Ok(match phase { + 2 => Phase::WaitingCAck, + 3 => Phase::Complete, + _ => panic!("bad FML|HS server phase: {}", phase), + }) + } + + /// Send client handshake state from client + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + buf.write_u8(match self { + Phase::WaitingServerData => 2, + Phase::WaitingServerComplete => 3, + Phase::PendingComplete => 4, + Phase::Complete => 5, + _ => panic!("bad FML|HS client phase: {:?}", self), + })?; + Ok(()) + } +} + + +#[derive(Clone, Debug, Default)] +pub struct ForgeMod { + pub modid: String, + pub version: String, +} + +impl Serializable for ForgeMod { + fn read_from(buf: &mut R) -> Result { + Ok(ForgeMod { + modid: Serializable::read_from(buf)?, + version: Serializable::read_from(buf)?, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.modid.write_to(buf)?; + self.version.write_to(buf) + } +} + +#[derive(Debug)] +pub struct ModIdMapping { + pub name: String, + pub id: VarInt, +} + +impl Serializable for ModIdMapping { + fn read_from(buf: &mut R) -> Result { + Ok(ModIdMapping { + name: Serializable::read_from(buf)?, + id: Serializable::read_from(buf)?, + }) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.name.write_to(buf)?; + self.id.write_to(buf) + } +} + +#[derive(Debug)] +pub enum FmlHs { + ServerHello { + fml_protocol_version: i8, + override_dimension: Option, + }, + ClientHello { + fml_protocol_version: i8, + }, + ModList { + mods: LenPrefixed, + }, + /* TODO: 1.8+ https://wiki.vg/Minecraft_Forge_Handshake#Differences_from_Forge_1.7.10 + RegistryData { + has_more: bool, + name: String, + ids: LenPrefixed, + substitutions: LenPrefixed, + dummies: LenPrefixed, + }, + */ + ModIdData { + mappings: LenPrefixed, + block_substitutions: LenPrefixed, + item_substitutions: LenPrefixed, + }, + HandshakeAck { + phase: Phase, + }, + HandshakeReset, +} + +impl Serializable for FmlHs { + fn read_from(buf: &mut R) -> Result { + let discriminator: u8 = Serializable::read_from(buf)?; + + match discriminator { + 0 => { + let fml_protocol_version: i8 = Serializable::read_from(buf)?; + let override_dimension = if fml_protocol_version > 1 { + let dimension: i32 = Serializable::read_from(buf)?; + Some(dimension) + } else { + None + }; + + println!("FML|HS ServerHello: fml_protocol_version={}, override_dimension={:?}", fml_protocol_version, override_dimension); + + Ok(FmlHs::ServerHello { + fml_protocol_version, + override_dimension, + }) + }, + 1 => panic!("Received unexpected FML|HS ClientHello from server"), + 2 => { + Ok(FmlHs::ModList { + mods: Serializable::read_from(buf)?, + }) + }, + 3 => { + Ok(FmlHs::ModIdData { + mappings: Serializable::read_from(buf)?, + block_substitutions: Serializable::read_from(buf)?, + item_substitutions: Serializable::read_from(buf)?, + }) + }, + 255 => { + Ok(FmlHs::HandshakeAck { + phase: Serializable::read_from(buf)?, + }) + }, + _ => panic!("Unhandled FML|HS packet: discriminator={}", discriminator), + } + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + match self { + FmlHs::ClientHello { fml_protocol_version } => { + buf.write_u8(1)?; + fml_protocol_version.write_to(buf) + }, + FmlHs::ModList { mods } => { + buf.write_u8(2)?; + mods.write_to(buf) + }, + FmlHs::HandshakeAck { phase } => { + buf.write_u8(255)?; + phase.write_to(buf) + }, + _ => unimplemented!() + } + } +} diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index be57ae3..97423ca 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -23,6 +23,7 @@ use serde_json; use reqwest; pub mod mojang; +pub mod forge; use crate::nbt; use crate::format; @@ -660,6 +661,65 @@ impl fmt::Debug for VarInt { } } +/// `VarShort` have a variable size (2 or 3 bytes) and are backwards-compatible +/// with vanilla shorts, used for Forge custom payloads +#[derive(Clone, Copy)] +pub struct VarShort(pub i32); + +impl Lengthable for VarShort { + fn into(self) -> usize { + self.0 as usize + } + + fn from(u: usize) -> VarShort { + VarShort(u as i32) + } +} + +impl Serializable for VarShort { + fn read_from(buf: &mut R) -> Result { + let low = buf.read_u16::()? as u32; + let val = if (low & 0x8000) != 0 { + let high = buf.read_u8()? as u32; + + (high << 15) | (low & 0x7fff) + } else { + low + }; + + Result::Ok(VarShort(val as i32)) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + assert!(self.0 >= 0 && self.0 <= 0x7fffff, "VarShort invalid value: {}", self.0); + let mut low = self.0 & 0x7fff; + let high = (self.0 & 0x7f8000) >> 15; + if high != 0 { + low |= 0x8000; + } + + buf.write_u16::(low as u16)?; + + if high != 0 { + buf.write_u8(high as u8)?; + } + + Ok(()) + } +} + +impl default::Default for VarShort { + fn default() -> VarShort { + VarShort(0) + } +} + +impl fmt::Debug for VarShort { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + /// `VarLong` have a variable size (between 1 and 10 bytes) when encoded based /// on the size of the number #[derive(Clone, Copy)] @@ -1005,6 +1065,29 @@ impl Conn { let version = val.get("version").ok_or(invalid_status())?; let players = val.get("players").ok_or(invalid_status())?; + // For modded servers, get the list of Forge mods installed + let mut forge_mods: std::vec::Vec = vec![]; + if let Some(modinfo) = val.get("modinfo") { + if let Some(modinfo_type) = modinfo.get("type") { + if modinfo_type == "FML" { + if let Some(modlist) = modinfo.get("modList") { + if let Value::Array(items) = modlist { + for item in items { + if let Value::Object(obj) = item { + let modid = obj.get("modid").unwrap().as_str().unwrap().to_string(); + let version = obj.get("version").unwrap().as_str().unwrap().to_string(); + + forge_mods.push(crate::protocol::forge::ForgeMod { modid, version }); + } + } + } + } + } else { + panic!("Unrecognized modinfo type in server ping response: {} in {}", modinfo_type, modinfo); + } + } + } + Ok((Status { version: StatusVersion { name: version.get("name").and_then(Value::as_str).ok_or(invalid_status())? @@ -1025,6 +1108,7 @@ impl Conn { description: format::Component::from_value(val.get("description") .ok_or(invalid_status())?), favicon: val.get("favicon").and_then(Value::as_str).map(|v| v.to_owned()), + forge_mods, }, ping)) } @@ -1036,6 +1120,7 @@ pub struct Status { pub players: StatusPlayers, pub description: format::Component, pub favicon: Option, + pub forge_mods: Vec, } #[derive(Debug)] diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 6b75acb..6e78020 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -161,7 +161,7 @@ state_packets!( } packet PluginMessageServerbound_i16 { field channel: String =, - field data: LenPrefixedBytes =, + field data: LenPrefixedBytes =, } packet EditBook { field new_book: Option =, @@ -875,7 +875,7 @@ state_packets!( } packet PluginMessageClientbound_i16 { field channel: String =, - field data: LenPrefixedBytes =, + field data: LenPrefixedBytes =, } /// Plays a sound by name on the client packet NamedSoundEffect { @@ -1747,6 +1747,9 @@ state_packets!( field id: VarInt =, field trades: LenPrefixed =, } + packet CoFHLib_SendUUID { + field player_uuid: UUID =, + } } } login Login { diff --git a/protocol/src/protocol/versions/v1_7_10.rs b/protocol/src/protocol/versions/v1_7_10.rs index 5ca41d9..1bc92ff 100644 --- a/protocol/src/protocol/versions/v1_7_10.rs +++ b/protocol/src/protocol/versions/v1_7_10.rs @@ -99,6 +99,7 @@ protocol_packet_ids!( 0x3e => Teams_NoVisColor 0x3f => PluginMessageClientbound_i16 0x40 => Disconnect + -0x1a => CoFHLib_SendUUID } } login Login { From 04246d4c887c93b94a66d925f890d9f26adf31b1 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Wed, 8 May 2019 19:12:36 -0700 Subject: [PATCH 133/160] Fix non-JSON sign text rendering, such as on 1.7.10. Closes #135 src/format.rs Component from_string() attempts JSON deserialization using serde_json::from_str, and if it fails falls back to a literal text string. Call from_string() instead of deserializing in format::Component read_from() and then from_value(). Note the from_string() comment: // Sometimes mojang sends a literal string, so we should interpret it literally --- protocol/src/protocol/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 97423ca..ecd3402 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -277,8 +277,7 @@ impl Serializable for format::Component { let len = VarInt::read_from(buf)?.0; let mut ret = String::new(); buf.take(len as u64).read_to_string(&mut ret)?; - let val: serde_json::Value = serde_json::from_str(&ret[..]).unwrap(); - Result::Ok(Self::from_value(&val)) + Result::Ok(Self::from_string(&ret[..])) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { let val = serde_json::to_string(&self.to_value()).unwrap(); From 72d73f529fe0382ce96afa21d7d2fb13653c3eff Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 11 May 2019 13:03:24 -0700 Subject: [PATCH 134/160] Change Lengthable trait method names to into_len/from_len std::convert::From cannot be used here because we cannot implement bool<->usize conversions, due to Rust's orphan rules: http://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/ "prevent you from implementing external traits for external types" Nonetheless, Lengthable used the same methods as From. This is allowed but can require disambiguation if both are used, no longer strictly needed for #140 but to reduce confusion and improve clarity, renamed `from` to `from_len` and `into` to `into_len`. --- protocol/src/protocol/mod.rs | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index ecd3402..4ded3fc 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -446,8 +446,8 @@ impl Serializable for UUID { pub trait Lengthable : Serializable + Copy + Default { - fn into(self) -> usize; - fn from(_: usize) -> Self; + fn into_len(self) -> usize; + fn from_len(_: usize) -> Self; } pub struct LenPrefixed { @@ -467,7 +467,7 @@ impl LenPrefixed { impl Serializable for LenPrefixed { fn read_from(buf: &mut R) -> Result, Error> { let len_data: L = Serializable::read_from(buf)?; - let len: usize = len_data.into(); + let len: usize = len_data.into_len(); let mut data: Vec = Vec::with_capacity(len); for _ in 0..len { data.push(Serializable::read_from(buf)?); @@ -479,7 +479,7 @@ impl Serializable for LenPrefixed { } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - let len_data: L = L::from(self.data.len()); + let len_data: L = L::from_len(self.data.len()); len_data.write_to(buf)?; let data = &self.data; for val in data { @@ -523,7 +523,7 @@ impl LenPrefixedBytes { impl Serializable for LenPrefixedBytes { fn read_from(buf: &mut R) -> Result, Error> { let len_data: L = Serializable::read_from(buf)?; - let len: usize = len_data.into(); + let len: usize = len_data.into_len(); let mut data: Vec = Vec::with_capacity(len); buf.take(len as u64).read_to_end(&mut data)?; Result::Ok(LenPrefixedBytes { @@ -533,7 +533,7 @@ impl Serializable for LenPrefixedBytes { } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - let len_data: L = L::from(self.data.len()); + let len_data: L = L::from_len(self.data.len()); len_data.write_to(buf)?; buf.write_all(&self.data[..])?; Result::Ok(()) @@ -557,42 +557,42 @@ impl fmt::Debug for LenPrefixedBytes { } impl Lengthable for bool { - fn into(self) -> usize { + fn into_len(self) -> usize { if self { 1 } else { 0 } } - fn from(u: usize) -> bool { + fn from_len(u: usize) -> bool { u != 0 } } impl Lengthable for u8 { - fn into(self) -> usize { + fn into_len(self) -> usize { self as usize } - fn from(u: usize) -> u8 { + fn from_len(u: usize) -> u8 { u as u8 } } impl Lengthable for i16 { - fn into(self) -> usize { + fn into_len(self) -> usize { self as usize } - fn from(u: usize) -> i16 { + fn from_len(u: usize) -> i16 { u as i16 } } impl Lengthable for i32 { - fn into(self) -> usize { + fn into_len(self) -> usize { self as usize } - fn from(u: usize) -> i32 { + fn from_len(u: usize) -> i32 { u as i32 } } @@ -603,11 +603,11 @@ impl Lengthable for i32 { pub struct VarInt(pub i32); impl Lengthable for VarInt { - fn into(self) -> usize { + fn into_len(self) -> usize { self.0 as usize } - fn from(u: usize) -> VarInt { + fn from_len(u: usize) -> VarInt { VarInt(u as i32) } } @@ -666,11 +666,11 @@ impl fmt::Debug for VarInt { pub struct VarShort(pub i32); impl Lengthable for VarShort { - fn into(self) -> usize { + fn into_len(self) -> usize { self.0 as usize } - fn from(u: usize) -> VarShort { + fn from_len(u: usize) -> VarShort { VarShort(u as i32) } } @@ -725,11 +725,11 @@ impl fmt::Debug for VarShort { pub struct VarLong(pub i64); impl Lengthable for VarLong { - fn into(self) -> usize { + fn into_len(self) -> usize { self.0 as usize } - fn from(u: usize) -> VarLong { + fn from_len(u: usize) -> VarLong { VarLong(u as i64) } } From 0034756339e3923a7a6a02ad21ff210c4c35510b Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sat, 11 May 2019 13:27:52 -0700 Subject: [PATCH 135/160] Fix movement fixed-point packets, misplaced players on 1.7/8 (#140) Movement packets were handled incorrectly, because although the fields are specified as integers they are actually fixed-point values, which need to be converted to floating-point before use. These fields were converted with `as f64`, but they actually need to be scaled. To fix this add several new types, FixedPoint5 for 5-bit fractional fixed-point and FixedPoint12 for 12-bit. Both are parameterized by an integer type: FixedPoint5 and FixedPoint5 for 1.7.10/1.8.9, FixedPoint12 for 1.9+. This moves the calculation into the packet field parsing, so it no longer has to be calculated in src/server/mod.rs since the scaling is taken care of as part of the field type. This fixes the long-standing invisible or actually misplaced players bug on 1.7.10 and 1.8.9, closes #139. * Add new FixedPoint5 type for 1.7/8, https://wiki.vg/Data_types#Fixed-point_numbers * Add FixedPoint12 for 1.9+, moving type conversion into packet type https://wiki.vg/index.php?title=Protocol#Entity_Relative_Move * Add num-traits 0.2.6 dependency for NumCast to use instead of From * Use FixedPoint5 in spawn object, experience orb, global entity, mob, player, teleport * Use FixedPoint5 and FixedPoint12 in entity move, look and move * Update packet handling bouncer functions, using f64::from for each conversion --- protocol/src/protocol/mod.rs | 84 ++++++++++++++++++++++++++ protocol/src/protocol/packet.rs | 102 ++++++++++++++++---------------- 2 files changed, 135 insertions(+), 51 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 4ded3fc..bfdd683 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -597,6 +597,90 @@ impl Lengthable for i32 { } } +use num_traits::cast::{cast, NumCast}; +/// `FixedPoint5` has the 5 least-significant bits for the fractional +/// part, upper for integer part: https://wiki.vg/Data_types#Fixed-point_numbers +#[derive(Clone, Copy)] +pub struct FixedPoint5(T); + +impl Serializable for FixedPoint5 { + fn read_from(buf: &mut R) -> Result { + Ok(Self(Serializable::read_from(buf)?)) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.0.write_to(buf) + } +} + +impl default::Default for FixedPoint5 { + fn default() -> Self { + Self(T::default()) + } +} + +impl convert::From for FixedPoint5 { + fn from(x: f64) -> Self { + let n: T = cast(x * 32.0).unwrap(); + FixedPoint5::(n) + } +} + +impl convert::From> for f64 { + fn from(x: FixedPoint5) -> Self { + let f: f64 = cast(x.0).unwrap(); + f / 32.0 + } +} + +impl fmt::Debug for FixedPoint5 where T: fmt::Display, f64: convert::From, T: NumCast + Copy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let x: f64 = (*self).into(); + write!(f, "FixedPoint5(#{} = {}f)", self.0, x) + } +} + +/// `FixedPoint12` is like `FixedPoint5` but the fractional part is 12-bit +#[derive(Clone, Copy)] +pub struct FixedPoint12(T); + +impl Serializable for FixedPoint12 { + fn read_from(buf: &mut R) -> Result { + Ok(Self(Serializable::read_from(buf)?)) + } + + fn write_to(&self, buf: &mut W) -> Result<(), Error> { + self.0.write_to(buf) + } +} + +impl default::Default for FixedPoint12 { + fn default() -> Self { + Self(T::default()) + } +} + +impl convert::From for FixedPoint12 { + fn from(x: f64) -> Self { + let n: T = cast(x * 32.0 * 128.0).unwrap(); + FixedPoint12::(n) + } +} + +impl convert::From> for f64 { + fn from(x: FixedPoint12) -> Self { + let f: f64 = cast(x.0).unwrap(); + f / (32.0 * 128.0) + } +} + +impl fmt::Debug for FixedPoint12 where T: fmt::Display, f64: convert::From, T: NumCast + Copy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let x: f64 = (*self).into(); + write!(f, "FixedPoint12(#{} = {}f)", self.0, x) + } +} + /// `VarInt` have a variable size (between 1 and 5 bytes) when encoded based /// on the size of the number #[derive(Clone, Copy)] diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 6e78020..21b0615 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -495,9 +495,9 @@ state_packets!( field entity_id: VarInt =, field uuid: UUID =, field ty: u8 =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, field pitch: i8 =, field yaw: i8 =, field data: i32 =, @@ -508,9 +508,9 @@ state_packets!( packet SpawnObject_i32_NoUUID { field entity_id: VarInt =, field ty: u8 =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, field pitch: i8 =, field yaw: i8 =, field data: i32 =, @@ -530,9 +530,9 @@ state_packets!( } packet SpawnExperienceOrb_i32 { field entity_id: VarInt =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, field count: i16 =, } /// SpawnGlobalEntity spawns an entity which is visible from anywhere in the @@ -547,9 +547,9 @@ state_packets!( packet SpawnGlobalEntity_i32 { field entity_id: VarInt =, field ty: u8 =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, } /// SpawnMob is used to spawn a living entity into the world when it is in /// range of the client. @@ -587,9 +587,9 @@ state_packets!( field entity_id: VarInt =, field uuid: UUID =, field ty: u8 =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, field yaw: i8 =, field pitch: i8 =, field head_pitch: i8 =, @@ -601,9 +601,9 @@ state_packets!( packet SpawnMob_u8_i32_NoUUID { field entity_id: VarInt =, field ty: u8 =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, field yaw: i8 =, field pitch: i8 =, field head_pitch: i8 =, @@ -651,9 +651,9 @@ state_packets!( packet SpawnPlayer_i32 { field entity_id: VarInt =, field uuid: UUID =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, field yaw: i8 =, field pitch: i8 =, field metadata: types::Metadata =, @@ -661,9 +661,9 @@ state_packets!( packet SpawnPlayer_i32_HeldItem { field entity_id: VarInt =, field uuid: UUID =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, field yaw: i8 =, field pitch: i8 =, field current_item: u16 =, @@ -674,9 +674,9 @@ state_packets!( field uuid: String =, field name: String =, field properties: LenPrefixed =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, field yaw: i8 =, field pitch: i8 =, field current_item: u16 =, @@ -1159,48 +1159,48 @@ state_packets!( /// EntityMove moves the entity with the id by the offsets provided. packet EntityMove_i16 { field entity_id: VarInt =, - field delta_x: i16 =, - field delta_y: i16 =, - field delta_z: i16 =, + field delta_x: FixedPoint12 =, + field delta_y: FixedPoint12 =, + field delta_z: FixedPoint12 =, field on_ground: bool =, } packet EntityMove_i8 { field entity_id: VarInt =, - field delta_x: i8 =, - field delta_y: i8 =, - field delta_z: i8 =, + field delta_x: FixedPoint5 =, + field delta_y: FixedPoint5 =, + field delta_z: FixedPoint5 =, field on_ground: bool =, } packet EntityMove_i8_i32_NoGround { field entity_id: i32 =, - field delta_x: i8 =, - field delta_y: i8 =, - field delta_z: i8 =, + field delta_x: FixedPoint5 =, + field delta_y: FixedPoint5 =, + field delta_z: FixedPoint5 =, } /// EntityLookAndMove is a combination of EntityMove and EntityLook. packet EntityLookAndMove_i16 { field entity_id: VarInt =, - field delta_x: i16 =, - field delta_y: i16 =, - field delta_z: i16 =, + field delta_x: FixedPoint12 =, + field delta_y: FixedPoint12 =, + field delta_z: FixedPoint12 =, field yaw: i8 =, field pitch: i8 =, field on_ground: bool =, } packet EntityLookAndMove_i8 { field entity_id: VarInt =, - field delta_x: i8 =, - field delta_y: i8 =, - field delta_z: i8 =, + field delta_x: FixedPoint5 =, + field delta_y: FixedPoint5 =, + field delta_z: FixedPoint5 =, field yaw: i8 =, field pitch: i8 =, field on_ground: bool =, } packet EntityLookAndMove_i8_i32_NoGround { field entity_id: i32 =, - field delta_x: i8 =, - field delta_y: i8 =, - field delta_z: i8 =, + field delta_x: FixedPoint5 =, + field delta_y: FixedPoint5 =, + field delta_z: FixedPoint5 =, field yaw: i8 =, field pitch: i8 =, } @@ -1677,18 +1677,18 @@ state_packets!( } packet EntityTeleport_i32 { field entity_id: VarInt =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, field yaw: i8 =, field pitch: i8 =, field on_ground: bool =, } packet EntityTeleport_i32_i32_NoGround { field entity_id: i32 =, - field x: i32 =, - field y: i32 =, - field z: i32 =, + field x: FixedPoint5 =, + field y: FixedPoint5 =, + field z: FixedPoint5 =, field yaw: i8 =, field pitch: i8 =, } From a081c73215328812df40e2967269a7e48a976b89 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 11 May 2019 14:53:42 -0700 Subject: [PATCH 136/160] 1.7.10: Fix player position too high on login. Closes #87 Adds a new TeleportPlayer_NoGround packet, which is subtly different from TeleportPlayer_NoConfirm. The flags u8 is replaced with an on_ground bool, but more importantly the Y position is the eyes position, so we have to translate to feet position for the client. 1.7.10: https://wiki.vg/index.php?title=Protocol&oldid=6003#Player_Position_And_Look 1.8.9: https://wiki.vg/index.php?title=Protocol&oldid=7368#Player_Position_And_Look --- protocol/src/protocol/packet.rs | 8 ++++++++ protocol/src/protocol/versions/v1_7_10.rs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 21b0615..6fb57e9 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1310,6 +1310,14 @@ state_packets!( field pitch: f32 =, field flags: u8 =, } + packet TeleportPlayer_OnGround { + field x: f64 =, + field eyes_y: f64 =, + field z: f64 =, + field yaw: f32 =, + field pitch: f32 =, + field on_ground: bool =, + } /// EntityUsedBed is sent by the server when a player goes to bed. packet EntityUsedBed { field entity_id: VarInt =, diff --git a/protocol/src/protocol/versions/v1_7_10.rs b/protocol/src/protocol/versions/v1_7_10.rs index 1bc92ff..c930e42 100644 --- a/protocol/src/protocol/versions/v1_7_10.rs +++ b/protocol/src/protocol/versions/v1_7_10.rs @@ -42,7 +42,7 @@ protocol_packet_ids!( 0x05 => SpawnPosition_i32 0x06 => UpdateHealth_u16 0x07 => Respawn - 0x08 => TeleportPlayer_NoConfirm + 0x08 => TeleportPlayer_OnGround 0x09 => SetCurrentHotbarSlot 0x0a => EntityUsedBed_i32 0x0b => Animation From 2451e780bd69f4c5eee1eac3750fbb72b8dbdb4d Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sat, 11 May 2019 18:37:33 -0700 Subject: [PATCH 137/160] Forge 1.8.9-1.12.2 handshake protocol support (#144) Adds support for connecting to Forge servers from 1.8.9 up to 1.12.2. (1.7.10 was already supported with #134 #88) Tested on: - 1.8.9 + forge 11.15.1.2318 + ironchest - 1.10.2 + forge 12.18.3.2511 + ironchest - 1.11.2 + forge 13.20.1.2588 + ironchest - 1.12.2 + forge 14.23.5.2837 + ironchest Changes: * Parse and handle FmlHs::RegistryData packet for 1.8+ * Fix RegistryData acknowledgement phase WaitingServerComplete * Fix acknowledgement phase for 1.7.10 ModIdData too, somehow it worked accidentally * Append \0FML\0 to end of server hostname if Forge mods detected https://wiki.vg/Minecraft_Forge_Handshake#Connection_to_a_forge_server --- protocol/src/protocol/forge.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/protocol/src/protocol/forge.rs b/protocol/src/protocol/forge.rs index 21d47b3..841df92 100644 --- a/protocol/src/protocol/forge.rs +++ b/protocol/src/protocol/forge.rs @@ -97,7 +97,6 @@ pub enum FmlHs { ModList { mods: LenPrefixed, }, - /* TODO: 1.8+ https://wiki.vg/Minecraft_Forge_Handshake#Differences_from_Forge_1.7.10 RegistryData { has_more: bool, name: String, @@ -105,7 +104,6 @@ pub enum FmlHs { substitutions: LenPrefixed, dummies: LenPrefixed, }, - */ ModIdData { mappings: LenPrefixed, block_substitutions: LenPrefixed, @@ -145,11 +143,23 @@ impl Serializable for FmlHs { }) }, 3 => { - Ok(FmlHs::ModIdData { - mappings: Serializable::read_from(buf)?, - block_substitutions: Serializable::read_from(buf)?, - item_substitutions: Serializable::read_from(buf)?, - }) + let protocol_version = unsafe { crate::protocol::CURRENT_PROTOCOL_VERSION }; + + if protocol_version >= 47 { + Ok(FmlHs::RegistryData { + has_more: Serializable::read_from(buf)?, + name: Serializable::read_from(buf)?, + ids: Serializable::read_from(buf)?, + substitutions: Serializable::read_from(buf)?, + dummies: Serializable::read_from(buf)?, + }) + } else { + Ok(FmlHs::ModIdData { + mappings: Serializable::read_from(buf)?, + block_substitutions: Serializable::read_from(buf)?, + item_substitutions: Serializable::read_from(buf)?, + }) + } }, 255 => { Ok(FmlHs::HandshakeAck { From 83c848fa6f8f396dd3155acddbf5749e98decf81 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 12 May 2019 12:15:02 -0700 Subject: [PATCH 138/160] Fix packet compression (fixes FTB Beyond, etc.). Closes #146 (#147) Previously, the zlib compressor was initialized once, lazily, then reused for each packet by resetting the stream. This didn't properly emit the zlib headers, so packet compression was broken and caused "java.util.zip.DataFormatException: incorrect header check" on the server. Since packets are only compressed above a threshold, this problem manifested itself only on larger servers, such as 1.10.2 FTB Beyond and Skyfactory 3, and 1.12.2 SevTech: Ages, which require sending a large ModList above the compression threshold enabled by the server. Change to instead recreate the zlib compressor, each time it is used as to capture the full header. For symmetry, the decompressor is also recreated each time. * Removes self.compression_write and self.compression_read instance variables * Remove self.compression_write, initialize with cursor * Log compressing/decompressing packets in network debug mode --- protocol/src/protocol/mod.rs | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index bfdd683..da88f15 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -972,8 +972,6 @@ pub struct Conn { cipher: Option, compression_threshold: i32, - compression_read: Option>>>, - compression_write: Option>>>, } impl Conn { @@ -1000,8 +998,6 @@ impl Conn { protocol_version, cipher: Option::None, compression_threshold: -1, - compression_read: Option::None, - compression_write: Option::None, }) } @@ -1016,16 +1012,18 @@ impl Conn { 0 }; if self.compression_threshold >= 0 && buf.len() as i32 > self.compression_threshold { - if self.compression_write.is_none() { - self.compression_write = Some(ZlibEncoder::new(io::Cursor::new(Vec::new()), Compression::default())); - } extra = 0; let uncompressed_size = buf.len(); let mut new = Vec::new(); VarInt(uncompressed_size as i32).write_to(&mut new)?; - let write = self.compression_write.as_mut().unwrap(); - write.reset(io::Cursor::new(buf)); + let mut write = ZlibEncoder::new(io::Cursor::new(buf), Compression::default()); write.read_to_end(&mut new)?; + let network_debug = unsafe { NETWORK_DEBUG }; + if network_debug { + println!("Compressed for sending {} bytes to {} since > threshold {}, new={:?}", + uncompressed_size, new.len(), self.compression_threshold, + new); + } buf = new; } @@ -1039,6 +1037,7 @@ impl Conn { } pub fn read_packet(&mut self) -> Result { + let network_debug = unsafe { NETWORK_DEBUG }; let len = VarInt::read_from(self)?.0 as usize; let mut ibuf = vec![0; len]; self.read_exact(&mut ibuf)?; @@ -1046,17 +1045,17 @@ impl Conn { let mut buf = io::Cursor::new(ibuf); if self.compression_threshold >= 0 { - if self.compression_read.is_none() { - self.compression_read = Some(ZlibDecoder::new(io::Cursor::new(Vec::new()))); - } let uncompressed_size = VarInt::read_from(&mut buf)?.0; if uncompressed_size != 0 { let mut new = Vec::with_capacity(uncompressed_size as usize); { - let reader = self.compression_read.as_mut().unwrap(); - reader.reset(buf); + let mut reader = ZlibDecoder::new(buf); reader.read_to_end(&mut new)?; } + if network_debug { + println!("Decompressed threshold={} len={} uncompressed_size={} to {} bytes", + self.compression_threshold, len, uncompressed_size, new.len()); + } buf = io::Cursor::new(new); } } @@ -1067,8 +1066,6 @@ impl Conn { Direction::Serverbound => Direction::Clientbound, }; - let network_debug = unsafe { NETWORK_DEBUG }; - if network_debug { println!("about to parse id={:x}, dir={:?} state={:?}", id, dir, self.state); std::fs::File::create("last-packet")?.write_all(buf.get_ref())?; @@ -1274,8 +1271,6 @@ impl Clone for Conn { protocol_version: self.protocol_version, cipher: Option::None, compression_threshold: self.compression_threshold, - compression_read: Option::None, - compression_write: Option::None, } } } From 16886110be5f42b26f6fbafa488da02711f07792 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 12 May 2019 14:08:53 -0700 Subject: [PATCH 139/160] Replace read_to_string -> read_to_end to improve UTF-8 deserialization See https://github.com/iceiix/stevenarella/commit/c1692e950aceccb9872478ed43f65848d90dd347 There are two more instances, encountered when debugging #148 > Instead of read_to_string(), use read_to_end() to read into a buffer, > then convert using String::from_utf8() and unwrap it. This gives a > better error message when UTF-8 fails to decode. which includes the offending bytes that can't be converted --- protocol/src/protocol/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index da88f15..3f1ee80 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -275,8 +275,9 @@ impl Serializable for String { impl Serializable for format::Component { fn read_from(buf: &mut R) -> Result { let len = VarInt::read_from(buf)?.0; - let mut ret = String::new(); - buf.take(len as u64).read_to_string(&mut ret)?; + let mut bytes = Vec::::new(); + buf.take(len as u64).read_to_end(&mut bytes)?; + let ret = String::from_utf8(bytes).unwrap(); Result::Ok(Self::from_string(&ret[..])) } fn write_to(&self, buf: &mut W) -> Result<(), Error> { From f15ece0377f4922cc726541850b109d10f1e8ad9 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 12 May 2019 14:08:53 -0700 Subject: [PATCH 140/160] Replace read_to_string -> read_to_end to improve UTF-8 deserialization See https://github.com/iceiix/stevenarella/commit/c1692e950aceccb9872478ed43f65848d90dd347 There are two more instances, encountered when debugging #148 > Instead of read_to_string(), use read_to_end() to read into a buffer, > then convert using String::from_utf8() and unwrap it. This gives a > better error message when UTF-8 fails to decode. which includes the offending bytes that can't be converted --- protocol/src/nbt/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocol/src/nbt/mod.rs b/protocol/src/nbt/mod.rs index e72fad6..37c3eca 100644 --- a/protocol/src/nbt/mod.rs +++ b/protocol/src/nbt/mod.rs @@ -304,7 +304,8 @@ pub fn write_string(buf: &mut W, s: &str) -> Result<(), protocol:: pub fn read_string(buf: &mut R) -> Result { let len: i16 = buf.read_i16::()?; - let mut ret = String::new(); - buf.take(len as u64).read_to_string(&mut ret)?; + let mut bytes = Vec::::new(); + buf.take(len as u64).read_to_end(&mut bytes)?; + let ret = String::from_utf8(bytes).unwrap(); Result::Ok(ret) } From 604a09b3ce13d2dbe51416c3a969095367a58660 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Mon, 13 May 2019 16:35:39 -0700 Subject: [PATCH 141/160] Enhance -p/--protocol-version to accept game version string Now you can pass `-p 1.7.10` as an alternative to `-p 5`, etc --- protocol/src/protocol/versions.rs | 87 ++++++++++++++++--------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index dbc569a..2131f21 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -13,49 +13,50 @@ mod v15w39c; mod v1_8_9; mod v1_7_10; -pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { - match version { - // https://wiki.vg/Protocol_History - // https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite +// https://wiki.vg/Protocol_History +// https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite - 477 => v1_14::translate_internal_packet_id(state, dir, id, to_internal), - - // 19w02a - 452 => v19w02a::translate_internal_packet_id(state, dir, id, to_internal), - - // 18w50a - 451 => v18w50a::translate_internal_packet_id(state, dir, id, to_internal), - - // 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), - - // 1.11.2 - 316 => v1_11_2::translate_internal_packet_id(state, dir, id, to_internal), - - // 1.11 - 315 => v1_11_2::translate_internal_packet_id(state, dir, id, to_internal), - - // 1.10.2 - 210 => v1_10_2::translate_internal_packet_id(state, dir, id, to_internal), - - // 1.9.2 - 109 => v1_9_2::translate_internal_packet_id(state, dir, id, to_internal), - - // 1.9 - 107 => v1_9::translate_internal_packet_id(state, dir, id, to_internal), - - // 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), - - // 1.7.10 - 1.7.6 - 5 => v1_7_10::translate_internal_packet_id(state, dir, id, to_internal), - - _ => panic!("unsupported protocol version"), +pub fn protocol_name_to_protocol_version(s: String) -> i32 { + match s.as_ref() { + "" => SUPPORTED_PROTOCOLS[0], + "1.14" => 477, + "19w02a" => 452, + "18w50a" => 451, + "1.13.2" => 404, + "1.12.2" => 340, + "1.11.2" => 316, + "1.11" => 315, + "1.10.2" => 210, + "1.9.2" => 109, + "1.9" => 107, + "15w39c" => 74, + "1.8.9" => 47, + "1.7.10" => 5, + _ => { + if let Ok(n) = s.parse::() { + n + } else { + panic!("Unrecognized protocol name: {}", s) + } + } + } +} + +pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { + match version { + 477 => v1_14::translate_internal_packet_id(state, dir, id, to_internal), + 452 => v19w02a::translate_internal_packet_id(state, dir, id, to_internal), + 451 => v18w50a::translate_internal_packet_id(state, dir, id, to_internal), + 404 => v1_13_2::translate_internal_packet_id(state, dir, id, to_internal), + 340 => v1_12_2::translate_internal_packet_id(state, dir, id, to_internal), + 316 => v1_11_2::translate_internal_packet_id(state, dir, id, to_internal), + 315 => v1_11_2::translate_internal_packet_id(state, dir, id, to_internal), + 210 => v1_10_2::translate_internal_packet_id(state, dir, id, to_internal), + 109 => v1_9_2::translate_internal_packet_id(state, dir, id, to_internal), + 107 => v1_9::translate_internal_packet_id(state, dir, id, to_internal), + 74 => v15w39c::translate_internal_packet_id(state, dir, id, to_internal), + 47 => v1_8_9::translate_internal_packet_id(state, dir, id, to_internal), + 5 => v1_7_10::translate_internal_packet_id(state, dir, id, to_internal), + _ => panic!("unsupported protocol version: {}", version), } } From 1a600c7192b1b55e5d6e7a38d88881764fc48dbc Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Mon, 13 May 2019 16:44:31 -0700 Subject: [PATCH 142/160] 1.14.1 protocol support (480) (#151) * Copy v1_14_1 from v1_14 * Add protocol 1.14.1 (480) * Update readme * 0x08 serverbound is now ClickWindowButton https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14764#Click_Window_Button --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 5 + protocol/src/protocol/versions.rs | 3 + protocol/src/protocol/versions/v1_14.rs | 3 +- protocol/src/protocol/versions/v1_14_1.rs | 179 ++++++++++++++++++++++ 5 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 protocol/src/protocol/versions/v1_14_1.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 3f1ee80..fa500b8 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -39,7 +39,7 @@ use flate2::Compression; use std::time::{Instant, Duration}; use crate::shared::Position; -pub const SUPPORTED_PROTOCOLS: [i32; 13] = [477, 452, 451, 404, 340, 316, 315, 210, 109, 107, 74, 47, 5]; +pub const SUPPORTED_PROTOCOLS: [i32; 14] = [480, 477, 452, 451, 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]; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 6fb57e9..6087ed6 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -131,6 +131,11 @@ state_packets!( field id: u8 =, field enchantment: u8 =, } + /// ClickWindowButton is used for clicking an enchantment, lectern, stonecutter, or loom. + packet ClickWindowButton { + field id: u8 =, + field button: u8 =, + } /// ClickWindow is sent when the client clicks in a window. packet ClickWindow { field id: u8 =, diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index 2131f21..554ce88 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -1,5 +1,6 @@ use crate::protocol::*; +mod v1_14_1; mod v1_14; mod v19w02a; mod v18w50a; @@ -19,6 +20,7 @@ mod v1_7_10; pub fn protocol_name_to_protocol_version(s: String) -> i32 { match s.as_ref() { "" => SUPPORTED_PROTOCOLS[0], + "1.14.1" => 480, "1.14" => 477, "19w02a" => 452, "18w50a" => 451, @@ -44,6 +46,7 @@ pub fn protocol_name_to_protocol_version(s: String) -> i32 { pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { + 480 => v1_14_1::translate_internal_packet_id(state, dir, id, to_internal), 477 => v1_14::translate_internal_packet_id(state, dir, id, to_internal), 452 => v19w02a::translate_internal_packet_id(state, dir, id, to_internal), 451 => v18w50a::translate_internal_packet_id(state, dir, id, to_internal), diff --git a/protocol/src/protocol/versions/v1_14.rs b/protocol/src/protocol/versions/v1_14.rs index 7949206..d30f36a 100644 --- a/protocol/src/protocol/versions/v1_14.rs +++ b/protocol/src/protocol/versions/v1_14.rs @@ -16,8 +16,7 @@ protocol_packet_ids!( 0x05 => ClientSettings 0x06 => TabComplete 0x07 => ConfirmTransactionServerbound - //0x08 => EnchantItem - //0x08 => ClickWindowButton + 0x08 => ClickWindowButton 0x09 => ClickWindow 0x0a => CloseWindow 0x0b => PluginMessageServerbound diff --git a/protocol/src/protocol/versions/v1_14_1.rs b/protocol/src/protocol/versions/v1_14_1.rs new file mode 100644 index 0000000..d30f36a --- /dev/null +++ b/protocol/src/protocol/versions/v1_14_1.rs @@ -0,0 +1,179 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => QueryBlockNBT + 0x02 => SetDifficulty + 0x03 => ChatMessage + 0x04 => ClientStatus + 0x05 => ClientSettings + 0x06 => TabComplete + 0x07 => ConfirmTransactionServerbound + 0x08 => ClickWindowButton + 0x09 => ClickWindow + 0x0a => CloseWindow + 0x0b => PluginMessageServerbound + 0x0c => EditBook + 0x0d => QueryEntityNBT + 0x0e => UseEntity + 0x0f => KeepAliveServerbound_i64 + 0x10 => LockDifficulty + 0x11 => PlayerPosition + 0x12 => PlayerPositionLook + 0x13 => PlayerLook + 0x14 => Player + 0x15 => VehicleMove + 0x16 => SteerBoat + 0x17 => PickItem + 0x18 => CraftRecipeRequest + 0x19 => ClientAbilities + 0x1a => PlayerDigging + 0x1b => PlayerAction + 0x1c => SteerVehicle + 0x1d => CraftingBookData + 0x1e => NameItem + 0x1f => ResourcePackStatus + 0x20 => AdvancementTab + 0x21 => SelectTrade + 0x22 => SetBeaconEffect + 0x23 => HeldItemChange + 0x24 => UpdateCommandBlock + 0x25 => UpdateCommandBlockMinecart + 0x26 => CreativeInventoryAction + 0x27 => UpdateJigsawBlock + 0x28 => UpdateStructureBlock + 0x29 => SetSign + 0x2a => ArmSwing + 0x2b => SpectateTeleport + 0x2c => PlayerBlockPlacement_f32 + 0x2d => 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_Locked + 0x0e => ServerMessage + 0x0f => MultiBlockChange_VarInt + 0x10 => TabCompleteReply + 0x11 => DeclareCommands + 0x12 => ConfirmTransaction + 0x13 => WindowClose + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => NamedSoundEffect + 0x1a => Disconnect + 0x1b => EntityAction + 0x1c => Explosion + 0x1d => ChunkUnload + 0x1e => ChangeGameState + 0x1f => WindowOpenHorse + 0x20 => KeepAliveClientbound_i64 + 0x21 => ChunkData_HeightMap + 0x22 => Effect + 0x23 => Particle_Data + 0x24 => UpdateLight + 0x25 => JoinGame_i32_ViewDistance + 0x26 => Maps + 0x27 => TradeList + 0x28 => EntityMove_i16 + 0x29 => EntityLookAndMove_i16 + 0x2a => EntityLook_VarInt + 0x2b => Entity + 0x2c => VehicleTeleport + 0x2d => OpenBook + 0x2e => WindowOpen_VarInt + 0x2f => SignEditorOpen + 0x30 => CraftRecipeResponse + 0x31 => PlayerAbilities + 0x32 => CombatEvent + 0x33 => PlayerInfo + 0x34 => FacePlayer + 0x35 => TeleportPlayer_WithConfirm + 0x36 => UnlockRecipes_WithSmelting + 0x37 => EntityDestroy + 0x38 => EntityRemoveEffect + 0x39 => ResourcePackSend + 0x3a => Respawn + 0x3b => EntityHeadLook + 0x3c => SelectAdvancementTab + 0x3d => WorldBorder + 0x3e => Camera + 0x3f => SetCurrentHotbarSlot + 0x40 => UpdateViewPosition + 0x41 => UpdateViewDistance + 0x42 => ScoreboardDisplay + 0x43 => EntityMetadata + 0x44 => EntityAttach + 0x45 => EntityVelocity + 0x46 => EntityEquipment + 0x47 => SetExperience + 0x48 => UpdateHealth + 0x49 => ScoreboardObjective + 0x4a => SetPassengers + 0x4b => Teams + 0x4c => UpdateScore + 0x4d => SpawnPosition + 0x4e => TimeUpdate + 0x4f => Title + 0x50 => EntitySoundEffect + 0x51 => SoundEffect + 0x52 => StopSound + 0x53 => PlayerListHeaderFooter + 0x54 => NBTQueryResponse + 0x55 => CollectItem + 0x56 => EntityTeleport_f64 + 0x57 => Advancements + 0x58 => EntityProperties + 0x59 => EntityEffect + 0x5a => DeclareRecipes + 0x5b => TagsWithEntities + } + } + 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 + } + } +); + + From 5467304bcf6520847c5baa88dfd5e1528fbcdf3e Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Mon, 13 May 2019 17:13:22 -0700 Subject: [PATCH 143/160] Disable parsing advancements to fix 1.12.2 Forge SevTech crash (#148) (#149) Leave the advancements packet as an opaque blob for now instead of trying to deserialize it, because it apparently is changed on some modded servers - see https://github.com/iceiix/stevenarella/issues/148 --- protocol/src/protocol/packet.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 6087ed6..3783f13 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1706,10 +1706,14 @@ state_packets!( field pitch: i8 =, } packet Advancements { + field data: Vec =, + /* TODO: fix parsing modded advancements 1.12.2 (e.g. SevTech Ages) + * see https://github.com/iceiix/stevenarella/issues/148 field reset_clear: bool =, field mapping: LenPrefixed =, field identifiers: LenPrefixed =, field progress: LenPrefixed =, + */ } /// EntityProperties updates the properties for an entity. packet EntityProperties { From 3bbab7a12899414e716ef6684f249370e79fbeae Mon Sep 17 00:00:00 2001 From: ice_iix Date: Wed, 15 May 2019 12:11:05 -0700 Subject: [PATCH 144/160] Refactor recipe parsing minecraft: prefix, related to #155 --- protocol/src/protocol/packet.rs | 59 +++++++++++++++------------------ 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 3783f13..29f666d 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -2480,7 +2480,7 @@ pub struct Recipe { impl Serializable for Recipe { fn read_from(buf: &mut R) -> Result { - let (id, ty, namespace) = { + let (id, ty) = { let a = String::read_from(buf)?; let b = String::read_from(buf)?; @@ -2491,33 +2491,26 @@ impl Serializable for Recipe { let ty = a; let id = b; - if let Some(at) = ty.find(':') { - let (namespace, ty) = ty.split_at(at + 1); - let ty: String = ty.into(); - let namespace: String = namespace.into(); - (id, ty, namespace) + if ty.find(':').is_some() { + (id, ty) } else { - (id, ty, "minecraft:".to_string()) + (id, format!("minecraft:{}", ty)) } } else { let ty = b; let id = a; - (id, ty, "minecraft:".to_string()) + (id, format!("minecraft:{}", ty)) } }; - if namespace != "minecraft:" { - panic!("unrecognized recipe type namespace: {}", namespace); - } - let data = match ty.as_ref() { - "crafting_shapeless" => RecipeData::Shapeless { + "minecraft:crafting_shapeless" => RecipeData::Shapeless { group: Serializable::read_from(buf)?, ingredients: Serializable::read_from(buf)?, result: Serializable::read_from(buf)?, }, - "crafting_shaped" => { + "minecraft:crafting_shaped" => { let width: VarInt = Serializable::read_from(buf)?; let height: VarInt = Serializable::read_from(buf)?; let group: String = Serializable::read_from(buf)?; @@ -2532,49 +2525,49 @@ impl Serializable for Recipe { 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, - "crafting_special_suspiciousstew" => RecipeData::SuspiciousStew, - "smelting" => RecipeData::Smelting { + "minecraft:crafting_special_armordye" => RecipeData::ArmorDye, + "minecraft:crafting_special_bookcloning" => RecipeData::BookCloning, + "minecraft:crafting_special_mapcloning" => RecipeData::MapCloning, + "minecraft:crafting_special_mapextending" => RecipeData::MapExtending, + "minecraft:crafting_special_firework_rocket" => RecipeData::FireworkRocket, + "minecraft:crafting_special_firework_star" => RecipeData::FireworkStar, + "minecraft:crafting_special_firework_star_fade" => RecipeData::FireworkStarFade, + "minecraft:crafting_special_repairitem" => RecipeData::RepairItem, + "minecraft:crafting_special_tippedarrow" => RecipeData::TippedArrow, + "minecraft:crafting_special_bannerduplicate" => RecipeData::BannerDuplicate, + "minecraft:crafting_special_banneraddpattern" => RecipeData::BannerAddPattern, + "minecraft:crafting_special_shielddecoration" => RecipeData::ShieldDecoration, + "minecraft:crafting_special_shulkerboxcoloring" => RecipeData::ShulkerBoxColoring, + "minecraft:crafting_special_suspiciousstew" => RecipeData::SuspiciousStew, + "minecraft: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)?, }, - "blasting" => RecipeData::Blasting { + "minecraft:blasting" => RecipeData::Blasting { 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)?, }, - "smoking" => RecipeData::Smoking { + "minecraft:smoking" => RecipeData::Smoking { 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)?, }, - "campfire" | "campfire_cooking" => RecipeData::Campfire { + "minecraft:campfire" | "minecraft:campfire_cooking" => RecipeData::Campfire { 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)?, }, - "stonecutting" => RecipeData::Stonecutting { + "minecraft:stonecutting" => RecipeData::Stonecutting { group: Serializable::read_from(buf)?, ingredient: Serializable::read_from(buf)?, result: Serializable::read_from(buf)?, From c532c31530899c0f84b168ce83fa474722c9734a Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Wed, 15 May 2019 12:22:24 -0700 Subject: [PATCH 145/160] Add support for a modded block: rockwool from Thermal Expansion (#153) The first in support for modded content, a simple custom block: "rockwool" from the Thermal Expansion and Thermal Foundation mods for Forge: https://ftb.gamepedia.com/Rockwool_(Thermal_Expansion_3) This makes use of the Forge handshake (#88 #134 #144), matching the mod block names from the negotiation to numeric identifiers in the world to steven_blocks. Rockwool was chosen due to ease of implementation, it looks like wool from vanilla (except is fire-proof), and by supporting it the groundwork necessary is laid for more sophisticated mod support. Tested with Thermal Expansion on 1.7.10, 1.10.2 (FTB Beyond), and 1.12.2 Forge servers. * Add `modid` macro token, skipped from vanilla mappings * Add ThermalExpansionRockwool block (1.7.10) * Register modded blocks by modid->[data], and lookup block metadata * Save block IDs from ModIdData/RegistryData to World modded_block_ids * Add namespaced mod ids for ModIdData, \u{1}=block \u{2}=item * Add ThermalFoundation's Rockwool (1.12.2) --- protocol/src/protocol/forge.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocol/src/protocol/forge.rs b/protocol/src/protocol/forge.rs index 841df92..005a887 100644 --- a/protocol/src/protocol/forge.rs +++ b/protocol/src/protocol/forge.rs @@ -85,6 +85,9 @@ impl Serializable for ModIdMapping { } } +pub static BLOCK_NAMESPACE: &'static str = "\u{1}"; +pub static ITEM_NAMESPACE: &'static str = "\u{2}"; + #[derive(Debug)] pub enum FmlHs { ServerHello { From bdf18cc6d4daf1b6176ede45f524dd1f220e15f7 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Wed, 22 May 2019 16:01:14 -0700 Subject: [PATCH 146/160] Replace println! with log crate facade macros info!, debug!, etc. --- protocol/src/protocol/forge.rs | 3 ++- protocol/src/protocol/mod.rs | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/protocol/src/protocol/forge.rs b/protocol/src/protocol/forge.rs index 005a887..cdfbd5f 100644 --- a/protocol/src/protocol/forge.rs +++ b/protocol/src/protocol/forge.rs @@ -2,6 +2,7 @@ /// Implements https://wiki.vg/Minecraft_Forge_Handshake use std::io; use byteorder::WriteBytesExt; +use log::debug; use crate::protocol::{Serializable, Error, LenPrefixed, VarInt}; @@ -132,7 +133,7 @@ impl Serializable for FmlHs { None }; - println!("FML|HS ServerHello: fml_protocol_version={}, override_dimension={:?}", fml_protocol_version, override_dimension); + debug!("FML|HS ServerHello: fml_protocol_version={}, override_dimension={:?}", fml_protocol_version, override_dimension); Ok(FmlHs::ServerHello { fml_protocol_version, diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index fa500b8..0514312 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -38,6 +38,7 @@ use flate2::read::{ZlibDecoder, ZlibEncoder}; use flate2::Compression; use std::time::{Instant, Duration}; use crate::shared::Position; +use log::debug; pub const SUPPORTED_PROTOCOLS: [i32; 14] = [480, 477, 452, 451, 404, 340, 316, 315, 210, 109, 107, 74, 47, 5]; @@ -1021,7 +1022,7 @@ impl Conn { write.read_to_end(&mut new)?; let network_debug = unsafe { NETWORK_DEBUG }; if network_debug { - println!("Compressed for sending {} bytes to {} since > threshold {}, new={:?}", + debug!("Compressed for sending {} bytes to {} since > threshold {}, new={:?}", uncompressed_size, new.len(), self.compression_threshold, new); } @@ -1054,7 +1055,7 @@ impl Conn { reader.read_to_end(&mut new)?; } if network_debug { - println!("Decompressed threshold={} len={} uncompressed_size={} to {} bytes", + debug!("Decompressed threshold={} len={} uncompressed_size={} to {} bytes", self.compression_threshold, len, uncompressed_size, new.len()); } buf = io::Cursor::new(new); @@ -1068,14 +1069,14 @@ impl Conn { }; if network_debug { - println!("about to parse id={:x}, dir={:?} state={:?}", id, dir, self.state); + debug!("about to parse id={:x}, dir={:?} state={:?}", id, dir, self.state); std::fs::File::create("last-packet")?.write_all(buf.get_ref())?; } let packet = packet::packet_by_id(self.protocol_version, self.state, dir, id, &mut buf)?; if network_debug { - println!("packet = {:?}", packet); + debug!("packet = {:?}", packet); } match packet { From d7340007a064badb336ead70d392be978be46bf9 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 25 May 2019 12:22:46 -0700 Subject: [PATCH 147/160] Use super:: relative instead of crate::protocol absolute import paths --- protocol/src/protocol/forge.rs | 4 ++-- protocol/src/protocol/packet.rs | 2 +- protocol/src/protocol/versions.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/protocol/src/protocol/forge.rs b/protocol/src/protocol/forge.rs index cdfbd5f..8f8fa58 100644 --- a/protocol/src/protocol/forge.rs +++ b/protocol/src/protocol/forge.rs @@ -4,7 +4,7 @@ use std::io; use byteorder::WriteBytesExt; use log::debug; -use crate::protocol::{Serializable, Error, LenPrefixed, VarInt}; +use super::{Serializable, Error, LenPrefixed, VarInt}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Phase { @@ -147,7 +147,7 @@ impl Serializable for FmlHs { }) }, 3 => { - let protocol_version = unsafe { crate::protocol::CURRENT_PROTOCOL_VERSION }; + let protocol_version = unsafe { super::CURRENT_PROTOCOL_VERSION }; if protocol_version >= 47 { Ok(FmlHs::RegistryData { diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 29f666d..9e1734a 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -2484,7 +2484,7 @@ impl Serializable for Recipe { let a = String::read_from(buf)?; let b = String::read_from(buf)?; - let protocol_version = unsafe { crate::protocol::CURRENT_PROTOCOL_VERSION }; + let protocol_version = unsafe { super::CURRENT_PROTOCOL_VERSION }; // 1.14+ swaps recipe identifier and type, and adds namespace to type if protocol_version >= 477 { diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index 554ce88..e55cd8f 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -1,4 +1,4 @@ -use crate::protocol::*; +use super::*; mod v1_14_1; mod v1_14; From 8faeb9f5ccbda0186c1f3073abc2d678b031452d Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Wed, 29 May 2019 08:21:56 -0700 Subject: [PATCH 148/160] wasm: Fix operation not supported on std::fs. Closes #115 (#166) * Add new web-based std::fs replacement, localstoragefs: https://github.com/iceiix/localstoragefs * Add std_or_web to switch between std::fs (native) or localstoragefs (web) * Update www readme for new missing glutin/winit links, opens issue #171 --- protocol/src/protocol/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 0514312..18b251d 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -19,6 +19,7 @@ use aes::Aes128; use cfb8::Cfb8; use cfb8::stream_cipher::{NewStreamCipher, StreamCipher}; use serde_json; +use std_or_web::fs; #[cfg(not(target_arch = "wasm32"))] use reqwest; @@ -1070,7 +1071,7 @@ impl Conn { if network_debug { debug!("about to parse id={:x}, dir={:?} state={:?}", id, dir, self.state); - std::fs::File::create("last-packet")?.write_all(buf.get_ref())?; + fs::File::create("last-packet")?.write_all(buf.get_ref())?; } let packet = packet::packet_by_id(self.protocol_version, self.state, dir, id, &mut buf)?; From 75b94356d25f21b7490d74af29b15abdf52a7108 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Wed, 29 May 2019 08:22:45 -0700 Subject: [PATCH 149/160] 1.14.2 protocol support (485) (#170) * Add protocol 1.14.2 (485) * Copy v1_14_2 from v1_14_1 * Update readme for protocol support --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/versions.rs | 3 + protocol/src/protocol/versions/v1_14_2.rs | 179 ++++++++++++++++++++++ 3 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 protocol/src/protocol/versions/v1_14_2.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 18b251d..ae069f9 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -41,7 +41,7 @@ use std::time::{Instant, Duration}; use crate::shared::Position; use log::debug; -pub const SUPPORTED_PROTOCOLS: [i32; 14] = [480, 477, 452, 451, 404, 340, 316, 315, 210, 109, 107, 74, 47, 5]; +pub const SUPPORTED_PROTOCOLS: [i32; 15] = [485, 480, 477, 452, 451, 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]; diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index e55cd8f..d56d6c3 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -1,5 +1,6 @@ use super::*; +mod v1_14_2; mod v1_14_1; mod v1_14; mod v19w02a; @@ -20,6 +21,7 @@ mod v1_7_10; pub fn protocol_name_to_protocol_version(s: String) -> i32 { match s.as_ref() { "" => SUPPORTED_PROTOCOLS[0], + "1.14.2" => 485, "1.14.1" => 480, "1.14" => 477, "19w02a" => 452, @@ -46,6 +48,7 @@ pub fn protocol_name_to_protocol_version(s: String) -> i32 { pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { + 485 => v1_14_2::translate_internal_packet_id(state, dir, id, to_internal), 480 => v1_14_1::translate_internal_packet_id(state, dir, id, to_internal), 477 => v1_14::translate_internal_packet_id(state, dir, id, to_internal), 452 => v19w02a::translate_internal_packet_id(state, dir, id, to_internal), diff --git a/protocol/src/protocol/versions/v1_14_2.rs b/protocol/src/protocol/versions/v1_14_2.rs new file mode 100644 index 0000000..d30f36a --- /dev/null +++ b/protocol/src/protocol/versions/v1_14_2.rs @@ -0,0 +1,179 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => QueryBlockNBT + 0x02 => SetDifficulty + 0x03 => ChatMessage + 0x04 => ClientStatus + 0x05 => ClientSettings + 0x06 => TabComplete + 0x07 => ConfirmTransactionServerbound + 0x08 => ClickWindowButton + 0x09 => ClickWindow + 0x0a => CloseWindow + 0x0b => PluginMessageServerbound + 0x0c => EditBook + 0x0d => QueryEntityNBT + 0x0e => UseEntity + 0x0f => KeepAliveServerbound_i64 + 0x10 => LockDifficulty + 0x11 => PlayerPosition + 0x12 => PlayerPositionLook + 0x13 => PlayerLook + 0x14 => Player + 0x15 => VehicleMove + 0x16 => SteerBoat + 0x17 => PickItem + 0x18 => CraftRecipeRequest + 0x19 => ClientAbilities + 0x1a => PlayerDigging + 0x1b => PlayerAction + 0x1c => SteerVehicle + 0x1d => CraftingBookData + 0x1e => NameItem + 0x1f => ResourcePackStatus + 0x20 => AdvancementTab + 0x21 => SelectTrade + 0x22 => SetBeaconEffect + 0x23 => HeldItemChange + 0x24 => UpdateCommandBlock + 0x25 => UpdateCommandBlockMinecart + 0x26 => CreativeInventoryAction + 0x27 => UpdateJigsawBlock + 0x28 => UpdateStructureBlock + 0x29 => SetSign + 0x2a => ArmSwing + 0x2b => SpectateTeleport + 0x2c => PlayerBlockPlacement_f32 + 0x2d => 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_Locked + 0x0e => ServerMessage + 0x0f => MultiBlockChange_VarInt + 0x10 => TabCompleteReply + 0x11 => DeclareCommands + 0x12 => ConfirmTransaction + 0x13 => WindowClose + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => NamedSoundEffect + 0x1a => Disconnect + 0x1b => EntityAction + 0x1c => Explosion + 0x1d => ChunkUnload + 0x1e => ChangeGameState + 0x1f => WindowOpenHorse + 0x20 => KeepAliveClientbound_i64 + 0x21 => ChunkData_HeightMap + 0x22 => Effect + 0x23 => Particle_Data + 0x24 => UpdateLight + 0x25 => JoinGame_i32_ViewDistance + 0x26 => Maps + 0x27 => TradeList + 0x28 => EntityMove_i16 + 0x29 => EntityLookAndMove_i16 + 0x2a => EntityLook_VarInt + 0x2b => Entity + 0x2c => VehicleTeleport + 0x2d => OpenBook + 0x2e => WindowOpen_VarInt + 0x2f => SignEditorOpen + 0x30 => CraftRecipeResponse + 0x31 => PlayerAbilities + 0x32 => CombatEvent + 0x33 => PlayerInfo + 0x34 => FacePlayer + 0x35 => TeleportPlayer_WithConfirm + 0x36 => UnlockRecipes_WithSmelting + 0x37 => EntityDestroy + 0x38 => EntityRemoveEffect + 0x39 => ResourcePackSend + 0x3a => Respawn + 0x3b => EntityHeadLook + 0x3c => SelectAdvancementTab + 0x3d => WorldBorder + 0x3e => Camera + 0x3f => SetCurrentHotbarSlot + 0x40 => UpdateViewPosition + 0x41 => UpdateViewDistance + 0x42 => ScoreboardDisplay + 0x43 => EntityMetadata + 0x44 => EntityAttach + 0x45 => EntityVelocity + 0x46 => EntityEquipment + 0x47 => SetExperience + 0x48 => UpdateHealth + 0x49 => ScoreboardObjective + 0x4a => SetPassengers + 0x4b => Teams + 0x4c => UpdateScore + 0x4d => SpawnPosition + 0x4e => TimeUpdate + 0x4f => Title + 0x50 => EntitySoundEffect + 0x51 => SoundEffect + 0x52 => StopSound + 0x53 => PlayerListHeaderFooter + 0x54 => NBTQueryResponse + 0x55 => CollectItem + 0x56 => EntityTeleport_f64 + 0x57 => Advancements + 0x58 => EntityProperties + 0x59 => EntityEffect + 0x5a => DeclareRecipes + 0x5b => TagsWithEntities + } + } + 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 + } + } +); + + From 74d37d7150870a1a682b0fcc44bf48a3ff76be88 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Thu, 30 May 2019 18:13:54 -0700 Subject: [PATCH 150/160] Fix redundant imports on rustc 0.35.0. Fixes #175 --- protocol/src/protocol/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index ae069f9..76a9888 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -22,6 +22,7 @@ use serde_json; use std_or_web::fs; #[cfg(not(target_arch = "wasm32"))] use reqwest; +use hex; pub mod mojang; pub mod forge; @@ -409,7 +410,6 @@ pub struct UUID(u64, u64); impl UUID { pub fn from_str(s: &str) -> UUID { - use hex; // TODO: Panics aren't the best idea here if s.len() != 36 { panic!("Invalid UUID format"); From c0849e4a29bbc6efd0822398376a0b6828def878 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 7 Jul 2019 14:58:49 -0700 Subject: [PATCH 151/160] 1.14.3 protocol support (490) (#194) * Copy v1_14_3 from v1_14_2 * Add protocol 490 for v1_14_3 * Add missing 1.14 release TradeList packet fields TradeList only had fields from the prerelease: 19w02a (452): https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14515#Trade_List add the six missing fields present in the 1.14 release: 1.14 (477): https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14752#Trade_List Note this breaks 19w02a TradeList parsing, can be fixed by splitting the packet for 19w02a/1.14 if needed later. * Add TradeList_WithRestock variant with new can_restock field --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 19 ++- protocol/src/protocol/versions.rs | 3 + protocol/src/protocol/versions/v19w02a.rs | 2 +- protocol/src/protocol/versions/v1_14.rs | 2 +- protocol/src/protocol/versions/v1_14_1.rs | 2 +- protocol/src/protocol/versions/v1_14_2.rs | 2 +- protocol/src/protocol/versions/v1_14_3.rs | 179 ++++++++++++++++++++++ 8 files changed, 205 insertions(+), 6 deletions(-) create mode 100644 protocol/src/protocol/versions/v1_14_3.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 76a9888..9eae973 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -42,7 +42,7 @@ use std::time::{Instant, Duration}; use crate::shared::Position; use log::debug; -pub const SUPPORTED_PROTOCOLS: [i32; 15] = [485, 480, 477, 452, 451, 404, 340, 316, 315, 210, 109, 107, 74, 47, 5]; +pub const SUPPORTED_PROTOCOLS: [i32; 16] = [490, 485, 480, 477, 452, 451, 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]; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 9e1734a..e3c8fb4 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1760,9 +1760,20 @@ state_packets!( field empty_sky_light_mask: VarInt =, field light_arrays: Vec =, } - packet TradeList { + packet TradeList_WithoutRestock { field id: VarInt =, field trades: LenPrefixed =, + field villager_level: VarInt =, + field experience: VarInt =, + field is_regular_villager: bool =, + } + packet TradeList_WithRestock { + field id: VarInt =, + field trades: LenPrefixed =, + field villager_level: VarInt =, + field experience: VarInt =, + field is_regular_villager: bool =, + field can_restock: bool =, } packet CoFHLib_SendUUID { field player_uuid: UUID =, @@ -2611,6 +2622,9 @@ pub struct Trade { pub trades_disabled: bool, pub tool_uses: i32, pub max_trade_uses: i32, + pub xp: i32, + pub special_price: i32, + pub price_multiplier: f32, } impl Serializable for Trade { @@ -2623,6 +2637,9 @@ impl Serializable for Trade { trades_disabled: Serializable::read_from(buf)?, tool_uses: Serializable::read_from(buf)?, max_trade_uses: Serializable::read_from(buf)?, + xp: Serializable::read_from(buf)?, + special_price: Serializable::read_from(buf)?, + price_multiplier: Serializable::read_from(buf)?, }) } diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index d56d6c3..b61b7bf 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -1,5 +1,6 @@ use super::*; +mod v1_14_3; mod v1_14_2; mod v1_14_1; mod v1_14; @@ -21,6 +22,7 @@ mod v1_7_10; pub fn protocol_name_to_protocol_version(s: String) -> i32 { match s.as_ref() { "" => SUPPORTED_PROTOCOLS[0], + "1.14.3" => 490, "1.14.2" => 485, "1.14.1" => 480, "1.14" => 477, @@ -48,6 +50,7 @@ pub fn protocol_name_to_protocol_version(s: String) -> i32 { pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { + 490 => v1_14_3::translate_internal_packet_id(state, dir, id, to_internal), 485 => v1_14_2::translate_internal_packet_id(state, dir, id, to_internal), 480 => v1_14_1::translate_internal_packet_id(state, dir, id, to_internal), 477 => v1_14::translate_internal_packet_id(state, dir, id, to_internal), diff --git a/protocol/src/protocol/versions/v19w02a.rs b/protocol/src/protocol/versions/v19w02a.rs index e8259c2..b3ca589 100644 --- a/protocol/src/protocol/versions/v19w02a.rs +++ b/protocol/src/protocol/versions/v19w02a.rs @@ -142,7 +142,7 @@ protocol_packet_ids!( 0x57 => TagsWithEntities 0x58 => UpdateLight 0x59 => WindowOpen_VarInt - 0x5a => TradeList + 0x5a => TradeList_WithoutRestock // TODO: without 1.14 added fields } } login Login { diff --git a/protocol/src/protocol/versions/v1_14.rs b/protocol/src/protocol/versions/v1_14.rs index d30f36a..98bfb41 100644 --- a/protocol/src/protocol/versions/v1_14.rs +++ b/protocol/src/protocol/versions/v1_14.rs @@ -95,7 +95,7 @@ protocol_packet_ids!( 0x24 => UpdateLight 0x25 => JoinGame_i32_ViewDistance 0x26 => Maps - 0x27 => TradeList + 0x27 => TradeList_WithoutRestock 0x28 => EntityMove_i16 0x29 => EntityLookAndMove_i16 0x2a => EntityLook_VarInt diff --git a/protocol/src/protocol/versions/v1_14_1.rs b/protocol/src/protocol/versions/v1_14_1.rs index d30f36a..98bfb41 100644 --- a/protocol/src/protocol/versions/v1_14_1.rs +++ b/protocol/src/protocol/versions/v1_14_1.rs @@ -95,7 +95,7 @@ protocol_packet_ids!( 0x24 => UpdateLight 0x25 => JoinGame_i32_ViewDistance 0x26 => Maps - 0x27 => TradeList + 0x27 => TradeList_WithoutRestock 0x28 => EntityMove_i16 0x29 => EntityLookAndMove_i16 0x2a => EntityLook_VarInt diff --git a/protocol/src/protocol/versions/v1_14_2.rs b/protocol/src/protocol/versions/v1_14_2.rs index d30f36a..98bfb41 100644 --- a/protocol/src/protocol/versions/v1_14_2.rs +++ b/protocol/src/protocol/versions/v1_14_2.rs @@ -95,7 +95,7 @@ protocol_packet_ids!( 0x24 => UpdateLight 0x25 => JoinGame_i32_ViewDistance 0x26 => Maps - 0x27 => TradeList + 0x27 => TradeList_WithoutRestock 0x28 => EntityMove_i16 0x29 => EntityLookAndMove_i16 0x2a => EntityLook_VarInt diff --git a/protocol/src/protocol/versions/v1_14_3.rs b/protocol/src/protocol/versions/v1_14_3.rs new file mode 100644 index 0000000..e5dcc1d --- /dev/null +++ b/protocol/src/protocol/versions/v1_14_3.rs @@ -0,0 +1,179 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => QueryBlockNBT + 0x02 => SetDifficulty + 0x03 => ChatMessage + 0x04 => ClientStatus + 0x05 => ClientSettings + 0x06 => TabComplete + 0x07 => ConfirmTransactionServerbound + 0x08 => ClickWindowButton + 0x09 => ClickWindow + 0x0a => CloseWindow + 0x0b => PluginMessageServerbound + 0x0c => EditBook + 0x0d => QueryEntityNBT + 0x0e => UseEntity + 0x0f => KeepAliveServerbound_i64 + 0x10 => LockDifficulty + 0x11 => PlayerPosition + 0x12 => PlayerPositionLook + 0x13 => PlayerLook + 0x14 => Player + 0x15 => VehicleMove + 0x16 => SteerBoat + 0x17 => PickItem + 0x18 => CraftRecipeRequest + 0x19 => ClientAbilities + 0x1a => PlayerDigging + 0x1b => PlayerAction + 0x1c => SteerVehicle + 0x1d => CraftingBookData + 0x1e => NameItem + 0x1f => ResourcePackStatus + 0x20 => AdvancementTab + 0x21 => SelectTrade + 0x22 => SetBeaconEffect + 0x23 => HeldItemChange + 0x24 => UpdateCommandBlock + 0x25 => UpdateCommandBlockMinecart + 0x26 => CreativeInventoryAction + 0x27 => UpdateJigsawBlock + 0x28 => UpdateStructureBlock + 0x29 => SetSign + 0x2a => ArmSwing + 0x2b => SpectateTeleport + 0x2c => PlayerBlockPlacement_f32 + 0x2d => 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_Locked + 0x0e => ServerMessage + 0x0f => MultiBlockChange_VarInt + 0x10 => TabCompleteReply + 0x11 => DeclareCommands + 0x12 => ConfirmTransaction + 0x13 => WindowClose + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => NamedSoundEffect + 0x1a => Disconnect + 0x1b => EntityAction + 0x1c => Explosion + 0x1d => ChunkUnload + 0x1e => ChangeGameState + 0x1f => WindowOpenHorse + 0x20 => KeepAliveClientbound_i64 + 0x21 => ChunkData_HeightMap + 0x22 => Effect + 0x23 => Particle_Data + 0x24 => UpdateLight + 0x25 => JoinGame_i32_ViewDistance + 0x26 => Maps + 0x27 => TradeList_WithRestock + 0x28 => EntityMove_i16 + 0x29 => EntityLookAndMove_i16 + 0x2a => EntityLook_VarInt + 0x2b => Entity + 0x2c => VehicleTeleport + 0x2d => OpenBook + 0x2e => WindowOpen_VarInt + 0x2f => SignEditorOpen + 0x30 => CraftRecipeResponse + 0x31 => PlayerAbilities + 0x32 => CombatEvent + 0x33 => PlayerInfo + 0x34 => FacePlayer + 0x35 => TeleportPlayer_WithConfirm + 0x36 => UnlockRecipes_WithSmelting + 0x37 => EntityDestroy + 0x38 => EntityRemoveEffect + 0x39 => ResourcePackSend + 0x3a => Respawn + 0x3b => EntityHeadLook + 0x3c => SelectAdvancementTab + 0x3d => WorldBorder + 0x3e => Camera + 0x3f => SetCurrentHotbarSlot + 0x40 => UpdateViewPosition + 0x41 => UpdateViewDistance + 0x42 => ScoreboardDisplay + 0x43 => EntityMetadata + 0x44 => EntityAttach + 0x45 => EntityVelocity + 0x46 => EntityEquipment + 0x47 => SetExperience + 0x48 => UpdateHealth + 0x49 => ScoreboardObjective + 0x4a => SetPassengers + 0x4b => Teams + 0x4c => UpdateScore + 0x4d => SpawnPosition + 0x4e => TimeUpdate + 0x4f => Title + 0x50 => EntitySoundEffect + 0x51 => SoundEffect + 0x52 => StopSound + 0x53 => PlayerListHeaderFooter + 0x54 => NBTQueryResponse + 0x55 => CollectItem + 0x56 => EntityTeleport_f64 + 0x57 => Advancements + 0x58 => EntityProperties + 0x59 => EntityEffect + 0x5a => DeclareRecipes + 0x5b => TagsWithEntities + } + } + 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 + } + } +); + + From a095e26a221448eff2894f8dc1068966d6d4d96f Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 11 Aug 2019 17:26:20 -0700 Subject: [PATCH 152/160] 1.14.4 protocol support (498) (#207) * Add protocol 498 for 1.14.4 * Add 0x5c AcknowledgePlayerDigging packet, https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14870#Acknowledge_Player_Digging * Add new demand field in protocol::Trade https://wiki.vg/index.php?title=Pre-release_protocol&type=revision&diff=14862&oldid=14856 MC-151282 - Villager trade GUI does not show the correct price on servers if trade demand is high --- protocol/src/protocol/mod.rs | 2 +- protocol/src/protocol/packet.rs | 10 ++ protocol/src/protocol/versions.rs | 3 + protocol/src/protocol/versions/v1_14_4.rs | 180 ++++++++++++++++++++++ 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 protocol/src/protocol/versions/v1_14_4.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 9eae973..a879066 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -42,7 +42,7 @@ use std::time::{Instant, Duration}; use crate::shared::Position; use log::debug; -pub const SUPPORTED_PROTOCOLS: [i32; 16] = [490, 485, 480, 477, 452, 451, 404, 340, 316, 315, 210, 109, 107, 74, 47, 5]; +pub const SUPPORTED_PROTOCOLS: [i32; 17] = [498, 490, 485, 480, 477, 452, 451, 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]; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index e3c8fb4..93fc393 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1752,6 +1752,12 @@ state_packets!( field fluid_tags: LenPrefixed =, field entity_tags: LenPrefixed =, } + packet AcknowledgePlayerDigging { + field location: Position =, + field block: VarInt =, + field status: VarInt =, + field successful: bool =, + } packet UpdateLight { field chunk_x: VarInt =, field chunk_z: VarInt =, @@ -2625,10 +2631,13 @@ pub struct Trade { pub xp: i32, pub special_price: i32, pub price_multiplier: f32, + pub demand: Option, } impl Serializable for Trade { fn read_from(buf: &mut R) -> Result { + let protocol_version = unsafe { super::CURRENT_PROTOCOL_VERSION }; + Ok(Trade { input_item_1: Serializable::read_from(buf)?, output_item: Serializable::read_from(buf)?, @@ -2640,6 +2649,7 @@ impl Serializable for Trade { xp: Serializable::read_from(buf)?, special_price: Serializable::read_from(buf)?, price_multiplier: Serializable::read_from(buf)?, + demand: if protocol_version >= 498 { Some(Serializable::read_from(buf)?) } else { None }, }) } diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index b61b7bf..9912bb1 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -1,5 +1,6 @@ use super::*; +mod v1_14_4; mod v1_14_3; mod v1_14_2; mod v1_14_1; @@ -22,6 +23,7 @@ mod v1_7_10; pub fn protocol_name_to_protocol_version(s: String) -> i32 { match s.as_ref() { "" => SUPPORTED_PROTOCOLS[0], + "1.14.4" => 498, "1.14.3" => 490, "1.14.2" => 485, "1.14.1" => 480, @@ -50,6 +52,7 @@ pub fn protocol_name_to_protocol_version(s: String) -> i32 { pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { + 498 => v1_14_4::translate_internal_packet_id(state, dir, id, to_internal), 490 => v1_14_3::translate_internal_packet_id(state, dir, id, to_internal), 485 => v1_14_2::translate_internal_packet_id(state, dir, id, to_internal), 480 => v1_14_1::translate_internal_packet_id(state, dir, id, to_internal), diff --git a/protocol/src/protocol/versions/v1_14_4.rs b/protocol/src/protocol/versions/v1_14_4.rs new file mode 100644 index 0000000..8e7e35b --- /dev/null +++ b/protocol/src/protocol/versions/v1_14_4.rs @@ -0,0 +1,180 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => QueryBlockNBT + 0x02 => SetDifficulty + 0x03 => ChatMessage + 0x04 => ClientStatus + 0x05 => ClientSettings + 0x06 => TabComplete + 0x07 => ConfirmTransactionServerbound + 0x08 => ClickWindowButton + 0x09 => ClickWindow + 0x0a => CloseWindow + 0x0b => PluginMessageServerbound + 0x0c => EditBook + 0x0d => QueryEntityNBT + 0x0e => UseEntity + 0x0f => KeepAliveServerbound_i64 + 0x10 => LockDifficulty + 0x11 => PlayerPosition + 0x12 => PlayerPositionLook + 0x13 => PlayerLook + 0x14 => Player + 0x15 => VehicleMove + 0x16 => SteerBoat + 0x17 => PickItem + 0x18 => CraftRecipeRequest + 0x19 => ClientAbilities + 0x1a => PlayerDigging + 0x1b => PlayerAction + 0x1c => SteerVehicle + 0x1d => CraftingBookData + 0x1e => NameItem + 0x1f => ResourcePackStatus + 0x20 => AdvancementTab + 0x21 => SelectTrade + 0x22 => SetBeaconEffect + 0x23 => HeldItemChange + 0x24 => UpdateCommandBlock + 0x25 => UpdateCommandBlockMinecart + 0x26 => CreativeInventoryAction + 0x27 => UpdateJigsawBlock + 0x28 => UpdateStructureBlock + 0x29 => SetSign + 0x2a => ArmSwing + 0x2b => SpectateTeleport + 0x2c => PlayerBlockPlacement_f32 + 0x2d => 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_Locked + 0x0e => ServerMessage + 0x0f => MultiBlockChange_VarInt + 0x10 => TabCompleteReply + 0x11 => DeclareCommands + 0x12 => ConfirmTransaction + 0x13 => WindowClose + 0x14 => WindowItems + 0x15 => WindowProperty + 0x16 => WindowSetSlot + 0x17 => SetCooldown + 0x18 => PluginMessageClientbound + 0x19 => NamedSoundEffect + 0x1a => Disconnect + 0x1b => EntityAction + 0x1c => Explosion + 0x1d => ChunkUnload + 0x1e => ChangeGameState + 0x1f => WindowOpenHorse + 0x20 => KeepAliveClientbound_i64 + 0x21 => ChunkData_HeightMap + 0x22 => Effect + 0x23 => Particle_Data + 0x24 => UpdateLight + 0x25 => JoinGame_i32_ViewDistance + 0x26 => Maps + 0x27 => TradeList_WithRestock + 0x28 => EntityMove_i16 + 0x29 => EntityLookAndMove_i16 + 0x2a => EntityLook_VarInt + 0x2b => Entity + 0x2c => VehicleTeleport + 0x2d => OpenBook + 0x2e => WindowOpen_VarInt + 0x2f => SignEditorOpen + 0x30 => CraftRecipeResponse + 0x31 => PlayerAbilities + 0x32 => CombatEvent + 0x33 => PlayerInfo + 0x34 => FacePlayer + 0x35 => TeleportPlayer_WithConfirm + 0x36 => UnlockRecipes_WithSmelting + 0x37 => EntityDestroy + 0x38 => EntityRemoveEffect + 0x39 => ResourcePackSend + 0x3a => Respawn + 0x3b => EntityHeadLook + 0x3c => SelectAdvancementTab + 0x3d => WorldBorder + 0x3e => Camera + 0x3f => SetCurrentHotbarSlot + 0x40 => UpdateViewPosition + 0x41 => UpdateViewDistance + 0x42 => ScoreboardDisplay + 0x43 => EntityMetadata + 0x44 => EntityAttach + 0x45 => EntityVelocity + 0x46 => EntityEquipment + 0x47 => SetExperience + 0x48 => UpdateHealth + 0x49 => ScoreboardObjective + 0x4a => SetPassengers + 0x4b => Teams + 0x4c => UpdateScore + 0x4d => SpawnPosition + 0x4e => TimeUpdate + 0x4f => Title + 0x50 => EntitySoundEffect + 0x51 => SoundEffect + 0x52 => StopSound + 0x53 => PlayerListHeaderFooter + 0x54 => NBTQueryResponse + 0x55 => CollectItem + 0x56 => EntityTeleport_f64 + 0x57 => Advancements + 0x58 => EntityProperties + 0x59 => EntityEffect + 0x5a => DeclareRecipes + 0x5b => TagsWithEntities + 0x5c => AcknowledgePlayerDigging + } + } + 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 + } + } +); + + From 40c7a64c45ba77720c74b9ca4e0f9eececae15c5 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 11 Aug 2019 17:46:34 -0700 Subject: [PATCH 153/160] 1.13+: Fix parsing Teams packet. Closes #205 (#208) * Add Teams_VarInt packet variant for 1.13+ https://wiki.vg/index.php?title=Pre-release_protocol&oldid=14150#Teams * Rename the variant for 1.9-1.12.2 to Teams_u8 --- protocol/src/protocol/packet.rs | 31 ++++++++++++++++------- protocol/src/protocol/versions/v15w39c.rs | 2 +- protocol/src/protocol/versions/v18w50a.rs | 2 +- protocol/src/protocol/versions/v19w02a.rs | 2 +- protocol/src/protocol/versions/v1_10_2.rs | 2 +- protocol/src/protocol/versions/v1_11_2.rs | 2 +- protocol/src/protocol/versions/v1_12_2.rs | 2 +- protocol/src/protocol/versions/v1_13_2.rs | 2 +- protocol/src/protocol/versions/v1_14.rs | 2 +- protocol/src/protocol/versions/v1_14_1.rs | 2 +- protocol/src/protocol/versions/v1_14_2.rs | 2 +- protocol/src/protocol/versions/v1_14_3.rs | 2 +- protocol/src/protocol/versions/v1_14_4.rs | 2 +- protocol/src/protocol/versions/v1_8_9.rs | 2 +- protocol/src/protocol/versions/v1_9.rs | 2 +- protocol/src/protocol/versions/v1_9_2.rs | 2 +- 16 files changed, 37 insertions(+), 24 deletions(-) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 93fc393..9e7fb1f 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -1529,17 +1529,30 @@ state_packets!( field passengers: LenPrefixed =, } /// Teams creates and updates teams - packet Teams { + packet Teams_VarInt { field name: String =, field mode: u8 =, - field display_name: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - field prefix: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - field suffix: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - field flags: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - field name_tag_visibility: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - field collision_rule: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - field color: Option = when(|p: &Teams| p.mode == 0 || p.mode == 2), - field players: Option> = when(|p: &Teams| p.mode == 0 || p.mode == 3 || p.mode == 4), + field display_name: Option = when(|p: &Teams_VarInt| p.mode == 0 || p.mode == 2), + field flags: Option = when(|p: &Teams_VarInt| p.mode == 0 || p.mode == 2), + field name_tag_visibility: Option = when(|p: &Teams_VarInt| p.mode == 0 || p.mode == 2), + field collision_rule: Option = when(|p: &Teams_VarInt| p.mode == 0 || p.mode == 2), + field formatting: Option = when(|p: &Teams_VarInt| p.mode == 0 || p.mode == 2), + field prefix: Option = when(|p: &Teams_VarInt| p.mode == 0 || p.mode == 2), + field suffix: Option = when(|p: &Teams_VarInt| p.mode == 0 || p.mode == 2), + field players: Option> = when(|p: &Teams_VarInt| p.mode == 0 || p.mode == 3 || p.mode == 4), + } + packet Teams_u8 { + field name: String =, + field mode: u8 =, + field data: Vec =, + field display_name: Option = when(|p: &Teams_u8| p.mode == 0 || p.mode == 2), + field prefix: Option = when(|p: &Teams_u8| p.mode == 0 || p.mode == 2), + field suffix: Option = when(|p: &Teams_u8| p.mode == 0 || p.mode == 2), + field flags: Option = when(|p: &Teams_u8| p.mode == 0 || p.mode == 2), + field name_tag_visibility: Option = when(|p: &Teams_u8| p.mode == 0 || p.mode == 2), + field collision_rule: Option = when(|p: &Teams_u8| p.mode == 0 || p.mode == 2), + field color: Option = when(|p: &Teams_u8| p.mode == 0 || p.mode == 2), + field players: Option> = when(|p: &Teams_u8| p.mode == 0 || p.mode == 3 || p.mode == 4), } packet Teams_NoVisColor { field name: String =, diff --git a/protocol/src/protocol/versions/v15w39c.rs b/protocol/src/protocol/versions/v15w39c.rs index f78c0bd..9801571 100644 --- a/protocol/src/protocol/versions/v15w39c.rs +++ b/protocol/src/protocol/versions/v15w39c.rs @@ -101,7 +101,7 @@ protocol_packet_ids!( 0x3d => SetExperience 0x3e => UpdateHealth 0x3f => ScoreboardObjective - 0x40 => Teams + 0x40 => Teams_u8 0x41 => UpdateScore 0x42 => SpawnPosition 0x43 => TimeUpdate diff --git a/protocol/src/protocol/versions/v18w50a.rs b/protocol/src/protocol/versions/v18w50a.rs index 93e4628..ffb0d03 100644 --- a/protocol/src/protocol/versions/v18w50a.rs +++ b/protocol/src/protocol/versions/v18w50a.rs @@ -125,7 +125,7 @@ protocol_packet_ids!( 0x45 => UpdateHealth 0x46 => ScoreboardObjective 0x47 => SetPassengers - 0x48 => Teams + 0x48 => Teams_u8 0x49 => UpdateScore 0x4a => SpawnPosition 0x4b => TimeUpdate diff --git a/protocol/src/protocol/versions/v19w02a.rs b/protocol/src/protocol/versions/v19w02a.rs index b3ca589..2bc3fa5 100644 --- a/protocol/src/protocol/versions/v19w02a.rs +++ b/protocol/src/protocol/versions/v19w02a.rs @@ -125,7 +125,7 @@ protocol_packet_ids!( 0x45 => UpdateHealth 0x46 => ScoreboardObjective 0x47 => SetPassengers - 0x48 => Teams + 0x48 => Teams_u8 0x49 => UpdateScore 0x4a => SpawnPosition 0x4b => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_10_2.rs b/protocol/src/protocol/versions/v1_10_2.rs index 85dc83a..b28c6a8 100644 --- a/protocol/src/protocol/versions/v1_10_2.rs +++ b/protocol/src/protocol/versions/v1_10_2.rs @@ -105,7 +105,7 @@ protocol_packet_ids!( 0x3e => UpdateHealth 0x3f => ScoreboardObjective 0x40 => SetPassengers - 0x41 => Teams + 0x41 => Teams_u8 0x42 => UpdateScore 0x43 => SpawnPosition 0x44 => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_11_2.rs b/protocol/src/protocol/versions/v1_11_2.rs index 3db3c06..0a622a8 100644 --- a/protocol/src/protocol/versions/v1_11_2.rs +++ b/protocol/src/protocol/versions/v1_11_2.rs @@ -105,7 +105,7 @@ protocol_packet_ids!( 0x3e => UpdateHealth 0x3f => ScoreboardObjective 0x40 => SetPassengers - 0x41 => Teams + 0x41 => Teams_u8 0x42 => UpdateScore 0x43 => SpawnPosition 0x44 => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_12_2.rs b/protocol/src/protocol/versions/v1_12_2.rs index ebd23a0..e98798d 100644 --- a/protocol/src/protocol/versions/v1_12_2.rs +++ b/protocol/src/protocol/versions/v1_12_2.rs @@ -111,7 +111,7 @@ protocol_packet_ids!( 0x41 => UpdateHealth 0x42 => ScoreboardObjective 0x43 => SetPassengers - 0x44 => Teams + 0x44 => Teams_u8 0x45 => UpdateScore 0x46 => SpawnPosition 0x47 => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_13_2.rs b/protocol/src/protocol/versions/v1_13_2.rs index 659ed85..325cb44 100644 --- a/protocol/src/protocol/versions/v1_13_2.rs +++ b/protocol/src/protocol/versions/v1_13_2.rs @@ -124,7 +124,7 @@ protocol_packet_ids!( 0x44 => UpdateHealth 0x45 => ScoreboardObjective 0x46 => SetPassengers - 0x47 => Teams + 0x47 => Teams_VarInt 0x48 => UpdateScore 0x49 => SpawnPosition 0x4a => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_14.rs b/protocol/src/protocol/versions/v1_14.rs index 98bfb41..07cd5c5 100644 --- a/protocol/src/protocol/versions/v1_14.rs +++ b/protocol/src/protocol/versions/v1_14.rs @@ -131,7 +131,7 @@ protocol_packet_ids!( 0x48 => UpdateHealth 0x49 => ScoreboardObjective 0x4a => SetPassengers - 0x4b => Teams + 0x4b => Teams_VarInt 0x4c => UpdateScore 0x4d => SpawnPosition 0x4e => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_14_1.rs b/protocol/src/protocol/versions/v1_14_1.rs index 98bfb41..07cd5c5 100644 --- a/protocol/src/protocol/versions/v1_14_1.rs +++ b/protocol/src/protocol/versions/v1_14_1.rs @@ -131,7 +131,7 @@ protocol_packet_ids!( 0x48 => UpdateHealth 0x49 => ScoreboardObjective 0x4a => SetPassengers - 0x4b => Teams + 0x4b => Teams_VarInt 0x4c => UpdateScore 0x4d => SpawnPosition 0x4e => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_14_2.rs b/protocol/src/protocol/versions/v1_14_2.rs index 98bfb41..07cd5c5 100644 --- a/protocol/src/protocol/versions/v1_14_2.rs +++ b/protocol/src/protocol/versions/v1_14_2.rs @@ -131,7 +131,7 @@ protocol_packet_ids!( 0x48 => UpdateHealth 0x49 => ScoreboardObjective 0x4a => SetPassengers - 0x4b => Teams + 0x4b => Teams_VarInt 0x4c => UpdateScore 0x4d => SpawnPosition 0x4e => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_14_3.rs b/protocol/src/protocol/versions/v1_14_3.rs index e5dcc1d..83d4f32 100644 --- a/protocol/src/protocol/versions/v1_14_3.rs +++ b/protocol/src/protocol/versions/v1_14_3.rs @@ -131,7 +131,7 @@ protocol_packet_ids!( 0x48 => UpdateHealth 0x49 => ScoreboardObjective 0x4a => SetPassengers - 0x4b => Teams + 0x4b => Teams_VarInt 0x4c => UpdateScore 0x4d => SpawnPosition 0x4e => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_14_4.rs b/protocol/src/protocol/versions/v1_14_4.rs index 8e7e35b..0ba3d73 100644 --- a/protocol/src/protocol/versions/v1_14_4.rs +++ b/protocol/src/protocol/versions/v1_14_4.rs @@ -131,7 +131,7 @@ protocol_packet_ids!( 0x48 => UpdateHealth 0x49 => ScoreboardObjective 0x4a => SetPassengers - 0x4b => Teams + 0x4b => Teams_VarInt 0x4c => UpdateScore 0x4d => SpawnPosition 0x4e => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_8_9.rs b/protocol/src/protocol/versions/v1_8_9.rs index 6dc9682..c645c45 100644 --- a/protocol/src/protocol/versions/v1_8_9.rs +++ b/protocol/src/protocol/versions/v1_8_9.rs @@ -98,7 +98,7 @@ protocol_packet_ids!( 0x3b => ScoreboardObjective 0x3c => UpdateScore 0x3d => ScoreboardDisplay - 0x3e => Teams + 0x3e => Teams_u8 0x3f => PluginMessageClientbound 0x40 => Disconnect 0x41 => ServerDifficulty diff --git a/protocol/src/protocol/versions/v1_9.rs b/protocol/src/protocol/versions/v1_9.rs index 9ce2b03..c22d778 100644 --- a/protocol/src/protocol/versions/v1_9.rs +++ b/protocol/src/protocol/versions/v1_9.rs @@ -105,7 +105,7 @@ protocol_packet_ids!( 0x3e => UpdateHealth 0x3f => ScoreboardObjective 0x40 => SetPassengers - 0x41 => Teams + 0x41 => Teams_u8 0x42 => UpdateScore 0x43 => SpawnPosition 0x44 => TimeUpdate diff --git a/protocol/src/protocol/versions/v1_9_2.rs b/protocol/src/protocol/versions/v1_9_2.rs index 4aec3d0..d6e0b81 100644 --- a/protocol/src/protocol/versions/v1_9_2.rs +++ b/protocol/src/protocol/versions/v1_9_2.rs @@ -105,7 +105,7 @@ protocol_packet_ids!( 0x3e => UpdateHealth 0x3f => ScoreboardObjective 0x40 => SetPassengers - 0x41 => Teams + 0x41 => Teams_u8 0x42 => UpdateScore 0x43 => SpawnPosition 0x44 => TimeUpdate From 57da39bb79fe912cdd588aa31d5ea91f6dae07c9 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sat, 17 Aug 2019 13:36:07 -0700 Subject: [PATCH 154/160] Fix SpawnPainting parsing 1.13.2-1.14.4, closes #212 (#213) The packet changed in 1.13.2 but wasn't updated, so we split the packet variants into SpawnPainting_String for the old version and SpawnPainting_VarInt for the new version with a 'motive' VarInt field instead of a String title. 1.9-1.12.2 SpawnPainting_String: https://wiki.vg/index.php?title=Protocol&oldid=14204#Spawn_Painting 1.13.2-1.14.4 SpawnPainting_VarInt: https://wiki.vg/index.php?title=Protocol&oldid=14889#Spawn_Painting --- protocol/src/protocol/packet.rs | 9 ++++++++- protocol/src/protocol/versions/v18w50a.rs | 2 +- protocol/src/protocol/versions/v19w02a.rs | 2 +- protocol/src/protocol/versions/v1_10_2.rs | 2 +- protocol/src/protocol/versions/v1_11_2.rs | 2 +- protocol/src/protocol/versions/v1_12_2.rs | 2 +- protocol/src/protocol/versions/v1_13_2.rs | 2 +- protocol/src/protocol/versions/v1_14.rs | 2 +- protocol/src/protocol/versions/v1_14_1.rs | 2 +- protocol/src/protocol/versions/v1_14_2.rs | 2 +- protocol/src/protocol/versions/v1_14_3.rs | 2 +- protocol/src/protocol/versions/v1_14_4.rs | 2 +- protocol/src/protocol/versions/v1_9.rs | 2 +- protocol/src/protocol/versions/v1_9_2.rs | 2 +- 14 files changed, 21 insertions(+), 14 deletions(-) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 9e7fb1f..10ade72 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -619,7 +619,14 @@ state_packets!( } /// 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. - packet SpawnPainting { + packet SpawnPainting_VarInt { + field entity_id: VarInt =, + field uuid: UUID =, + field motive: VarInt =, + field location: Position =, + field direction: u8 =, + } + packet SpawnPainting_String { field entity_id: VarInt =, field uuid: UUID =, field title: String =, diff --git a/protocol/src/protocol/versions/v18w50a.rs b/protocol/src/protocol/versions/v18w50a.rs index ffb0d03..8eafbd9 100644 --- a/protocol/src/protocol/versions/v18w50a.rs +++ b/protocol/src/protocol/versions/v18w50a.rs @@ -57,7 +57,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob - 0x04 => SpawnPainting + 0x04 => SpawnPainting_String 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v19w02a.rs b/protocol/src/protocol/versions/v19w02a.rs index 2bc3fa5..0014b0e 100644 --- a/protocol/src/protocol/versions/v19w02a.rs +++ b/protocol/src/protocol/versions/v19w02a.rs @@ -57,7 +57,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob - 0x04 => SpawnPainting + 0x04 => SpawnPainting_String 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_10_2.rs b/protocol/src/protocol/versions/v1_10_2.rs index b28c6a8..02ddc6c 100644 --- a/protocol/src/protocol/versions/v1_10_2.rs +++ b/protocol/src/protocol/versions/v1_10_2.rs @@ -44,7 +44,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob_u8 - 0x04 => SpawnPainting + 0x04 => SpawnPainting_String 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_11_2.rs b/protocol/src/protocol/versions/v1_11_2.rs index 0a622a8..4619ba5 100644 --- a/protocol/src/protocol/versions/v1_11_2.rs +++ b/protocol/src/protocol/versions/v1_11_2.rs @@ -44,7 +44,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob - 0x04 => SpawnPainting + 0x04 => SpawnPainting_String 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_12_2.rs b/protocol/src/protocol/versions/v1_12_2.rs index e98798d..338b5fa 100644 --- a/protocol/src/protocol/versions/v1_12_2.rs +++ b/protocol/src/protocol/versions/v1_12_2.rs @@ -47,7 +47,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob - 0x04 => SpawnPainting + 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_13_2.rs b/protocol/src/protocol/versions/v1_13_2.rs index 325cb44..874cca7 100644 --- a/protocol/src/protocol/versions/v1_13_2.rs +++ b/protocol/src/protocol/versions/v1_13_2.rs @@ -57,7 +57,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob - 0x04 => SpawnPainting + 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_14.rs b/protocol/src/protocol/versions/v1_14.rs index 07cd5c5..0107cce 100644 --- a/protocol/src/protocol/versions/v1_14.rs +++ b/protocol/src/protocol/versions/v1_14.rs @@ -60,7 +60,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob - 0x04 => SpawnPainting + 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_14_1.rs b/protocol/src/protocol/versions/v1_14_1.rs index 07cd5c5..0107cce 100644 --- a/protocol/src/protocol/versions/v1_14_1.rs +++ b/protocol/src/protocol/versions/v1_14_1.rs @@ -60,7 +60,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob - 0x04 => SpawnPainting + 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_14_2.rs b/protocol/src/protocol/versions/v1_14_2.rs index 07cd5c5..0107cce 100644 --- a/protocol/src/protocol/versions/v1_14_2.rs +++ b/protocol/src/protocol/versions/v1_14_2.rs @@ -60,7 +60,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob - 0x04 => SpawnPainting + 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_14_3.rs b/protocol/src/protocol/versions/v1_14_3.rs index 83d4f32..75ab579 100644 --- a/protocol/src/protocol/versions/v1_14_3.rs +++ b/protocol/src/protocol/versions/v1_14_3.rs @@ -60,7 +60,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob - 0x04 => SpawnPainting + 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_14_4.rs b/protocol/src/protocol/versions/v1_14_4.rs index 0ba3d73..941dce1 100644 --- a/protocol/src/protocol/versions/v1_14_4.rs +++ b/protocol/src/protocol/versions/v1_14_4.rs @@ -60,7 +60,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob - 0x04 => SpawnPainting + 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_9.rs b/protocol/src/protocol/versions/v1_9.rs index c22d778..7b714e8 100644 --- a/protocol/src/protocol/versions/v1_9.rs +++ b/protocol/src/protocol/versions/v1_9.rs @@ -44,7 +44,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob_u8 - 0x04 => SpawnPainting + 0x04 => SpawnPainting_String 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics diff --git a/protocol/src/protocol/versions/v1_9_2.rs b/protocol/src/protocol/versions/v1_9_2.rs index d6e0b81..ecd1e28 100644 --- a/protocol/src/protocol/versions/v1_9_2.rs +++ b/protocol/src/protocol/versions/v1_9_2.rs @@ -44,7 +44,7 @@ protocol_packet_ids!( 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity 0x03 => SpawnMob_u8 - 0x04 => SpawnPainting + 0x04 => SpawnPainting_String 0x05 => SpawnPlayer_f64 0x06 => Animation 0x07 => Statistics From 7c49ca3c0d3f161feb041adfa27891a009d290bd Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 2 Nov 2019 13:43:00 -0700 Subject: [PATCH 155/160] Add missing time, nbt{_compound,}_tag command nodes. Fixes #227 --- protocol/src/protocol/packet.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 10ade72..a25c865 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -2724,6 +2724,7 @@ pub enum CommandProperty { GameProfile, BlockPos, ColumnPos, + Time, Vec3, Vec2, BlockState, @@ -2735,6 +2736,8 @@ pub enum CommandProperty { Message, Nbt, NbtPath, + NbtTag, + NbtCompoundTag, Objective, ObjectiveCriteria, Operation, @@ -2824,6 +2827,7 @@ impl Serializable for CommandNode { "minecraft:game_profile" => CommandProperty::GameProfile, "minecraft:block_pos" => CommandProperty::BlockPos, "minecraft:column_pos" => CommandProperty::ColumnPos, + "minecraft:time" => CommandProperty::Time, "minecraft:vec3" => CommandProperty::Vec3, "minecraft:vec2" => CommandProperty::Vec2, "minecraft:block_state" => CommandProperty::BlockState, @@ -2835,6 +2839,8 @@ impl Serializable for CommandNode { "minecraft:message" => CommandProperty::Message, "minecraft:nbt" => CommandProperty::Nbt, "minecraft:nbt_path" => CommandProperty::NbtPath, + "minecraft:nbt_tag" => CommandProperty::NbtTag, + "minecraft:nbt_compound_tag" => CommandProperty::NbtCompoundTag, "minecraft:objective" => CommandProperty::Objective, "minecraft:objective_criteria" => CommandProperty::ObjectiveCriteria, "minecraft:operation" => CommandProperty::Operation, From dd2fb32df1a01a95266f6fdd04b95d8d92753bd2 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sun, 29 Dec 2019 15:55:19 -0800 Subject: [PATCH 156/160] 1.15.1 protocol support (575) (#252) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add v1_15_1 for protocol 575 * Update for shifted packet IDs https://wiki.vg/index.php?title=Pre-release_protocol&oldid=15213#Packets https://wiki.vg/Pre-release_protocol * Add SpawnMob packet variant, no metadata * Add SpawnPlayer packet variant, no metadata * Add block update actions comments, including beehive * Add particle packet variant with 64-bit floats * Add and handle join game and respawn packet variants, with hashed seeds * Add chunk data packet variant with 3D biomes https://wiki.vg/index.php?title=Pre-release_protocol&oldid=15213#Chunk_Data "1024 biome IDs, ordered by x then z then d, in 4×4×4 blocks. Not present if full chunk is false." This is a fixed-size array of integers, but Rust doesn't yet support generics over integers, so the 1024-element array doesn't support fmt::Debug, hence we wrap it and implement fmt::Debug ourselves. * Add load_chunk115 to not read chunk data structure biomes https://wiki.vg/index.php?title=Pre-release_protocol&oldid=15213#Data_structure --- protocol/src/protocol/mod.rs | 39 ++++- protocol/src/protocol/packet.rs | 81 +++++++++- protocol/src/protocol/versions.rs | 3 + protocol/src/protocol/versions/v18w50a.rs | 2 +- protocol/src/protocol/versions/v19w02a.rs | 2 +- protocol/src/protocol/versions/v1_11_2.rs | 2 +- protocol/src/protocol/versions/v1_12_2.rs | 2 +- protocol/src/protocol/versions/v1_13_2.rs | 2 +- protocol/src/protocol/versions/v1_14.rs | 2 +- protocol/src/protocol/versions/v1_14_1.rs | 2 +- protocol/src/protocol/versions/v1_14_2.rs | 2 +- protocol/src/protocol/versions/v1_14_3.rs | 2 +- protocol/src/protocol/versions/v1_14_4.rs | 2 +- protocol/src/protocol/versions/v1_15_1.rs | 180 ++++++++++++++++++++++ 14 files changed, 311 insertions(+), 12 deletions(-) create mode 100644 protocol/src/protocol/versions/v1_15_1.rs diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index a879066..1187c18 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -42,7 +42,7 @@ use std::time::{Instant, Duration}; use crate::shared::Position; use log::debug; -pub const SUPPORTED_PROTOCOLS: [i32; 17] = [498, 490, 485, 480, 477, 452, 451, 404, 340, 316, 315, 210, 109, 107, 74, 47, 5]; +pub const SUPPORTED_PROTOCOLS: [i32; 18] = [575, 498, 490, 485, 480, 477, 452, 451, 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]; @@ -447,6 +447,43 @@ impl Serializable for UUID { } } +pub struct Biomes3D { + pub data: [i32; 1024], +} + +impl fmt::Debug for Biomes3D { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Biomes3D(")?; + for i in 0..1024 { + write!(f, "{}, ", self.data[i])?; + } + write!(f, ")") + } +} + +impl Default for Biomes3D { + fn default() -> Self { + Biomes3D { data: [0; 1024] } + } +} + +impl Serializable for Biomes3D { + fn read_from(buf: &mut R) -> Result { + let mut data: [i32; 1024] = [0; 1024]; + + // Non-length-prefixed three-dimensional biome data + for i in 0..1024 { + let b: i32 = Serializable::read_from(buf)?; + data[i] = b; + } + + Result::Ok(Biomes3D { data }) + } + fn write_to(&self, _buf: &mut W) -> Result<(), Error> { + unimplemented!() + } +} + pub trait Lengthable : Serializable + Copy + Default { fn into_len(self) -> usize; diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index a25c865..535f65c 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -558,7 +558,21 @@ state_packets!( } /// SpawnMob is used to spawn a living entity into the world when it is in /// range of the client. - packet SpawnMob { + packet SpawnMob_NoMeta { + field entity_id: VarInt =, + field uuid: UUID =, + field ty: VarInt =, + field x: f64 =, + field y: f64 =, + field z: f64 =, + field yaw: i8 =, + field pitch: i8 =, + field head_pitch: i8 =, + field velocity_x: i16 =, + field velocity_y: i16 =, + field velocity_z: i16 =, + } + packet SpawnMob_WithMeta { field entity_id: VarInt =, field uuid: UUID =, field ty: VarInt =, @@ -650,6 +664,15 @@ state_packets!( /// SpawnPlayer is used to spawn a player when they are in range of the client. /// This packet alone isn't enough to display the player as the skin and username /// information is in the player information packet. + packet SpawnPlayer_f64_NoMeta { + field entity_id: VarInt =, + field uuid: UUID =, + field x: f64 =, + field y: f64 =, + field z: f64 =, + field yaw: i8 =, + field pitch: i8 =, + } packet SpawnPlayer_f64 { field entity_id: VarInt =, field uuid: UUID =, @@ -969,6 +992,16 @@ state_packets!( } /// ChunkData sends or updates a single chunk on the client. If New is set /// then biome data should be sent too. + packet ChunkData_Biomes3D { + field chunk_x: i32 =, + field chunk_z: i32 =, + field new: bool =, + field bitmask: VarInt =, + field heightmaps: Option =, + field biomes: Biomes3D = when(|p: &ChunkData_Biomes3D| p.new), + field data: LenPrefixedBytes =, + field block_entities: LenPrefixed> =, + } packet ChunkData_HeightMap { field chunk_x: i32 =, field chunk_z: i32 =, @@ -1038,6 +1071,24 @@ state_packets!( } /// Particle spawns particles at the target location with the various /// modifiers. + packet Particle_f64 { + field particle_id: i32 =, + field long_distance: bool =, + field x: f64 =, + field y: f64=, + field z: f64 =, + field offset_x: f32 =, + field offset_y: f32 =, + field offset_z: f32 =, + field speed: f32 =, + field count: i32 =, + field block_state: VarInt = when(|p: &Particle_f64| p.particle_id == 3 || p.particle_id == 20), + field red: f32 = when(|p: &Particle_f64| p.particle_id == 11), + field green: f32 = when(|p: &Particle_f64| p.particle_id == 11), + field blue: f32 = when(|p: &Particle_f64| p.particle_id == 11), + field scale: f32 = when(|p: &Particle_f64| p.particle_id == 11), + field item: Option = when(|p: &Particle_f64| p.particle_id == 27), + } packet Particle_Data { field particle_id: i32 =, field long_distance: bool =, @@ -1083,6 +1134,27 @@ state_packets!( } /// JoinGame is sent after completing the login process. This /// sets the initial state for the client. + packet JoinGame_HashedSeed_Respawn { + /// The entity id the client will be referenced by + field entity_id: i32 =, + /// The starting gamemode of the client + field gamemode: u8 =, + /// The dimension the client is starting in + field dimension: i32 =, + /// Truncated SHA-256 hash of world's seed + field hashed_seed: i64 =, + /// The max number of players on the server + field max_players: u8 =, + /// The level type of the server + field level_type: String =, + /// The render distance (2-32) + field view_distance: VarInt =, + /// Whether the client should reduce the amount of debug + /// information it displays in F3 mode + field reduced_debug_info: bool =, + /// Whether to prompt or immediately respawn + field enable_respawn_screen: bool =, + } packet JoinGame_i32_ViewDistance { /// The entity id the client will be referenced by field entity_id: i32 =, @@ -1387,6 +1459,13 @@ state_packets!( field gamemode: u8 =, field level_type: String =, } + packet Respawn_HashedSeed { + field dimension: i32 =, + field hashed_seed: i64 =, + field difficulty: u8 =, + field gamemode: u8 =, + field level_type: String =, + } /// EntityHeadLook rotates an entity's head to the new angle. packet EntityHeadLook { field entity_id: VarInt =, diff --git a/protocol/src/protocol/versions.rs b/protocol/src/protocol/versions.rs index 9912bb1..461bca1 100644 --- a/protocol/src/protocol/versions.rs +++ b/protocol/src/protocol/versions.rs @@ -1,5 +1,6 @@ use super::*; +mod v1_15_1; mod v1_14_4; mod v1_14_3; mod v1_14_2; @@ -23,6 +24,7 @@ mod v1_7_10; pub fn protocol_name_to_protocol_version(s: String) -> i32 { match s.as_ref() { "" => SUPPORTED_PROTOCOLS[0], + "1.15.1" => 575, "1.14.4" => 498, "1.14.3" => 490, "1.14.2" => 485, @@ -52,6 +54,7 @@ pub fn protocol_name_to_protocol_version(s: String) -> i32 { pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 { match version { + 575 => v1_15_1::translate_internal_packet_id(state, dir, id, to_internal), 498 => v1_14_4::translate_internal_packet_id(state, dir, id, to_internal), 490 => v1_14_3::translate_internal_packet_id(state, dir, id, to_internal), 485 => v1_14_2::translate_internal_packet_id(state, dir, id, to_internal), diff --git a/protocol/src/protocol/versions/v18w50a.rs b/protocol/src/protocol/versions/v18w50a.rs index 8eafbd9..0a9d6a9 100644 --- a/protocol/src/protocol/versions/v18w50a.rs +++ b/protocol/src/protocol/versions/v18w50a.rs @@ -56,7 +56,7 @@ protocol_packet_ids!( 0x00 => SpawnObject 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity - 0x03 => SpawnMob + 0x03 => SpawnMob_WithMeta 0x04 => SpawnPainting_String 0x05 => SpawnPlayer_f64 0x06 => Animation diff --git a/protocol/src/protocol/versions/v19w02a.rs b/protocol/src/protocol/versions/v19w02a.rs index 0014b0e..9a2af5c 100644 --- a/protocol/src/protocol/versions/v19w02a.rs +++ b/protocol/src/protocol/versions/v19w02a.rs @@ -56,7 +56,7 @@ protocol_packet_ids!( 0x00 => SpawnObject 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity - 0x03 => SpawnMob + 0x03 => SpawnMob_WithMeta 0x04 => SpawnPainting_String 0x05 => SpawnPlayer_f64 0x06 => Animation diff --git a/protocol/src/protocol/versions/v1_11_2.rs b/protocol/src/protocol/versions/v1_11_2.rs index 4619ba5..aa82c97 100644 --- a/protocol/src/protocol/versions/v1_11_2.rs +++ b/protocol/src/protocol/versions/v1_11_2.rs @@ -43,7 +43,7 @@ protocol_packet_ids!( 0x00 => SpawnObject 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity - 0x03 => SpawnMob + 0x03 => SpawnMob_WithMeta 0x04 => SpawnPainting_String 0x05 => SpawnPlayer_f64 0x06 => Animation diff --git a/protocol/src/protocol/versions/v1_12_2.rs b/protocol/src/protocol/versions/v1_12_2.rs index 338b5fa..1e3b909 100644 --- a/protocol/src/protocol/versions/v1_12_2.rs +++ b/protocol/src/protocol/versions/v1_12_2.rs @@ -46,7 +46,7 @@ protocol_packet_ids!( 0x00 => SpawnObject 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity - 0x03 => SpawnMob + 0x03 => SpawnMob_WithMeta 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation diff --git a/protocol/src/protocol/versions/v1_13_2.rs b/protocol/src/protocol/versions/v1_13_2.rs index 874cca7..04871b1 100644 --- a/protocol/src/protocol/versions/v1_13_2.rs +++ b/protocol/src/protocol/versions/v1_13_2.rs @@ -56,7 +56,7 @@ protocol_packet_ids!( 0x00 => SpawnObject 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity - 0x03 => SpawnMob + 0x03 => SpawnMob_WithMeta 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation diff --git a/protocol/src/protocol/versions/v1_14.rs b/protocol/src/protocol/versions/v1_14.rs index 0107cce..b5c9a9a 100644 --- a/protocol/src/protocol/versions/v1_14.rs +++ b/protocol/src/protocol/versions/v1_14.rs @@ -59,7 +59,7 @@ protocol_packet_ids!( 0x00 => SpawnObject 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity - 0x03 => SpawnMob + 0x03 => SpawnMob_WithMeta 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation diff --git a/protocol/src/protocol/versions/v1_14_1.rs b/protocol/src/protocol/versions/v1_14_1.rs index 0107cce..b5c9a9a 100644 --- a/protocol/src/protocol/versions/v1_14_1.rs +++ b/protocol/src/protocol/versions/v1_14_1.rs @@ -59,7 +59,7 @@ protocol_packet_ids!( 0x00 => SpawnObject 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity - 0x03 => SpawnMob + 0x03 => SpawnMob_WithMeta 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation diff --git a/protocol/src/protocol/versions/v1_14_2.rs b/protocol/src/protocol/versions/v1_14_2.rs index 0107cce..b5c9a9a 100644 --- a/protocol/src/protocol/versions/v1_14_2.rs +++ b/protocol/src/protocol/versions/v1_14_2.rs @@ -59,7 +59,7 @@ protocol_packet_ids!( 0x00 => SpawnObject 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity - 0x03 => SpawnMob + 0x03 => SpawnMob_WithMeta 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation diff --git a/protocol/src/protocol/versions/v1_14_3.rs b/protocol/src/protocol/versions/v1_14_3.rs index 75ab579..a5eebf2 100644 --- a/protocol/src/protocol/versions/v1_14_3.rs +++ b/protocol/src/protocol/versions/v1_14_3.rs @@ -59,7 +59,7 @@ protocol_packet_ids!( 0x00 => SpawnObject 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity - 0x03 => SpawnMob + 0x03 => SpawnMob_WithMeta 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation diff --git a/protocol/src/protocol/versions/v1_14_4.rs b/protocol/src/protocol/versions/v1_14_4.rs index 941dce1..60be91a 100644 --- a/protocol/src/protocol/versions/v1_14_4.rs +++ b/protocol/src/protocol/versions/v1_14_4.rs @@ -59,7 +59,7 @@ protocol_packet_ids!( 0x00 => SpawnObject 0x01 => SpawnExperienceOrb 0x02 => SpawnGlobalEntity - 0x03 => SpawnMob + 0x03 => SpawnMob_WithMeta 0x04 => SpawnPainting_VarInt 0x05 => SpawnPlayer_f64 0x06 => Animation diff --git a/protocol/src/protocol/versions/v1_15_1.rs b/protocol/src/protocol/versions/v1_15_1.rs new file mode 100644 index 0000000..6c3386c --- /dev/null +++ b/protocol/src/protocol/versions/v1_15_1.rs @@ -0,0 +1,180 @@ +protocol_packet_ids!( + handshake Handshaking { + serverbound Serverbound { + 0x00 => Handshake + } + clientbound Clientbound { + } + } + play Play { + serverbound Serverbound { + 0x00 => TeleportConfirm + 0x01 => QueryBlockNBT + 0x02 => SetDifficulty + 0x03 => ChatMessage + 0x04 => ClientStatus + 0x05 => ClientSettings + 0x06 => TabComplete + 0x07 => ConfirmTransactionServerbound + 0x08 => ClickWindowButton + 0x09 => ClickWindow + 0x0a => CloseWindow + 0x0b => PluginMessageServerbound + 0x0c => EditBook + 0x0d => QueryEntityNBT + 0x0e => UseEntity + 0x0f => KeepAliveServerbound_i64 + 0x10 => LockDifficulty + 0x11 => PlayerPosition + 0x12 => PlayerPositionLook + 0x13 => PlayerLook + 0x14 => Player + 0x15 => VehicleMove + 0x16 => SteerBoat + 0x17 => PickItem + 0x18 => CraftRecipeRequest + 0x19 => ClientAbilities + 0x1a => PlayerDigging + 0x1b => PlayerAction + 0x1c => SteerVehicle + 0x1d => CraftingBookData + 0x1e => NameItem + 0x1f => ResourcePackStatus + 0x20 => AdvancementTab + 0x21 => SelectTrade + 0x22 => SetBeaconEffect + 0x23 => HeldItemChange + 0x24 => UpdateCommandBlock + 0x25 => UpdateCommandBlockMinecart + 0x26 => CreativeInventoryAction + 0x27 => UpdateJigsawBlock + 0x28 => UpdateStructureBlock + 0x29 => SetSign + 0x2a => ArmSwing + 0x2b => SpectateTeleport + 0x2c => PlayerBlockPlacement_f32 + 0x2d => UseItem + } + clientbound Clientbound { + 0x00 => SpawnObject + 0x01 => SpawnExperienceOrb + 0x02 => SpawnGlobalEntity + 0x03 => SpawnMob_NoMeta + 0x04 => SpawnPainting_VarInt + 0x05 => SpawnPlayer_f64_NoMeta + 0x06 => Animation + 0x07 => Statistics + 0x08 => AcknowledgePlayerDigging + 0x09 => BlockBreakAnimation + 0x0a => UpdateBlockEntity + 0x0b => BlockAction + 0x0c => BlockChange_VarInt + 0x0d => BossBar + 0x0e => ServerDifficulty_Locked + 0x0f => ServerMessage + 0x10 => MultiBlockChange_VarInt + 0x11 => TabCompleteReply + 0x12 => DeclareCommands + 0x13 => ConfirmTransaction + 0x14 => WindowClose + 0x15 => WindowItems + 0x16 => WindowProperty + 0x17 => WindowSetSlot + 0x18 => SetCooldown + 0x19 => PluginMessageClientbound + 0x1a => NamedSoundEffect + 0x1b => Disconnect + 0x1c => EntityAction + 0x1d => Explosion + 0x1e => ChunkUnload + 0x1f => ChangeGameState + 0x20 => WindowOpenHorse + 0x21 => KeepAliveClientbound_i64 + 0x22 => ChunkData_Biomes3D + 0x23 => Effect + 0x24 => Particle_f64 + 0x25 => UpdateLight + 0x26 => JoinGame_HashedSeed_Respawn + 0x27 => Maps + 0x28 => TradeList_WithRestock + 0x29 => EntityMove_i16 + 0x2a => EntityLookAndMove_i16 + 0x2b => EntityLook_VarInt + 0x2c => Entity + 0x2d => VehicleTeleport + 0x2e => OpenBook + 0x2f => WindowOpen_VarInt + 0x30 => SignEditorOpen + 0x31 => CraftRecipeResponse + 0x32 => PlayerAbilities + 0x33 => CombatEvent + 0x34 => PlayerInfo + 0x35 => FacePlayer + 0x36 => TeleportPlayer_WithConfirm + 0x37 => UnlockRecipes_WithSmelting + 0x38 => EntityDestroy + 0x39 => EntityRemoveEffect + 0x3a => ResourcePackSend + 0x3b => Respawn_HashedSeed + 0x3c => EntityHeadLook + 0x3d => SelectAdvancementTab + 0x3e => WorldBorder + 0x3f => Camera + 0x40 => SetCurrentHotbarSlot + 0x41 => UpdateViewPosition + 0x42 => UpdateViewDistance + 0x43 => ScoreboardDisplay + 0x44 => EntityMetadata + 0x45 => EntityAttach + 0x46 => EntityVelocity + 0x47 => EntityEquipment + 0x48 => SetExperience + 0x49 => UpdateHealth + 0x4a => ScoreboardObjective + 0x4b => SetPassengers + 0x4c => Teams_VarInt + 0x4d => UpdateScore + 0x4e => SpawnPosition + 0x4f => TimeUpdate + 0x50 => Title + 0x51 => EntitySoundEffect + 0x52 => SoundEffect + 0x53 => StopSound + 0x54 => PlayerListHeaderFooter + 0x55 => NBTQueryResponse + 0x56 => CollectItem + 0x57 => EntityTeleport_f64 + 0x58 => Advancements + 0x59 => EntityProperties + 0x5a => EntityEffect + 0x5b => DeclareRecipes + 0x5c => TagsWithEntities + } + } + 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 + } + } +); + + From a833ef7bf57c78a59d2e19ca4900e87424da57b3 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Sat, 4 Jan 2020 19:20:36 -0800 Subject: [PATCH 157/160] Remove deprecated Error description, replaced by Display The Display trait is already implemented, so this is only a code deletion. Fixes 1.42-nightly warning: warning: use of deprecated item 'std::error::Error::description': use the Display impl or to_string() --> src/protocol/mod.rs:981:40 | 981 | Error::IOError(ref e) => e.description(), | ^^^^^^^^^^^ | = note: `#[warn(deprecated)]` on by default --- protocol/src/protocol/mod.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 1187c18..2d9284a 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -973,18 +973,7 @@ impl convert::From for Error { } } -impl ::std::error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::Err(ref val) => &val[..], - Error::Disconnect(_) => "Disconnect", - Error::IOError(ref e) => e.description(), - Error::Json(ref e) => e.description(), - #[cfg(not(target_arch = "wasm32"))] - Error::Reqwest(ref e) => e.description(), - } - } -} +impl ::std::error::Error for Error {} impl ::std::fmt::Display for Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { From 9077c22304a320e61eed0cf38c624c18c1f589b5 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 5 Jan 2020 10:38:21 -0800 Subject: [PATCH 158/160] Update to reqwest 0.10.0. Closes #259 * Bump reqwest from 0.9.22 to 0.10.0 * Use blocking reqwest API instead of futures, for now --- protocol/src/protocol/mojang.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index 8a11e75..165fa38 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -42,7 +42,7 @@ impl Profile { }}); let req = serde_json::to_string(&req_msg)?; - let client = reqwest::Client::new(); + let client = reqwest::blocking::Client::new(); let res = client.post(LOGIN_URL) .header(reqwest::header::CONTENT_TYPE, "application/json") .body(req) @@ -70,7 +70,7 @@ impl Profile { }); let req = serde_json::to_string(&req_msg)?; - let client = reqwest::Client::new(); + let client = reqwest::blocking::Client::new(); let res = client.post(VALIDATE_URL) .header(reqwest::header::CONTENT_TYPE, "application/json") .body(req) @@ -129,7 +129,7 @@ impl Profile { }); let join = serde_json::to_string(&join_msg).unwrap(); - let client = reqwest::Client::new(); + let client = reqwest::blocking::Client::new(); let res = client.post(JOIN_URL) .header(reqwest::header::CONTENT_TYPE, "application/json") .body(join) From e834ee2f53533cb2c12771516736fdb4970fddf1 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Wed, 8 Jan 2020 18:57:57 -0800 Subject: [PATCH 159/160] protocol: atomics replace unsafe for version/debug. Closes #261 --- protocol/src/item.rs | 2 +- protocol/src/types/metadata.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/src/item.rs b/protocol/src/item.rs index 9d791e9..8aa6ab4 100644 --- a/protocol/src/item.rs +++ b/protocol/src/item.rs @@ -39,7 +39,7 @@ impl Default for Stack { impl Serializable for Option { fn read_from(buf: &mut R) -> Result, protocol::Error> { - let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; + let protocol_version = protocol::current_protocol_version(); if protocol_version >= 404 { let present = buf.read_u8()? != 0; diff --git a/protocol/src/types/metadata.rs b/protocol/src/types/metadata.rs index e27e043..17c18eb 100644 --- a/protocol/src/types/metadata.rs +++ b/protocol/src/types/metadata.rs @@ -433,7 +433,7 @@ impl Metadata { impl Serializable for Metadata { fn read_from(buf: &mut R) -> Result { - let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; + let protocol_version = protocol::current_protocol_version(); if protocol_version >= 404 { Metadata::read_from113(buf) @@ -445,7 +445,7 @@ impl Serializable for Metadata { } fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { - let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; + let protocol_version = protocol::current_protocol_version(); if protocol_version >= 404 { self.write_to113(buf) From b563ca4394ba539b53ba10f973aecd4f9df78f91 Mon Sep 17 00:00:00 2001 From: ice_iix Date: Wed, 8 Jan 2020 18:57:57 -0800 Subject: [PATCH 160/160] protocol: atomics replace unsafe for version/debug. Closes #261 --- protocol/src/protocol/forge.rs | 2 +- protocol/src/protocol/mod.rs | 32 ++++++++++++++++++++------------ protocol/src/protocol/packet.rs | 4 ++-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/protocol/src/protocol/forge.rs b/protocol/src/protocol/forge.rs index 8f8fa58..c3096e1 100644 --- a/protocol/src/protocol/forge.rs +++ b/protocol/src/protocol/forge.rs @@ -147,7 +147,7 @@ impl Serializable for FmlHs { }) }, 3 => { - let protocol_version = unsafe { super::CURRENT_PROTOCOL_VERSION }; + let protocol_version = super::current_protocol_version(); if protocol_version >= 47 { Ok(FmlHs::RegistryData { diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 2d9284a..13be61a 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -35,6 +35,7 @@ use std::net::TcpStream; use std::io; use std::io::{Write, Read}; use std::convert; +use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; use flate2::read::{ZlibDecoder, ZlibEncoder}; use flate2::Compression; @@ -44,9 +45,20 @@ use log::debug; pub const SUPPORTED_PROTOCOLS: [i32; 18] = [575, 498, 490, 485, 480, 477, 452, 451, 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]; -pub static mut NETWORK_DEBUG: bool = false; +static CURRENT_PROTOCOL_VERSION: AtomicI32 = AtomicI32::new(SUPPORTED_PROTOCOLS[0]); +static NETWORK_DEBUG: AtomicBool = AtomicBool::new(false); + +pub fn current_protocol_version() -> i32 { + CURRENT_PROTOCOL_VERSION.load(Ordering::Relaxed) +} + +pub fn enable_network_debug() { + NETWORK_DEBUG.store(true, Ordering::Relaxed); +} + +pub fn is_network_debug() -> bool { + NETWORK_DEBUG.load(Ordering::Relaxed) +} /// Helper macro for defining packets #[macro_export] @@ -1005,9 +1017,7 @@ pub struct Conn { impl Conn { pub fn new(target: &str, protocol_version: i32) -> Result { - unsafe { - CURRENT_PROTOCOL_VERSION = protocol_version; - } + CURRENT_PROTOCOL_VERSION.store(protocol_version, Ordering::Relaxed); // TODO SRV record support let mut parts = target.split(':').collect::>(); @@ -1047,8 +1057,7 @@ impl Conn { VarInt(uncompressed_size as i32).write_to(&mut new)?; let mut write = ZlibEncoder::new(io::Cursor::new(buf), Compression::default()); write.read_to_end(&mut new)?; - let network_debug = unsafe { NETWORK_DEBUG }; - if network_debug { + if is_network_debug() { debug!("Compressed for sending {} bytes to {} since > threshold {}, new={:?}", uncompressed_size, new.len(), self.compression_threshold, new); @@ -1066,7 +1075,6 @@ impl Conn { } pub fn read_packet(&mut self) -> Result { - let network_debug = unsafe { NETWORK_DEBUG }; let len = VarInt::read_from(self)?.0 as usize; let mut ibuf = vec![0; len]; self.read_exact(&mut ibuf)?; @@ -1081,7 +1089,7 @@ impl Conn { let mut reader = ZlibDecoder::new(buf); reader.read_to_end(&mut new)?; } - if network_debug { + if is_network_debug() { debug!("Decompressed threshold={} len={} uncompressed_size={} to {} bytes", self.compression_threshold, len, uncompressed_size, new.len()); } @@ -1095,14 +1103,14 @@ impl Conn { Direction::Serverbound => Direction::Clientbound, }; - if network_debug { + if is_network_debug() { debug!("about to parse id={:x}, dir={:?} state={:?}", id, dir, self.state); fs::File::create("last-packet")?.write_all(buf.get_ref())?; } let packet = packet::packet_by_id(self.protocol_version, self.state, dir, id, &mut buf)?; - if network_debug { + if is_network_debug() { debug!("packet = {:?}", packet); } diff --git a/protocol/src/protocol/packet.rs b/protocol/src/protocol/packet.rs index 535f65c..a1acce9 100644 --- a/protocol/src/protocol/packet.rs +++ b/protocol/src/protocol/packet.rs @@ -2600,7 +2600,7 @@ impl Serializable for Recipe { let a = String::read_from(buf)?; let b = String::read_from(buf)?; - let protocol_version = unsafe { super::CURRENT_PROTOCOL_VERSION }; + let protocol_version = super::current_protocol_version(); // 1.14+ swaps recipe identifier and type, and adds namespace to type if protocol_version >= 477 { @@ -2735,7 +2735,7 @@ pub struct Trade { impl Serializable for Trade { fn read_from(buf: &mut R) -> Result { - let protocol_version = unsafe { super::CURRENT_PROTOCOL_VERSION }; + let protocol_version = super::current_protocol_version(); Ok(Trade { input_item_1: Serializable::read_from(buf)?,