1use chrono::DateTime;
2use chrono::{serde::ts_seconds, Utc};
3use serde::Deserialize;
4#[cfg(feature = "serialize")]
5use serde::Serialize;
6use serde_repr::{Deserialize_repr, Serialize_repr};
7use uuid::Uuid;
8
9use crate::types::Time;
10use crate::types::{Elo, EloChange, MatchId, Rank, Season};
11use crate::user::UserProfile;
12#[cfg(feature = "variations")]
13use crate::variations::Variation;
14
15pub mod requests;
16#[cfg(test)]
17mod tests;
18pub mod versus;
19
20#[cfg_attr(feature = "serialize", derive(Serialize))]
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
22#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
23pub enum MatchCategory {
24 Any,
25 Custom,
26 High,
27 KillAllBosses,
28 KillWither,
29 KillElderGuardian,
30 AllAdvancements,
31 Half,
32 PoglootQuater,
33 HowDidWeGetHere,
34 HeroOfTheVillage,
35 Arbalistic,
36 CoverMeInDebris,
37 EnterNether,
38 EnterEnd,
39 AllSwords,
40 AllMinerals,
41 #[serde(rename = "FULL_IA_15_LVL")]
42 FullIa15Lvl,
43 AllWorkstations,
44 FullInv,
45 StackOfLimeWool,
46 AllPortals,
47 AllBlocks,
48 MineAChunk,
49}
50
51#[cfg_attr(feature = "serialize", derive(Serialize))]
52#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
53pub struct MatchSeedInfo {
54 pub id: Option<Box<str>>,
56 pub overworld: Option<OverworldType>,
57 pub bastion: Option<BastionType>,
58 #[serde(default)]
60 pub end_towers: Option<[u8; 4]>,
61 #[serde(default)]
62 #[cfg(feature = "variations")]
63 pub variations: Box<[Variation]>,
64 #[cfg(not(feature = "variations"))]
65 pub variations: Box<[Box<str>]>,
66}
67impl MatchSeedInfo {
68 pub fn id(&self) -> Option<&str> {
70 self.id.as_ref().map(AsRef::as_ref)
71 }
72 #[cfg(not(feature = "variations"))]
73 pub fn variations(&self) -> &[Box<str>] {
74 self.variations.as_ref()
75 }
76 #[cfg(feature = "variations")]
77 pub fn variations(&self) -> &[Variation] {
78 self.variations.as_ref()
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize_repr, Serialize_repr)]
83#[repr(u8)]
84pub enum MatchType {
85 Causal = 1,
86 Ranked = 2,
87 Private = 3,
88 Event = 4,
89}
90
91#[cfg_attr(feature = "serialize", derive(Serialize))]
92#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
93#[serde(rename_all = "camelCase")]
94pub struct MatchOutcome {
95 #[serde(rename = "uuid")]
96 pub winner_uuid: Option<Uuid>,
97 pub time: Time,
98}
99
100#[cfg_attr(feature = "serialize", derive(Serialize))]
102#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
103#[serde(rename_all = "camelCase")]
104pub struct MatchRank {
105 pub season: Option<Rank>,
106 pub all_time: Option<Rank>,
107}
108
109#[cfg_attr(feature = "serialize", derive(Serialize))]
111#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
112#[serde(rename_all = "camelCase")]
113pub struct MatchEloUpdate {
114 #[serde(rename = "uuid")]
115 pub player_uuid: Uuid,
116 pub change: Option<EloChange>,
117 #[serde(rename = "eloRate")]
118 pub elo: Option<Elo>,
119}
120
121#[cfg_attr(feature = "serialize", derive(Serialize))]
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
124#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
125pub enum OverworldType {
126 Village,
127 BuriedTreasure,
128 Shipwreck,
129 RuinedPortal,
130 DesertTemple,
131}
132
133#[cfg_attr(feature = "serialize", derive(Serialize))]
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
136#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
137pub enum BastionType {
138 Housing,
139 Treasure,
140 Bridge,
141 Stables,
142}
143
144#[cfg_attr(feature = "serialize", derive(Serialize))]
146#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
147#[serde(rename_all = "camelCase")]
148pub struct MatchCompletion {
149 #[serde(rename = "uuid")]
150 pub player_uuid: Uuid,
151 pub time: Time,
152}
153
154#[cfg_attr(feature = "serialize", derive(Serialize))]
156#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
157#[serde(rename_all = "camelCase")]
158pub struct MatchTimelineEvent {
159 #[serde(rename = "uuid")]
160 pub player_uuid: Uuid,
161 pub time: Time,
162 #[serde(rename = "type")]
163 pub id: Box<str>,
164}
165impl MatchTimelineEvent {
166 pub fn id(&self) -> &str {
167 &self.id
168 }
169}
170
171#[cfg_attr(feature = "serialize", derive(Serialize))]
173#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
174#[serde(rename_all = "camelCase")]
175pub struct MatchInfo {
176 pub id: MatchId,
177 #[serde(rename = "type")]
178 pub kind: MatchType,
179 pub season: Season,
180 pub category: Option<MatchCategory>,
181 #[serde(with = "ts_seconds")]
182 pub date: DateTime<Utc>,
183 pub players: Box<[UserProfile]>,
184 pub spectators: Box<[UserProfile]>,
185 pub seed: Option<MatchSeedInfo>,
186 pub result: MatchOutcome,
187 pub forfeited: bool,
188 pub decayed: bool,
189 pub rank: MatchRank,
190 #[serde(rename = "changes")]
191 pub elo_updates: Box<[MatchEloUpdate]>,
192}
193impl MatchInfo {
194 pub fn players(&self) -> &[UserProfile] {
196 &self.players
197 }
198 pub fn spectators(&self) -> &[UserProfile] {
200 &self.spectators
201 }
202 pub fn elo_updates(&self) -> &[MatchEloUpdate] {
204 &self.elo_updates
205 }
206}
207
208#[cfg_attr(feature = "serialize", derive(Serialize))]
210#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
211#[serde(rename_all = "camelCase")]
212pub struct AdvancedMatchInfo {
213 #[serde(flatten)]
215 info: MatchInfo,
216 completions: Box<[MatchCompletion]>,
218 #[serde(rename = "timelines")]
219 timeline_events: Box<[MatchTimelineEvent]>,
221 #[serde(rename = "replayExist")]
222 replay_exists: bool,
223}
224impl AdvancedMatchInfo {
225 pub fn completions(&self) -> &[MatchCompletion] {
227 &self.completions
228 }
229 pub fn timeline_events(&self) -> &[MatchTimelineEvent] {
231 &self.timeline_events
232 }
233}