mcsr_ranked_api/game/
mod.rs

1use chrono::DateTime;
2use chrono::{serde::ts_seconds, Utc};
3use serde::Deserialize;
4use serde_repr::{Deserialize_repr, Serialize_repr};
5use uuid::Uuid;
6
7use crate::types::Time;
8use crate::types::{Elo, EloChange, MatchId, Rank, Season};
9use crate::user::UserProfile;
10
11pub mod requests;
12#[cfg(test)]
13mod tests;
14pub mod versus;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
17#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
18pub enum MatchCategory {
19	Any,
20	Custom,
21	High,
22	KillAllBosses,
23	KillWither,
24	KillElderGuardian,
25	AllAdvancements,
26	Half,
27	PoglootQuater,
28	HowDidWeGetHere,
29	HeroOfTheVillage,
30	Arbalistic,
31	CoverMeInDebris,
32	EnterNether,
33	EnterEnd,
34	AllSwords,
35	AllMinerals,
36	#[serde(rename = "FULL_IA_15_LVL")]
37	FullIa15Lvl,
38	AllWorkstations,
39	FullInv,
40	StackOfLimeWool,
41	AllPortals,
42	AllBlocks,
43	MineAChunk,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
47pub struct MatchSeedInfo {
48	id: Option<Box<str>>,
49	overworld: Option<OverworldType>,
50	bastion: Option<BastionType>,
51	variations: Box<[Box<str>]>,
52}
53impl MatchSeedInfo {
54	/// Id of the seed (not the seed itself)
55	pub fn id(&self) -> Option<&str> {
56		self.id.as_ref().map(AsRef::as_ref)
57	}
58	/// Overworld type of the seed
59	pub fn overworld(&self) -> Option<OverworldType> {
60		self.overworld
61	}
62	/// Bastion type of the seed
63	pub fn bastion(&self) -> Option<BastionType> {
64		self.bastion
65	}
66	/// Variations of the seed
67	pub fn variations(&self) -> &[Box<str>] {
68		&self.variations
69	}
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize_repr, Serialize_repr)]
73#[repr(u8)]
74pub enum MatchType {
75	Causal = 1,
76	Ranked = 2,
77	Private = 3,
78	Event = 4,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct MatchOutcome {
84	#[serde(rename = "uuid")]
85	winner_uuid: Option<Uuid>,
86	time: Time,
87}
88impl MatchOutcome {
89	pub fn winner_uuid(&self) -> Option<Uuid> {
90		self.winner_uuid
91	}
92	pub fn time(&self) -> Time {
93		self.time
94	}
95}
96
97/// Match leaderboard ranking
98#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
99#[serde(rename_all = "camelCase")]
100pub struct MatchRank {
101	season: Option<Rank>,
102	all_time: Option<Rank>,
103}
104impl MatchRank {
105	/// Seasonal ranking of the match, 1-indexed
106	pub fn season(&self) -> Option<Rank> {
107		self.season
108	}
109	/// All-time ranking of the match, 1-indexed
110	pub fn all_time(&self) -> Option<Rank> {
111		self.all_time
112	}
113}
114
115/// Match contestant's elo update
116#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
117#[serde(rename_all = "camelCase")]
118pub struct MatchEloUpdate {
119	#[serde(rename = "uuid")]
120	player_uuid: Uuid,
121	change: Option<EloChange>,
122	#[serde(rename = "eloRate")]
123	elo: Option<Elo>,
124}
125impl MatchEloUpdate {
126	/// Player (whose ELO changed) miencraft UUID
127	pub fn player_uuid(&self) -> Uuid {
128		self.player_uuid
129	}
130	/// Elo change (delta)
131	pub fn elo_change(&self) -> Option<EloChange> {
132		self.change
133	}
134	/// Current elo
135	pub fn elo(&self) -> Option<Elo> {
136		self.elo
137	}
138}
139
140/// Seed type (overworld)
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
142#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
143pub enum OverworldType {
144	Village,
145	BuriedTreasure,
146	Shipwreck,
147	RuinedPortal,
148	DesertTemple,
149}
150
151/// Bastion type
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
153#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
154pub enum BastionType {
155	Housing,
156	Treasure,
157	Bridge,
158	Stables,
159}
160
161/// Match completion info
162#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
163#[serde(rename_all = "camelCase")]
164pub struct MatchCompletion {
165	#[serde(rename = "uuid")]
166	player_uuid: Uuid,
167	time: Time,
168}
169impl MatchCompletion {
170	/// UUID of the player who completed the match
171	pub fn player_uuid(&self) -> Uuid {
172		self.player_uuid
173	}
174	/// Completion time
175	pub fn time(&self) -> Time {
176		self.time
177	}
178}
179
180/// Match timeline event
181#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
182#[serde(rename_all = "camelCase")]
183pub struct MatchTimelineEvent {
184	#[serde(rename = "uuid")]
185	player_uuid: Uuid,
186	time: Time,
187	#[serde(rename = "type")]
188	id: Box<str>,
189}
190impl MatchTimelineEvent {
191	pub fn player_uuid(&self) -> Uuid {
192		self.player_uuid
193	}
194	pub fn time(&self) -> Time {
195		self.time
196	}
197	pub fn id(&self) -> &str {
198		&self.id
199	}
200}
201
202/// Match info
203#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
204#[serde(rename_all = "camelCase")]
205pub struct MatchInfo {
206	id: MatchId,
207	#[serde(rename = "type")]
208	kind: MatchType,
209	season: Season,
210	category: MatchCategory,
211	#[serde(with = "ts_seconds")]
212	date: DateTime<Utc>,
213	players: Box<[UserProfile]>,
214	spectators: Box<[UserProfile]>,
215	seed: MatchSeedInfo,
216	result: MatchOutcome,
217	forfeited: bool,
218	decayed: bool,
219	rank: MatchRank,
220	changes: Box<[MatchEloUpdate]>,
221}
222impl MatchInfo {
223	/// Id of the match
224	pub fn id(&self) -> MatchId {
225		self.id
226	}
227	/// Type of the match
228	pub fn kind(&self) -> MatchType {
229		self.kind
230	}
231	/// Season during which the match took place
232	pub fn season(&self) -> Season {
233		self.season
234	}
235	/// SpeedrunIGT category of the match
236	pub fn category(&self) -> MatchCategory {
237		self.category
238	}
239	/// Date and time when the match took place
240	pub fn date(&self) -> DateTime<Utc> {
241		self.date
242	}
243	/// Users participating in the match
244	pub fn players(&self) -> &[UserProfile] {
245		&self.players
246	}
247	/// Users spectating the match
248	pub fn spectators(&self) -> &[UserProfile] {
249		&self.spectators
250	}
251	/// Seed info
252	pub fn seed_info(&self) -> &MatchSeedInfo {
253		&self.seed
254	}
255	/// The outcome of the match
256	pub fn result(&self) -> &MatchOutcome {
257		&self.result
258	}
259	/// Whether the match ended by forfeit or not
260	pub fn forfeited(&self) -> bool {
261		self.forfeited
262	}
263	/// Whether the match was a decay match or not
264	pub fn decayed(&self) -> bool {
265		self.decayed
266	}
267	/// The leaderboard ranking of the match
268	pub fn rank(&self) -> &MatchRank {
269		&self.rank
270	}
271	/// The updates to the participants' ELOs
272	pub fn elo_updates(&self) -> &[MatchEloUpdate] {
273		&self.changes
274	}
275}
276
277/// Advanced (full) match info
278#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
279#[serde(rename_all = "camelCase")]
280pub struct AdvancedMatchInfo {
281	#[serde(flatten)]
282	info: MatchInfo,
283	completions: Box<[MatchCompletion]>,
284	timelines: Box<[MatchTimelineEvent]>,
285	replay_exist: bool,
286}
287impl AdvancedMatchInfo {
288	/// The completions info of the match
289	pub fn completions(&self) -> &[MatchCompletion] {
290		&self.completions
291	}
292	/// The events (achievements) timeline
293	pub fn timeline_events(&self) -> &[MatchTimelineEvent] {
294		&self.timelines
295	}
296	/// Whether the replay for the match exists
297	pub fn replay_exists(&self) -> bool {
298		self.replay_exist
299	}
300}