From 2d10d38e4c272adef624d4a5a15c776a882f3b07 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sat, 12 Sep 2015 20:31:26 +0100 Subject: [PATCH] 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::*;