diff --git a/devenv.nix b/devenv.nix index 656bae8..36d9034 100644 --- a/devenv.nix +++ b/devenv.nix @@ -6,7 +6,7 @@ # https://devenv.sh/processes/ # processes.lspmux.exec = "lspmux server"; processes.spacetimedb_start.exec = "spacetime start"; - processes.spacetimedb_generate_bindings = { + processes.spacetimedb_dev = { exec = "spacetime dev --module-bindings-path jong/src/stdb jongline --delete-data=always"; # notify.enable = true; # TODO features not yet supp??? diff --git a/jong-types/src/lib.rs b/jong-types/src/lib.rs index 535a9cd..27b8fc5 100644 --- a/jong-types/src/lib.rs +++ b/jong-types/src/lib.rs @@ -130,7 +130,6 @@ pub enum TurnState { Tsumo, Menzen, RiichiKan, - Discard, RonChiiPonKan, End, } diff --git a/jong/src/game.rs b/jong/src/game.rs index 4f947ba..d22b3cc 100644 --- a/jong/src/game.rs +++ b/jong/src/game.rs @@ -8,16 +8,16 @@ use bevy_spacetimedb::{ use spacetimedb::Identity; use spacetimedb_sdk::{DbContext, Table, credentials}; -use crate::game::hand::Drawn; +use crate::stdb::{self, DbConnection, LobbyTableAccess, PlayerTableAccess, RemoteTables}; use crate::stdb::{ - self, DbConnection, LobbyTableAccess, PlayerTableAccess, RemoteTables, add_bot, draw_tile, - join_or_create_lobby, login_or_add_player, set_ready, shuffle_deal, start_game, + add_bot, draw_tile, join_or_create_lobby, login_or_add_player, set_ready, shuffle_deal, + skip_call, start_game, }; use crate::{ SpacetimeDB, creds_store, game::{ self, - hand::{Hand, Pond}, + hand::{Drawn, Hand, Pond}, player::{CurrentPlayer, MainPlayer, Player}, round::Wind, wall::Wall, @@ -227,9 +227,11 @@ fn on_lobby_insert_update( } stdb::TurnState::Menzen => todo!(), stdb::TurnState::RiichiKan => todo!(), - stdb::TurnState::Discard => todo!(), - stdb::TurnState::RonChiiPonKan => todo!(), + stdb::TurnState::RonChiiPonKan => { + stdb.reducers().skip_call().unwrap(); + } stdb::TurnState::End => todo!(), + // _ => todo!(), } next_turnstate.set(msg.new.turn_state.into()); } diff --git a/jong/src/stdb/bot_type.rs b/jong/src/stdb/bot_type.rs index 6e82cbf..23ef7d9 100644 --- a/jong/src/stdb/bot_type.rs +++ b/jong/src/stdb/bot_type.rs @@ -13,6 +13,7 @@ pub struct Bot { pub lobby_id: u32, pub hand: Vec, pub pond: Vec, + pub drawn_tile: Option, } impl __sdk::InModule for Bot { diff --git a/jong/src/stdb/lobby_type.rs b/jong/src/stdb/lobby_type.rs index ce82250..a295a91 100644 --- a/jong/src/stdb/lobby_type.rs +++ b/jong/src/stdb/lobby_type.rs @@ -14,6 +14,8 @@ pub struct Lobby { pub id: u32, pub host_player_id: u32, pub players: Vec, + pub dealer_idx: u8, + pub current_idx: u8, pub game_state: GameState, pub turn_state: TurnState, } diff --git a/jong/src/stdb/mod.rs b/jong/src/stdb/mod.rs index 61b46d3..2784d78 100644 --- a/jong/src/stdb/mod.rs +++ b/jong/src/stdb/mod.rs @@ -25,6 +25,7 @@ pub mod player_type; pub mod rank_type; pub mod set_ready_reducer; pub mod shuffle_deal_reducer; +pub mod skip_call_reducer; pub mod start_game_reducer; pub mod suit_type; pub mod tile_table; @@ -56,6 +57,7 @@ pub use player_type::Player; pub use rank_type::Rank; pub use set_ready_reducer::{set_flags_for_set_ready, set_ready, SetReadyCallbackId}; pub use shuffle_deal_reducer::{set_flags_for_shuffle_deal, shuffle_deal, ShuffleDealCallbackId}; +pub use skip_call_reducer::{set_flags_for_skip_call, skip_call, SkipCallCallbackId}; pub use start_game_reducer::{set_flags_for_start_game, start_game, StartGameCallbackId}; pub use suit_type::Suit; pub use tile_table::*; @@ -79,6 +81,7 @@ pub enum Reducer { LoginOrAddPlayer, SetReady { ready: bool }, ShuffleDeal { lobby_id: u32 }, + SkipCall, StartGame, } @@ -96,6 +99,7 @@ impl __sdk::Reducer for Reducer { Reducer::LoginOrAddPlayer => "login_or_add_player", Reducer::SetReady { .. } => "set_ready", Reducer::ShuffleDeal { .. } => "shuffle_deal", + Reducer::SkipCall => "skip_call", Reducer::StartGame => "start_game", _ => unreachable!(), } @@ -146,6 +150,13 @@ impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer { )? .into(), ), + "skip_call" => Ok( + __sdk::parse_reducer_args::( + "skip_call", + &value.args, + )? + .into(), + ), "start_game" => Ok( __sdk::parse_reducer_args::( "start_game", diff --git a/jong/src/stdb/skip_call_reducer.rs b/jong/src/stdb/skip_call_reducer.rs new file mode 100644 index 0000000..a78c70a --- /dev/null +++ b/jong/src/stdb/skip_call_reducer.rs @@ -0,0 +1,100 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub(super) struct SkipCallArgs {} + +impl From for super::Reducer { + fn from(args: SkipCallArgs) -> Self { + Self::SkipCall + } +} + +impl __sdk::InModule for SkipCallArgs { + type Module = super::RemoteModule; +} + +pub struct SkipCallCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `skip_call`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait skip_call { + /// Request that the remote module invoke the reducer `skip_call` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_skip_call`] callbacks. + fn skip_call(&self) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `skip_call`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`SkipCallCallbackId`] can be passed to [`Self::remove_on_skip_call`] + /// to cancel the callback. + fn on_skip_call( + &self, + callback: impl FnMut(&super::ReducerEventContext) + Send + 'static, + ) -> SkipCallCallbackId; + /// Cancel a callback previously registered by [`Self::on_skip_call`], + /// causing it not to run in the future. + fn remove_on_skip_call(&self, callback: SkipCallCallbackId); +} + +impl skip_call for super::RemoteReducers { + fn skip_call(&self) -> __sdk::Result<()> { + self.imp.call_reducer("skip_call", SkipCallArgs {}) + } + fn on_skip_call( + &self, + mut callback: impl FnMut(&super::ReducerEventContext) + Send + 'static, + ) -> SkipCallCallbackId { + SkipCallCallbackId(self.imp.on_reducer( + "skip_call", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::SkipCall {}, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx) + }), + )) + } + fn remove_on_skip_call(&self, callback: SkipCallCallbackId) { + self.imp.remove_on_reducer("skip_call", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `skip_call`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_skip_call { + /// Set the call-reducer flags for the reducer `skip_call` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn skip_call(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_skip_call for super::SetReducerFlags { + fn skip_call(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("skip_call", flags); + } +} diff --git a/jong/src/stdb/turn_state_type.rs b/jong/src/stdb/turn_state_type.rs index 1bbd7d5..e09ddbe 100644 --- a/jong/src/stdb/turn_state_type.rs +++ b/jong/src/stdb/turn_state_type.rs @@ -16,8 +16,6 @@ pub enum TurnState { RiichiKan, - Discard, - RonChiiPonKan, End, diff --git a/jong/src/tui.rs b/jong/src/tui.rs index e1852f4..6201947 100644 --- a/jong/src/tui.rs +++ b/jong/src/tui.rs @@ -109,13 +109,13 @@ fn discard_tile( mut writer: MessageWriter, mut selected: MessageReader, + mut commands: Commands, drawn: Single<(Entity, &TileId), With>, // curr_player: Single>, // player_hands: Populated<(&Player, &Children), With>, // hands: Populated<&Children, (With, Without)>, main_player: Single<(&Player, Entity), With>, - players: Query<(&Player, Entity, &Children)>, - hands: Query<(&Children, Entity), With>, + hands: Query<(&mut Children, Entity), With >, tiles: Populated<&TileId>, ) { // trace!("discard_tile"); @@ -127,6 +127,8 @@ fn discard_tile( { if message.0 == drawn.0 { stdb.reducers().discard_tile(drawn.1.0).unwrap(); + // commands.get_entity(drawn.0).unwrap().remove_parent_in_place(); + commands.get_entity(drawn.0).unwrap().despawn(); } else if let Some(tile_ent) = hand.iter().find(|t| *t == message.0) { stdb.reducers() .discard_tile(tiles.get(tile_ent).unwrap().0) diff --git a/justfile b/justfile index 0d4b9f7..de4df8c 100644 --- a/justfile +++ b/justfile @@ -4,12 +4,15 @@ alias rt := run-tui alias s := spacetime alias g := spacetime_generate-bindings +rrt: + just spacetime_restart_dev + sleep 3sec + just run-tui + default: just --list run-tui: - mprocs -s localhost:4050 --ctl $"({c: restart-proc, name: spacetimedb_generate_bindings} | to yaml)" - sleep 3sec cargo run -- run-tui update: @@ -24,3 +27,7 @@ spacetime_dev: spacetime_generate-bindings: spacetime generate --lang rust --out-dir jong/src/stdb --project-path spacetimedb + +spacetime_restart_dev: + mprocs -s localhost:4050 --ctl $"({c: restart-proc, name: spacetimedb_dev} | to yaml)" + diff --git a/spacetimedb/src/reducers/game.rs b/spacetimedb/src/reducers/game.rs index 4747375..69cf86c 100644 --- a/spacetimedb/src/reducers/game.rs +++ b/spacetimedb/src/reducers/game.rs @@ -1,7 +1,7 @@ use log::{info, trace}; use spacetimedb::{ReducerContext, Table, rand::seq::SliceRandom, reducer}; -use crate::tables::{player::player, wall, *}; +use crate::tables::{player::player, *}; use jong_types::*; mod deal; @@ -24,7 +24,8 @@ pub fn join_or_create_lobby(ctx: &ReducerContext, mut lobby_id: u32) -> Result<( players: vec![PlayerOrBot::Player { id: player.id }], game_state: GameState::Lobby, turn_state: TurnState::None, - // dealt_idx: -1, + dealer_idx: 0, + current_idx: 0, }); info!("created lobby: {:?}", lobby); @@ -53,6 +54,7 @@ pub fn add_bot(ctx: &ReducerContext, lobby_id: u32) -> Result<(), String> { lobby_id, hand: vec![], pond: vec![], + drawn_tile: None, }); lobby.players.push(PlayerOrBot::Bot { id: bot.id }); ctx.db.lobby().id().update(lobby); @@ -83,6 +85,10 @@ pub fn start_game(ctx: &ReducerContext) { { lobby.game_state = GameState::Setup; lobby.players.shuffle(&mut ctx.rng()); + lobby.dealer_idx += 1; + if lobby.dealer_idx > 3 { + lobby.dealer_idx = 0; + } ctx.db.lobby().id().update(lobby); } } diff --git a/spacetimedb/src/reducers/game/bot.rs b/spacetimedb/src/reducers/game/bot.rs new file mode 100644 index 0000000..e69de29 diff --git a/spacetimedb/src/reducers/game/deal.rs b/spacetimedb/src/reducers/game/deal.rs index bd871f7..acc74a7 100644 --- a/spacetimedb/src/reducers/game/deal.rs +++ b/spacetimedb/src/reducers/game/deal.rs @@ -40,44 +40,3 @@ pub fn new_shuffled_wall(ctx: &ReducerContext) -> Vec { wall } - -// TODO make sure this can't be called or just error here? -#[reducer] -pub fn discard_tile(ctx: &ReducerContext, tile_id: u32) -> Result<(), String> { - trace!("discard_tile"); - - let mut player = ctx.db.player().identity().find(ctx.sender).unwrap(); - let mut lobby = ctx.db.lobby().id().find(player.lobby_id).unwrap(); - - let tile = if let Some(drawn) = player.drawn_tile - && drawn.id == tile_id - { - player.hand.push(drawn); - // lobby.dealt_idx = -1; - - drawn - } else if let Some((i, &hand)) = player - .hand - .iter() - .enumerate() - .find(|(_, t)| t.id == tile_id) - { - player.hand.remove(i); - // lobby.dealt_idx = 0; - - hand - } else { - return Err(format!( - "player {} attempted to deal nonexistant tile {}", - player.id, tile_id - )); - }; - - player.drawn_tile = None; - lobby.turn_state = TurnState::Discard; - - ctx.db.player().id().update(player); - ctx.db.lobby().id().update(lobby); - - Ok(()) -} diff --git a/spacetimedb/src/reducers/game/hand.rs b/spacetimedb/src/reducers/game/hand.rs index 72d83e3..b1904bc 100644 --- a/spacetimedb/src/reducers/game/hand.rs +++ b/spacetimedb/src/reducers/game/hand.rs @@ -1,3 +1,4 @@ +use log::{debug, info, trace}; use spacetimedb::{ReducerContext, Table, ViewContext, reducer, view}; use crate::tables::{player::player, *}; @@ -39,6 +40,79 @@ pub fn draw_tile(ctx: &ReducerContext) { ctx.db.player().id().update(player); } +// 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 mut player = ctx.db.player().identity().find(ctx.sender).unwrap(); + let mut lobby = ctx.db.lobby().id().find(player.lobby_id).unwrap(); + + let dealt_tile = if let Some(drawn) = player.drawn_tile + && drawn.id == tile_id + { + drawn + } else if let Some((i, _)) = player + .hand + .iter() + .enumerate() + .find(|(_, t)| t.id == tile_id) + { + player.hand.remove(i) + } else { + return Err(format!( + "player {} attempted to deal nonexistant tile {}", + player.id, tile_id + )); + }; + + player.pond.push(dealt_tile); + player.drawn_tile = None; + lobby.turn_state = TurnState::RonChiiPonKan; + + let player = ctx.db.player().id().update(player); + ctx.db.lobby().id().update(lobby); + + debug!("player {} discarded tile {:?}", player.id, dealt_tile.tile); + + Ok(()) +} + +#[reducer] +pub fn skip_call(ctx: &ReducerContext) { + trace!("skip_call"); + + let mut player = ctx.db.player().identity().find(ctx.sender).unwrap(); + let mut lobby = ctx.db.lobby().id().find(player.lobby_id).unwrap(); + + lobby.turn_state = TurnState::Tsumo; + lobby.current_idx += 1; + if lobby.current_idx >= 3 { + lobby.current_idx = 0; + } + + // FIXME where better can this go + bot_moves(ctx, &mut lobby); + + ctx.db.player().id().update(player); + ctx.db.lobby().id().update(lobby); +} + +fn bot_moves(ctx: &ReducerContext, lobby: &mut Lobby) { + let mut wall = ctx.db.wall().lobby_id().find(lobby.id).unwrap(); + if let Some(PlayerOrBot::Bot { id }) = lobby.players.get(lobby.current_idx as usize + 1) { + let mut bot = ctx.db.bot().id().find(id).unwrap(); + bot.pond.push(wall.tiles.pop().unwrap()); + ctx.db.bot().id().update(bot); + lobby.turn_state = TurnState::RonChiiPonKan; + } else { + lobby.turn_state = TurnState::Tsumo; + } + + lobby.current_idx += 1; + if lobby.current_idx >= 3 { + lobby.current_idx = 0; + } +} + // #[view(name = view_player_hand, public)] // pub fn view_player_hand(ctx: &ViewContext) -> Option { // ctx.db diff --git a/spacetimedb/src/tables.rs b/spacetimedb/src/tables.rs index 890eb1e..6288970 100644 --- a/spacetimedb/src/tables.rs +++ b/spacetimedb/src/tables.rs @@ -15,11 +15,11 @@ pub struct Lobby { #[unique] pub host_player_id: u32, pub players: Vec, + pub dealer_idx: u8, + pub current_idx: u8, pub game_state: GameState, pub turn_state: TurnState, - - // pub dealt_idx: i8, } #[table(name = wall)] diff --git a/spacetimedb/src/tables/player.rs b/spacetimedb/src/tables/player.rs index f0b2f8a..1767758 100644 --- a/spacetimedb/src/tables/player.rs +++ b/spacetimedb/src/tables/player.rs @@ -39,6 +39,8 @@ pub struct Bot { pub hand: Vec, pub pond: Vec, + + pub drawn_tile: Option, } #[derive(Debug, Clone, SpacetimeType)]