mcbe_lan_advertizer/
pong.rs

1//! [`Pong`] Represents a UDP raknet Pong packet as a struct
2//!
3//! You can create it from [`ServerProperties`] using the [`From`] trait
4
5use rand::Rng;
6use std::time::Instant;
7
8use crate::config::server_properties::{Gamemode, ServerProperties};
9
10const PONG: u8 = 0x1c;
11type Uuid = u64;
12const MAGIC: [u8; 16] = [
13    0x00, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78,
14];
15type StringLength = u16;
16
17/// Represents a Pong packet
18///
19/// You can generate a Pong packet from [`ServerProperties`] or use the [`Default::default()`]
20///
21/// Use [`as_bytes()`](`Pong::as_bytes()`) to render a RakNet Pong packet
22pub struct Pong {
23    /// Used internally as part of the Raknet protocol
24    init_time: Instant,
25    /// Seems like this is just a random number. Official servers seem to have
26    /// cool GUIDs like `8` but it does not seem to matter too much for a LAN
27    /// server.
28    guid: Uuid,
29    /// Seems to always be MCPE
30    server_type: String,
31    /// First line in the Friends tab list item
32    pub title: String,
33    /// Second line in the Friends tab list item
34    pub description: String,
35    /// The string must be in format X.X.X, where X is a number, otherwise the client wont display it.
36    /// See [Bedrock version numbers](https://wiki.vg/Bedrock_Protocol_version_numbers)
37    pub protocol: (u64, String),
38    /// Not displayed in the friends tab as of MCBE v1.19.51
39    pub gamemode: Gamemode,
40    /// Server's IPv4 port
41    pub port4: u16,
42    /// Server's IPv6 port
43    pub port6: u16,
44}
45
46/// Fills the Pong packet with default values for MCBE Dedicated server v1.19.51
47impl Default for Pong {
48    fn default() -> Self {
49        Self {
50            init_time: Instant::now(),
51            guid: rand::thread_rng().gen(),
52            server_type: "MCPE".to_string(),
53            title: "Minecraft server".to_string(),
54            description: "Bedrock level".to_string(),
55            protocol: (560, "1.19.51".to_string()),
56            gamemode: Gamemode::Creative,
57            port4: 19132,
58            port6: 19133,
59        }
60    }
61}
62
63/// Takes everything it can from server.properties and the rest is set
64/// using [`Default::default()`]
65impl From<ServerProperties> for Pong {
66    fn from(props: ServerProperties) -> Self {
67        Self {
68            title: props.server_name,
69            description: props.level_name,
70            gamemode: props.gamemode,
71            port4: props.port4,
72            port6: props.port6,
73            ..Default::default()
74        }
75    }
76}
77
78impl Pong {
79    /// Renders a Pong packet that can be sent over UDP
80    pub fn as_bytes(&self) -> Vec<u8> {
81        let mut msg = Vec::<u8>::with_capacity(120);
82
83        msg.push(PONG);
84
85        let timestamp = self.init_time.elapsed().as_millis();
86        msg.append(&mut timestamp.to_be_bytes()[8..].to_vec());
87
88        msg.append(&mut self.guid.to_be_bytes().to_vec());
89        msg.append(&mut MAGIC.to_vec());
90
91        let descriptor = format!(
92                "{server_type};{title};{version_number};{version_str};0;1;999;{description};{gamemode};1;{p4};{p6};0;",
93                server_type = self.server_type,
94                title = self.title,
95                version_number = self.protocol.0,
96                version_str = self.protocol.1,
97                description = self.description,
98                gamemode = self.gamemode.to_string(),
99                p4 = self.port4,
100                p6 = self.port6,
101            );
102        msg.append(&mut (descriptor.len() as StringLength).to_be_bytes().to_vec());
103        msg.append(&mut descriptor.as_bytes().to_vec());
104        msg
105    }
106}