let game handler own the games

This commit is contained in:
Adam 2024-10-13 17:04:28 -04:00
parent 3fb3d2f521
commit c8375d1b15
4 changed files with 60 additions and 84 deletions

View file

@ -142,7 +142,7 @@ impl Game {
} }
/// Judge Game /// Judge Game
pub fn judge_round(&mut self, request: JudgeDecisionRequest, player_user_id: String) { pub fn judge_round(&mut self, request: &JudgeDecisionRequest, player_user_id: String) {
// Check if player is czar // Check if player is czar
if self.czar.read().unwrap().uuid.to_string() == player_user_id { if self.czar.read().unwrap().uuid.to_string() == player_user_id {
if let Some(winner_id) = self.judge_pile_meta.get(&request.winning_cards) { if let Some(winner_id) = self.judge_pile_meta.get(&request.winning_cards) {
@ -158,7 +158,7 @@ impl Game {
/// Process player move /// Process player move
pub fn player_move( pub fn player_move(
&mut self, &mut self,
request: PlayerMoveRequest, request: &PlayerMoveRequest,
player_user_id: String, player_user_id: String,
) -> Result<Option<(JudgeRound, String)>, String> { ) -> Result<Option<(JudgeRound, String)>, String> {
if self.czar.read().unwrap().uuid == player_user_id { if self.czar.read().unwrap().uuid == player_user_id {

View file

@ -1,4 +1,5 @@
use crate::game::*; use crate::game::*;
use crate::AppState; use crate::AppState;
use crate::DmUserMethod::*; use crate::DmUserMethod::*;
use crate::GameHandlerMessage::*; use crate::GameHandlerMessage::*;
@ -6,10 +7,8 @@ use crate::SendUserMessage::*;
use crate::UserHandlerMessage::*; use crate::UserHandlerMessage::*;
use lib::*; use lib::*;
use serde_json::to_string; use serde_json::to_string;
use std::{ use std::collections::HashMap;
net::SocketAddr, use std::{net::SocketAddr, sync::Arc};
sync::{Arc, RwLock},
};
/// For interacting with the game handler /// For interacting with the game handler
#[derive(Debug)] #[derive(Debug)]
@ -28,20 +27,22 @@ pub enum GameHandlerMessage {
pub struct GameHandler { pub struct GameHandler {
/// Global state pointer /// Global state pointer
state: Arc<AppState>, state: Arc<AppState>,
games: HashMap<String, Game>,
} }
impl GameHandler { impl GameHandler {
/// Returns a new game handler /// Returns a new game handler
pub fn new(state: Arc<AppState>) -> Self { pub fn new(state: Arc<AppState>) -> Self {
GameHandler { state } let games = HashMap::new();
GameHandler { state, games }
} }
/// Handles incoming messages /// Handles incoming messages
pub async fn handle(&self, message: GameHandlerMessage) { pub async fn handle(&mut self, message: GameHandlerMessage) {
match message { match message {
NewGame(request, addr) => self.create_new_game(request, addr).await, NewGame(request, addr) => self.create_new_game(request, addr).await,
JoinGame(request, addr) => self.join_game(request, addr).await, JoinGame(request, addr) => self.join_game(request, addr).await,
MoveRequest(request, addr) => self.handle_player_move(request, addr).await, MoveRequest(request, addr) => self.handle_player_move(request, addr),
JudgeDecision(request, addr) => self.handle_judging(request, addr).await, JudgeDecision(request, addr) => self.handle_judging(request, addr).await,
DeleteGame(request) => self.delete_game(request).await, DeleteGame(request) => self.delete_game(request).await,
SendGameStateUpdate(game_ids) => self.send_game_state_update_all(game_ids), SendGameStateUpdate(game_ids) => self.send_game_state_update_all(game_ids),
@ -51,16 +52,9 @@ impl GameHandler {
} }
/// Delete game /// Delete game
async fn delete_game(&self, request: GameDeleteRequest) { async fn delete_game(&mut self, request: GameDeleteRequest) {
// TODO: add auth lol // TODO: add auth lol
if self if self.games.remove(&request.delete_game_id).is_some() {
.state
.games
.write()
.unwrap()
.remove(&request.delete_game_id)
.is_some()
{
self.broadcast_game_browser_update(); self.broadcast_game_browser_update();
} else { } else {
tracing::error!("User tried to delete a nonexistent game!"); tracing::error!("User tried to delete a nonexistent game!");
@ -68,17 +62,14 @@ impl GameHandler {
} }
/// Process judging /// Process judging
async fn handle_judging(&self, request: JudgeDecisionRequest, addr: SocketAddr) { async fn handle_judging(&mut self, request: JudgeDecisionRequest, addr: SocketAddr) {
if let Some(this_game) = self.state.games.read().unwrap().get(&request.game_id) { if let Some(this_game) = self.games.get_mut(&request.game_id) {
if let Some(player_user) = self.state.online_users.read().unwrap().get(&addr) { if let Some(player_user) = self.state.online_users.read().unwrap().get(&addr) {
let player_user_id = player_user.read().unwrap().uuid.to_string(); let player_user_id = player_user.read().unwrap().uuid.to_string();
let game_id = request.game_id.to_string(); let game_id = request.game_id.to_string();
// Send to game // Send to game
this_game this_game.judge_round(&request, player_user_id);
.write()
.unwrap()
.judge_round(request.clone(), player_user_id);
self.send_game_state_update_all(vec![game_id.clone()]); self.send_game_state_update_all(vec![game_id.clone()]);
self.send_game_meta_update(vec![game_id]); self.send_game_meta_update(vec![game_id]);
@ -95,44 +86,38 @@ impl GameHandler {
} }
/// Process player move request /// Process player move request
async fn handle_player_move(&self, request: PlayerMoveRequest, addr: SocketAddr) { fn handle_player_move(&mut self, request: PlayerMoveRequest, addr: SocketAddr) {
if let Some(this_game) = self.state.games.read().unwrap().get(&request.game_id) { if let Some(this_game) = self.games.get_mut(&request.game_id) {
if let Some(player_user) = self.state.online_users.read().unwrap().get(&addr) { if let Some(player_user) = self.state.online_users.read().unwrap().get(&addr) {
let player_user_id = player_user.read().unwrap().uuid.to_string(); let player_user_id = player_user.read().unwrap().uuid.to_string();
// Do the stuff // Do the stuff
match this_game match this_game.player_move(&request, player_user_id) {
.write()
.unwrap()
.player_move(request.clone(), player_user_id)
{
Err(err) => { Err(err) => {
let message = ChatMessage { text: err }; let message = ChatMessage { text: err };
let tx = self.state.users_tx.clone(); let users_tx = self.state.users_tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = if let Err(e) = users_tx
tx.send(DmUser(SendChatMessage(message), Addr(addr))).await .send(DmUser(SendChatMessage(message), Addr(addr)))
.await
{ {
tracing::error!("Could not send message: {}", e); tracing::error!("Could not send message: {}", e);
} }
}) });
} }
Ok(None) => { Ok(None) => {
tokio::spawn( tracing::debug!("TODO: whatever i'm supposed to do")
async move { tracing::debug!("TODO: whatever i'm supposed to do") },
)
} }
Ok(Some((judge_round, czar_id))) => { Ok(Some((judge_round, czar_id))) => {
let tx = self.state.users_tx.clone(); let users_tx = self.state.users_tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = tx if let Err(e) = users_tx
.send(DmUser(SendJudgeRound(judge_round), Id(czar_id))) .send(DmUser(SendJudgeRound(judge_round), Id(czar_id)))
.await .await
{ {
tracing::error!("Could not send message: {}", e); tracing::error!("Could not send message: {}", e);
} }
}) });
} }
}; };
} else { } else {
@ -176,8 +161,8 @@ impl GameHandler {
} }
/// Puts a user in a game /// Puts a user in a game
async fn join_game(&self, game_id: String, addr: SocketAddr) { async fn join_game(&mut self, game_id: String, addr: SocketAddr) {
if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) { if self.games.contains_key(&game_id) {
if let Some(this_user) = self.state.online_users.read().unwrap().get(&addr) { if let Some(this_user) = self.state.online_users.read().unwrap().get(&addr) {
let this_user_id = this_user.read().unwrap().uuid.clone(); let this_user_id = this_user.read().unwrap().uuid.clone();
@ -185,7 +170,10 @@ impl GameHandler {
self.register_user_in_game(game_id.clone(), this_user_id.clone()); self.register_user_in_game(game_id.clone(), this_user_id.clone());
// Create player // Create player
this_game.write().unwrap().create_player(this_user.clone()); self.games
.get_mut(&game_id)
.unwrap()
.create_player(this_user.clone());
// Send cards // Send cards
self.send_game_state_update_single(this_user_id, game_id.clone()) self.send_game_state_update_single(this_user_id, game_id.clone())
@ -208,18 +196,14 @@ impl GameHandler {
/// Send game meta update for all players of a game /// Send game meta update for all players of a game
fn send_game_meta_update(&self, game_ids: Vec<String>) { fn send_game_meta_update(&self, game_ids: Vec<String>) {
for game_id in game_ids { for game_id in game_ids {
if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) { if let Some(this_game) = self.games.get(&game_id) {
let players = this_game let players = this_game
.read()
.unwrap()
.players .players
.values() .values()
.map(|player| GamePlayerMeta { .map(|player| GamePlayerMeta {
name: player.user.read().unwrap().name.clone(), name: player.user.read().unwrap().name.clone(),
score: player.black.len(), score: player.black.len(),
submitted: this_game submitted: this_game
.read()
.unwrap()
.judge_pile_meta .judge_pile_meta
.values() .values()
.collect::<Vec<&String>>() .collect::<Vec<&String>>()
@ -227,18 +211,18 @@ impl GameHandler {
}) })
.collect::<Vec<GamePlayerMeta>>(); .collect::<Vec<GamePlayerMeta>>();
for player in this_game.read().unwrap().players.values() { for player in this_game.players.values() {
// Create update for user's game view // Create update for user's game view
let meta = GameMeta { let meta = GameMeta {
uuid: game_id.clone(), uuid: game_id.clone(),
name: this_game.read().unwrap().name.clone(), name: this_game.name.clone(),
host: this_game.read().unwrap().host.read().unwrap().name.clone(), host: this_game.host.read().unwrap().name.clone(),
players: players.clone(), players: players.clone(),
czar: this_game.read().unwrap().czar.read().unwrap().name.clone(), czar: this_game.czar.read().unwrap().name.clone(),
packs: this_game.read().unwrap().packs.clone(), packs: this_game.packs.clone(),
white_count: this_game.read().unwrap().white.len(), white_count: this_game.white.len(),
black_count: this_game.read().unwrap().black.len(), black_count: this_game.black.len(),
white_discard_count: this_game.read().unwrap().white_discard.len(), white_discard_count: this_game.white_discard.len(),
}; };
// Send user's update // Send user's update
@ -263,13 +247,13 @@ impl GameHandler {
/// Send game state update for all players of a game /// Send game state update for all players of a game
fn send_game_state_update_all(&self, game_ids: Vec<String>) { fn send_game_state_update_all(&self, game_ids: Vec<String>) {
for game_id in game_ids { for game_id in game_ids {
if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) { if let Some(this_game) = self.games.get(&game_id) {
for player in this_game.read().unwrap().players.values() { for player in this_game.players.values() {
// Create update for user's game view // Create update for user's game view
let meta = GameStateMeta { let meta = GameStateMeta {
black: ( black: (
this_game.read().unwrap().current_black.text.clone(), this_game.current_black.text.clone(),
this_game.read().unwrap().current_black.pick, this_game.current_black.pick,
), ),
white: player white: player
.white .white
@ -294,13 +278,13 @@ impl GameHandler {
/// Send game state update for a single user /// Send game state update for a single user
fn send_game_state_update_single(&self, user_id: String, game_id: String) { fn send_game_state_update_single(&self, user_id: String, game_id: String) {
if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) { if let Some(this_game) = self.games.get(&game_id) {
if let Some(player) = this_game.read().unwrap().players.get(&user_id) { if let Some(player) = this_game.players.get(&user_id) {
// Create update for user's game view // Create update for user's game view
let meta = GameStateMeta { let meta = GameStateMeta {
black: ( black: (
this_game.read().unwrap().current_black.text.clone(), this_game.current_black.text.clone(),
this_game.read().unwrap().current_black.pick, this_game.current_black.pick,
), ),
white: player white: player
.white .white
@ -325,7 +309,7 @@ impl GameHandler {
} }
/// Creates a new game /// Creates a new game
async fn create_new_game(&self, new_game: NewGameRequest, addr: SocketAddr) { async fn create_new_game(&mut self, new_game: NewGameRequest, addr: SocketAddr) {
if new_game.packs.is_empty() { if new_game.packs.is_empty() {
tracing::error!("New game cards are empty!"); tracing::error!("New game cards are empty!");
return; return;
@ -364,10 +348,8 @@ impl GameHandler {
let game_id = new_game_object.uuid.to_string(); let game_id = new_game_object.uuid.to_string();
// Add game to active list // Add game to active list
self.state.games.write().unwrap().insert( self.games
new_game_object.uuid.to_string(), .insert(new_game_object.uuid.to_string(), new_game_object);
Arc::new(RwLock::new(new_game_object)),
);
// Register game to user // Register game to user
self.register_user_in_game(game_id.clone(), host.read().unwrap().uuid.clone()); self.register_user_in_game(game_id.clone(), host.read().unwrap().uuid.clone());
@ -386,17 +368,14 @@ impl GameHandler {
// TODO: this may get expensive if there are many games // TODO: this may get expensive if there are many games
let games = self let games = self
.state
.games .games
.read()
.unwrap()
.values() .values()
.map(|game| GameBrowserMeta { .map(|game| GameBrowserMeta {
uuid: game.read().unwrap().uuid.to_string(), uuid: game.uuid.to_string(),
name: game.read().unwrap().name.clone(), name: game.name.clone(),
host: game.read().unwrap().host.read().unwrap().name.clone(), host: game.host.read().unwrap().name.clone(),
players: game.read().unwrap().players.len(), players: game.players.len(),
packs: game.read().unwrap().packs.clone(), packs: game.packs.clone(),
}) })
.collect::<Vec<GameBrowserMeta>>(); .collect::<Vec<GameBrowserMeta>>();
@ -412,7 +391,7 @@ impl GameHandler {
/// Broadcast updated game count /// Broadcast updated game count
fn broadcast_game_count(&self) { fn broadcast_game_count(&self) {
let tx = self.state.broadcast_tx.clone(); let tx = self.state.broadcast_tx.clone();
let active_games = self.state.games.read().unwrap().len(); let active_games = self.games.len();
let msg = to_string(&ServerActiveGames { active_games }).unwrap(); let msg = to_string(&ServerActiveGames { active_games }).unwrap();
tokio::spawn(async move { tx.send(msg) }); tokio::spawn(async move { tx.send(msg) });
} }

View file

@ -76,7 +76,6 @@ pub struct AppState {
pub offline_users: RwLock<HashMap<String, Arc<RwLock<User>>>>, pub offline_users: RwLock<HashMap<String, Arc<RwLock<User>>>>,
pub packs: CardPacks, pub packs: CardPacks,
pub packs_meta: CardPacksMeta, pub packs_meta: CardPacksMeta,
pub games: RwLock<HashMap<String, Arc<RwLock<Game>>>>,
} }
/// Card Set /// Card Set

View file

@ -80,7 +80,6 @@ async fn main() -> Result<()> {
let online_users = RwLock::new(HashMap::<SocketAddr, Arc<RwLock<User>>>::new()); let online_users = RwLock::new(HashMap::<SocketAddr, Arc<RwLock<User>>>::new());
let offline_users = RwLock::new(HashMap::<String, Arc<RwLock<User>>>::new()); let offline_users = RwLock::new(HashMap::<String, Arc<RwLock<User>>>::new());
let (packs, packs_meta, white_cards_by_id) = load_cards_from_json("data/cah-cards-full.json")?; let (packs, packs_meta, white_cards_by_id) = load_cards_from_json("data/cah-cards-full.json")?;
let games = RwLock::new(HashMap::new());
let app_state = Arc::new(AppState { let app_state = Arc::new(AppState {
white_cards_by_id, white_cards_by_id,
@ -97,7 +96,6 @@ async fn main() -> Result<()> {
offline_users, offline_users,
packs, packs,
packs_meta, packs_meta,
games,
}); });
// Spawn task to handle incoming messages, also handles outging messages // Spawn task to handle incoming messages, also handles outging messages
@ -126,7 +124,7 @@ async fn main() -> Result<()> {
.unwrap(); .unwrap();
// Spawn task to handle Game things // Spawn task to handle Game things
let game_handler = GameHandler::new(app_state.clone()); let mut game_handler = GameHandler::new(app_state.clone());
tokio::task::Builder::new() tokio::task::Builder::new()
.name("Game Handler") .name("Game Handler")
.spawn(async move { .spawn(async move {