msp/server/
bedrock_server.rs1use serde::Serialize;
2
3use crate::{
4 conf::Conf,
5 share::{create_udp_socket, UdpReader},
6 MspErr,
7};
8
9const MAGIC_BYTES: &[u8] = &[
10 0x00, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD, 0xFD, 0xFD, 0x12, 0x34, 0x56, 0x78,
11];
12
13#[derive(Serialize, Debug)]
25pub struct BedrockServer {
26 pub edition: String,
28 pub motd_line_1: String,
30 pub protocol_version: i32,
32 pub version_name: String,
34 pub online_players: i32,
36 pub max_players: i32,
38 pub server_id: String,
40 pub motd_line_2: String,
42 pub game_mode: String,
44 pub game_mode_id: u8,
46 pub port_ipv4: u16,
48 pub port_ipv6: u16,
50}
51
52impl std::fmt::Display for BedrockServer {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 write!(
55 f,
56 "{}",
57 serde_json::to_string_pretty(self).map_err(|_| std::fmt::Error)?
58 )
59 }
60}
61
62pub fn get_bedrock_server_status(conf: &Conf) -> Result<BedrockServer, MspErr> {
63 let socket = create_udp_socket(&conf.socket_conf)?;
64
65 let packet = [
66 &[0x01],
68 [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice(),
70 MAGIC_BYTES,
72 ]
73 .concat();
74
75 socket.send_to(packet.as_slice(), conf)?;
76
77 let mut udp_reader = UdpReader::create_with_idx(socket, 0);
78
79 match udp_reader.read_bufs(1)?.get(0) {
80 Some(&first_buf) if first_buf != 0x1C => {
81 return Err(MspErr::DataErr(format!(
82 "Packet response excepted start with: 0x1C, but got: 0x{:02X}",
83 first_buf
84 )));
85 }
86 Some(_) | None => {}
87 };
88
89 udp_reader.set_current_idx_forward(8);
91
92 let _server_guid = udp_reader.read_bufs(8)?;
93 let _magic_bytes = udp_reader.read_bufs(16)?;
94 let server_info_len = match udp_reader.read_bufs(2)?.try_into() {
95 Ok(len) => u16::from_be_bytes(len) as usize,
96 Err(_) => {
97 return Err(MspErr::DataErr("Cannot convert to u16.".into()));
98 }
99 };
100 let server_info_buf = udp_reader.read_bufs(server_info_len)?;
101 let server_info = String::from_utf8_lossy(server_info_buf.as_slice());
102
103 println!("{:?}", server_info);
104
105 let server_info_split = server_info.split(";").collect::<Vec<_>>();
106
107 if server_info_split.len() < 10 {
108 return Err(MspErr::DataErr(format!(
109 "Expected return at least 10 parts of server information, but {} were obtained.",
110 server_info_split.len()
111 )));
112 }
113
114 Ok(BedrockServer {
115 edition: server_info_split[0].into(),
116 motd_line_1: server_info_split[1].into(),
117 protocol_version: server_info_split[2].parse()?,
118 version_name: server_info_split[3].into(),
119 online_players: server_info_split[4].parse()?,
120 max_players: server_info_split[5].parse()?,
121 server_id: server_info_split[6].into(),
122 motd_line_2: server_info_split[7].into(),
123 game_mode: server_info_split[8].into(),
124 game_mode_id: server_info_split[9].parse()?,
125 port_ipv4: if let Some(&p4) = server_info_split.get(10) {
126 p4.parse()?
127 } else {
128 conf.port
129 },
130 port_ipv6: if let Some(&p6) = server_info_split.get(11) {
131 p6.parse()?
132 } else {
133 0
134 },
135 })
136}