This is getting so stupidly complicated
This commit is contained in:
parent
91ec035439
commit
bef66328f7
197
src/api/event.rs
197
src/api/event.rs
|
@ -220,7 +220,11 @@ mod test {
|
||||||
|
|
||||||
mod team_game {
|
mod team_game {
|
||||||
use super::{
|
use super::{
|
||||||
free_for_all_game::{FreeForAllGame, FreeForAllGameSpec, FreeForAllGameUpdate},
|
free_for_all_game::{
|
||||||
|
FreeForAllGame, FreeForAllGameSpec, FreeForAllGameUpdate,
|
||||||
|
FreeForAllGameUpdateParticipants, FreeForAllGameUpdateRanking,
|
||||||
|
FreeForAllGameUpdateRewards,
|
||||||
|
},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -252,9 +256,32 @@ mod team_game {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct TeamGameUpdate {
|
pub enum TeamGameUpdateInner {
|
||||||
#[serde(flatten)]
|
/// Add or replace a team with the given name and array of members
|
||||||
inner: FreeForAllGameUpdate,
|
SetTeam { team: String, members: Vec<String> },
|
||||||
|
|
||||||
|
/// Remove team with given name
|
||||||
|
RemoveTeam(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum TeamGameFfaInheritedUpdate {
|
||||||
|
/// Change the ranking and scores
|
||||||
|
Ranking(FreeForAllGameUpdateRanking),
|
||||||
|
/// Update rewards
|
||||||
|
Rewards(FreeForAllGameUpdateRewards),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum TeamGameUpdate {
|
||||||
|
/// Team specific updates
|
||||||
|
Team(TeamGameUpdateInner),
|
||||||
|
/// Inherited from FreeForAllGame
|
||||||
|
Ffa(TeamGameFfaInheritedUpdate),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventTrait for TeamGame {
|
impl EventTrait for TeamGame {
|
||||||
|
@ -262,15 +289,8 @@ mod team_game {
|
||||||
type Update = TeamGameUpdate;
|
type Update = TeamGameUpdate;
|
||||||
|
|
||||||
fn from_spec(spec: TeamGameSpec) -> Self {
|
fn from_spec(spec: TeamGameSpec) -> Self {
|
||||||
let players: Vec<String> = spec
|
|
||||||
.teams
|
|
||||||
.values()
|
|
||||||
.map(|v| v.iter().cloned())
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let ffa_game_spec = FreeForAllGameSpec {
|
let ffa_game_spec = FreeForAllGameSpec {
|
||||||
players,
|
participants: spec.teams.keys().cloned().collect(),
|
||||||
win_rewards: spec.win_rewards,
|
win_rewards: spec.win_rewards,
|
||||||
lose_rewards: spec.lose_rewards,
|
lose_rewards: spec.lose_rewards,
|
||||||
};
|
};
|
||||||
|
@ -282,7 +302,34 @@ mod team_game {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> {
|
fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> {
|
||||||
self.ffa_game.apply_update(update.inner)
|
match update {
|
||||||
|
TeamGameUpdate::Ffa(update) => match update {
|
||||||
|
TeamGameFfaInheritedUpdate::Ranking(u) => {
|
||||||
|
self.ffa_game.apply_update(FreeForAllGameUpdate::Ranking(u))
|
||||||
|
}
|
||||||
|
TeamGameFfaInheritedUpdate::Rewards(u) => {
|
||||||
|
self.ffa_game.apply_update(FreeForAllGameUpdate::Rewards(u))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TeamGameUpdate::Team(update) => match update {
|
||||||
|
TeamGameUpdateInner::SetTeam { team, members } => {
|
||||||
|
self.ffa_game
|
||||||
|
.apply_update(FreeForAllGameUpdate::Participants(
|
||||||
|
FreeForAllGameUpdateParticipants::AddParticipant(team.clone()),
|
||||||
|
))?;
|
||||||
|
self.teams.insert(team, members);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
TeamGameUpdateInner::RemoveTeam(team) => {
|
||||||
|
self.ffa_game
|
||||||
|
.apply_update(FreeForAllGameUpdate::Participants(
|
||||||
|
FreeForAllGameUpdateParticipants::RemoveParticipant(team.clone()),
|
||||||
|
))?;
|
||||||
|
self.teams.remove(&team);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn outcome(&self) -> EventOutcome {
|
fn outcome(&self) -> EventOutcome {
|
||||||
|
@ -305,22 +352,33 @@ mod team_game {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod free_for_all_game {
|
mod free_for_all_game {
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub enum FreeForAllGameRanking {
|
pub enum FreeForAllGameRanking {
|
||||||
/// Ranking of players by user id (first element is first place, second element is second
|
/// Ranking of participants by user id or team name (first element is first place, second element is second
|
||||||
/// place, etc.)
|
/// place, etc.)
|
||||||
Ranking(Vec<String>),
|
Ranking(Vec<String>),
|
||||||
/// Score based ranking of players by user id
|
/// Score based ranking of participants/teams
|
||||||
Scores(HashMap<String, i64>),
|
Scores(HashMap<String, i64>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FreeForAllGameRanking {
|
||||||
|
pub fn is_valid(&self, participants: &HashSet<String>) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Ranking(v) => v.iter().all(|p| participants.contains(p)),
|
||||||
|
Self::Scores(m) => m.keys().all(|p| participants.contains(p)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct FreeForAllGame {
|
pub struct FreeForAllGame {
|
||||||
/// Ranking of players by user id (first element is first place, second element is second
|
/// Ranking of participants by user id or team name (first element is first place, second element is second
|
||||||
/// place, etc.)
|
/// place, etc.)
|
||||||
ranking: Option<FreeForAllGameRanking>,
|
ranking: Option<FreeForAllGameRanking>,
|
||||||
|
|
||||||
|
@ -341,7 +399,7 @@ mod free_for_all_game {
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct FreeForAllGameSpec {
|
pub struct FreeForAllGameSpec {
|
||||||
/// Array of user ids that participate in the game
|
/// Array of user ids that participate in the game
|
||||||
pub(crate) players: Vec<String>,
|
pub(crate) participants: HashSet<String>,
|
||||||
|
|
||||||
/// Rewards for winning the game (first element for first place, second element for second
|
/// Rewards for winning the game (first element for first place, second element for second
|
||||||
/// place, etc.)
|
/// place, etc.)
|
||||||
|
@ -355,14 +413,49 @@ mod free_for_all_game {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub enum FreeForAllGameUpdate {
|
pub enum FreeForAllGameUpdateRanking {
|
||||||
/// Replace the current ranking with the given ranking
|
/// Replace the current ranking with the given ranking
|
||||||
Ranking(FreeForAllGameRanking),
|
SetRanking(FreeForAllGameRanking),
|
||||||
|
|
||||||
/// If the current ranking is of type `Scores`, apply the given score deltas
|
/// If the current ranking is of type `Scores`, apply the given score deltas
|
||||||
ScoreDelta(HashMap<String, i64>),
|
ScoreDelta(HashMap<String, i64>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub enum FreeForAllGameUpdateRewards {
|
||||||
|
/// Set rewards for winning the game
|
||||||
|
SetWinRewards(Vec<i64>),
|
||||||
|
|
||||||
|
/// Set rewards for losing the game
|
||||||
|
SetLoseRewards(Vec<i64>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub enum FreeForAllGameUpdateParticipants {
|
||||||
|
/// Set list of participants participating in the game
|
||||||
|
SetParticipants(HashSet<String>),
|
||||||
|
|
||||||
|
/// Add participant by id
|
||||||
|
AddParticipant(String),
|
||||||
|
|
||||||
|
/// Remove participant by id
|
||||||
|
RemoveParticipant(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum FreeForAllGameUpdate {
|
||||||
|
/// Change the ranking and scores
|
||||||
|
Ranking(FreeForAllGameUpdateRanking),
|
||||||
|
/// Update rewards
|
||||||
|
Rewards(FreeForAllGameUpdateRewards),
|
||||||
|
/// Update participants
|
||||||
|
Participants(FreeForAllGameUpdateParticipants),
|
||||||
|
}
|
||||||
|
|
||||||
impl EventTrait for FreeForAllGame {
|
impl EventTrait for FreeForAllGame {
|
||||||
type Spec = FreeForAllGameSpec;
|
type Spec = FreeForAllGameSpec;
|
||||||
type Update = FreeForAllGameUpdate;
|
type Update = FreeForAllGameUpdate;
|
||||||
|
@ -376,19 +469,63 @@ mod free_for_all_game {
|
||||||
|
|
||||||
fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> {
|
fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> {
|
||||||
match update {
|
match update {
|
||||||
FreeForAllGameUpdate::Ranking(r) => self.ranking = Some(r),
|
FreeForAllGameUpdate::Ranking(update) => match update {
|
||||||
FreeForAllGameUpdate::ScoreDelta(d) => match &mut self.ranking {
|
FreeForAllGameUpdateRanking::SetRanking(r) => {
|
||||||
|
if !r.is_valid(&self.spec.participants) {
|
||||||
|
return Err(PartyError::Unknown("invalid ranking, all participants mentioned in ranking must be participating".into()));
|
||||||
|
}
|
||||||
|
self.ranking = Some(r)
|
||||||
|
}
|
||||||
|
FreeForAllGameUpdateRanking::ScoreDelta(d) => match &mut self.ranking {
|
||||||
Some(FreeForAllGameRanking::Ranking(_)) | None => {
|
Some(FreeForAllGameRanking::Ranking(_)) | None => {
|
||||||
return Err(PartyError::Unknown("cannot apply score delta".into()))
|
return Err(PartyError::Unknown("cannot apply score delta".into()))
|
||||||
}
|
}
|
||||||
Some(FreeForAllGameRanking::Scores(s)) => {
|
Some(FreeForAllGameRanking::Scores(s)) => {
|
||||||
for (player, delta) in d.iter() {
|
for (participant, delta) in d.iter() {
|
||||||
if let Some(value) = s.get(player) {
|
if let Some(value) = s.get(participant) {
|
||||||
s.insert(player.clone(), value + delta);
|
s.insert(participant.clone(), value + delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
FreeForAllGameUpdate::Participants(update) => match update {
|
||||||
|
FreeForAllGameUpdateParticipants::AddParticipant(id) => {
|
||||||
|
self.spec.participants.insert(id);
|
||||||
|
}
|
||||||
|
FreeForAllGameUpdateParticipants::RemoveParticipant(id) => {
|
||||||
|
self.spec.participants.remove(&id);
|
||||||
|
|
||||||
|
if !self
|
||||||
|
.ranking
|
||||||
|
.as_ref()
|
||||||
|
.map(|r| r.is_valid(&self.spec.participants))
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
|
self.spec.participants.insert(id);
|
||||||
|
return Err(PartyError::Unknown("cannot remove participant, all participants mentioned in ranking must be participating".into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FreeForAllGameUpdateParticipants::SetParticipants(participants) => {
|
||||||
|
if !self
|
||||||
|
.ranking
|
||||||
|
.as_ref()
|
||||||
|
.map(|r| r.is_valid(&participants))
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
|
return Err(PartyError::Unknown("invalid list of participants, all participants mentioned in ranking must be participating".into()));
|
||||||
|
}
|
||||||
|
self.spec.participants = participants;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FreeForAllGameUpdate::Rewards(update) => match update {
|
||||||
|
FreeForAllGameUpdateRewards::SetWinRewards(rewards) => {
|
||||||
|
self.spec.win_rewards = rewards;
|
||||||
|
}
|
||||||
|
FreeForAllGameUpdateRewards::SetLoseRewards(rewards) => {
|
||||||
|
self.spec.lose_rewards = rewards;
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -406,14 +543,14 @@ mod free_for_all_game {
|
||||||
|
|
||||||
let mut points = HashMap::new();
|
let mut points = HashMap::new();
|
||||||
|
|
||||||
for (player, reward) in ranking.iter().zip(self.spec.win_rewards.iter()) {
|
for (participant, reward) in ranking.iter().zip(self.spec.win_rewards.iter()) {
|
||||||
let score = points.get(player).unwrap_or(&0);
|
let score = points.get(participant).unwrap_or(&0);
|
||||||
points.insert(player.clone(), score + reward);
|
points.insert(participant.clone(), score + reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (player, reward) in ranking.iter().rev().zip(self.spec.lose_rewards.iter()) {
|
for (participant, reward) in ranking.iter().rev().zip(self.spec.lose_rewards.iter()) {
|
||||||
let score = points.get(player).unwrap_or(&0);
|
let score = points.get(participant).unwrap_or(&0);
|
||||||
points.insert(player.clone(), score + reward);
|
points.insert(participant.clone(), score + reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
EventOutcome { points }
|
EventOutcome { points }
|
||||||
|
|
Loading…
Reference in New Issue