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}