Add status response
This commit is contained in:
parent
824061e9a9
commit
6fcf5fe0ed
@ -15,3 +15,7 @@ name = "mcpl"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
uuid = { version = "0.7", features = ["v4", "serde"] }
|
||||||
|
mc-varint = { git = "https://github.com/luojia65/mc-varint" }
|
||||||
|
100
src/lib.rs
100
src/lib.rs
@ -1,27 +1,111 @@
|
|||||||
|
use mc_varint::{VarIntRead, VarIntWrite};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
pub mod status;
|
pub mod status;
|
||||||
|
|
||||||
/// Current supported protocol version.
|
/// Current supported protocol version.
|
||||||
pub const PROTOCOL_VERSION: usize = 498;
|
pub const PROTOCOL_VERSION: usize = 498;
|
||||||
|
|
||||||
/// Possible errors while decoding packet.
|
/// Possible errors while encoding packet.
|
||||||
pub enum DecodePacketError {
|
pub enum EncodeError {
|
||||||
UnknownPacketType { type_id: u8 },
|
StringTooLong,
|
||||||
IOError { io_error: io::Error },
|
IOError {
|
||||||
|
io_error: io::Error,
|
||||||
|
},
|
||||||
|
JsonError {
|
||||||
|
json_error: serde_json::error::Error,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for DecodePacketError {
|
impl From<io::Error> for EncodeError {
|
||||||
fn from(io_error: io::Error) -> Self {
|
fn from(io_error: io::Error) -> Self {
|
||||||
DecodePacketError::IOError { io_error }
|
EncodeError::IOError { io_error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::error::Error> for EncodeError {
|
||||||
|
fn from(json_error: serde_json::error::Error) -> Self {
|
||||||
|
EncodeError::JsonError { json_error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Possible errors while decoding packet.
|
||||||
|
pub enum DecodeError {
|
||||||
|
UnknownPacketType {
|
||||||
|
type_id: u8,
|
||||||
|
},
|
||||||
|
StringTooLong,
|
||||||
|
IOError {
|
||||||
|
io_error: io::Error,
|
||||||
|
},
|
||||||
|
JsonError {
|
||||||
|
json_error: serde_json::error::Error,
|
||||||
|
},
|
||||||
|
Utf8Error {
|
||||||
|
utf8_error: FromUtf8Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for DecodeError {
|
||||||
|
fn from(io_error: io::Error) -> Self {
|
||||||
|
DecodeError::IOError { io_error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::error::Error> for DecodeError {
|
||||||
|
fn from(json_error: serde_json::error::Error) -> Self {
|
||||||
|
DecodeError::JsonError { json_error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FromUtf8Error> for DecodeError {
|
||||||
|
fn from(utf8_error: FromUtf8Error) -> Self {
|
||||||
|
DecodeError::Utf8Error { utf8_error }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Packet {
|
trait Packet {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), io::Error>;
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError>;
|
||||||
|
|
||||||
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodePacketError>;
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait PacketRead {
|
||||||
|
fn read_string(&mut self) -> Result<String, DecodeError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait PacketWrite {
|
||||||
|
fn write_string(&mut self, value: &str) -> Result<(), EncodeError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> PacketRead for R {
|
||||||
|
fn read_string(&mut self) -> Result<String, DecodeError> {
|
||||||
|
let length = self.read_var_u32()?;
|
||||||
|
|
||||||
|
if length > 32_767 {
|
||||||
|
return Err(DecodeError::StringTooLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = vec![0; length as usize];
|
||||||
|
self.read_exact(&mut buf)?;
|
||||||
|
|
||||||
|
Ok(String::from_utf8(buf)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> PacketWrite for W {
|
||||||
|
fn write_string(&mut self, value: &str) -> Result<(), EncodeError> {
|
||||||
|
if value.len() > 32_767 {
|
||||||
|
return Err(EncodeError::StringTooLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_var_u32(value.len() as u32)?;
|
||||||
|
self.write_all(value.as_bytes())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::{DecodePacketError, Packet};
|
use crate::{DecodeError, EncodeError, Packet, PacketWrite};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use std::io;
|
use serde::{Deserialize, Serialize};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub enum StatusServerBoundPacket {
|
pub enum StatusServerBoundPacket {
|
||||||
StatusRequest,
|
StatusRequest,
|
||||||
@ -9,7 +10,7 @@ pub enum StatusServerBoundPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub enum StatusClientBoundPacket {
|
pub enum StatusClientBoundPacket {
|
||||||
StatusResponse,
|
StatusResponse(StatusResponse),
|
||||||
PingResponse(PingResponse),
|
PingResponse(PingResponse),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ impl StatusServerBoundPacket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode<R: Read>(type_id: u8, reader: &mut R) -> Result<Self, DecodePacketError> {
|
pub fn decode<R: Read>(type_id: u8, reader: &mut R) -> Result<Self, DecodeError> {
|
||||||
match type_id {
|
match type_id {
|
||||||
0x0 => Ok(StatusServerBoundPacket::StatusRequest),
|
0x0 => Ok(StatusServerBoundPacket::StatusRequest),
|
||||||
0x1 => {
|
0x1 => {
|
||||||
@ -29,7 +30,7 @@ impl StatusServerBoundPacket {
|
|||||||
|
|
||||||
Ok(StatusServerBoundPacket::PingRequest(ping_request))
|
Ok(StatusServerBoundPacket::PingRequest(ping_request))
|
||||||
}
|
}
|
||||||
_ => Err(DecodePacketError::UnknownPacketType { type_id }),
|
_ => Err(DecodeError::UnknownPacketType { type_id }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,7 +38,7 @@ impl StatusServerBoundPacket {
|
|||||||
impl StatusClientBoundPacket {
|
impl StatusClientBoundPacket {
|
||||||
pub fn get_type_id(&self) -> u8 {
|
pub fn get_type_id(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
StatusClientBoundPacket::StatusResponse => 0x0,
|
StatusClientBoundPacket::StatusResponse(_) => 0x0,
|
||||||
StatusClientBoundPacket::PingResponse(_) => 0x1,
|
StatusClientBoundPacket::PingResponse(_) => 0x1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,13 +59,13 @@ impl PingRequest {
|
|||||||
impl Packet for PingRequest {
|
impl Packet for PingRequest {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
writer.write_u64::<BigEndian>(self.time)?;
|
writer.write_u64::<BigEndian>(self.time)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodePacketError> {
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
||||||
let time = reader.read_u64::<BigEndian>()?;
|
let time = reader.read_u64::<BigEndian>()?;
|
||||||
|
|
||||||
Ok(PingRequest { time })
|
Ok(PingRequest { time })
|
||||||
@ -86,15 +87,71 @@ impl PingResponse {
|
|||||||
impl Packet for PingResponse {
|
impl Packet for PingResponse {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
writer.write_u64::<BigEndian>(self.time)?;
|
writer.write_u64::<BigEndian>(self.time)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodePacketError> {
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
||||||
let time = reader.read_u64::<BigEndian>()?;
|
let time = reader.read_u64::<BigEndian>()?;
|
||||||
|
|
||||||
Ok(PingResponse { time })
|
Ok(PingResponse { time })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct ServerStatus {
|
||||||
|
pub version: ServerVersion,
|
||||||
|
pub description: String,
|
||||||
|
pub players: OnlinePlayers,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct ServerVersion {
|
||||||
|
pub name: String,
|
||||||
|
pub protocol: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct OnlinePlayers {
|
||||||
|
pub online: u32,
|
||||||
|
pub max: u32,
|
||||||
|
pub sample: Vec<OnlinePlayer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct OnlinePlayer {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StatusResponse {
|
||||||
|
pub server_status: ServerStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatusResponse {
|
||||||
|
pub fn new(server_status: ServerStatus) -> StatusClientBoundPacket {
|
||||||
|
let status_response = StatusResponse { server_status };
|
||||||
|
|
||||||
|
StatusClientBoundPacket::StatusResponse(status_response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Packet for StatusResponse {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
|
let json = serde_json::to_string(&self.server_status)?;
|
||||||
|
writer.write_string(&json)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
||||||
|
let server_status = serde_json::from_reader(reader)?;
|
||||||
|
let status_response = StatusResponse { server_status };
|
||||||
|
|
||||||
|
Ok(status_response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user