Replace packets with a macro
This commit is contained in:
parent
6a58f58cc0
commit
28f1656c81
@ -8,12 +8,8 @@ pub mod error;
|
||||
pub mod inventory;
|
||||
/// Network packets.
|
||||
///
|
||||
/// The packet naming convention used is "DSIDName" where
|
||||
/// 'D' is either 'S' for serverbound or 'C' for clientbound,
|
||||
/// 'S' is the current connection state (**H**andshake, **S**tatus, **L**ogin, or **P**lay),
|
||||
/// "ID" is the packet id in uppercase hexadecimal (ex. 1B, 05, 3A),
|
||||
/// and "Name" is the packet's name as found on [wiki.vg](https://wiki.vg/Protocol) in PascalCase.
|
||||
/// Examples include "SH00Handshake", "CP00SpawnEntity", and "SP11KeepAlive".
|
||||
/// Packet names are as found on [wiki.vg](https://wiki.vg/Protocol)
|
||||
/// in PascalCase, with some exceptions for uniqueness.
|
||||
pub mod packets;
|
||||
/// Useful shared parsing functions.
|
||||
pub mod parsing;
|
||||
@ -27,13 +23,14 @@ use types::VarInt;
|
||||
///
|
||||
/// Parsing packets requires knowing which state the connection is in.
|
||||
/// [Relevant wiki.vg page](https://wiki.vg/How_to_Write_a_Server#FSM_example_of_handling_new_TCP_connections)
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ClientState {
|
||||
/// The connection is freshly established.
|
||||
///
|
||||
/// The only packet in this state is `SH00Handshake`.
|
||||
/// After this packet is sent, the connection immediately
|
||||
/// transitions to `Status` or `Login`.
|
||||
#[default]
|
||||
Handshake,
|
||||
/// The client is performing [server list ping](https://wiki.vg/Server_List_Ping).
|
||||
Status,
|
||||
@ -59,8 +56,8 @@ impl parsing::Parsable for ClientState {
|
||||
}
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let byte = match &self {
|
||||
&ClientState::Status => 1,
|
||||
&ClientState::Login => 2,
|
||||
ClientState::Status => 1,
|
||||
ClientState::Login => 2,
|
||||
_ => 0,
|
||||
};
|
||||
vec![byte]
|
||||
|
232
src/protocol/packets.rs
Normal file
232
src/protocol/packets.rs
Normal file
@ -0,0 +1,232 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
// Inspired by https://github.com/iceiix/stevenarella.
|
||||
|
||||
/// Enum representation of a packet's direction.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum PacketDirection {
|
||||
Serverbound,
|
||||
Clientbound,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! packets {
|
||||
($($state:ident $state_name:ident {
|
||||
$($dir:ident $dir_name:ident {
|
||||
$(
|
||||
$(#[$attr:meta])*
|
||||
packet $name:ident $id:literal {
|
||||
$($(#[$fattr:meta])* field $field:ident: $field_type:ty,)*
|
||||
$($(#[$rattr:meta])* rest $rest:ident,)?
|
||||
}
|
||||
)*
|
||||
})+
|
||||
})+) => {
|
||||
use $crate::protocol::{ClientState, parsing::{VarInt, Parsable, IResult}};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Packet {
|
||||
$($($(
|
||||
$name($state::$dir::$name),
|
||||
)*)+)+
|
||||
}
|
||||
$($($(
|
||||
impl From<$state::$dir::$name> for Packet {
|
||||
fn from(value: $state::$dir::$name) -> Packet {
|
||||
Packet::$name(value)
|
||||
}
|
||||
}
|
||||
)*)+)+
|
||||
impl Packet {
|
||||
fn parser(client_state: ClientState, direction: PacketDirection) -> impl Fn(&[u8]) -> IResult<&[u8], Self> {
|
||||
move |input: &[u8]| {
|
||||
use nom::{combinator::verify, bytes::streaming::take};
|
||||
|
||||
if client_state == ClientState::Disconnected {
|
||||
return nom::combinator::fail(input);
|
||||
}
|
||||
|
||||
let (input, packet_len) = VarInt::parse_usize(input)?;
|
||||
let (packet_body, packet_id) = verify(VarInt::parse, |v| {
|
||||
match client_state {
|
||||
$(ClientState::$state_name => {
|
||||
match direction {
|
||||
$(PacketDirection::$dir_name => {
|
||||
match **v {
|
||||
$($id => true,)*
|
||||
_ => false,
|
||||
}
|
||||
})*
|
||||
}
|
||||
})*
|
||||
ClientState::Disconnected => false,
|
||||
}
|
||||
})(input)?;
|
||||
let (input, packet_body) = take(packet_len)(packet_body)?;
|
||||
let (_, packet) = Packet::body_parser(client_state, direction, packet_id)(packet_body)?;
|
||||
Ok((input, packet))
|
||||
}
|
||||
}
|
||||
fn body_parser(client_state: ClientState, direction: PacketDirection, packet_id: VarInt) -> impl Fn(&[u8]) -> IResult<&[u8], Self> {
|
||||
move |input: &[u8]| {
|
||||
match client_state {
|
||||
$(ClientState::$state_name => {
|
||||
match direction {
|
||||
$(PacketDirection::$dir_name => {
|
||||
match *packet_id {
|
||||
$($id => {
|
||||
let (rest, inner) = $state::$dir::$name::parse(input)?;
|
||||
// The packet should have consumed all of the input specified by packet_len.
|
||||
nom::combinator::eof(rest)?;
|
||||
Ok((rest, Packet::$name(inner)))
|
||||
},)*
|
||||
// Invalid packet id.
|
||||
_ => Ok(nom::combinator::fail(input)?),
|
||||
}
|
||||
})*
|
||||
}
|
||||
})*
|
||||
// Invalid client state.
|
||||
_ => Ok(nom::combinator::fail(input)?),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn parse(client_state: ClientState, direction: PacketDirection, input: &[u8]) -> IResult<&[u8], Self> {
|
||||
Packet::parser(client_state, direction)(input)
|
||||
}
|
||||
pub fn parse_as<T: TryFrom<Packet, Error = Packet>>(client_state: ClientState, direction: PacketDirection, input: &[u8]) -> IResult<&[u8], Result<T, Self>> {
|
||||
nom::combinator::map(Self::parser(client_state, direction), T::try_from)(input)
|
||||
}
|
||||
pub fn serialize(&self) -> (VarInt, Vec<u8>) {
|
||||
match &self {
|
||||
$($($(
|
||||
Packet::$name(inner) => (VarInt::from($id), inner.serialize()),
|
||||
)*)*)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(pub mod $state {
|
||||
$(pub mod $dir {
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use $crate::protocol::{ClientState, parsing::{VarInt, Parsable, IResult}, types::*};
|
||||
use super::super::Packet;
|
||||
|
||||
$(
|
||||
$(#[$attr])*
|
||||
#[derive(Default, Debug, Clone, PartialEq)]
|
||||
pub struct $name {
|
||||
$($(#[$fattr])* pub $field: $field_type,)*
|
||||
$($(#[$rattr])* pub $rest: Vec<u8>,)?
|
||||
}
|
||||
impl TryFrom<Packet> for $name {
|
||||
type Error = Packet;
|
||||
fn try_from(value: Packet) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Packet::$name(inner) => Ok(inner),
|
||||
_ => Err(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Parsable for $name {
|
||||
fn parse(input: &[u8]) -> IResult<&[u8], Self> {
|
||||
$(let (input, $field) = <$field_type>::parse(input)?;)*
|
||||
$(let (input, $rest) = nom::combinator::rest(input)?;)?
|
||||
Ok((input, $name {
|
||||
$($field: $field,)*
|
||||
$($rest: $rest.to_vec(),)?
|
||||
}))
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
$(output.extend(self.$field.serialize());)*
|
||||
$(output.extend(&self.$rest);)?
|
||||
output
|
||||
}
|
||||
}
|
||||
)*
|
||||
})+
|
||||
})+
|
||||
};
|
||||
}
|
||||
|
||||
packets!(
|
||||
handshake Handshake {
|
||||
serverbound Serverbound {
|
||||
packet Handshake 0x00 {
|
||||
field protocol_version: VarInt,
|
||||
field host: String,
|
||||
field port: u16,
|
||||
field next_state: ClientState,
|
||||
}
|
||||
}
|
||||
clientbound Clientbound {}
|
||||
}
|
||||
status Status {
|
||||
serverbound Serverbound {
|
||||
packet StatusRequest 0x00 {}
|
||||
packet PingRequest 0x01 {
|
||||
field payload: i64,
|
||||
}
|
||||
}
|
||||
clientbound Clientbound {
|
||||
packet StatusResponse 0x00 {
|
||||
field response: Json,
|
||||
}
|
||||
packet PingResponse 0x01 {
|
||||
field payload: i64,
|
||||
}
|
||||
}
|
||||
}
|
||||
login Login {
|
||||
serverbound Serverbound {
|
||||
packet LoginStart 0x00 {
|
||||
field name: String,
|
||||
field uuid: Option<Uuid>,
|
||||
}
|
||||
packet EncryptionResponse 0x01 {
|
||||
field shared_secret: Vec<u8>,
|
||||
field verify_token: Vec<u8>,
|
||||
}
|
||||
packet LoginPluginResponse 0x02 {
|
||||
field message_id: VarInt,
|
||||
field successful: bool,
|
||||
rest data,
|
||||
}
|
||||
}
|
||||
clientbound Clientbound {
|
||||
packet LoginDisconnect 0x00 {
|
||||
field reason: Chat,
|
||||
}
|
||||
packet EncryptionRequest 0x01 {
|
||||
field server_id: String,
|
||||
field public_key: Vec<u8>,
|
||||
field verify_token: Vec<u8>,
|
||||
}
|
||||
packet LoginSuccess 0x02 {
|
||||
field uuid: Uuid,
|
||||
field username: String,
|
||||
// TODO: Re-implement CL02LoginSuccessProperty
|
||||
rest properties,
|
||||
}
|
||||
packet SetCompression 0x03 {
|
||||
field threshold: VarInt,
|
||||
}
|
||||
packet LoginPluginRequest 0x04 {
|
||||
field message_id: VarInt,
|
||||
field channel: String,
|
||||
rest data,
|
||||
}
|
||||
}
|
||||
}
|
||||
play Play {
|
||||
serverbound Serverbound {}
|
||||
clientbound Clientbound {
|
||||
packet PlayDisconnect 0x17 {
|
||||
field reason: Chat,
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
@ -1,164 +0,0 @@
|
||||
use crate::protocol::parsing::Parsable;
|
||||
use crate::protocol::types::{Chat, Json, Uuid, VarInt};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CL00Disconnect {
|
||||
pub reason: Chat,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CL00Disconnect,
|
||||
0x00,
|
||||
crate::protocol::ClientState::Login,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CL00Disconnect> {
|
||||
let (data, reason) = Json::parse(data)?;
|
||||
Ok((data, CL00Disconnect { reason }))
|
||||
},
|
||||
|packet: &CL00Disconnect| -> Vec<u8> { packet.reason.serialize() }
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CL01EncryptionRequest {
|
||||
pub server_id: String,
|
||||
pub public_key: Vec<u8>,
|
||||
pub verify_token: Vec<u8>,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CL01EncryptionRequest,
|
||||
0x01,
|
||||
crate::protocol::ClientState::Login,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CL01EncryptionRequest> {
|
||||
let (data, server_id) = String::parse(data)?;
|
||||
let (data, public_key) = u8::parse_vec(data)?;
|
||||
let (data, verify_token) = u8::parse_vec(data)?;
|
||||
|
||||
Ok((
|
||||
data,
|
||||
CL01EncryptionRequest {
|
||||
server_id,
|
||||
public_key,
|
||||
verify_token,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &CL01EncryptionRequest| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.server_id.serialize());
|
||||
output.extend(packet.public_key.serialize());
|
||||
output.extend(packet.verify_token.serialize());
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CL02LoginSuccess {
|
||||
pub uuid: Uuid,
|
||||
pub username: String,
|
||||
pub properties: Vec<CL02LoginSuccessProperty>,
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CL02LoginSuccessProperty {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
pub signature: Option<String>,
|
||||
}
|
||||
impl Parsable for CL02LoginSuccessProperty {
|
||||
#[tracing::instrument]
|
||||
fn parse(data: &[u8]) -> crate::protocol::parsing::IResult<&[u8], Self> {
|
||||
let (data, name) = String::parse(data)?;
|
||||
let (data, value) = String::parse(data)?;
|
||||
let (data, signature) = String::parse_optional(data)?;
|
||||
Ok((
|
||||
data,
|
||||
CL02LoginSuccessProperty {
|
||||
name,
|
||||
value,
|
||||
signature,
|
||||
},
|
||||
))
|
||||
}
|
||||
#[tracing::instrument]
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(self.name.serialize());
|
||||
output.extend(self.value.serialize());
|
||||
output.extend(self.signature.serialize());
|
||||
output
|
||||
}
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CL02LoginSuccess,
|
||||
0x02,
|
||||
crate::protocol::ClientState::Login,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CL02LoginSuccess> {
|
||||
let (data, uuid) = Uuid::parse(data)?;
|
||||
let (data, username) = String::parse(data)?;
|
||||
let (data, properties) = CL02LoginSuccessProperty::parse_vec(data)?;
|
||||
|
||||
Ok((
|
||||
data,
|
||||
CL02LoginSuccess {
|
||||
uuid,
|
||||
username,
|
||||
properties,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &CL02LoginSuccess| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.uuid.serialize());
|
||||
output.extend(packet.username.serialize());
|
||||
output.extend(packet.properties.serialize());
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct CL03SetCompression {
|
||||
pub threshold: VarInt,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CL03SetCompression,
|
||||
0x03,
|
||||
crate::protocol::ClientState::Login,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CL03SetCompression> {
|
||||
let (data, threshold) = VarInt::parse(data)?;
|
||||
Ok((data, CL03SetCompression { threshold }))
|
||||
},
|
||||
|packet: &CL03SetCompression| -> Vec<u8> { packet.threshold.serialize() }
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CL04LoginPluginRequest {
|
||||
pub message_id: VarInt,
|
||||
pub channel: String,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CL04LoginPluginRequest,
|
||||
0x04,
|
||||
crate::protocol::ClientState::Login,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CL04LoginPluginRequest> {
|
||||
let (data, message_id) = VarInt::parse(data)?;
|
||||
let (data, channel) = String::parse(data)?;
|
||||
Ok((
|
||||
data,
|
||||
CL04LoginPluginRequest {
|
||||
message_id,
|
||||
channel,
|
||||
data: data.to_vec(),
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &CL04LoginPluginRequest| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.message_id.serialize());
|
||||
output.extend(packet.channel.serialize());
|
||||
output.extend(&packet.data);
|
||||
output
|
||||
}
|
||||
);
|
@ -1,10 +0,0 @@
|
||||
/// Packets for the `ClientState::Login` state.
|
||||
pub mod login;
|
||||
/// Packets for the `ClientState::Play` state.
|
||||
pub mod play;
|
||||
/// Packets for the `ClientState::Status` state.
|
||||
pub mod status;
|
||||
|
||||
pub use login::*;
|
||||
pub use play::*;
|
||||
pub use status::*;
|
@ -1,283 +0,0 @@
|
||||
use crate::protocol::{
|
||||
entities::{EntityPosition, EntityRotation, EntityVelocity},
|
||||
types::{Chat, Difficulty, Position, Uuid, VarInt},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct CP00SpawnEntity {
|
||||
pub id: VarInt,
|
||||
pub uuid: Uuid,
|
||||
pub kind: VarInt,
|
||||
pub position: EntityPosition,
|
||||
pub rotation: EntityRotation,
|
||||
pub head_yaw: u8,
|
||||
pub data: VarInt,
|
||||
pub velocity: EntityVelocity,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CP00SpawnEntity,
|
||||
0x00,
|
||||
crate::protocol::ClientState::Play,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP00SpawnEntity> {
|
||||
let (data, id) = VarInt::parse(data)?;
|
||||
let (data, uuid) = Uuid::parse(data)?;
|
||||
let (data, kind) = VarInt::parse(data)?;
|
||||
let (data, position) = EntityPosition::parse(data)?;
|
||||
let (data, rotation) = EntityRotation::parse(data)?;
|
||||
let (data, head_yaw) = u8::parse(data)?;
|
||||
let (data, d) = VarInt::parse(data)?;
|
||||
let (data, velocity) = EntityVelocity::parse(data)?;
|
||||
|
||||
Ok((
|
||||
data,
|
||||
CP00SpawnEntity {
|
||||
id,
|
||||
uuid,
|
||||
kind,
|
||||
position,
|
||||
rotation,
|
||||
head_yaw,
|
||||
data: d,
|
||||
velocity,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &CP00SpawnEntity| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.id.serialize());
|
||||
output.extend(packet.uuid.serialize());
|
||||
output.extend(packet.kind.serialize());
|
||||
output.extend(packet.position.serialize());
|
||||
output.extend(packet.rotation.serialize());
|
||||
output.extend(packet.head_yaw.serialize());
|
||||
output.extend(packet.data.serialize());
|
||||
output.extend(packet.velocity.serialize());
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct CP0BChangeDifficulty {
|
||||
pub difficulty: Difficulty,
|
||||
pub is_locked: bool,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CP0BChangeDifficulty,
|
||||
0x0b,
|
||||
crate::protocol::ClientState::Play,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP0BChangeDifficulty> {
|
||||
let (data, difficulty) = Difficulty::parse(data)?;
|
||||
let (data, is_locked) = bool::parse(data)?;
|
||||
Ok((
|
||||
data,
|
||||
CP0BChangeDifficulty {
|
||||
difficulty,
|
||||
is_locked,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &CP0BChangeDifficulty| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.difficulty.serialize());
|
||||
output.extend(packet.is_locked.serialize());
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CP17Disconnect {
|
||||
pub reason: Chat,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CP17Disconnect,
|
||||
0x17,
|
||||
crate::protocol::ClientState::Play,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP17Disconnect> {
|
||||
let (data, reason) = Chat::parse(data)?;
|
||||
Ok((data, CP17Disconnect { reason }))
|
||||
},
|
||||
|packet: &CP17Disconnect| -> Vec<u8> { packet.reason.serialize() }
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct CP1FKeepAlive {
|
||||
pub payload: i64,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CP1FKeepAlive,
|
||||
0x1f,
|
||||
crate::protocol::ClientState::Play,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP1FKeepAlive> {
|
||||
let (data, payload) = i64::parse(data)?;
|
||||
Ok((data, CP1FKeepAlive { payload }))
|
||||
},
|
||||
|packet: &CP1FKeepAlive| -> Vec<u8> { packet.payload.serialize() }
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct CP21WorldEvent {
|
||||
pub event: i32,
|
||||
pub location: Position,
|
||||
pub data: i32,
|
||||
pub disable_relative_volume: bool,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CP21WorldEvent,
|
||||
0x21,
|
||||
crate::protocol::ClientState::Play,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP21WorldEvent> {
|
||||
let (data, event) = i32::parse(data)?;
|
||||
let (data, location) = Position::parse(data)?;
|
||||
let (data, d) = i32::parse(data)?;
|
||||
let (data, disable_relative_volume) = bool::parse(data)?;
|
||||
Ok((
|
||||
data,
|
||||
CP21WorldEvent {
|
||||
event,
|
||||
location,
|
||||
data: d,
|
||||
disable_relative_volume,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &CP21WorldEvent| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.event.serialize());
|
||||
output.extend(packet.location.serialize());
|
||||
output.extend(packet.data.serialize());
|
||||
output.extend(packet.disable_relative_volume.serialize());
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct CP50SetEntityVelocity {
|
||||
pub entity_id: VarInt,
|
||||
pub entity_velocity: EntityVelocity,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CP50SetEntityVelocity,
|
||||
0x50,
|
||||
crate::protocol::ClientState::Play,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP50SetEntityVelocity> {
|
||||
let (data, entity_id) = VarInt::parse(data)?;
|
||||
let (data, entity_velocity) = EntityVelocity::parse(data)?;
|
||||
Ok((
|
||||
data,
|
||||
CP50SetEntityVelocity {
|
||||
entity_id,
|
||||
entity_velocity,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &CP50SetEntityVelocity| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.entity_id.serialize());
|
||||
output.extend(packet.entity_velocity.serialize());
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct CP52SetExperience {
|
||||
pub experience_bar: f32,
|
||||
pub total_experience: VarInt,
|
||||
pub level: VarInt,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CP52SetExperience,
|
||||
0x52,
|
||||
crate::protocol::ClientState::Play,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP52SetExperience> {
|
||||
let (data, experience_bar) = f32::parse(data)?;
|
||||
let (data, total_experience) = VarInt::parse(data)?;
|
||||
let (data, level) = VarInt::parse(data)?;
|
||||
Ok((
|
||||
data,
|
||||
CP52SetExperience {
|
||||
experience_bar,
|
||||
total_experience,
|
||||
level,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &CP52SetExperience| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.experience_bar.serialize());
|
||||
output.extend(packet.total_experience.serialize());
|
||||
output.extend(packet.level.serialize());
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CP68EntityEffect {
|
||||
pub entity_id: VarInt,
|
||||
pub effect_id: VarInt,
|
||||
pub amplifier: i8,
|
||||
pub duration: VarInt,
|
||||
pub is_ambient: bool,
|
||||
pub show_particles: bool,
|
||||
pub show_icon: bool,
|
||||
pub has_factor_data: bool,
|
||||
// TODO: pub factor_codec: NBT
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CP68EntityEffect,
|
||||
0x68,
|
||||
crate::protocol::ClientState::Play,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP68EntityEffect> {
|
||||
let (data, entity_id) = VarInt::parse(data)?;
|
||||
let (data, effect_id) = VarInt::parse(data)?;
|
||||
let (data, amplifier) = i8::parse(data)?;
|
||||
let (data, duration) = VarInt::parse(data)?;
|
||||
let (data, flags) = u8::parse(data)?;
|
||||
let is_ambient = flags & 0x01 > 0;
|
||||
let show_particles = flags & 0x02 > 0;
|
||||
let show_icon = flags & 0x04 > 0;
|
||||
let (data, has_factor_data) = bool::parse(data)?;
|
||||
// TODO: factor_codec
|
||||
|
||||
Ok((
|
||||
data,
|
||||
CP68EntityEffect {
|
||||
entity_id,
|
||||
effect_id,
|
||||
amplifier,
|
||||
duration,
|
||||
is_ambient,
|
||||
show_particles,
|
||||
show_icon,
|
||||
has_factor_data,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &CP68EntityEffect| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.entity_id.serialize());
|
||||
output.extend(packet.effect_id.serialize());
|
||||
output.extend(packet.amplifier.serialize());
|
||||
output.extend(packet.duration.serialize());
|
||||
let mut flags = 0x00u8;
|
||||
if packet.is_ambient {
|
||||
flags |= 0x01;
|
||||
}
|
||||
if packet.show_particles {
|
||||
flags |= 0x02;
|
||||
}
|
||||
if packet.show_icon {
|
||||
flags |= 0x04;
|
||||
}
|
||||
output.extend(flags.serialize());
|
||||
// TODO: factor_codec
|
||||
output
|
||||
}
|
||||
);
|
@ -1,33 +0,0 @@
|
||||
use crate::protocol::types::Json;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CS00StatusResponse {
|
||||
pub response: Json,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CS00StatusResponse,
|
||||
0x00,
|
||||
crate::protocol::ClientState::Status,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CS00StatusResponse> {
|
||||
let (data, response) = Json::parse(data)?;
|
||||
Ok((data, CS00StatusResponse { response }))
|
||||
},
|
||||
|packet: &CS00StatusResponse| -> Vec<u8> { packet.response.serialize() }
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct CS01PingResponse {
|
||||
pub payload: i64,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
CS01PingResponse,
|
||||
0x01,
|
||||
crate::protocol::ClientState::Status,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CS01PingResponse> {
|
||||
let (data, payload) = i64::parse(data)?;
|
||||
Ok((data, CS01PingResponse { payload }))
|
||||
},
|
||||
|packet: &CS01PingResponse| -> Vec<u8> { packet.payload.serialize() }
|
||||
);
|
@ -1,178 +0,0 @@
|
||||
/// Packets that are heading to the client.
|
||||
pub mod clientbound;
|
||||
/// Packets that are heading to the server.
|
||||
pub mod serverbound;
|
||||
|
||||
use crate::protocol::parsing::{Parsable, VarInt};
|
||||
|
||||
/// Alias for a `VarInt`.
|
||||
pub type PacketId = VarInt;
|
||||
|
||||
pub trait Packet:
|
||||
std::fmt::Debug + Clone + TryFrom<GenericPacket> + Into<GenericPacket> + Parsable
|
||||
{
|
||||
const ID: i32;
|
||||
const CLIENT_STATE: crate::protocol::ClientState;
|
||||
const IS_SERVERBOUND: bool;
|
||||
}
|
||||
|
||||
macro_rules! generic_packet {
|
||||
($($packet_type: ident),*) => {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum GenericPacket {
|
||||
$(
|
||||
$packet_type($packet_type),
|
||||
)*
|
||||
}
|
||||
impl GenericPacket {
|
||||
#[tracing::instrument]
|
||||
pub fn parse_uncompressed<'data>(
|
||||
client_state: crate::protocol::ClientState,
|
||||
is_serverbound: bool,
|
||||
data: &'data [u8]
|
||||
) -> crate::protocol::parsing::IResult<&'data [u8], Self> {
|
||||
use crate::protocol::parsing::Parsable;
|
||||
tracing::trace!(
|
||||
"GenericPacket::parse_uncompressed: {:?} {} {:?}",
|
||||
client_state,
|
||||
is_serverbound,
|
||||
data
|
||||
);
|
||||
let (data, packet_length) = crate::protocol::types::VarInt::parse(data)?;
|
||||
let (data, packet_data) = nom::bytes::streaming::take(*packet_length as usize)(data)?;
|
||||
|
||||
let (packet_data, packet_id) = PacketId::parse(packet_data)?;
|
||||
let (_packet_data, packet_body) =
|
||||
Self::parse_body(client_state, packet_id, is_serverbound, packet_data)?;
|
||||
|
||||
// if !packet_data.is_empty() {
|
||||
// println!("Packet data not empty after parsing!");
|
||||
// }
|
||||
|
||||
Ok((data, packet_body))
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn parse_body<'data>(
|
||||
client_state: crate::protocol::ClientState,
|
||||
packet_id: crate::protocol::packets::PacketId,
|
||||
is_serverbound: bool,
|
||||
data: &'data [u8],
|
||||
) -> crate::protocol::parsing::IResult<&'data [u8], Self> {
|
||||
use crate::protocol::parsing::Parsable;
|
||||
tracing::trace!(
|
||||
"GenericPacket::parse_body: {:?} {} {}",
|
||||
client_state,
|
||||
packet_id,
|
||||
is_serverbound
|
||||
);
|
||||
match (client_state, *packet_id, is_serverbound) {
|
||||
$(
|
||||
($packet_type::CLIENT_STATE, $packet_type::ID, $packet_type::IS_SERVERBOUND) => $packet_type::parse(data).map(|(data, packet)| (data, Into::<GenericPacket>::into(packet))),
|
||||
)*
|
||||
_ => Ok((data, Self::UnimplementedPacket(UnimplementedPacket(packet_id)))),
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn serialize(&self) -> (crate::protocol::packets::PacketId, Vec<u8>) {
|
||||
use crate::protocol::parsing::Parsable;
|
||||
tracing::trace!("GenericPacket::serialize: {:?}", self);
|
||||
match self {
|
||||
$(
|
||||
Self::$packet_type(packet) => (PacketId::from($packet_type::ID), packet.serialize()),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct UnimplementedPacket(VarInt);
|
||||
packet!(
|
||||
UnimplementedPacket,
|
||||
0x00,
|
||||
crate::protocol::ClientState::Disconnected,
|
||||
false,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], UnimplementedPacket> {
|
||||
Ok((data, UnimplementedPacket(0i32.into())))
|
||||
},
|
||||
|_packet: &UnimplementedPacket| -> Vec<u8> { vec![] }
|
||||
);
|
||||
|
||||
use clientbound::*;
|
||||
use serverbound::*;
|
||||
generic_packet!(
|
||||
UnimplementedPacket,
|
||||
// Handshake
|
||||
SH00Handshake,
|
||||
// Status
|
||||
SS00StatusRequest,
|
||||
SS01PingRequest,
|
||||
CS00StatusResponse,
|
||||
CS01PingResponse,
|
||||
// Login
|
||||
SL00LoginStart,
|
||||
SL01EncryptionResponse,
|
||||
SL02LoginPluginResponse,
|
||||
CL00Disconnect,
|
||||
CL01EncryptionRequest,
|
||||
CL02LoginSuccess,
|
||||
CL03SetCompression,
|
||||
CL04LoginPluginRequest,
|
||||
// Play
|
||||
SP08CommandSuggestionsRequest,
|
||||
SP11KeepAlive,
|
||||
SP13SetPlayerPosition,
|
||||
SP14SetPlayerPositionAndRotation,
|
||||
SP15SetPlayerRotation,
|
||||
CP00SpawnEntity,
|
||||
CP0BChangeDifficulty,
|
||||
CP17Disconnect,
|
||||
CP1FKeepAlive,
|
||||
CP21WorldEvent,
|
||||
CP50SetEntityVelocity,
|
||||
CP52SetExperience,
|
||||
CP68EntityEffect
|
||||
);
|
||||
|
||||
macro_rules! packet {
|
||||
($packet_type: ident, $id: literal, $client_state: expr, $serverbound: literal, $parse_body: expr, $serialize_body: expr) => {
|
||||
impl crate::protocol::packets::Packet for $packet_type {
|
||||
const ID: i32 = $id;
|
||||
const CLIENT_STATE: crate::protocol::ClientState = $client_state;
|
||||
const IS_SERVERBOUND: bool = $serverbound;
|
||||
}
|
||||
impl crate::protocol::parsing::Parsable for $packet_type {
|
||||
#[tracing::instrument]
|
||||
fn parse<'data>(
|
||||
data: &'data [u8],
|
||||
) -> crate::protocol::parsing::IResult<&'data [u8], Self> {
|
||||
$parse_body(data)
|
||||
}
|
||||
#[tracing::instrument]
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
$serialize_body(self)
|
||||
}
|
||||
}
|
||||
impl From<$packet_type> for crate::protocol::packets::GenericPacket {
|
||||
fn from(value: $packet_type) -> Self {
|
||||
crate::protocol::packets::GenericPacket::$packet_type(value)
|
||||
}
|
||||
}
|
||||
impl TryFrom<crate::protocol::packets::GenericPacket> for $packet_type {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(
|
||||
value: crate::protocol::packets::GenericPacket,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
crate::protocol::packets::GenericPacket::$packet_type(packet) => Ok(packet),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use packet;
|
@ -1,40 +0,0 @@
|
||||
use crate::protocol::{types::VarInt, ClientState};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SH00Handshake {
|
||||
pub protocol_version: VarInt,
|
||||
pub server_address: String,
|
||||
pub server_port: u16,
|
||||
pub next_state: ClientState,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
SH00Handshake,
|
||||
0x00,
|
||||
ClientState::Handshake,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SH00Handshake> {
|
||||
let (data, protocol_version) = VarInt::parse(data)?;
|
||||
let (data, server_address) = String::parse(data)?;
|
||||
let (data, server_port) = u16::parse(data)?;
|
||||
let (data, next_state) = ClientState::parse(data)?;
|
||||
// let (data, next_state) = VarInt::parse(data)?;
|
||||
|
||||
Ok((
|
||||
data,
|
||||
SH00Handshake {
|
||||
protocol_version,
|
||||
server_address,
|
||||
server_port,
|
||||
next_state,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &SH00Handshake| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.protocol_version.serialize());
|
||||
output.extend(packet.server_address.serialize());
|
||||
output.extend(packet.server_port.serialize());
|
||||
output.extend(packet.next_state.serialize());
|
||||
output
|
||||
}
|
||||
);
|
@ -1,118 +0,0 @@
|
||||
use crate::protocol::types::{Uuid, VarInt};
|
||||
use nom::bytes::streaming::take;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SL00LoginStart {
|
||||
pub name: String,
|
||||
pub uuid: Option<Uuid>,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
SL00LoginStart,
|
||||
0x00,
|
||||
crate::protocol::ClientState::Login,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SL00LoginStart> {
|
||||
let (data, name) = String::parse(data)?;
|
||||
let (data, has_uuid) = bool::parse(data)?;
|
||||
if has_uuid {
|
||||
let (data, uuid) = Uuid::parse(data)?;
|
||||
Ok((
|
||||
data,
|
||||
SL00LoginStart {
|
||||
name,
|
||||
uuid: Some(uuid),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
Ok((data, SL00LoginStart { name, uuid: None }))
|
||||
}
|
||||
},
|
||||
|packet: &SL00LoginStart| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.name.serialize());
|
||||
output.extend(packet.uuid.is_some().serialize());
|
||||
if let Some(uuid) = packet.uuid {
|
||||
output.extend(uuid.serialize());
|
||||
}
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SL01EncryptionResponse {
|
||||
pub shared_secret: Vec<u8>,
|
||||
pub verify_token: Vec<u8>,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
SL01EncryptionResponse,
|
||||
0x01,
|
||||
crate::protocol::ClientState::Login,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SL01EncryptionResponse> {
|
||||
let (data, shared_secret_len) = VarInt::parse(data)?;
|
||||
let (data, shared_secret) = take(*shared_secret_len as usize)(data)?;
|
||||
let (data, verify_token_len) = VarInt::parse(data)?;
|
||||
let (data, verify_token) = take(*verify_token_len as usize)(data)?;
|
||||
|
||||
Ok((
|
||||
data,
|
||||
SL01EncryptionResponse {
|
||||
shared_secret: shared_secret.to_vec(),
|
||||
verify_token: verify_token.to_vec(),
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &SL01EncryptionResponse| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(VarInt::from(packet.shared_secret.len() as i32).serialize());
|
||||
output.extend(&packet.shared_secret);
|
||||
output.extend(VarInt::from(packet.verify_token.len() as i32).serialize());
|
||||
output.extend(&packet.verify_token);
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SL02LoginPluginResponse {
|
||||
pub message_id: VarInt,
|
||||
pub successful: bool,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
SL02LoginPluginResponse,
|
||||
0x02,
|
||||
crate::protocol::ClientState::Login,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SL02LoginPluginResponse> {
|
||||
let (data, message_id) = VarInt::parse(data)?;
|
||||
let (data, successful) = bool::parse(data)?;
|
||||
if successful {
|
||||
Ok((
|
||||
&[],
|
||||
SL02LoginPluginResponse {
|
||||
message_id,
|
||||
successful,
|
||||
data: data.to_vec(),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
Ok((
|
||||
data,
|
||||
SL02LoginPluginResponse {
|
||||
message_id,
|
||||
successful,
|
||||
data: vec![],
|
||||
},
|
||||
))
|
||||
}
|
||||
},
|
||||
|packet: &SL02LoginPluginResponse| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.message_id.serialize());
|
||||
output.extend(packet.successful.serialize());
|
||||
if packet.successful {
|
||||
output.extend(&packet.data);
|
||||
}
|
||||
output
|
||||
}
|
||||
);
|
@ -1,13 +0,0 @@
|
||||
/// Packets for the `ClientState::Handshake` state.
|
||||
pub mod handshake;
|
||||
/// Packets for the `ClientState::Login` state.
|
||||
pub mod login;
|
||||
/// Packets for the `ClientState::Play` state.
|
||||
pub mod play;
|
||||
/// Packets for the `ClientState::Status` state.
|
||||
pub mod status;
|
||||
|
||||
pub use handshake::*;
|
||||
pub use login::*;
|
||||
pub use play::*;
|
||||
pub use status::*;
|
@ -1,134 +0,0 @@
|
||||
use crate::protocol::{
|
||||
entities::{EntityPosition, EntityRotation},
|
||||
types::VarInt,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SP08CommandSuggestionsRequest {
|
||||
pub transaction_id: VarInt,
|
||||
pub text: String,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
SP08CommandSuggestionsRequest,
|
||||
0x08,
|
||||
crate::protocol::ClientState::Play,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SP08CommandSuggestionsRequest> {
|
||||
let (data, transaction_id) = VarInt::parse(data)?;
|
||||
let (data, text) = String::parse(data)?;
|
||||
Ok((data, SP08CommandSuggestionsRequest {
|
||||
transaction_id,
|
||||
text,
|
||||
}))
|
||||
},
|
||||
|packet: &SP08CommandSuggestionsRequest| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.transaction_id.serialize());
|
||||
output.extend(packet.text.serialize());
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct SP11KeepAlive {
|
||||
pub payload: i64,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
SP11KeepAlive,
|
||||
0x11,
|
||||
crate::protocol::ClientState::Play,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SP11KeepAlive> {
|
||||
let (data, payload) = i64::parse(data)?;
|
||||
Ok((data, SP11KeepAlive { payload }))
|
||||
},
|
||||
|packet: &SP11KeepAlive| -> Vec<u8> { packet.payload.serialize() }
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct SP13SetPlayerPosition {
|
||||
pub position: EntityPosition,
|
||||
pub on_ground: bool,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
SP13SetPlayerPosition,
|
||||
0x13,
|
||||
crate::protocol::ClientState::Play,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SP13SetPlayerPosition> {
|
||||
let (data, position) = EntityPosition::parse(data)?;
|
||||
let (data, on_ground) = bool::parse(data)?;
|
||||
Ok((
|
||||
data,
|
||||
SP13SetPlayerPosition {
|
||||
position,
|
||||
on_ground,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &SP13SetPlayerPosition| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.position.serialize());
|
||||
output.extend(packet.on_ground.serialize());
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct SP14SetPlayerPositionAndRotation {
|
||||
pub position: EntityPosition,
|
||||
pub rotation: EntityRotation,
|
||||
pub on_ground: bool,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
SP14SetPlayerPositionAndRotation,
|
||||
0x14,
|
||||
crate::protocol::ClientState::Play,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SP14SetPlayerPositionAndRotation> {
|
||||
let (data, position) = EntityPosition::parse(data)?;
|
||||
let (data, rotation) = EntityRotation::parse(data)?;
|
||||
let (data, on_ground) = bool::parse(data)?;
|
||||
Ok((data, SP14SetPlayerPositionAndRotation {
|
||||
position,
|
||||
rotation,
|
||||
on_ground,
|
||||
}))
|
||||
},
|
||||
|packet: &SP14SetPlayerPositionAndRotation| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.position.serialize());
|
||||
output.extend(packet.rotation.serialize());
|
||||
output.extend(packet.on_ground.serialize());
|
||||
output
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct SP15SetPlayerRotation {
|
||||
pub rotation: EntityRotation,
|
||||
pub on_ground: bool,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
SP15SetPlayerRotation,
|
||||
0x15,
|
||||
crate::protocol::ClientState::Play,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SP15SetPlayerRotation> {
|
||||
let (data, rotation) = EntityRotation::parse(data)?;
|
||||
let (data, on_ground) = bool::parse(data)?;
|
||||
Ok((
|
||||
data,
|
||||
SP15SetPlayerRotation {
|
||||
rotation,
|
||||
on_ground,
|
||||
},
|
||||
))
|
||||
},
|
||||
|packet: &SP15SetPlayerRotation| -> Vec<u8> {
|
||||
let mut output = vec![];
|
||||
output.extend(packet.rotation.serialize());
|
||||
output.extend(packet.on_ground.serialize());
|
||||
output
|
||||
}
|
||||
);
|
@ -1,28 +0,0 @@
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct SS00StatusRequest;
|
||||
crate::protocol::packets::packet!(
|
||||
SS00StatusRequest,
|
||||
0x00,
|
||||
crate::protocol::ClientState::Status,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SS00StatusRequest> {
|
||||
Ok((data, SS00StatusRequest))
|
||||
},
|
||||
|_packet: &SS00StatusRequest| -> Vec<u8> { vec![] }
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct SS01PingRequest {
|
||||
pub payload: i64,
|
||||
}
|
||||
crate::protocol::packets::packet!(
|
||||
SS01PingRequest,
|
||||
0x01,
|
||||
crate::protocol::ClientState::Status,
|
||||
true,
|
||||
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SS01PingRequest> {
|
||||
let (data, payload) = i64::parse(data)?;
|
||||
Ok((data, SS01PingRequest { payload }))
|
||||
},
|
||||
|packet: &SS01PingRequest| -> Vec<u8> { packet.payload.serialize() }
|
||||
);
|
@ -2,7 +2,8 @@ pub use nom::IResult;
|
||||
use nom::{
|
||||
bytes::streaming::{take, take_while_m_n},
|
||||
combinator::map_res,
|
||||
number::streaming as nom_nums, Parser,
|
||||
number::streaming as nom_nums,
|
||||
Parser,
|
||||
};
|
||||
|
||||
/// Implementation of the protocol's VarInt type.
|
||||
@ -11,6 +12,11 @@ use nom::{
|
||||
/// When the original i32 value is needed, simply `Deref` it.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
|
||||
pub struct VarInt(i32);
|
||||
impl VarInt {
|
||||
pub fn parse_usize(data: &[u8]) -> IResult<&[u8], usize> {
|
||||
nom::combinator::map_res(Self::parse, usize::try_from)(data)
|
||||
}
|
||||
}
|
||||
impl std::ops::Deref for VarInt {
|
||||
type Target = i32;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -37,6 +43,12 @@ impl From<usize> for VarInt {
|
||||
(value as i32).into()
|
||||
}
|
||||
}
|
||||
impl TryFrom<VarInt> for usize {
|
||||
type Error = <usize as TryFrom<i32>>::Error;
|
||||
fn try_from(value: VarInt) -> Result<Self, Self::Error> {
|
||||
usize::try_from(*value)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for VarInt {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
|
@ -37,17 +37,10 @@ impl ProxyConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ProxyArgs {
|
||||
upstream: String,
|
||||
}
|
||||
impl Default for ProxyArgs {
|
||||
fn default() -> Self {
|
||||
ProxyArgs {
|
||||
upstream: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ProxyArgs {
|
||||
pub fn instance() -> Option<&'static Self> {
|
||||
Args::instance().proxy.as_ref()
|
||||
|
@ -199,7 +199,8 @@ impl Server {
|
||||
.filter(|client| matches!(client.state, NetworkClientState::Play))
|
||||
.count();
|
||||
'clients: for client in clients.iter_mut() {
|
||||
use crate::protocol::packets::{clientbound::*, serverbound::*};
|
||||
use crate::protocol::packets;
|
||||
|
||||
'packets: while !client.incoming_packet_queue.is_empty() {
|
||||
// client.read_packet()
|
||||
// None: The client doesn't have any more packets.
|
||||
@ -207,7 +208,9 @@ impl Server {
|
||||
// Some(Ok(_)): The client read the expected packet.
|
||||
match client.state.clone() {
|
||||
NetworkClientState::Handshake => {
|
||||
let handshake = match client.read_packet::<SH00Handshake>() {
|
||||
use packets::handshake::serverbound::Handshake;
|
||||
|
||||
let handshake = match client.read_packet::<Handshake>() {
|
||||
None => continue 'packets,
|
||||
Some(Err(_)) => continue 'clients,
|
||||
Some(Ok(handshake)) => handshake,
|
||||
@ -235,7 +238,11 @@ impl Server {
|
||||
received_request,
|
||||
received_ping,
|
||||
} if !received_request => {
|
||||
let _status_request = match client.read_packet::<SS00StatusRequest>() {
|
||||
use packets::status::{
|
||||
clientbound::StatusResponse, serverbound::StatusRequest,
|
||||
};
|
||||
|
||||
let _status_request = match client.read_packet::<StatusRequest>() {
|
||||
None => continue 'packets,
|
||||
Some(Err(_)) => continue 'clients,
|
||||
Some(Ok(p)) => p,
|
||||
@ -246,7 +253,7 @@ impl Server {
|
||||
};
|
||||
let config = Config::instance();
|
||||
use base64::Engine;
|
||||
client.queue_packet(CS00StatusResponse {
|
||||
client.queue_packet(StatusResponse {
|
||||
response: serde_json::json!({
|
||||
"version": {
|
||||
"name": config.global.game_version,
|
||||
@ -267,19 +274,25 @@ impl Server {
|
||||
}
|
||||
// Status !received_ping: Read SS00StatusRequest and respond with CS00StatusResponse
|
||||
NetworkClientState::Status { received_ping, .. } if !received_ping => {
|
||||
let ping = match client.read_packet::<SS01PingRequest>() {
|
||||
use packets::status::{
|
||||
clientbound::PingResponse, serverbound::PingRequest,
|
||||
};
|
||||
|
||||
let ping = match client.read_packet::<PingRequest>() {
|
||||
None => continue 'packets,
|
||||
Some(Err(_)) => continue 'clients,
|
||||
Some(Ok(p)) => p,
|
||||
};
|
||||
client.queue_packet(CS01PingResponse {
|
||||
client.queue_packet(PingResponse {
|
||||
payload: ping.payload,
|
||||
});
|
||||
client.state = NetworkClientState::Disconnected;
|
||||
}
|
||||
NetworkClientState::Status { .. } => unreachable!(),
|
||||
NetworkClientState::Login { received_start, .. } if !received_start.0 => {
|
||||
let login_start = match client.read_packet::<SL00LoginStart>() {
|
||||
use packets::login::{clientbound::*, serverbound::*};
|
||||
|
||||
let login_start = match client.read_packet::<LoginStart>() {
|
||||
None => continue 'packets,
|
||||
Some(Err(_)) => continue 'clients,
|
||||
Some(Ok(p)) => p,
|
||||
@ -287,7 +300,7 @@ impl Server {
|
||||
// TODO: Authenticate the user.
|
||||
// TODO: Get the user from the stored database.
|
||||
// TODO: Encryption/compression.
|
||||
client.queue_packet(CL02LoginSuccess {
|
||||
client.queue_packet(LoginSuccess {
|
||||
uuid: login_start.uuid.unwrap_or(0u128),
|
||||
username: login_start.name.clone(),
|
||||
properties: vec![],
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::protocol::{
|
||||
packets::{serverbound::SL00LoginStart, GenericPacket},
|
||||
packets::{self, Packet, PacketDirection},
|
||||
parsing::Parsable,
|
||||
ClientState,
|
||||
};
|
||||
@ -30,7 +30,7 @@ pub(crate) enum NetworkClientState {
|
||||
/// The client sent `SH00Handshake` with `next_state = ClientState::Login`
|
||||
/// and is attempting to join the server.
|
||||
Login {
|
||||
received_start: (bool, Option<SL00LoginStart>),
|
||||
received_start: (bool, Option<packets::login::serverbound::LoginStart>),
|
||||
},
|
||||
/// The server sent `CL02LoginSuccess` and transitioned to `Play`.
|
||||
#[allow(dead_code)]
|
||||
@ -76,13 +76,13 @@ pub(crate) struct NetworkClient {
|
||||
incoming_data: VecDeque<u8>,
|
||||
/// Packets get appended to the back as they get read,
|
||||
/// and popped from the front as they get handled.
|
||||
pub incoming_packet_queue: VecDeque<GenericPacket>,
|
||||
pub incoming_packet_queue: VecDeque<Packet>,
|
||||
/// Keeps track of the last time the client sent data.
|
||||
///
|
||||
/// This is useful for removing clients that have timed out.
|
||||
pub last_received_data_time: Instant,
|
||||
/// Packets get appended to the back and get popped from the front as they get sent.
|
||||
pub outgoing_packet_queue: VecDeque<GenericPacket>,
|
||||
pub outgoing_packet_queue: VecDeque<Packet>,
|
||||
}
|
||||
impl NetworkClient {
|
||||
#[tracing::instrument]
|
||||
@ -139,7 +139,11 @@ impl NetworkClient {
|
||||
|
||||
let mut bytes_consumed = 0;
|
||||
while !data.is_empty() {
|
||||
let p = GenericPacket::parse_uncompressed(self.state.clone().into(), true, data);
|
||||
let p = Packet::parse(
|
||||
self.state.clone().into(),
|
||||
PacketDirection::Serverbound,
|
||||
data,
|
||||
);
|
||||
trace!("{} got {:?}", self.id, p);
|
||||
match p {
|
||||
Ok((d, packet)) => {
|
||||
@ -166,9 +170,9 @@ impl NetworkClient {
|
||||
// Some(Err(())): The packet was the wrong type.
|
||||
// Some(Ok(_)): The packet was successfully read.
|
||||
#[tracing::instrument]
|
||||
pub fn read_packet<P: std::fmt::Debug + TryFrom<GenericPacket>>(
|
||||
pub fn read_packet<P: std::fmt::Debug + TryFrom<Packet>>(
|
||||
&mut self,
|
||||
) -> Option<std::result::Result<P, GenericPacket>> {
|
||||
) -> Option<std::result::Result<P, Packet>> {
|
||||
if let Some(generic_packet) = self.incoming_packet_queue.pop_back() {
|
||||
if let Ok(packet) = TryInto::<P>::try_into(generic_packet.clone()) {
|
||||
Some(Ok(packet))
|
||||
@ -181,7 +185,7 @@ impl NetworkClient {
|
||||
}
|
||||
}
|
||||
#[tracing::instrument]
|
||||
pub fn queue_packet<P: std::fmt::Debug + Into<GenericPacket>>(&mut self, packet: P) {
|
||||
pub fn queue_packet<P: std::fmt::Debug + Into<Packet>>(&mut self, packet: P) {
|
||||
self.outgoing_packet_queue.push_back(packet.into());
|
||||
}
|
||||
#[tracing::instrument]
|
||||
@ -195,11 +199,11 @@ impl NetworkClient {
|
||||
Ok(())
|
||||
}
|
||||
#[tracing::instrument]
|
||||
pub async fn send_packet<P: std::fmt::Debug + Into<GenericPacket>>(
|
||||
pub async fn send_packet<P: std::fmt::Debug + Into<Packet>>(
|
||||
&self,
|
||||
packet: P,
|
||||
) -> tokio::io::Result<()> {
|
||||
let packet: GenericPacket = packet.into();
|
||||
let packet: Packet = packet.into();
|
||||
|
||||
debug!("Sending packet {:?} to client {}", packet, self.id);
|
||||
let (packet_id, mut packet_body) = packet.serialize();
|
||||
@ -219,7 +223,8 @@ impl NetworkClient {
|
||||
}
|
||||
#[tracing::instrument]
|
||||
pub async fn disconnect(&mut self, reason: Option<crate::protocol::types::Chat>) {
|
||||
use crate::protocol::packets::clientbound::{CL00Disconnect, CP17Disconnect};
|
||||
use packets::{login::clientbound::LoginDisconnect, play::clientbound::PlayDisconnect};
|
||||
|
||||
let reason = reason.unwrap_or(serde_json::json!({
|
||||
"text": "You have been disconnected!"
|
||||
}));
|
||||
@ -229,10 +234,10 @@ impl NetworkClient {
|
||||
// Impossible to send a disconnect in these states.
|
||||
}
|
||||
ClientState::Login => {
|
||||
let _ = self.send_packet(CL00Disconnect { reason }).await;
|
||||
let _ = self.send_packet(LoginDisconnect { reason }).await;
|
||||
}
|
||||
ClientState::Play => {
|
||||
let _ = self.send_packet(CP17Disconnect { reason }).await;
|
||||
let _ = self.send_packet(PlayDisconnect { reason }).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user