ddapi_rs/scheme/
ddnet.rs

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