use crate::core::{BoundedVec, Instant, SimpleSemVer};
use crate::game::{FamiliesString, GameChannelKey, GameIdRef, GameModeKey, GameOptionKey};
use crate::user::ShortUser;
use async_trait::async_trait;
use auto_impl::auto_impl;
use etwin_core::core::LocaleId;
use etwin_core::declare_new_uuid;
pub use etwin_core::hammerfest::HammerfestItemId;
use etwin_core::types::AnyError;
use etwin_core::user::UserIdRef;
#[cfg(feature = "serde")]
use etwin_serde_tools::{Deserialize, Serialize};
pub use serde_json::Value as JsonValue;
use std::collections::BTreeMap;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(tag = "type", rename = "Run")
)]
pub struct Run {
pub id: RunId,
pub created_at: Instant,
pub started_at: Option<Instant>,
pub result: Option<RunResult>,
pub game: GameIdRef,
pub user: ShortUser,
pub game_mode: GameModeKey,
pub game_options: Vec<GameOptionKey>,
pub settings: RunSettings,
}
declare_new_uuid! {
pub struct RunId(Uuid);
pub type ParseError = RunIdParseError;
const SQL_NAME = "run_id";
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(tag = "type", rename = "Run")
)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RunIdRef {
pub id: RunId,
}
impl RunIdRef {
pub const fn new(id: RunId) -> Self {
Self { id }
}
}
impl From<RunId> for RunIdRef {
fn from(id: RunId) -> Self {
Self::new(id)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RunResult {
pub created_at: Instant,
pub is_victory: bool,
pub max_level: u32,
pub scores: BoundedVec<u32, 1, 2>,
pub items: BTreeMap<HammerfestItemId, u32>,
pub stats: JsonValue,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RunSettings {
pub detail: bool,
pub shake: bool,
pub sound: bool,
pub music: bool,
pub volume: i32,
pub locale: LocaleId,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GetUserItems {
pub user: UserIdRef,
pub game: GameIdRef,
pub channel: GameChannelKey,
}
#[derive(Debug, thiserror::Error)]
pub enum GetUserItemsError {
#[error(transparent)]
Other(#[from] AnyError),
}
impl GetUserItemsError {
pub fn other<E>(e: E) -> Self
where
E: ::std::error::Error + Send + Sync + 'static,
{
Self::Other(Box::new(e))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CreateRunOptions {
pub game: GameIdRef,
pub channel: GameChannelKey,
pub version: SimpleSemVer,
pub user: UserIdRef,
pub game_mode: GameModeKey,
pub game_options: Vec<GameOptionKey>,
pub settings: RunSettings,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StoreCreateRun {
pub game: GameIdRef,
pub channel: GameChannelKey,
pub version: SimpleSemVer,
pub user: UserIdRef,
pub mode: GameModeKey,
pub options: Vec<GameOptionKey>,
pub settings: RunSettings,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StoreRun {
pub id: RunId,
pub created_at: Instant,
pub started_at: Option<Instant>,
pub result: Option<RunResult>,
pub game: GameIdRef,
pub channel: GameChannelKey,
pub version: SimpleSemVer,
pub user: UserIdRef,
pub mode: GameModeKey,
pub options: Vec<GameOptionKey>,
pub settings: RunSettings,
}
#[derive(Debug, thiserror::Error)]
pub enum StoreCreateRunError {
#[error(transparent)]
Other(#[from] AnyError),
}
impl StoreCreateRunError {
pub fn other<E>(e: E) -> Self
where
E: ::std::error::Error + Send + Sync + 'static,
{
Self::Other(Box::new(e))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StoreGetRun {
pub run: RunIdRef,
}
#[derive(Debug, thiserror::Error)]
pub enum StoreGetRunError {
#[error("run not found")]
NotFound,
#[error(transparent)]
Other(#[from] AnyError),
}
impl StoreGetRunError {
pub fn other<E>(e: E) -> Self
where
E: ::std::error::Error + Send + Sync + 'static,
{
Self::Other(Box::new(e))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StoreStartRun {
pub run: RunIdRef,
pub if_user: UserIdRef,
}
#[derive(Debug, thiserror::Error)]
pub enum StoreStartRunError {
#[error("run not found")]
NotFound,
#[error("invalid user: run created by {created_by:?} but trying started by {started_by:?}")]
InvalidUser {
created_by: UserIdRef,
started_by: UserIdRef,
},
#[error(transparent)]
Other(#[from] AnyError),
}
impl StoreStartRunError {
pub fn other<E>(e: E) -> Self
where
E: ::std::error::Error + Send + Sync + 'static,
{
Self::Other(Box::new(e))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RunStart {
pub run: RunIdRef,
pub key: Uuid,
pub families: FamiliesString,
pub items: BTreeMap<HammerfestItemId, u32>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SetRunResultOptions {
pub is_victory: bool,
pub max_level: u32,
pub scores: BoundedVec<u32, 1, 2>,
pub items: BTreeMap<HammerfestItemId, u32>,
pub stats: JsonValue,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StoreSetRunResult {
pub run: RunIdRef,
pub if_user: UserIdRef,
pub is_victory: bool,
pub max_level: u32,
pub scores: BoundedVec<u32, 1, 2>,
pub items: BTreeMap<HammerfestItemId, u32>,
pub stats: JsonValue,
}
#[derive(Debug, thiserror::Error)]
pub enum StoreSetRunResultError {
#[error("run not found")]
NotFound,
#[error(transparent)]
Other(#[from] AnyError),
}
impl StoreSetRunResultError {
pub fn other<E>(e: E) -> Self
where
E: ::std::error::Error + Send + Sync + 'static,
{
Self::Other(Box::new(e))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StoreGetLeaderboard {
pub game: GameIdRef,
pub channel: GameChannelKey,
pub mode: GameModeKey,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StoreLeaderboard {
pub game: GameIdRef,
pub channel: GameChannelKey,
pub mode: GameModeKey,
pub results: Vec<StoreLeaderboardEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Leaderboard {
pub game: GameIdRef,
pub channel: GameChannelKey,
pub mode: GameModeKey,
pub results: Vec<LeaderboardEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StoreLeaderboardEntry {
pub score: u32,
pub user: UserIdRef,
pub run: LeaderboardEntryRun,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LeaderboardEntry {
pub score: u32,
pub user: ShortUser,
pub run: LeaderboardEntryRun,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(tag = "type", rename = "Run")
)]
pub struct LeaderboardEntryRun {
pub id: RunId,
pub max_level: u32,
pub game_options: Vec<GameOptionKey>,
}
#[derive(Debug, thiserror::Error)]
pub enum StoreGetLeaderboardError {
#[error("game not found")]
GameNotFound,
#[error(transparent)]
Other(#[from] AnyError),
}
impl StoreGetLeaderboardError {
pub fn other<E>(e: E) -> Self
where
E: ::std::error::Error + Send + Sync + 'static,
{
Self::Other(Box::new(e))
}
}
#[async_trait]
#[auto_impl(&, Arc)]
pub trait RunStore: Send + Sync {
async fn get_user_items(&self, req: &GetUserItems) -> Result<BTreeMap<HammerfestItemId, u32>, GetUserItemsError>;
async fn create_run(&self, req: &StoreCreateRun) -> Result<StoreRun, StoreCreateRunError>;
async fn get_run(&self, req: &StoreGetRun) -> Result<StoreRun, StoreGetRunError>;
async fn start_run(&self, req: &StoreStartRun) -> Result<RunStart, StoreStartRunError>;
async fn set_run_result(&self, req: &StoreSetRunResult) -> Result<StoreRun, StoreSetRunResultError>;
async fn get_leaderboard(&self, req: &StoreGetLeaderboard) -> Result<StoreLeaderboard, StoreGetLeaderboardError>;
}