use serde::Serialize;
use serde_json::Value;
use crate::protocol::{EngineKind, GameFormat, SeatTransportReport};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum GameEndReason {
GameOver,
EngineFatal,
HostEnded,
ReconnectTimeout,
HostLost,
StaleExpired,
Abandoned,
}
impl GameEndReason {
pub fn as_str(self) -> &'static str {
match self {
Self::GameOver => "game_over",
Self::EngineFatal => "engine_fatal",
Self::HostEnded => "host_ended",
Self::ReconnectTimeout => "reconnect_timeout",
Self::HostLost => "host_lost",
Self::StaleExpired => "stale_expired",
Self::Abandoned => "abandoned",
}
}
}
#[derive(Debug, Serialize)]
pub struct SeatInfo {
pub username: String,
pub is_bot: bool,
pub deck_name: Option<String>,
pub commander: Option<String>,
pub published_deck_id: Option<String>,
pub deck_fingerprint: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct CardEntry {
pub name: String,
pub set_code: String,
pub count: u32,
}
#[derive(Debug, Serialize)]
#[serde(tag = "event", rename_all = "snake_case")]
pub enum AnalyticsEvent {
ClientConnected {
ts: String,
username: String,
platform: String,
#[serde(skip_serializing_if = "Option::is_none")]
version: Option<String>,
reconnected: bool,
},
GameStarted {
ts: String,
game_id: String,
room_id: String,
format: GameFormat,
engine: EngineKind,
hosted: bool,
official: bool,
#[serde(skip_serializing_if = "Option::is_none")]
table_style: Option<String>,
starting_life: i32,
players: Vec<SeatInfo>,
},
GameEnded {
ts: String,
game_id: String,
room_id: String,
reason: GameEndReason,
duration_s: f64,
game_over: bool,
winner: Option<String>,
conceded: Vec<String>,
fatal_message: Option<String>,
reported: bool,
#[serde(skip_serializing_if = "Option::is_none")]
turns: Option<u32>,
},
TransportUsed {
ts: String,
room_id: String,
game_id: String,
host: String,
seats: Vec<SeatTransportReport>,
},
PlaneQuality {
ts: String,
#[serde(skip_serializing_if = "Option::is_none")]
room_id: Option<String>,
username: String,
peer: String,
plane: String,
outcome: String,
phase: String,
#[serde(skip_serializing_if = "Option::is_none")]
connect_ms: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
rtt_ms: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
relay_rtt_ms: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
candidate_pair: Option<String>,
},
EngineStats {
ts: String,
#[serde(skip_serializing_if = "Option::is_none")]
room_id: Option<String>,
username: String,
report_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
game_id: Option<String>,
engine: String,
client_version: String,
platform: String,
#[serde(skip_serializing_if = "Option::is_none")]
format: Option<String>,
seats: u32,
multiplayer: bool,
duration_s: u32,
end_reason: String,
decisions: u32,
turnaround_p50: u32,
turnaround_p90: u32,
turnaround_max: u32,
#[serde(skip_serializing_if = "Option::is_none")]
engine_p50: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
engine_p90: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
engine_max: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
engine_same_p50: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
engine_same_p90: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
engine_same_max: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
engine_cross_p50: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
engine_cross_p90: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
engine_cross_max: Option<u32>,
think_hidden: u32,
},
DeckSelected {
ts: String,
room_id: String,
username: String,
is_bot: bool,
deck_name: String,
commander: Option<String>,
cards: Vec<CardEntry>,
sideboard_count: usize,
},
SeatJoined {
ts: String,
room_id: String,
username: String,
is_bot: bool,
observer: bool,
},
SeatLeft {
ts: String,
room_id: String,
username: String,
},
}
#[derive(Serialize)]
pub struct CaptureLine<'a> {
pub ts: String,
pub from: &'a str,
pub envelope: &'a Value,
#[serde(rename = "clientRttMs", skip_serializing_if = "Option::is_none")]
pub client_rtt_ms: Option<u32>,
}