elytra_ping/protocol/
frame.rs1use std::io::Cursor;
2
3use bytes::Buf;
4use mc_varint::{VarInt, VarIntRead};
5use snafu::{Backtrace, OptionExt, Snafu};
6use tracing::trace;
7
8use crate::mc_string::{decode_mc_string, McStringError};
9
10#[derive(Snafu, Debug)]
11pub enum FrameError {
12 Incomplete { backtrace: Backtrace },
14 #[snafu(display("I/O error: {source}"), context(false))]
16 Io {
17 source: std::io::Error,
18 backtrace: Backtrace,
19 },
20 InvalidLength { backtrace: Backtrace },
22 InvalidFrameId { id: i32, backtrace: Backtrace },
24 #[snafu(display("Failed to decode string: {source}"), context(false))]
26 StringDecodeFailed {
27 #[snafu(backtrace)]
28 source: McStringError,
29 },
30}
31
32#[derive(Debug)]
33#[non_exhaustive]
34pub enum Frame {
35 Handshake {
36 protocol: VarInt,
37 address: String,
38 port: u16,
39 state: VarInt,
41 },
42 StatusRequest,
43 StatusResponse {
44 json: String,
45 },
46 PingRequest {
47 payload: i64,
48 },
49 PingResponse {
50 payload: i64,
51 },
52}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56#[deprecated(
57 since = "5.1.0",
58 note = "Elytra Ping as a SLP server is untested and not supported"
59)]
60pub enum ServerState {
61 Handshake,
63 Status,
65}
66
67impl Frame {
68 pub const PROTOCOL_VERSION: i32 = 767;
69 pub const HANDSHAKE_ID: i32 = 0x00;
70 pub const STATUS_REQUEST_ID: i32 = 0x00;
71 pub const STATUS_RESPONSE_ID: i32 = 0x00;
72 pub const PING_REQUEST_ID: i32 = 0x01;
73 pub const PING_RESPONSE_ID: i32 = 0x01;
74
75 pub fn check(buf: &mut Cursor<&[u8]>) -> Result<(), FrameError> {
77 let available_data = buf.get_ref().len();
78
79 let remaining_data_len: usize =
81 i32::from(buf.read_var_int().ok().context(IncompleteSnafu)?)
82 .try_into()
83 .ok()
84 .context(InvalidLengthSnafu)?;
85 let header_len = buf.position() as usize;
86 let total_len = header_len + remaining_data_len;
87
88 let is_valid = available_data >= total_len;
90
91 if is_valid {
92 trace!("Valid frame, packet size: {total_len}, header size: {header_len}, body size: {remaining_data_len}, downloaded: {available_data}");
93 Ok(())
94 } else {
95 trace!("Invalid frame, packet size: {total_len}, downloaded: {available_data}");
96 IncompleteSnafu.fail()
97 }
98 }
99
100 pub fn parse(
107 cursor: &mut Cursor<&[u8]>,
108 server_state: Option<ServerState>,
109 ) -> Result<Frame, FrameError> {
110 let id = i32::from(cursor.read_var_int()?);
111
112 match server_state {
113 Some(ServerState::Handshake) => {
114 if id == Self::HANDSHAKE_ID {
115 let protocol = cursor.read_var_int()?;
116 let address = decode_mc_string(cursor)?;
117 let port = cursor.get_u16();
118 let state = cursor.read_var_int()?;
119 return Ok(Frame::Handshake {
120 protocol,
121 address,
122 port,
123 state,
124 });
125 }
126 }
127 Some(ServerState::Status) => {
128 match id {
129 Self::STATUS_REQUEST_ID => {
130 return Ok(Frame::StatusRequest);
131 }
132 Self::PING_REQUEST_ID => {
133 let payload = cursor.get_i64();
135 return Ok(Frame::PingRequest { payload });
136 }
137 _ => {}
138 }
139 }
140 None => {
141 match id {
142 Self::STATUS_RESPONSE_ID => {
143 let json = decode_mc_string(cursor)?;
144 return Ok(Frame::StatusResponse { json });
145 }
146 Self::PING_RESPONSE_ID => {
147 let payload = cursor.get_i64();
149 return Ok(Frame::PingResponse { payload });
150 }
151 _ => {}
152 }
153 }
154 }
155
156 InvalidFrameIdSnafu { id }.fail()
157 }
158}