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
54impl Frame {
55 pub const PROTOCOL_VERSION: i32 = 767;
56 pub const HANDSHAKE_ID: i32 = 0x00;
57 pub const STATUS_REQUEST_ID: i32 = 0x00;
58 pub const STATUS_RESPONSE_ID: i32 = 0x00;
59 pub const PING_REQUEST_ID: i32 = 0x01;
60 pub const PING_RESPONSE_ID: i32 = 0x01;
61
62 pub fn check(buf: &mut Cursor<&[u8]>) -> Result<(), FrameError> {
64 let available_data = buf.get_ref().len();
65
66 let remaining_data_len: usize =
68 i32::from(buf.read_var_int().ok().context(IncompleteSnafu)?)
69 .try_into()
70 .ok()
71 .context(InvalidLengthSnafu)?;
72 let header_len = buf.position() as usize;
73 let total_len = header_len + remaining_data_len;
74
75 let is_valid = available_data >= total_len;
77
78 if is_valid {
79 trace!("Valid frame, packet size: {total_len}, header size: {header_len}, body size: {remaining_data_len}, downloaded: {available_data}");
80 Ok(())
81 } else {
82 trace!("Invalid frame, packet size: {total_len}, downloaded: {available_data}");
83 IncompleteSnafu.fail()
84 }
85 }
86
87 pub fn parse(cursor: &mut Cursor<&[u8]>) -> Result<Frame, FrameError> {
89 let id = i32::from(cursor.read_var_int()?);
90
91 match id {
92 Self::STATUS_RESPONSE_ID => {
93 let json = decode_mc_string(cursor)?;
94 Ok(Frame::StatusResponse { json })
95 }
96 Self::PING_RESPONSE_ID => {
97 let payload = cursor.get_i64();
99 Ok(Frame::PingResponse { payload })
100 }
101 _ => InvalidFrameIdSnafu { id }.fail(),
102 }
103 }
104}