elytra_ping/
parse.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
4#[non_exhaustive]
5#[serde(rename_all = "camelCase")]
6pub struct JavaServerInfo {
7    pub version: Option<ServerVersion>,
8    pub players: Option<ServerPlayers>,
9    pub description: TextComponent,
10    pub favicon: Option<String>,
11    #[serde(rename = "modinfo")]
12    pub mod_info: Option<ServerModInfo>,
13    /// Servers with the No Chat Reports mod installed will set this field to `true` to indicate
14    /// to players that all chat messages sent on this server are not reportable to Mojang.
15    pub prevents_chat_reports: Option<bool>,
16    /// If the server supports Chat Preview (added in 1.19 and removed in 1.19.3), this field is set to `true`.
17    pub previews_chat: Option<bool>,
18    /// Servers will set this field to `true` if they block chat messages that cannot be reported to Mojang.
19    pub enforces_secure_chat: Option<bool>,
20}
21
22#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
23#[non_exhaustive]
24pub struct ServerVersion {
25    pub name: String,
26    pub protocol: u32,
27}
28
29#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
30#[non_exhaustive]
31pub struct ServerPlayers {
32    pub max: u32,
33    pub online: u32,
34    pub sample: Option<Vec<ServerPlayersSample>>,
35}
36
37/// Contains basic information about one of the players in a server.
38#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
39#[non_exhaustive]
40pub struct ServerPlayersSample {
41    /// The player's username
42    pub name: Option<String>,
43    /// The player's UUID
44    pub id: Option<String>,
45}
46
47impl ServerPlayersSample {
48    /// Returns whether the server has chosen to hide this player's identity and is reporting placeholder information. This is generally caused by a player having the [Allow Server Listings](https://wiki.vg/Protocol#Client_Information_.28configuration.29) option set to `false`.
49    pub fn is_anonymous(&self) -> bool {
50        self.id
51            .as_deref()
52            .map_or(true, |id| id == "00000000-0000-0000-0000-000000000000")
53    }
54}
55
56#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
57#[non_exhaustive]
58pub struct ServerModInfo {
59    #[serde(rename = "type")]
60    pub loader_type: String,
61    #[serde(rename = "modList")]
62    pub mod_list: Vec<ServerMod>,
63}
64
65#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
66#[non_exhaustive]
67pub struct ServerMod {
68    #[serde(rename = "modid")]
69    pub mod_id: String,
70    pub version: String,
71}
72
73impl std::str::FromStr for JavaServerInfo {
74    type Err = serde_json::Error;
75    fn from_str(json: &str) -> Result<Self, Self::Err> {
76        serde_json::from_str(json)
77    }
78}
79
80#[derive(Serialize, Deserialize, Debug, Hash, Clone, PartialEq, Eq)]
81#[serde(untagged)]
82pub enum TextComponent {
83    Plain(String),
84    Fancy(FancyText),
85    Extra(Vec<TextComponent>),
86}
87
88#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq, Default)]
89#[non_exhaustive]
90pub struct FancyText {
91    #[serde(default)]
92    pub text: Option<String>,
93    #[serde(default)]
94    pub color: Option<String>,
95    #[serde(default)]
96    pub bold: Option<bool>,
97    #[serde(default)]
98    pub italic: Option<bool>,
99    #[serde(default)]
100    pub underlined: Option<bool>,
101    #[serde(default)]
102    pub strikethrough: Option<bool>,
103    #[serde(default)]
104    pub obfuscated: Option<bool>,
105    #[serde(default)]
106    pub extra: Option<Vec<TextComponent>>,
107}
108
109impl From<TextComponent> for FancyText {
110    fn from(value: TextComponent) -> Self {
111        match value {
112            TextComponent::Plain(text) => FancyText {
113                text: Some(text),
114                ..Default::default()
115            },
116            TextComponent::Fancy(fancy) => fancy,
117            TextComponent::Extra(components) => {
118                let mut components = components.into_iter();
119                let mut first = components.next().map(FancyText::from).unwrap_or_default();
120                first.extra.get_or_insert_with(Vec::new).extend(components);
121                first
122            }
123        }
124    }
125}