Clean up protocol encoding/decoding
This commit is contained in:
parent
7d0d890b6f
commit
c015ae9e55
|
@ -1,54 +1,13 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
|
||||||
|
use std::default;
|
||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Write, Read};
|
use std::io::{Write, Read};
|
||||||
use std::convert;
|
use std::convert;
|
||||||
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
||||||
|
|
||||||
/// Serializes types into a buffer
|
|
||||||
macro_rules! serialize_type {
|
|
||||||
($dst:expr, $name:expr, u16) => {
|
|
||||||
$dst.write_u16::<BigEndian>($name).unwrap();
|
|
||||||
};
|
|
||||||
($dst:expr, $name:expr, i64) => {
|
|
||||||
$dst.write_i64::<BigEndian>($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::<BigEndian>().unwrap()
|
|
||||||
};
|
|
||||||
($src:expr, Empty) => { Empty };
|
|
||||||
($src:expr, $ftype:ident) => {
|
|
||||||
unimplemented!()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper macro for defining packets
|
/// Helper macro for defining packets
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! state_packets {
|
macro_rules! state_packets {
|
||||||
|
@ -61,8 +20,7 @@ macro_rules! state_packets {
|
||||||
})+) => {
|
})+) => {
|
||||||
use protocol::*;
|
use protocol::*;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Read, Write};
|
use protocol::{Serializable};
|
||||||
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
|
||||||
|
|
||||||
pub enum Packet {
|
pub enum Packet {
|
||||||
$(
|
$(
|
||||||
|
@ -78,12 +36,12 @@ macro_rules! state_packets {
|
||||||
pub mod $state {
|
pub mod $state {
|
||||||
$(
|
$(
|
||||||
pub mod $dir {
|
pub mod $dir {
|
||||||
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
|
||||||
use protocol::*;
|
use protocol::*;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Read, Write};
|
use protocol::{Serializable};
|
||||||
|
|
||||||
$(
|
$(
|
||||||
|
#[derive(Default)]
|
||||||
pub struct $name {
|
pub struct $name {
|
||||||
$(pub $field: $field_type),+,
|
$(pub $field: $field_type),+,
|
||||||
}
|
}
|
||||||
|
@ -94,7 +52,7 @@ macro_rules! state_packets {
|
||||||
|
|
||||||
fn write(self, buf: &mut Vec<u8>) -> Result<(), io::Error> {
|
fn write(self, buf: &mut Vec<u8>) -> Result<(), io::Error> {
|
||||||
$(
|
$(
|
||||||
serialize_type!(buf, self.$field, $field_type);
|
try!(self.$field.write_to(buf));
|
||||||
)+
|
)+
|
||||||
|
|
||||||
Result::Ok(())
|
Result::Ok(())
|
||||||
|
@ -108,7 +66,7 @@ macro_rules! state_packets {
|
||||||
|
|
||||||
/// Returns the packet for the given state, direction and id after parsing the fields
|
/// Returns the packet for the given state, direction and id after parsing the fields
|
||||||
/// from the buffer.
|
/// from the buffer.
|
||||||
pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut io::Cursor<Vec<u8>>) -> Option<Packet> {
|
pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut io::Cursor<Vec<u8>>) -> Result<Option<Packet>, io::Error> {
|
||||||
match state {
|
match state {
|
||||||
$(
|
$(
|
||||||
State::$stateName => {
|
State::$stateName => {
|
||||||
|
@ -117,11 +75,15 @@ macro_rules! state_packets {
|
||||||
Direction::$dirName => {
|
Direction::$dirName => {
|
||||||
match id {
|
match id {
|
||||||
$(
|
$(
|
||||||
$id => Option::Some(Packet::$name($state::$dir::$name {
|
$id => {
|
||||||
$($field: deserialize_type!(buf, $field_type)),+,
|
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;
|
pub mod packet;
|
||||||
|
|
||||||
/// VarInt have a variable size (between 1 and 5 bytes) when encoded based
|
trait Serializable {
|
||||||
/// on the size of the number
|
fn read_from(buf: &mut io::Read) -> Result<Self, io::Error>;
|
||||||
pub type VarInt = i32;
|
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Encodes a VarInt into the Writer
|
impl Serializable for String {
|
||||||
pub fn write_varint(buf: &mut io::Write, v: VarInt) -> Result<(), io::Error> {
|
fn read_from(buf: &mut io::Read) -> Result<String, io::Error> {
|
||||||
const PART : u32 = 0x7F;
|
let len = try!(VarInt::read_from(buf)).0;
|
||||||
let mut val = v as u32;
|
let mut ret = String::new();
|
||||||
loop {
|
try!(buf.take(len as u64).read_to_string(&mut ret));
|
||||||
if (val & !PART) == 0 {
|
Result::Ok(ret)
|
||||||
try!(buf.write_u8(val as u8));
|
}
|
||||||
return Result::Ok(());
|
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
|
||||||
}
|
let bytes = self.as_bytes();
|
||||||
try!(buf.write_u8(((val & PART) | 0x80) as u8));
|
try!(VarInt(bytes.len() as i32).write_to(buf));
|
||||||
val >>= 7;
|
try!(buf.write_all(bytes));
|
||||||
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes a VarInt from the Reader
|
impl Serializable for Empty {
|
||||||
pub fn read_variant(buf: &mut io::Read) -> Result<VarInt, io::Error> {
|
fn read_from(buf: &mut io::Read) -> Result<Empty, io::Error> {
|
||||||
const PART : u32 = 0x7F;
|
Result::Ok(Empty)
|
||||||
let mut size = 0;
|
}
|
||||||
let mut val = 0u32;
|
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
|
||||||
loop {
|
Result::Ok(())
|
||||||
let b = try!(buf.read_u8()) as u32;
|
}
|
||||||
val |= (b & PART) << (size * 7);
|
}
|
||||||
size+=1;
|
|
||||||
if size > 5 {
|
impl Default for Empty {
|
||||||
return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, Error::Err("VarInt too big".to_string())))
|
fn default() -> Empty { Empty }
|
||||||
}
|
}
|
||||||
if (b & 0x80) == 0 {
|
|
||||||
break
|
impl Serializable for i32 {
|
||||||
|
fn read_from(buf: &mut io::Read) -> Result<i32, io::Error> {
|
||||||
|
Result::Ok(try!(buf.read_i32::<BigEndian>()))
|
||||||
|
}
|
||||||
|
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
|
||||||
|
try!(buf.write_i32::<BigEndian>(*self));
|
||||||
|
Result::Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for i64 {
|
||||||
|
fn read_from(buf: &mut io::Read) -> Result<i64, io::Error> {
|
||||||
|
Result::Ok(try!(buf.read_i64::<BigEndian>()))
|
||||||
|
}
|
||||||
|
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
|
||||||
|
try!(buf.write_i64::<BigEndian>(*self));
|
||||||
|
Result::Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for u16 {
|
||||||
|
fn read_from(buf: &mut io::Read) -> Result<u16, io::Error> {
|
||||||
|
Result::Ok(try!(buf.read_u16::<BigEndian>()))
|
||||||
|
}
|
||||||
|
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error> {
|
||||||
|
try!(buf.write_u16::<BigEndian>(*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<VarInt, io::Error> {
|
||||||
|
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
|
/// Direction is used to define whether packets are going to the
|
||||||
|
@ -254,28 +285,28 @@ impl Conn {
|
||||||
|
|
||||||
pub fn write_packet<T: PacketType>(&mut self, packet: T) -> Result<(), Error> {
|
pub fn write_packet<T: PacketType>(&mut self, packet: T) -> Result<(), Error> {
|
||||||
let mut buf = Vec::new();
|
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!(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()));
|
try!(self.stream.write_all(&buf.into_boxed_slice()));
|
||||||
|
|
||||||
Result::Ok(())
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_packet(&mut self) -> Result<packet::Packet, Error> {
|
pub fn read_packet(&mut self) -> Result<packet::Packet, Error> {
|
||||||
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);
|
let mut ibuf = Vec::with_capacity(len);
|
||||||
try!((&mut self.stream).take(len as u64).read_to_end(&mut ibuf));
|
try!((&mut self.stream).take(len as u64).read_to_end(&mut ibuf));
|
||||||
|
|
||||||
let mut buf = io::Cursor::new(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 {
|
let dir = match self.direction {
|
||||||
Direction::Clientbound => Direction::Serverbound,
|
Direction::Clientbound => Direction::Serverbound,
|
||||||
Direction::Serverbound => Direction::Clientbound,
|
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 {
|
match packet {
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
|
@ -299,14 +330,13 @@ pub trait PacketType {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
return; // Skip
|
|
||||||
let mut c = Conn::new("localhost:25565").unwrap();
|
let mut c = Conn::new("localhost:25565").unwrap();
|
||||||
|
|
||||||
c.write_packet(packet::handshake::serverbound::Handshake{
|
c.write_packet(packet::handshake::serverbound::Handshake{
|
||||||
protocol_version: 69,
|
protocol_version: VarInt(69),
|
||||||
host: "localhost".to_string(),
|
host: "localhost".to_string(),
|
||||||
port: 25565,
|
port: 25565,
|
||||||
next: 1,
|
next: VarInt(1),
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
c.state = State::Status;
|
c.state = State::Status;
|
||||||
c.write_packet(packet::status::serverbound::StatusRequest{empty: Empty}).unwrap();
|
c.write_packet(packet::status::serverbound::StatusRequest{empty: Empty}).unwrap();
|
||||||
|
|
Loading…
Reference in New Issue