1use crate::scheme::{deserialize_datetime_timestamp, serialize_datetime_timestamp};
2use crate::util::encoding::{encode, slugify2};
3use chrono::NaiveDateTime;
4use serde_derive::{Deserialize, Serialize};
5use std::collections::{HashMap, HashSet};
6
7fn default_location() -> String {
8 "unknown".to_string()
9}
10
11pub enum MasterServer {
12 One,
13 Two,
14 Three,
15 Four,
16}
17
18impl MasterServer {
19 fn get_index(&self) -> i32 {
20 match &self {
21 Self::One => 1,
22 Self::Two => 2,
23 Self::Three => 3,
24 Self::Four => 4,
25 }
26 }
27}
28
29#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
30pub struct QueryMapper {
31 pub mapper: String,
32 pub num_maps: i64,
33}
34
35impl QueryMapper {
36 pub fn api(player: &str) -> String {
37 format!("https://ddnet.org/maps/?qmapper={}", encode(player))
38 }
39}
40
41#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
42pub struct QueryMap {
43 pub name: String,
44 pub r#type: String,
45 pub mapper: String,
46}
47
48impl QueryMap {
49 pub fn api(player: &str) -> String {
50 format!("https://ddnet.org/maps/?query={}", encode(player))
51 }
52}
53
54#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
55#[serde(rename_all = "camelCase")]
56pub struct Query {
57 pub points: i64,
58 pub name: String,
59}
60
61impl Query {
62 pub fn api(player: &str) -> String {
63 format!("https://ddnet.org/players/?query={}", encode(player))
64 }
65}
66
67#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
68pub struct ReleasesMaps {
69 pub name: String,
70 pub website: String,
71 pub thumbnail: String,
72 pub web_preview: String,
73 pub r#type: String,
74 pub points: u8,
75 pub difficulty: u8,
76 pub mapper: String,
77 pub release: String,
78 pub width: Option<u64>,
79 pub height: Option<u64>,
80 #[serde(default)]
81 pub tiles: Vec<String>,
82}
83
84impl ReleasesMaps {
85 pub fn url() -> String {
86 "https://ddnet.org/releases".to_string()
87 }
88
89 pub fn api() -> String {
90 "https://ddnet.org/releases/maps.json".to_string()
91 }
92}
93
94#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
95pub struct StatusData {
96 pub name: String,
97 pub r#type: String,
98 pub host: String,
99 pub location: String,
100 pub online4: bool,
101 pub online6: bool,
102 pub uptime: String,
103 pub load: f32,
104 pub network_rx: u64,
105 pub network_tx: u64,
106 pub packets_rx: u64,
107 pub packets_tx: u64,
108 pub cpu: u32,
109 pub memory_total: u64,
110 pub memory_used: u64,
111 pub swap_total: u64,
112 pub swap_used: u64,
113 pub hdd_total: u64,
114 pub hdd_used: u64,
115}
116
117#[derive(Serialize, Deserialize, Debug)]
118pub struct Status {
119 pub servers: Vec<StatusData>,
120 pub updated: String,
121}
122
123impl Status {
124 pub fn url() -> String {
125 "https://ddnet.org/status".to_string()
126 }
127
128 pub fn api() -> String {
129 "https://ddnet.org/status/json/stats.json".to_string()
130 }
131}
132
133#[derive(Default, Debug, Clone)]
134pub struct ClansCount {
135 pub name: String,
136 pub count: usize,
137}
138
139#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
140#[serde(rename_all = "camelCase")]
141pub struct Master {
142 pub servers: Vec<Server>,
143}
144
145impl Master {
146 pub fn api(master: MasterServer) -> String {
147 format!(
148 "https://master{}.ddnet.org/ddnet/15/servers.json",
149 master.get_index()
150 )
151 }
152
153 pub fn count_clients(&self) -> usize {
154 self.servers.iter().map(Server::count_client).sum()
155 }
156
157 pub fn get_clans(&self) -> Vec<ClansCount> {
158 self.get_clans_with_custom_rm(None)
159 }
160
161 pub fn get_clans_with_custom_rm(&self, rm: Option<Vec<&str>>) -> Vec<ClansCount> {
162 let remove_list: HashSet<&str> = rm
163 .unwrap_or_else(|| vec!["DD-Persian", "/vDQMHSss8W"])
164 .into_iter()
165 .collect();
166
167 if self.servers.is_empty() {
168 return vec![];
169 }
170
171 let mut dat: HashMap<String, usize> = HashMap::new();
172
173 self.servers.iter().for_each(|server| {
174 server
175 .info
176 .clients
177 .iter()
178 .filter(|client| !client.clan.is_empty())
179 .for_each(|client| {
180 *dat.entry(client.clan.clone()).or_insert(0) += 1;
181 });
182 });
183
184 for clan in remove_list {
185 dat.remove(clan);
186 }
187
188 let mut sorted_dat: Vec<ClansCount> = dat
189 .into_iter()
190 .map(|(name, count)| ClansCount { name, count })
191 .collect();
192
193 sorted_dat.sort_by(|a, b| b.count.cmp(&a.count));
194 sorted_dat
195 }
196}
197
198#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
199pub struct Server {
200 pub addresses: Vec<String>,
201 #[serde(default = "default_location")]
202 pub location: String,
203 pub info: Info,
204}
205
206impl Server {
207 #[allow(dead_code)]
208 pub fn count_client(&self) -> usize {
209 self.info.clients.len()
210 }
211}
212
213#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
214pub struct Info {
215 pub max_clients: i64,
216 pub max_players: i64,
217 #[serde(default)]
218 pub passworded: bool,
219 #[serde(rename = "game_type")]
220 pub gametype: String,
221 pub name: String,
222 pub map: IMap,
223 pub version: String,
224 #[serde(default)]
225 pub clients: Vec<Client>,
226 #[serde(default)]
227 pub requires_login: bool,
228 pub community: Option<Community>,
229}
230
231#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
232pub struct IMap {
233 pub name: String,
234 pub sha256: Option<String>,
235 pub size: Option<i64>,
236}
237
238#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
239pub struct Client {
240 pub name: String,
241 pub clan: String,
242 pub country: i32,
243 pub score: i64,
244 #[serde(default)]
245 pub is_player: bool,
246 pub skin: Option<Skin>,
247 #[serde(default)]
248 pub afk: bool,
249 #[serde(default)]
250 pub team: i64,
251}
252
253#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
254pub struct Skin {
255 pub name: Option<String>,
256 pub color_body: Option<i64>,
257 pub color_feet: Option<i64>,
258}
259
260#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
261pub struct Community {
262 pub id: String,
263 pub icon: String,
264 pub admin: Vec<String>,
265 pub public_key: Option<String>,
266 pub signature: Option<String>,
267}
268
269#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
270pub struct Player {
271 pub player: String,
272 pub points: Points,
273 pub team_rank: Option<Rank>,
274 pub rank: Option<Rank>,
275 pub points_last_year: Option<Rank>,
276 pub points_last_month: Option<Rank>,
277 pub points_last_week: Option<Rank>,
278 pub favorite_server: FavoriteServer,
279 pub first_finish: FirstFinish,
280 pub last_finishes: Vec<LastFinish>,
281 #[serde(default)]
282 pub favorite_partners: Vec<FavoritePartner>,
283 pub types: Types,
284 pub activity: Vec<Activity>,
285 pub hours_played_past_365_days: i64,
286}
287
288impl Player {
289 pub fn url(&self) -> String {
290 format!(
291 "https://ddnet.org/players/{}",
292 encode(&slugify2(&self.player))
293 )
294 }
295
296 pub fn url_with_name(player: &str) -> String {
297 format!("https://ddnet.org/players/{}", encode(&slugify2(player)))
298 }
299
300 pub fn api(player: &str) -> String {
301 format!("https://ddnet.org/players/?json2={}", encode(player))
302 }
303}
304
305#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
306pub struct Map {
307 pub name: String,
308 pub website: String,
309 pub thumbnail: String,
310 pub web_preview: String,
311 pub r#type: String,
312 pub points: i64,
313 pub difficulty: i64,
314 pub mapper: String,
315 pub release: Option<f64>,
316 pub median_time: f64,
317 pub first_finish: f64,
318 pub last_finish: f64,
319 pub finishes: i64,
320 pub finishers: i64,
321 pub biggest_team: i64,
322 pub width: i64,
323 pub height: i64,
324 pub tiles: Vec<String>,
325 pub team_ranks: Vec<DTeamRank>,
326 pub ranks: Vec<DRank>,
327 pub max_finishes: Vec<MaxFinish>,
328}
329
330impl Map {
331 pub fn url(&self) -> String {
332 format!("https://ddnet.org/maps/{}", encode(&slugify2(&self.name)))
333 }
334
335 pub fn url_with_name(map: &str) -> String {
336 format!("https://ddnet.org/maps/{}", encode(&slugify2(map)))
337 }
338
339 pub fn api(map: &str) -> String {
340 format!("https://ddnet.org/maps/?json={}", encode(map))
341 }
342}
343
344#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
345pub struct DTeamRank {
346 pub rank: i64,
347 pub players: Vec<String>,
348 pub time: f64,
349 #[serde(
350 serialize_with = "serialize_datetime_timestamp",
351 deserialize_with = "deserialize_datetime_timestamp"
352 )]
353 pub timestamp: NaiveDateTime,
354 pub country: String,
355}
356
357#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
358pub struct DRank {
359 pub rank: i64,
360 pub player: String,
361 pub time: f64,
362 #[serde(
363 serialize_with = "serialize_datetime_timestamp",
364 deserialize_with = "deserialize_datetime_timestamp"
365 )]
366 pub timestamp: NaiveDateTime,
367 pub country: String,
368}
369
370#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
371pub struct MaxFinish {
372 pub rank: i64,
373 pub player: String,
374 pub num: i64,
375 pub time: f64,
376 #[serde(
377 serialize_with = "serialize_datetime_timestamp",
378 deserialize_with = "deserialize_datetime_timestamp"
379 )]
380 pub min_timestamp: NaiveDateTime,
381 #[serde(
382 serialize_with = "serialize_datetime_timestamp",
383 deserialize_with = "deserialize_datetime_timestamp"
384 )]
385 pub max_timestamp: NaiveDateTime,
386}
387
388#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
389pub struct Points {
390 pub total: u64,
391 pub points: Option<u64>,
392 pub rank: Option<u64>,
393}
394
395#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
396pub struct Rank {
397 pub points: Option<u64>,
398 pub rank: Option<u64>,
399}
400
401#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
402pub struct FavoriteServer {
403 pub server: String,
404}
405
406#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
407pub struct FirstFinish {
408 #[serde(
409 serialize_with = "serialize_datetime_timestamp",
410 deserialize_with = "deserialize_datetime_timestamp"
411 )]
412 pub timestamp: NaiveDateTime,
413 pub map: String,
414 pub time: f64,
415}
416
417#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
418pub struct LastFinish {
419 #[serde(
420 serialize_with = "serialize_datetime_timestamp",
421 deserialize_with = "deserialize_datetime_timestamp"
422 )]
423 pub timestamp: NaiveDateTime,
424 pub map: String,
425 pub time: f64,
426 pub country: String,
427 #[serde(rename = "type")]
428 pub type_map: Option<String>,
429}
430
431#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
432pub struct FavoritePartner {
433 pub name: String,
434 pub finishes: i64,
435}
436
437#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
438#[serde(rename_all = "PascalCase")]
439pub struct Types {
440 pub novice: Type,
441 pub moderate: Type,
442 pub brutal: Type,
443 pub insane: Type,
444 pub dummy: Type,
445 #[serde(rename = "DDmaX.Easy")]
446 pub ddma_x_easy: Type,
447 #[serde(rename = "DDmaX.Next")]
448 pub ddma_x_next: Type,
449 #[serde(rename = "DDmaX.Pro")]
450 pub ddma_x_pro: Type,
451 #[serde(rename = "DDmaX.Nut")]
452 pub ddma_x_nut: Type,
453 pub oldschool: Type,
454 pub solo: Type,
455 pub race: Type,
456 pub fun: Type,
457}
458
459#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
460pub struct Type {
461 pub points: Points,
462 pub team_rank: Option<Rank>,
463 pub rank: Option<Rank>,
464 pub maps: HashMap<String, DDMap>,
465}
466
467#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
468pub struct DDMap {
469 pub points: i64,
470 pub total_finishes: i64,
471 pub finishes: i64,
472 pub team_rank: Option<i64>,
473 pub rank: Option<i64>,
474 pub time: Option<f64>,
475 pub first_finish: Option<f64>,
476}
477
478#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
479pub struct Activity {
480 pub date: String,
481 pub hours_played: i64,
482}