refactor jong-line
This commit is contained in:
parent
0c3fe6f87a
commit
147f939179
6 changed files with 242 additions and 278 deletions
|
|
@ -3,68 +3,46 @@
|
|||
use log::{debug, trace};
|
||||
use spacetimedb::{ReducerContext, Table, reducer};
|
||||
|
||||
use crate::tables::*;
|
||||
|
||||
mod reducers;
|
||||
mod tables;
|
||||
|
||||
#[reducer]
|
||||
pub fn clear_all(ctx: &ReducerContext) {
|
||||
for row in ctx.db.player().iter() {
|
||||
ctx.db.player().delete(row);
|
||||
}
|
||||
for row in ctx.db.lobby().iter() {
|
||||
ctx.db.lobby().delete(row);
|
||||
}
|
||||
for row in ctx.db.bot().iter() {
|
||||
ctx.db.bot().delete(row);
|
||||
}
|
||||
for row in ctx.db.wall().iter() {
|
||||
ctx.db.wall().delete(row);
|
||||
}
|
||||
for row in ctx.db.tile().iter() {
|
||||
ctx.db.tile().delete(row);
|
||||
}
|
||||
}
|
||||
use crate::tables::*;
|
||||
|
||||
#[reducer(client_connected)]
|
||||
pub fn connect(ctx: &ReducerContext) -> Result<(), String> {
|
||||
let player = if let Some(player) = ctx.db.logged_out_player().identity().find(ctx.sender()) {
|
||||
let player = ctx.db.player().insert(player);
|
||||
ctx.db.logged_out_player().identity().delete(ctx.sender());
|
||||
let player = if let Some(player) = ctx.db.logged_out_user().identity().find(ctx.sender()) {
|
||||
let player = ctx.db.user().insert(player);
|
||||
ctx.db.logged_out_user().identity().delete(ctx.sender());
|
||||
player
|
||||
} else {
|
||||
debug!("inserting new player with identity {:?}", ctx.sender());
|
||||
ctx.db.player().try_insert(Player {
|
||||
debug!("inserting new user with identity {:?}", ctx.sender());
|
||||
ctx.db.user().try_insert(User {
|
||||
identity: ctx.sender(),
|
||||
id: 0,
|
||||
name: None,
|
||||
name: String::new(),
|
||||
config_id: 0,
|
||||
lobby_id: 0,
|
||||
ready: false,
|
||||
sort: true,
|
||||
})?
|
||||
};
|
||||
|
||||
debug!("player connected: {:?}", player);
|
||||
debug!("user connected: {:?}", player);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[reducer(client_disconnected)]
|
||||
pub fn disconnect(ctx: &ReducerContext) -> Result<(), String> {
|
||||
let player = ctx
|
||||
let user = ctx
|
||||
.db
|
||||
.player()
|
||||
.user()
|
||||
.identity()
|
||||
.find(ctx.sender())
|
||||
.ok_or_else(|| format!("can't find player {} to disconnect", ctx.sender()))?;
|
||||
|
||||
let player = ctx.db.logged_out_player().insert(player);
|
||||
if !ctx.db.player().identity().delete(ctx.sender()) {
|
||||
Err("can't delete row")?
|
||||
let user = ctx.db.logged_out_user().insert(user);
|
||||
if !ctx.db.user().identity().delete(ctx.sender()) {
|
||||
Err(format!("can't delete user: {user:?}"))?
|
||||
}
|
||||
|
||||
debug!("player disconnected: {:?}", player);
|
||||
debug!("user disconnected: {:?}", user);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ use spacetimedb::{
|
|||
};
|
||||
|
||||
use crate::tables::{
|
||||
DbTile, DbWall, GameTimer, Lobby, PlayerClock, PlayerHand, bot, game_timer, lobby as _, player,
|
||||
player_clock, player_hand, tile as _, wall,
|
||||
DbTile, Lobby, LobbyTimer, PlayerClock, PlayerHand, Wall, bot, game_timer, lobby as _,
|
||||
player_clock, player_config, player_hand, tile, user, wall,
|
||||
};
|
||||
use jong_types::{GameState, PlayerOrBot, TurnState};
|
||||
use jong_types::{GameState, TurnState};
|
||||
|
||||
mod hand;
|
||||
mod lobby;
|
||||
|
|
@ -22,7 +22,7 @@ pub fn advance_game(ctx: &ReducerContext) -> Result<(), String> {
|
|||
.lobby_id()
|
||||
.find(
|
||||
ctx.db
|
||||
.player()
|
||||
.user()
|
||||
.identity()
|
||||
.find(ctx.sender())
|
||||
.ok_or("player not in lobby")?
|
||||
|
|
@ -43,8 +43,7 @@ fn shuffle_wall(ctx: &ReducerContext, lobby: &mut Lobby) {
|
|||
wall.shuffle(&mut rng);
|
||||
wall
|
||||
};
|
||||
ctx.db.wall().insert(DbWall {
|
||||
// id: 0,
|
||||
ctx.db.wall().insert(Wall {
|
||||
lobby_id: lobby.id,
|
||||
tiles,
|
||||
});
|
||||
|
|
@ -53,33 +52,17 @@ fn shuffle_wall(ctx: &ReducerContext, lobby: &mut Lobby) {
|
|||
|
||||
fn deal_hands(ctx: &ReducerContext, lobby: &mut Lobby) -> Result<(), String> {
|
||||
let mut wall = ctx.db.wall().lobby_id().find(lobby.id).unwrap();
|
||||
for pob in &lobby.players {
|
||||
for player in &lobby.players {
|
||||
let mut tiles = wall.tiles.split_off(wall.tiles.len() - 13);
|
||||
wall = ctx.db.wall().lobby_id().update(wall);
|
||||
tiles.sort_by_key(|t| t.tile);
|
||||
match pob {
|
||||
PlayerOrBot::Player { id } if let Some(p) = ctx.db.player().id().find(id) => {
|
||||
ctx.db.player_hand().insert(PlayerHand {
|
||||
id: 0,
|
||||
player_id: p.id,
|
||||
turn_state: jong_types::TurnState::None,
|
||||
pond: vec![],
|
||||
hand: tiles,
|
||||
working_tile: None,
|
||||
});
|
||||
ctx.db.player_clock().insert(PlayerClock {
|
||||
id: 0,
|
||||
player_id: p.id,
|
||||
renewable: 5,
|
||||
total: 30,
|
||||
});
|
||||
}
|
||||
PlayerOrBot::Bot { id } if let Some(mut b) = ctx.db.bot().id().find(id) => {
|
||||
b.hand = tiles;
|
||||
ctx.db.bot().id().update(b);
|
||||
}
|
||||
_ => Err("couldn't find player or bot".to_string())?,
|
||||
}
|
||||
ctx.db.player_hand().insert(PlayerHand {
|
||||
player_id: *player,
|
||||
turn_state: TurnState::None,
|
||||
hand: tiles,
|
||||
pond: vec![],
|
||||
working_tile: None,
|
||||
});
|
||||
}
|
||||
lobby.game_state = jong_types::states::GameState::Play;
|
||||
|
||||
|
|
@ -87,7 +70,10 @@ fn deal_hands(ctx: &ReducerContext, lobby: &mut Lobby) -> Result<(), String> {
|
|||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn advance_game_private(ctx: &ReducerContext, mut game_timer: GameTimer) -> Result<(), String> {
|
||||
pub fn advance_game_private(
|
||||
ctx: &ReducerContext,
|
||||
mut game_timer: LobbyTimer,
|
||||
) -> Result<(), String> {
|
||||
// checks every second (or more? when users make moves) on whether to advance the game's various states
|
||||
// TODO this, or allow player/debug to call this?
|
||||
|
||||
|
|
@ -99,6 +85,21 @@ pub fn advance_game_private(ctx: &ReducerContext, mut game_timer: GameTimer) ->
|
|||
// TODO keep a count to clear stale lobbies
|
||||
// trace!("shuffle wall");
|
||||
shuffle_wall(ctx, &mut lobby);
|
||||
|
||||
lobby.players.shuffle(&mut ctx.rng());
|
||||
|
||||
for player_id in lobby
|
||||
.players
|
||||
.iter()
|
||||
.filter(|id| ctx.db.user().config_id().find(*id).is_some())
|
||||
{
|
||||
ctx.db.player_clock().insert(PlayerClock {
|
||||
player_id: *player_id,
|
||||
renewable: 5,
|
||||
total: 20,
|
||||
});
|
||||
}
|
||||
|
||||
ctx.db.lobby().id().update(lobby);
|
||||
advance_game_private(ctx, game_timer)?;
|
||||
return Ok(());
|
||||
|
|
@ -114,62 +115,44 @@ pub fn advance_game_private(ctx: &ReducerContext, mut game_timer: GameTimer) ->
|
|||
GameState::Play => {
|
||||
// trace!("in play");
|
||||
let curr_player = lobby.players.get(lobby.current_idx as usize).unwrap();
|
||||
match curr_player {
|
||||
PlayerOrBot::Player { id: player_id } => {
|
||||
// trace!("current player is {player_id}");
|
||||
let mut clock = ctx.db.player_clock().player_id().find(player_id).unwrap();
|
||||
let mut hand = ctx.db.player_hand().player_id().find(player_id).unwrap();
|
||||
match hand.turn_state {
|
||||
TurnState::None => {
|
||||
// trace!("draw a tile");
|
||||
if let Some(mut wall) = ctx.db.wall().lobby_id().find(lobby.id)
|
||||
&& let Some(tile) = wall.tiles.pop()
|
||||
{
|
||||
hand.working_tile = Some(tile);
|
||||
hand.turn_state = TurnState::Tsumo;
|
||||
ctx.db.wall().lobby_id().update(wall);
|
||||
ctx.db.player_hand().id().update(hand);
|
||||
} else {
|
||||
// TODO out of tiles
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
TurnState::Tsumo => {
|
||||
// trace!("wait for discard");
|
||||
if clock.tick() {
|
||||
ctx.db.player_clock().id().update(clock);
|
||||
} else {
|
||||
// TODO auto-discard
|
||||
}
|
||||
}
|
||||
TurnState::Menzen => {}
|
||||
TurnState::RiichiKan => {}
|
||||
TurnState::RonChiiPonKan => {}
|
||||
TurnState::End => {}
|
||||
let mut hand = ctx.db.player_hand().player_id().find(curr_player).unwrap();
|
||||
match hand.turn_state {
|
||||
TurnState::None => {
|
||||
// trace!("draw a tile");
|
||||
if let Some(mut wall) = ctx.db.wall().lobby_id().find(lobby.id)
|
||||
&& let Some(tile) = wall.tiles.pop()
|
||||
{
|
||||
hand.working_tile = Some(tile);
|
||||
hand.turn_state = TurnState::Tsumo;
|
||||
ctx.db.wall().lobby_id().update(wall);
|
||||
ctx.db.player_hand().player_id().update(hand);
|
||||
} else {
|
||||
// TODO out of tiles
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
PlayerOrBot::Bot { id: bot_id } => {
|
||||
debug!("current bot is {bot_id}");
|
||||
let bot = ctx.db.bot().id().find(bot_id).unwrap();
|
||||
match bot.turn_state {
|
||||
// TurnState::None => todo!(),
|
||||
// TurnState::Tsumo => todo!(),
|
||||
// TurnState::Menzen => todo!(),
|
||||
// TurnState::RiichiKan => todo!(),
|
||||
// TurnState::RonChiiPonKan => todo!(),
|
||||
// TurnState::End => todo!(),
|
||||
_ => {}
|
||||
TurnState::Tsumo => {
|
||||
// only real players have clocks?
|
||||
if let Some(mut clock) = ctx.db.player_clock().player_id().find(curr_player)
|
||||
&& clock.tick()
|
||||
{
|
||||
ctx.db.player_clock().player_id().update(clock);
|
||||
} else {
|
||||
// TODO bot / auto discard
|
||||
}
|
||||
lobby.next_player();
|
||||
}
|
||||
TurnState::Menzen => {}
|
||||
TurnState::RiichiKan => {}
|
||||
TurnState::RonChiiPonKan => {}
|
||||
TurnState::End => {}
|
||||
}
|
||||
}
|
||||
GameState::Exit => {
|
||||
ctx.db.game_timer().id().delete(game_timer.id);
|
||||
ctx.db.lobby().id().delete(lobby.id);
|
||||
// ctx.db.game_timer().id().delete(game_timer.id);
|
||||
// ctx.db.lobby().id().delete(lobby.id);
|
||||
// TODO reset all player lobbies, delete bots, etc?
|
||||
// is there a way to do this automatically, or rely on elsewhere's checks clearing the state?
|
||||
return Ok(());
|
||||
todo!("lobby exit cleanup")
|
||||
}
|
||||
|
||||
// TODO handle stale lobbies
|
||||
|
|
@ -180,10 +163,10 @@ pub fn advance_game_private(ctx: &ReducerContext, mut game_timer: GameTimer) ->
|
|||
// ctx.db.game_timer().id().update(game_timer);
|
||||
ctx.db.lobby().id().update(lobby);
|
||||
} else {
|
||||
ctx.db.game_timer().id().delete(game_timer.id);
|
||||
// ctx.db.game_timer().id().delete(game_timer.id);
|
||||
Err(format!(
|
||||
"ran schedule {} for empty lobby {}",
|
||||
game_timer.id, game_timer.lobby_id
|
||||
game_timer.scheduled_id, game_timer.lobby_id
|
||||
))?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,56 +8,63 @@ use crate::tables::*;
|
|||
// TODO make sure this can't be called or just error here?
|
||||
#[reducer]
|
||||
pub fn discard_tile(ctx: &ReducerContext, tile_id: u32) -> Result<(), String> {
|
||||
let player = ctx.db.player().identity().find(ctx.sender()).unwrap();
|
||||
let mut hand = ctx.db.player_hand().player_id().find(player.id).unwrap();
|
||||
let player = ctx.db.user().identity().find(ctx.sender()).unwrap();
|
||||
let mut hand = ctx
|
||||
.db
|
||||
.player_hand()
|
||||
.player_id()
|
||||
.find(player.config_id)
|
||||
.unwrap();
|
||||
|
||||
// TODO we can probably remove a buncha these errors
|
||||
let dealt_tile = if let Some(dealt) = ctx.db.tile().id().find(tile_id) {
|
||||
if let Some(drawn) = hand.working_tile {
|
||||
if drawn.id == dealt.id {
|
||||
// dealt from drawn tile
|
||||
dealt
|
||||
} else if let Some((i, _)) = hand.hand.iter().enumerate().find(|(_, t)| t.id == tile_id)
|
||||
{
|
||||
// dealt from hand
|
||||
let dealt = hand.hand.remove(i);
|
||||
hand.hand.push(drawn);
|
||||
hand.hand.sort_by_key(|t| t.tile);
|
||||
let dealt = ctx.db.tile().id().find(tile_id).unwrap();
|
||||
let drawn = hand.working_tile.unwrap();
|
||||
|
||||
dealt
|
||||
} else {
|
||||
return Err(format!(
|
||||
"player {} attempted to deal tile {} not in hand or drawn",
|
||||
player.id, tile_id
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(format!(
|
||||
"player {} attempted to deal tile {} without having drawn",
|
||||
player.id, tile_id
|
||||
));
|
||||
let dealt_tile = if dealt.id == drawn.id {
|
||||
// dealt from drawn tile
|
||||
dealt
|
||||
} else if let Some((i, _)) = hand.hand.iter().enumerate().find(|(_, t)| dealt.id == t.id) {
|
||||
// dealt from hand
|
||||
let dealt = hand.hand.remove(i);
|
||||
hand.hand.push(drawn);
|
||||
if ctx
|
||||
.db
|
||||
.player_config()
|
||||
.id()
|
||||
.find(player.config_id)
|
||||
.is_some_and(|c| c.sort)
|
||||
{
|
||||
hand.hand.sort_by_key(|t| t.tile);
|
||||
}
|
||||
|
||||
dealt
|
||||
} else {
|
||||
return Err(format!(
|
||||
"player {} attempted to deal nonexistant tile {}",
|
||||
player.id, tile_id
|
||||
));
|
||||
// ERROR
|
||||
Err("dealt tile is missing")?
|
||||
};
|
||||
|
||||
hand.pond.push(dealt_tile);
|
||||
hand.working_tile = None;
|
||||
hand.turn_state = TurnState::None;
|
||||
ctx.db.player_hand().id().update(hand);
|
||||
ctx.db.player_hand().player_id().update(hand);
|
||||
|
||||
let mut clock = ctx.db.player_clock().player_id().find(player.id).unwrap();
|
||||
let mut clock = ctx
|
||||
.db
|
||||
.player_clock()
|
||||
.player_id()
|
||||
.find(player.config_id)
|
||||
.unwrap();
|
||||
clock.renew();
|
||||
ctx.db.player_clock().id().update(clock);
|
||||
ctx.db.player_clock().player_id().update(clock);
|
||||
|
||||
let mut lobby = ctx.db.lobby().id().find(player.lobby_id).unwrap();
|
||||
lobby.next_player();
|
||||
ctx.db.lobby().id().update(lobby);
|
||||
|
||||
debug!("player {} discarded tile {:?}", player.id, dealt_tile.tile);
|
||||
debug!(
|
||||
"player {} discarded tile {:?}",
|
||||
player.identity, dealt_tile.tile
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +1,52 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use log::info;
|
||||
use log::{info, warn};
|
||||
use spacetimedb::{ReducerContext, Table, rand::seq::SliceRandom, reducer};
|
||||
|
||||
use jong_types::PlayerOrBot;
|
||||
|
||||
use crate::{reducers::advance_game_private, tables::*};
|
||||
|
||||
#[reducer]
|
||||
pub fn join_or_create_lobby(ctx: &ReducerContext, mut lobby_id: u32) -> Result<(), String> {
|
||||
let mut player = ctx
|
||||
let mut user = ctx
|
||||
.db
|
||||
.player()
|
||||
.user()
|
||||
.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
|
||||
if lobby_id == 0 && user.lobby_id == 0 {
|
||||
let player = ctx.db.player_config().insert(PlayerConfig {
|
||||
id: 0,
|
||||
name: String::new(),
|
||||
ready: false,
|
||||
sort: true,
|
||||
});
|
||||
|
||||
let lobby = ctx.db.lobby().insert(Lobby {
|
||||
id: 0,
|
||||
players: vec![PlayerOrBot::Player { id: player.id }],
|
||||
players: vec![player.id],
|
||||
game_state: jong_types::states::GameState::Lobby,
|
||||
dealer_idx: 0,
|
||||
current_idx: 0,
|
||||
});
|
||||
lobby_id = lobby.id;
|
||||
info!("created lobby: {}", lobby.id);
|
||||
|
||||
lobby_id = lobby.id;
|
||||
player.lobby_id = lobby_id;
|
||||
user.config_id = player.id;
|
||||
user.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))?;
|
||||
.find(user.lobby_id)
|
||||
.ok_or(format!("can't find lobby {}", user.lobby_id))?;
|
||||
lobby_id = lobby.id;
|
||||
}
|
||||
|
||||
let player = ctx.db.player().identity().update(player);
|
||||
let user = ctx.db.user().identity().update(user);
|
||||
info!("user {} joined lobby {}", user.name, lobby_id);
|
||||
|
||||
info!("player {} joined lobby {}", player.id, lobby_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -51,19 +55,22 @@ 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.user().lobby_id().filter(lobby_id).count()
|
||||
+ ctx.db.bot().lobby_id().filter(lobby_id).count()
|
||||
< 4)
|
||||
{
|
||||
let player = ctx.db.player_config().insert(PlayerConfig {
|
||||
id: 0,
|
||||
name: String::new(),
|
||||
ready: true,
|
||||
sort: true,
|
||||
});
|
||||
let bot = ctx.db.bot().insert(Bot {
|
||||
id: 0,
|
||||
lobby_id,
|
||||
hand: vec![],
|
||||
pond: vec![],
|
||||
working_tile: None,
|
||||
turn_state: jong_types::TurnState::None,
|
||||
config_id: player.id,
|
||||
});
|
||||
lobby.players.push(PlayerOrBot::Bot { id: bot.id });
|
||||
lobby.players.push(player.id);
|
||||
ctx.db.lobby().id().update(lobby);
|
||||
info!("added bot {} to lobby {}", bot.id, lobby_id);
|
||||
Ok(())
|
||||
|
|
@ -74,33 +81,40 @@ pub fn add_bot(ctx: &ReducerContext, lobby_id: u32) -> Result<(), String> {
|
|||
|
||||
#[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);
|
||||
let mut user = ctx.db.user().identity().find(ctx.sender()).unwrap();
|
||||
let mut player = ctx.db.player_config().id().find(user.config_id).unwrap();
|
||||
|
||||
if let Some(mut lobby) = ctx.db.lobby().id().find(player.lobby_id)
|
||||
player.ready = ready;
|
||||
let player = ctx.db.player_config().id().update(player);
|
||||
|
||||
if let Some(mut lobby) = ctx.db.lobby().id().find(user.lobby_id)
|
||||
&& lobby.players.len() == 4
|
||||
&& ctx.db.player().lobby_id().filter(lobby.id).all(|p| p.ready)
|
||||
&& lobby.players.iter().all(|id| {
|
||||
ctx.db
|
||||
.player_config()
|
||||
.id()
|
||||
.find(id)
|
||||
.is_some_and(|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?
|
||||
let game_timer = ctx.db.game_timer().insert(GameTimer {
|
||||
id: 0,
|
||||
let game_timer = ctx.db.game_timer().insert(LobbyTimer {
|
||||
lobby_id: lobby.id,
|
||||
scheduled_id: 0,
|
||||
scheduled_at: spacetimedb::ScheduleAt::Interval(Duration::from_secs(1).into()),
|
||||
});
|
||||
|
||||
advance_game_private(ctx, game_timer)?;
|
||||
} else {
|
||||
// if lobby doesn't exist, reset player state
|
||||
player.lobby_id = 0;
|
||||
player.ready = false;
|
||||
player = ctx.db.player().identity().update(player);
|
||||
// TODO if lobby doesn't exist, reset player state
|
||||
|
||||
return Err(format!("couldn't find lobby with id: {}", player.lobby_id));
|
||||
user.lobby_id = 0;
|
||||
user = ctx.db.user().identity().update(user);
|
||||
|
||||
return Err(format!("couldn't find lobby with id: {}", user.lobby_id));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,13 +1,28 @@
|
|||
use spacetimedb::{SpacetimeType, ViewContext, table, view};
|
||||
use spacetimedb::{Identity, SpacetimeType, ViewContext, table, view};
|
||||
|
||||
use jong_types::{
|
||||
PlayerOrBot,
|
||||
states::{GameState, TurnState},
|
||||
tiles::Tile,
|
||||
};
|
||||
|
||||
use crate::reducers::advance_game_private;
|
||||
|
||||
#[table(accessor = user)]
|
||||
#[table(accessor = logged_out_user)]
|
||||
#[derive(Debug)]
|
||||
pub struct User {
|
||||
#[primary_key]
|
||||
pub identity: Identity,
|
||||
|
||||
pub name: String,
|
||||
|
||||
#[index(btree)]
|
||||
pub lobby_id: u32,
|
||||
|
||||
#[unique]
|
||||
pub config_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[table(accessor = lobby, public)]
|
||||
pub struct Lobby {
|
||||
|
|
@ -15,7 +30,7 @@ pub struct Lobby {
|
|||
#[auto_inc]
|
||||
pub id: u32,
|
||||
|
||||
pub players: Vec<PlayerOrBot>,
|
||||
pub players: Vec<u32>,
|
||||
pub dealer_idx: u8,
|
||||
pub current_idx: u8,
|
||||
|
||||
|
|
@ -23,8 +38,16 @@ pub struct Lobby {
|
|||
// pub open_hands: bool,
|
||||
}
|
||||
|
||||
// #[table(accessor = lobby_state, public)]
|
||||
// pub struct LobbyState {
|
||||
// #[unique]
|
||||
// lobby_id: u32,
|
||||
|
||||
// current_idx: u8,
|
||||
// }
|
||||
|
||||
#[table(accessor = wall)]
|
||||
pub struct DbWall {
|
||||
pub struct Wall {
|
||||
#[primary_key]
|
||||
pub lobby_id: u32,
|
||||
|
||||
|
|
@ -41,56 +64,6 @@ pub struct DbTile {
|
|||
pub tile: Tile,
|
||||
}
|
||||
|
||||
#[table(accessor = player, public)]
|
||||
#[table(accessor = logged_out_player)]
|
||||
#[derive(Debug)]
|
||||
pub struct Player {
|
||||
#[unique]
|
||||
#[auto_inc]
|
||||
pub id: u32,
|
||||
|
||||
#[primary_key]
|
||||
pub identity: spacetimedb::Identity,
|
||||
|
||||
pub name: Option<String>,
|
||||
|
||||
#[index(btree)]
|
||||
pub lobby_id: u32,
|
||||
pub ready: bool,
|
||||
|
||||
pub sort: bool,
|
||||
}
|
||||
|
||||
#[table(accessor = player_clock, public)]
|
||||
pub struct PlayerClock {
|
||||
#[primary_key]
|
||||
pub id: u32,
|
||||
|
||||
#[unique]
|
||||
pub player_id: u32,
|
||||
|
||||
pub renewable: u16,
|
||||
pub total: u16,
|
||||
}
|
||||
|
||||
#[table(accessor = player_hand)]
|
||||
pub struct PlayerHand {
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
pub id: u32,
|
||||
|
||||
#[unique]
|
||||
pub player_id: u32,
|
||||
|
||||
pub turn_state: TurnState,
|
||||
|
||||
pub pond: Vec<DbTile>,
|
||||
pub hand: Vec<DbTile>,
|
||||
|
||||
/// drawn or callable tile
|
||||
pub working_tile: Option<DbTile>,
|
||||
}
|
||||
|
||||
#[table(accessor = bot, public)]
|
||||
pub struct Bot {
|
||||
#[primary_key]
|
||||
|
|
@ -100,38 +73,68 @@ pub struct Bot {
|
|||
#[index(btree)]
|
||||
pub lobby_id: u32,
|
||||
|
||||
pub config_id: u32,
|
||||
}
|
||||
|
||||
#[table(accessor = player_config, public)]
|
||||
#[derive(Debug)]
|
||||
pub struct PlayerConfig {
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
pub id: u32,
|
||||
|
||||
// TODO randomly generate this from contributor names for bots
|
||||
pub name: String,
|
||||
pub ready: bool,
|
||||
pub sort: bool,
|
||||
}
|
||||
|
||||
#[table(accessor = player_clock, public)]
|
||||
pub struct PlayerClock {
|
||||
#[primary_key]
|
||||
pub player_id: u32,
|
||||
|
||||
pub renewable: u16,
|
||||
pub total: u16,
|
||||
}
|
||||
|
||||
#[table(accessor = player_hand)]
|
||||
pub struct PlayerHand {
|
||||
#[primary_key]
|
||||
pub player_id: u32,
|
||||
|
||||
pub turn_state: TurnState,
|
||||
|
||||
pub hand: Vec<DbTile>,
|
||||
pub pond: Vec<DbTile>,
|
||||
|
||||
/// drawn or callable tile
|
||||
pub working_tile: Option<DbTile>,
|
||||
}
|
||||
|
||||
#[table(accessor = game_timer, scheduled(advance_game_private), public)]
|
||||
pub struct GameTimer {
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
pub id: u64,
|
||||
|
||||
pub struct LobbyTimer {
|
||||
#[unique]
|
||||
pub lobby_id: u32,
|
||||
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
pub scheduled_id: u64,
|
||||
pub scheduled_at: spacetimedb::ScheduleAt,
|
||||
}
|
||||
|
||||
#[view(accessor = view_hand, public)]
|
||||
fn view_hand(ctx: &ViewContext) -> Option<PlayerHand> {
|
||||
ctx.db
|
||||
.player()
|
||||
.user()
|
||||
.identity()
|
||||
.find(ctx.sender())
|
||||
.and_then(|p| ctx.db.player_hand().player_id().find(p.id))
|
||||
.and_then(|p| ctx.db.player_hand().player_id().find(p.config_id))
|
||||
}
|
||||
|
||||
#[derive(SpacetimeType, Clone)]
|
||||
pub struct HandView {
|
||||
pub player: PlayerOrBot,
|
||||
pub player_id: u32,
|
||||
pub hand_length: u8,
|
||||
// pub melds: u8,
|
||||
pub pond: Vec<DbTile>,
|
||||
|
|
@ -140,38 +143,23 @@ pub struct HandView {
|
|||
|
||||
#[view(accessor = view_closed_hands, public)]
|
||||
fn view_closed_hands(ctx: &ViewContext) -> Vec<HandView> {
|
||||
if let Some(this_player) = ctx.db.player().identity().find(ctx.sender())
|
||||
if let Some(this_player) = ctx.db.user().identity().find(ctx.sender())
|
||||
&& let Some(lobby) = ctx.db.lobby().id().find(this_player.lobby_id)
|
||||
{
|
||||
lobby
|
||||
.players
|
||||
.iter()
|
||||
.filter_map(|&player| match player {
|
||||
PlayerOrBot::Player { id } => {
|
||||
if let Some(player_hand) = ctx.db.player_hand().player_id().find(id) {
|
||||
Some(HandView {
|
||||
player,
|
||||
hand_length: player_hand.hand.len() as u8,
|
||||
pond: player_hand.pond,
|
||||
drawn: player_hand.turn_state == TurnState::Tsumo
|
||||
&& player_hand.working_tile.is_some(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
PlayerOrBot::Bot { id } => {
|
||||
if let Some(bot) = ctx.db.bot().id().find(id) {
|
||||
Some(HandView {
|
||||
player,
|
||||
hand_length: bot.hand.len() as u8,
|
||||
pond: bot.pond,
|
||||
drawn: bot.turn_state == TurnState::Tsumo && bot.working_tile.is_some(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
.filter_map(|id| {
|
||||
ctx.db
|
||||
.player_hand()
|
||||
.player_id()
|
||||
.find(id)
|
||||
.map(|hand| HandView {
|
||||
player_id: hand.player_id,
|
||||
hand_length: hand.hand.len() as u8,
|
||||
pond: hand.pond,
|
||||
drawn: hand.turn_state == TurnState::Tsumo && hand.working_tile.is_some(),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue