Skip to content

Commit

Permalink
fix some issues when leaving a game, catch an edge case when joining …
Browse files Browse the repository at this point in the history
…a game and start managing hits for players
  • Loading branch information
Timtam committed Apr 1, 2024
1 parent 4b1d969 commit 655f74e
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 20 deletions.
9 changes: 9 additions & 0 deletions client/src/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ export const User = z.object({

export type User = z.infer<typeof User>

export const Hit = z.object({
interpret: z.string(),
title: z.string(),
year: z.number(),
})

export type Hit = z.infer<typeof Hit>

export enum GameState {
Open = "Open",
Guessing = "Guessing",
Expand All @@ -17,6 +25,7 @@ export const Player = z.object({
id: z.number(),
name: z.string(),
creator: z.boolean(),
hits: z.array(Hit),
})

export type Player = z.infer<typeof Player>
Expand Down
7 changes: 6 additions & 1 deletion client/src/game.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export function Game() {
if (ge.state === GameState.Guessing) {
setHitSrc("")
setHitSrc(`/api/games/${game.id}/hit`)
} else if (ge.state === GameState.Open) {
setHitSrc("")
}
})

Expand Down Expand Up @@ -132,7 +134,10 @@ export function Game() {
<tbody>
{game.players.map((p) => (
<tr>
<td>{p.name}</td>
<td>
{p.name +
(p.creator === true ? " (creator)" : "")}
</td>
</tr>
))}
</tbody>
Expand Down
10 changes: 6 additions & 4 deletions server/src/games.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{hits::Hit, users::User};
use rocket_okapi::okapi::{schemars, schemars::JsonSchema};
use serde::{Deserialize, Serialize};
use std::{convert::From, default::Default};
use std::{collections::VecDeque, convert::From, default::Default};

#[derive(Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq, Debug)]
#[serde(rename_all_fields = "snake_case")]
Expand All @@ -19,7 +19,7 @@ pub struct Game {
pub id: u32,
pub players: Vec<Player>,
pub state: GameState,
pub hits_remaining: Vec<Hit>,
pub hits_remaining: VecDeque<Hit>,
pub hit_duration: u8,
}

Expand All @@ -32,12 +32,13 @@ pub enum PlayerState {
Confirming,
}

#[derive(Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq, Debug)]
#[derive(Serialize, Deserialize, JsonSchema, Clone, Eq, PartialEq, Debug)]
pub struct Player {
pub id: u32,
pub name: String,
pub state: PlayerState,
pub creator: bool,
pub hits: Vec<Hit>,
}

impl From<&User> for Player {
Expand All @@ -47,11 +48,12 @@ impl From<&User> for Player {
name: u.username.clone(),
state: PlayerState::Waiting,
creator: false,
hits: vec![],
}
}
}

#[derive(Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq, Debug)]
#[derive(Serialize, Deserialize, JsonSchema, Clone, Eq, PartialEq, Debug)]
pub struct GameEvent {
#[serde(skip)]
pub game_id: u32,
Expand Down
8 changes: 6 additions & 2 deletions server/src/hits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use rocket::{
fairing::{self, Fairing, Info, Kind},
Build, Rocket,
};
use rocket_okapi::okapi::{schemars, schemars::JsonSchema};
use rusty_ytdl::{Video, VideoOptions, VideoQuality, VideoSearchOptions};
use serde::{Deserialize, Serialize};
use std::{
env,
fs::{create_dir_all, remove_file},
Expand All @@ -17,19 +19,21 @@ use strum::EnumString;

include!(concat!(env!("OUT_DIR"), "/hits.rs"));

#[derive(EnumString, Eq, PartialEq, Debug, Clone)]
#[derive(EnumString, Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub enum Pack {
Basic,
Schlagerparty,
}

#[derive(Clone, PartialEq, Eq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Hit {
pub interpret: String,
pub title: String,
pub year: u32,
pub pack: Pack,
#[serde(skip)]
pub yt_url: String,
#[serde(skip)]
pub playback_offset: u16,
}

Expand Down
11 changes: 11 additions & 0 deletions server/src/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ pub struct JoinGameError {
impl OpenApiResponderInner for JoinGameError {
fn responses(_generator: &mut OpenApiGenerator) -> Result<Responses, OpenApiError> {
let mut responses = Map::new();
responses.insert(
"403".to_string(),
RefOr::Object(OpenApiResponse {
description: "\
# [403 Forbidden](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403)\n\
The game is already running.\
"
.to_string(),
..Default::default()
}),
);
responses.insert(
"404".to_string(),
RefOr::Object(OpenApiResponse {
Expand Down
20 changes: 20 additions & 0 deletions server/src/routes/games.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,33 @@ pub async fn leave_game(
let game_svc = serv.game_service();
let games = game_svc.lock();

let state = games
.get(game_id)
.map(|g| g.state)
.unwrap_or(GameState::Open);

games.leave(game_id, &user).map(|_| {
let _ = queue.send(GameEvent {
game_id,
event: "leave".into(),
players: games.get(game_id).map(|g| g.players),
..Default::default()
});

let new_state = games
.get(game_id)
.map(|g| g.state)
.unwrap_or(GameState::Open);

if new_state != state {
let _ = queue.send(GameEvent {
game_id,
event: "change_state".into(),
state: Some(new_state),
..Default::default()
});
}

Json(MessageResponse {
message: "left the game successfully".into(),
r#type: "success".into(),
Expand Down
53 changes: 40 additions & 13 deletions server/src/services/games.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use crate::{
users::User,
};
use rand::prelude::{thread_rng, SliceRandom};
use std::{collections::HashMap, sync::Mutex};
use std::{
collections::{HashMap, VecDeque},
sync::Mutex,
};

pub struct GameServiceData {
games: HashMap<u32, Game>,
Expand Down Expand Up @@ -40,7 +43,7 @@ impl GameService {
id: data.id,
players: vec![player],
state: GameState::Open,
hits_remaining: vec![],
hits_remaining: VecDeque::new(),
hit_duration: 20,
};

Expand All @@ -67,7 +70,12 @@ impl GameService {
let mut data = self.data.lock().unwrap();

if let Some(game) = data.games.get_mut(&game_id) {
if game.players.iter().any(|p| p.id == user.id) {
if game.state != GameState::Open {
Err(JoinGameError {
message: "the game is already running".into(),
http_status_code: 403,
})
} else if game.players.iter().any(|p| p.id == user.id) {
Err(JoinGameError {
message: "user is already part of this game".into(),
http_status_code: 409,
Expand Down Expand Up @@ -96,25 +104,35 @@ impl GameService {
} else {
let pos = game.players.iter().position(|p| p.id == user.id).unwrap();

if game.players.iter().find(|p| p.id == user.id).unwrap().state
!= PlayerState::Waiting
{
if game.players.get(pos).unwrap().state != PlayerState::Waiting {
for i in 0..game.players.len() {
if (pos == game.players.len() - 1 && i == 0) || (i == pos + 1) {
game.players.get_mut(i).unwrap().state = PlayerState::Guessing;
} else {
game.players.get_mut(i).unwrap().state = PlayerState::Waiting;
}
}

game.hits_remaining.pop_front();
}

let creator = game.players.get(pos).unwrap().creator;

game.players.remove(pos);

if game.players.is_empty() {
data.games.remove(&game_id);
} else if game.players.len() == 1 {
drop(data);
let _ = self.stop(game_id, None);
} else {
if creator == true {
let idx = pos % game.players.len();

game.players.get_mut(idx).unwrap().creator = true;
}

if game.players.len() == 1 {
drop(data);
let _ = self.stop(game_id, None);
}
}

Ok(())
Expand Down Expand Up @@ -160,8 +178,17 @@ impl GameService {

game.state = GameState::Guessing;
game.players.get_mut(0).unwrap().state = PlayerState::Guessing;
game.hits_remaining = self.hit_service.lock().get_all();
game.hits_remaining.shuffle(&mut rng);
game.hits_remaining = self.hit_service.lock().get_all().into_iter().collect::<_>();
game.hits_remaining.make_contiguous().shuffle(&mut rng);

for i in 0..game.players.len() {
game.players
.get_mut(i)
.unwrap()
.hits
.push(game.hits_remaining.pop_front().unwrap());
}

Ok(())
}
} else {
Expand Down Expand Up @@ -200,7 +227,7 @@ impl GameService {
}

game.state = GameState::Open;
game.hits_remaining = vec![];
game.hits_remaining.clear();

for p in game.players.iter_mut() {
p.state = PlayerState::Waiting;
Expand All @@ -225,7 +252,7 @@ impl GameService {
http_status_code: 409,
})
} else {
Ok(game.hits_remaining.first().cloned().unwrap())
Ok(game.hits_remaining.front().cloned().unwrap())
}
} else {
Err(CurrentHitError {
Expand Down

0 comments on commit 655f74e

Please sign in to comment.