ddapi_rs/scheme/ddnet/
master.rs

1use crate::prelude::Addr;
2use crate::prelude::{addr_serialization, Protocol};
3use crate::scheme::DDNET_BASE_URL;
4use serde_derive::{Deserialize, Serialize};
5use std::collections::{HashMap, HashSet};
6
7fn default_location() -> String {
8    "unknown".to_string()
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum MasterServer {
13    One = 1,
14    Two = 2,
15    Three = 3,
16    Four = 4,
17}
18
19impl MasterServer {
20    pub fn get_index(&self) -> i32 {
21        *self as i32
22    }
23
24    pub fn api(&self) -> String {
25        format!(
26            "https://master{}.{}/ddnet/15/servers.json",
27            self.get_index(),
28            DDNET_BASE_URL
29        )
30    }
31}
32
33#[derive(Default, Debug, Clone)]
34pub struct ClanCount {
35    pub name: String,
36    pub count: usize,
37}
38
39#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct Master {
42    pub communities: Vec<Community>,
43    pub servers: Vec<Server>,
44}
45
46#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
47pub struct Community {
48    pub id: String,
49    pub name: String,
50    pub has_finishes: bool,
51    pub icon: Icon,
52    pub contact_urls: Vec<String>,
53}
54
55#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
56pub struct Icon {
57    pub sha256: String,
58    pub url: String,
59}
60
61impl Master {
62    pub fn api(master: MasterServer) -> String {
63        master.api()
64    }
65
66    pub fn count_clients(&self) -> usize {
67        self.servers.iter().map(|s| s.info.clients.len()).sum()
68    }
69
70    pub fn get_clans(&self) -> Vec<ClanCount> {
71        self.get_filtered_clans(None)
72    }
73
74    pub fn get_filtered_clans(&self, filters: Option<Vec<&str>>) -> Vec<ClanCount> {
75        if self.servers.is_empty() {
76            return Vec::new();
77        }
78
79        let filter_set: HashSet<&str> = filters.unwrap_or_default().into_iter().collect();
80
81        let mut clan_counts = HashMap::new();
82
83        for server in &self.servers {
84            for client in &server.info.clients {
85                if !client.clan.is_empty() && !filter_set.contains(client.clan.as_str()) {
86                    *clan_counts.entry(client.clan.clone()).or_insert(0) += 1;
87                }
88            }
89        }
90
91        let mut result: Vec<ClanCount> = clan_counts
92            .into_iter()
93            .map(|(name, count)| ClanCount { name, count })
94            .collect();
95
96        result.sort_by(|a, b| b.count.cmp(&a.count));
97        result
98    }
99}
100
101#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
102pub struct Server {
103    #[serde(with = "addr_serialization")]
104    pub addresses: Vec<Addr>,
105    pub community: Option<String>,
106    #[serde(default = "default_location")]
107    pub location: String,
108    pub info: Info,
109}
110
111impl Server {
112    pub fn count_client(&self) -> usize {
113        self.info.clients.len()
114    }
115
116    pub fn ipv4_addresses(&self) -> Vec<&Addr> {
117        self.addresses
118            .iter()
119            .filter(|addr| addr.ip.is_ipv4())
120            .collect()
121    }
122
123    pub fn ipv6_addresses(&self) -> Vec<&Addr> {
124        self.addresses
125            .iter()
126            .filter(|addr| addr.ip.is_ipv6())
127            .collect()
128    }
129
130    pub fn addresses_by_protocol(&self, protocol: Protocol) -> Vec<&Addr> {
131        self.addresses
132            .iter()
133            .filter(|addr| addr.protocol == protocol)
134            .collect()
135    }
136}
137
138#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
139pub struct Info {
140    pub max_clients: i64,
141    pub max_players: i64,
142    #[serde(default)]
143    pub passworded: bool,
144    #[serde(rename = "game_type")]
145    pub gametype: String,
146    pub name: String,
147    pub map: IMap,
148    pub version: String,
149    #[serde(default)]
150    pub clients: Vec<Client>,
151    #[serde(default)]
152    pub requires_login: bool,
153}
154
155#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
156pub struct IMap {
157    pub name: String,
158    pub sha256: Option<String>,
159    pub size: Option<i64>,
160}
161
162#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
163pub struct Client {
164    pub name: String,
165    pub clan: String,
166    pub country: i32,
167    pub score: i64,
168    #[serde(default)]
169    pub is_player: bool,
170    pub skin: Option<Skin>,
171    #[serde(default)]
172    pub afk: bool,
173    #[serde(default)]
174    pub team: i64,
175}
176
177#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
178pub struct Skin {
179    pub name: Option<String>,
180    pub color_body: Option<i64>,
181    pub color_feet: Option<i64>,
182}