use std::time::Duration; use log::info; use spacetimedb::{ReducerContext, Table, rand::seq::SliceRandom, reducer}; use crate::tables::*; #[reducer] pub fn join_or_create_lobby(ctx: &ReducerContext, mut lobby_id: u32) -> Result<(), String> { let mut player = ctx .db .player() .identity() .find(ctx.sender) .ok_or(format!("cannot find player {}", ctx.sender))?; if lobby_id == 0 && player.lobby_id == 0 { // TODO check first if player is already in a lobby let lobby = ctx.db.lobby().insert(Lobby { id: 0, players: vec![PlayerOrBot::Player { id: player.id }], game_state: jong_types::states::GameState::Lobby, dealer_idx: 0, current_idx: 0, }); info!("created lobby: {}", lobby.id); lobby_id = lobby.id; player.lobby_id = lobby_id; } else { let lobby = ctx .db .lobby() .id() .find(player.lobby_id) .ok_or(format!("can't find lobby {}", player.lobby_id))?; lobby_id = lobby.id; } let player = ctx.db.player().identity().update(player); info!("player {} joined lobby {}", player.id, lobby_id); Ok(()) } #[reducer] pub fn add_bot(ctx: &ReducerContext, lobby_id: u32) -> Result<(), String> { if lobby_id == 0 { Err("cannot add a bot without a lobby".into()) } else if let Some(mut lobby) = ctx.db.lobby().id().find(lobby_id) && (ctx.db.player().lobby_id().filter(lobby_id).count() + ctx.db.bot().lobby_id().filter(lobby_id).count() < 4) { let bot = ctx.db.bot().insert(Bot { id: 0, lobby_id, hand: vec![], pond: vec![], working_tile: None, turn_state: jong_types::TurnState::None, }); lobby.players.push(PlayerOrBot::Bot { id: bot.id }); ctx.db.lobby().id().update(lobby); info!("added bot {} to lobby {}", bot.id, lobby_id); Ok(()) } else { Err("lobby doesn't exist".into()) } } #[reducer] pub fn set_ready(ctx: &ReducerContext, ready: bool) -> Result<(), String> { let mut player = ctx.db.player().identity().find(ctx.sender).unwrap(); player.ready = ready; player = ctx.db.player().identity().update(player); if let Some(mut lobby) = ctx.db.lobby().id().find(player.lobby_id) && lobby.players.len() == 4 && ctx.db.player().lobby_id().filter(lobby.id).all(|p| p.ready) { lobby.game_state = jong_types::states::GameState::Setup; lobby.players.shuffle(&mut ctx.rng()); let lobby = ctx.db.lobby().id().update(lobby); // TODO should we schedule this outside so that we can clear out stale lobbies? ctx.db.game_timer().insert(GameTimer { id: 0, lobby_id: lobby.id, scheduled_at: spacetimedb::ScheduleAt::Interval(Duration::from_secs(1).into()), }); } else { // if lobby doesn't exist, reset player state player.lobby_id = 0; player.ready = false; player = ctx.db.player().identity().update(player); return Err(format!("couldn't find lobby with id: {}", player.lobby_id)); } Ok(()) }