From d0c79377aa6b445d41cf430a6b7f34ce7faf9560 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:21:23 -0800 Subject: [PATCH] squash --- jong/src/game.rs | 152 +++++++++++++----- jong/src/stdb/bot_hand_type.rs | 18 +++ jong/src/stdb/bothand_table.rs | 143 ++++++++++++++++ jong/src/stdb/hand_table.rs | 22 +-- jong/src/stdb/hand_type.rs | 2 +- jong/src/stdb/join_or_create_lobby_reducer.rs | 20 +-- jong/src/stdb/lobby_setup_reducer.rs | 100 ++++++++++++ jong/src/stdb/lobby_table.rs | 22 +-- jong/src/stdb/lobby_type.rs | 2 +- jong/src/stdb/mod.rs | 60 ++++--- jong/src/stdb/setup_game_reducer.rs | 105 ++++++++++++ jong/src/stdb/view_hand_table.rs | 96 +++++++++++ jong/src/stdb/view_player_hand_table.rs | 96 +++++++++++ jong/src/stdb/wall_table.rs | 31 ---- jong/src/stdb/wall_type.rs | 1 - spacetimedb/src/lib.rs | 103 +++++++++--- 16 files changed, 824 insertions(+), 149 deletions(-) create mode 100644 jong/src/stdb/bot_hand_type.rs create mode 100644 jong/src/stdb/bothand_table.rs create mode 100644 jong/src/stdb/lobby_setup_reducer.rs create mode 100644 jong/src/stdb/setup_game_reducer.rs create mode 100644 jong/src/stdb/view_hand_table.rs create mode 100644 jong/src/stdb/view_player_hand_table.rs diff --git a/jong/src/game.rs b/jong/src/game.rs index 05679a2..6b6b470 100644 --- a/jong/src/game.rs +++ b/jong/src/game.rs @@ -6,13 +6,13 @@ use bevy_spacetimedb::{ TableMessages, }; use spacetimedb::Identity; -use spacetimedb_sdk::credentials; +use spacetimedb_sdk::{DbContext, credentials}; use crate::{ SpacetimeDB, creds_store, stdb::{ - self, DbConnection, LobbyTableAccess, PlayerTableAccess, RemoteTables, add_bot, - join_or_create_lobby, login_or_add_player, + self, DbConnection, HandTableAccess, LobbyTableAccess, PlayerTableAccess, RemoteTables, + add_bot, join_or_create_lobby, login_or_add_player, setup_game, }, }; use crate::{ @@ -45,8 +45,14 @@ impl Plugin for Riichi { .with_uri("http://localhost:3000") .with_module_name("jongline") .with_run_fn(DbConnection::run_threaded) - // TODO change to partial no update or a subscription? - .add_table(RemoteTables::player); + + // TODO do these need to be subscription & vice-versa? + .add_table(RemoteTables::player) + .add_table(RemoteTables::lobby) + .add_table(RemoteTables::hand) + + // semicolon stopper + ; let plugins = if let Some(token) = creds_store().load().expect("i/o error loading credentials") { // FIXME patch plugin so this takes Option? @@ -73,22 +79,31 @@ impl Plugin for Riichi { // .add_systems(Update, round::ron_chi_pon_kan.run_if(in_state(TurnState::RonChiiPonKan)).after(round::notify_callable)) // .add_systems(OnEnter(TurnState::End), round::end) // stdb + .init_resource::() .add_systems(Update, on_connect) .add_systems(Update, on_disconnect) .add_systems(Update, on_player_insert_update) - // .add_systems(Update, on_lobby_insert_update) + .add_systems(Update, on_lobby_insert_update) .add_systems(OnEnter(GameState::Setup), join_or_create_lobby) - .add_systems(OnEnter(GameState::Deal), ||todo!()) + .add_systems(Update, (view_hand).run_if(in_state(GameState::Play))) // semicolon stopper ; } } +fn view_hand(stdb: SpacetimeDB) { + // stdb.db() + // .hand() + // .player_identity() + // .find(&stdb.identity()) + // .unwrap(); +} + #[derive(Resource, Deref)] struct Player(stdb::Player); // TODO or reconnect? -fn on_connect(stdb: SpacetimeDB, mut messages: ReadStdbConnectedMessage) { +fn on_connect(stdb: SpacetimeDB, mut messages: ReadStdbConnectedMessage, mut commands: Commands) { for msg in messages.read() { info!("you're now jongline"); debug!("with identity: {}", stdb.identity()); @@ -96,8 +111,15 @@ fn on_connect(stdb: SpacetimeDB, mut messages: ReadStdbConnectedMessage) { .save(&msg.access_token) .expect("i/o error saving token"); - // FIXME delet this in future - stdb.subscription_builder().subscribe_to_all_tables(); + stdb.subscription_builder() + .on_applied(|ctx| { + trace!("subbed to player table"); + }) + .on_error(|_, err| error!("sub to player failed: {err}")) + .subscribe(format!( + "SELECT * FROM player p WHERE p.identity = '{}'", + stdb.identity() + )); } } @@ -110,48 +132,100 @@ fn on_disconnect(stdb: SpacetimeDB, mut messages: ReadStdbDisconnectedMessage) { fn on_player_insert_update( stdb: SpacetimeDB, mut messages: ReadInsertUpdateMessage, + mut commands: Commands, - player: Option>, ) { for msg in messages.read() { - if player.is_some() { + debug!( + "player_insert_update msg:\n{:#?} -> \n{:#?}", + msg.old, msg.new + ); + + if msg.new.identity == stdb.identity() { + commands.insert_resource(Player(msg.new.clone())); + // TODO add bots elsewhere if msg.new.lobby_id != 0 { info!("joined lobby {}", msg.new.lobby_id); for _ in 0..3 { stdb.reducers().add_bot(msg.new.lobby_id).unwrap() } } - } else if let Some(player) = stdb.db().player().identity().find(&stdb.identity()) { - commands.insert_resource(Player(player)); - trace!("logged in"); - } else { - debug!( - "received player insert update msg: {:?} -> {:?}", - msg.old, msg.new - ) } } } -fn join_or_create_lobby( - stdb: SpacetimeDB, - player: Res, - mut next_gamestate: ResMut>, -) { - if player.lobby_id == 0 { - stdb.reducers().join_or_create_lobby(None).unwrap(); - let sub = stdb - .subscription_builder() - .on_applied(|ctx| trace!("subbed to lobby table")) - .on_error(|_, err| error!("sub to lobby table failed: {}", err)) - // JOIN player p ON l.host_id = p.id - .subscribe([format!( - "SELECT l.* +fn join_or_create_lobby(stdb: SpacetimeDB, player: Res, mut lobby_id: ResMut) { + let sub = stdb + .subscription_builder() + .on_applied(|_| { + trace!("subbed to lobby table"); + }) + .on_error(|_, err| error!("sub to lobby table failed: {err}")) + .subscribe([format!( + "SELECT l.* FROM lobby l - WHERE l.host_id = {}", - player.id - )]); - } else { - debug!("already in lobby") + WHERE l.host_player_id = {}", + player.id + )]); + + let mut player = player.clone(); + + if player.lobby_id == 0 { + stdb.reducers().join_or_create_lobby(0).unwrap(); + player.lobby_id = stdb + .db() + .lobby() + .host_player_id() + .find(&player.id) + .unwrap_or_else(|| panic!("can't find player with id {}", player.id)) + .id; + } + + if let Some(lobby) = stdb.db().lobby().id().find(&player.lobby_id) + && lobby.host_player_id == player.id + && matches!(lobby.game_state.into(), GameState::None) + { + debug!("setup_game({})", lobby.id); + stdb.reducers().setup_game(lobby.id).unwrap() + } + + *lobby_id = LobbyId(player.lobby_id); + stdb.reducers().setup_game(**lobby_id).unwrap(); + + stdb.subscription_builder() + .on_applied(|_| trace!("subbed to view_hand")) + .subscribe("SELECT * FROM view_hand"); +} + +impl From for GameState { + fn from(value: stdb::GameState) -> Self { + match value { + stdb::GameState::None => Self::None, + stdb::GameState::Setup => Self::Setup, + stdb::GameState::Deal => Self::Deal, + stdb::GameState::Play => Self::Play, + stdb::GameState::Exit => Self::Exit, + } + } +} + +#[derive(Resource, Default, Deref)] +struct LobbyId(u32); + +fn on_lobby_insert_update( + stdb: SpacetimeDB, + mut messages: ReadInsertUpdateMessage, + + mut commands: Commands, + mut next_gamestate: ResMut>, + + lobby_id: Res, +) { + for msg in messages.read() { + // TODO should this be an assert? + if msg.new.id == **lobby_id { + debug!("lobby: {:?}", msg.new); + next_gamestate.set(msg.new.game_state.into()); + } } } diff --git a/jong/src/stdb/bot_hand_type.rs b/jong/src/stdb/bot_hand_type.rs new file mode 100644 index 0000000..d2aeefb --- /dev/null +++ b/jong/src/stdb/bot_hand_type.rs @@ -0,0 +1,18 @@ +// 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}; + +use super::tile_type::Tile; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct BotHand { + pub bot_id: u32, + pub tiles: Vec, +} + +impl __sdk::InModule for BotHand { + type Module = super::RemoteModule; +} diff --git a/jong/src/stdb/bothand_table.rs b/jong/src/stdb/bothand_table.rs new file mode 100644 index 0000000..b2307c4 --- /dev/null +++ b/jong/src/stdb/bothand_table.rs @@ -0,0 +1,143 @@ +// 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 super::bot_hand_type::BotHand; +use super::tile_type::Tile; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `bothand`. +/// +/// Obtain a handle from the [`BothandTableAccess::bothand`] method on [`super::RemoteTables`], +/// like `ctx.db.bothand()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.bothand().on_insert(...)`. +pub struct BothandTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `bothand`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait BothandTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`BothandTableHandle`], which mediates access to the table `bothand`. + fn bothand(&self) -> BothandTableHandle<'_>; +} + +impl BothandTableAccess for super::RemoteTables { + fn bothand(&self) -> BothandTableHandle<'_> { + BothandTableHandle { + imp: self.imp.get_table::("bothand"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct BothandInsertCallbackId(__sdk::CallbackId); +pub struct BothandDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for BothandTableHandle<'ctx> { + type Row = BotHand; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = BothandInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> BothandInsertCallbackId { + BothandInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: BothandInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = BothandDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> BothandDeleteCallbackId { + BothandDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: BothandDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("bothand"); + _table.add_unique_constraint::("bot_id", |row| &row.bot_id); +} +pub struct BothandUpdateCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::TableWithPrimaryKey for BothandTableHandle<'ctx> { + type UpdateCallbackId = BothandUpdateCallbackId; + + fn on_update( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static, + ) -> BothandUpdateCallbackId { + BothandUpdateCallbackId(self.imp.on_update(Box::new(callback))) + } + + fn remove_on_update(&self, callback: BothandUpdateCallbackId) { + self.imp.remove_on_update(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} + +/// Access to the `bot_id` unique index on the table `bothand`, +/// which allows point queries on the field of the same name +/// via the [`BothandBotIdUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.bothand().bot_id().find(...)`. +pub struct BothandBotIdUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> BothandTableHandle<'ctx> { + /// Get a handle on the `bot_id` unique index on the table `bothand`. + pub fn bot_id(&self) -> BothandBotIdUnique<'ctx> { + BothandBotIdUnique { + imp: self.imp.get_unique_constraint::("bot_id"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> BothandBotIdUnique<'ctx> { + /// Find the subscribed row whose `bot_id` column value is equal to `col_val`, + /// if such a row is present in the client cache. + pub fn find(&self, col_val: &u32) -> Option { + self.imp.find(col_val) + } +} diff --git a/jong/src/stdb/hand_table.rs b/jong/src/stdb/hand_table.rs index 905ed76..e72e3a0 100644 --- a/jong/src/stdb/hand_table.rs +++ b/jong/src/stdb/hand_table.rs @@ -82,7 +82,7 @@ impl<'ctx> __sdk::Table for HandTableHandle<'ctx> { #[doc(hidden)] pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { let _table = client_cache.get_or_make_table::("hand"); - _table.add_unique_constraint::<__sdk::Identity>("player_ident", |row| &row.player_ident); + _table.add_unique_constraint::<__sdk::Identity>("player_identity", |row| &row.player_identity); } pub struct HandUpdateCallbackId(__sdk::CallbackId); @@ -112,32 +112,32 @@ pub(super) fn parse_table_update( }) } -/// Access to the `player_ident` unique index on the table `hand`, +/// Access to the `player_identity` unique index on the table `hand`, /// which allows point queries on the field of the same name -/// via the [`HandPlayerIdentUnique::find`] method. +/// via the [`HandPlayerIdentityUnique::find`] method. /// /// Users are encouraged not to explicitly reference this type, /// but to directly chain method calls, -/// like `ctx.db.hand().player_ident().find(...)`. -pub struct HandPlayerIdentUnique<'ctx> { +/// like `ctx.db.hand().player_identity().find(...)`. +pub struct HandPlayerIdentityUnique<'ctx> { imp: __sdk::UniqueConstraintHandle, phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, } impl<'ctx> HandTableHandle<'ctx> { - /// Get a handle on the `player_ident` unique index on the table `hand`. - pub fn player_ident(&self) -> HandPlayerIdentUnique<'ctx> { - HandPlayerIdentUnique { + /// Get a handle on the `player_identity` unique index on the table `hand`. + pub fn player_identity(&self) -> HandPlayerIdentityUnique<'ctx> { + HandPlayerIdentityUnique { imp: self .imp - .get_unique_constraint::<__sdk::Identity>("player_ident"), + .get_unique_constraint::<__sdk::Identity>("player_identity"), phantom: std::marker::PhantomData, } } } -impl<'ctx> HandPlayerIdentUnique<'ctx> { - /// Find the subscribed row whose `player_ident` column value is equal to `col_val`, +impl<'ctx> HandPlayerIdentityUnique<'ctx> { + /// Find the subscribed row whose `player_identity` column value is equal to `col_val`, /// if such a row is present in the client cache. pub fn find(&self, col_val: &__sdk::Identity) -> Option { self.imp.find(col_val) diff --git a/jong/src/stdb/hand_type.rs b/jong/src/stdb/hand_type.rs index f12c3c9..427b7c6 100644 --- a/jong/src/stdb/hand_type.rs +++ b/jong/src/stdb/hand_type.rs @@ -9,7 +9,7 @@ use super::tile_type::Tile; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct Hand { - pub player_ident: __sdk::Identity, + pub player_identity: __sdk::Identity, pub tiles: Vec, } diff --git a/jong/src/stdb/join_or_create_lobby_reducer.rs b/jong/src/stdb/join_or_create_lobby_reducer.rs index 594b092..2545903 100644 --- a/jong/src/stdb/join_or_create_lobby_reducer.rs +++ b/jong/src/stdb/join_or_create_lobby_reducer.rs @@ -7,12 +7,14 @@ 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 JoinOrCreateLobbyArgs { - pub lobby: Option, + pub lobby_id: u32, } impl From for super::Reducer { fn from(args: JoinOrCreateLobbyArgs) -> Self { - Self::JoinOrCreateLobby { lobby: args.lobby } + Self::JoinOrCreateLobby { + lobby_id: args.lobby_id, + } } } @@ -32,7 +34,7 @@ pub trait join_or_create_lobby { /// 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_join_or_create_lobby`] callbacks. - fn join_or_create_lobby(&self, lobby: Option) -> __sdk::Result<()>; + fn join_or_create_lobby(&self, lobby_id: u32) -> __sdk::Result<()>; /// Register a callback to run whenever we are notified of an invocation of the reducer `join_or_create_lobby`. /// /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] @@ -42,7 +44,7 @@ pub trait join_or_create_lobby { /// to cancel the callback. fn on_join_or_create_lobby( &self, - callback: impl FnMut(&super::ReducerEventContext, &Option) + Send + 'static, + callback: impl FnMut(&super::ReducerEventContext, &u32) + Send + 'static, ) -> JoinOrCreateLobbyCallbackId; /// Cancel a callback previously registered by [`Self::on_join_or_create_lobby`], /// causing it not to run in the future. @@ -50,13 +52,13 @@ pub trait join_or_create_lobby { } impl join_or_create_lobby for super::RemoteReducers { - fn join_or_create_lobby(&self, lobby: Option) -> __sdk::Result<()> { + fn join_or_create_lobby(&self, lobby_id: u32) -> __sdk::Result<()> { self.imp - .call_reducer("join_or_create_lobby", JoinOrCreateLobbyArgs { lobby }) + .call_reducer("join_or_create_lobby", JoinOrCreateLobbyArgs { lobby_id }) } fn on_join_or_create_lobby( &self, - mut callback: impl FnMut(&super::ReducerEventContext, &Option) + Send + 'static, + mut callback: impl FnMut(&super::ReducerEventContext, &u32) + Send + 'static, ) -> JoinOrCreateLobbyCallbackId { JoinOrCreateLobbyCallbackId(self.imp.on_reducer( "join_or_create_lobby", @@ -65,7 +67,7 @@ impl join_or_create_lobby for super::RemoteReducers { let super::ReducerEventContext { event: __sdk::ReducerEvent { - reducer: super::Reducer::JoinOrCreateLobby { lobby }, + reducer: super::Reducer::JoinOrCreateLobby { lobby_id }, .. }, .. @@ -73,7 +75,7 @@ impl join_or_create_lobby for super::RemoteReducers { else { unreachable!() }; - callback(ctx, lobby) + callback(ctx, lobby_id) }), )) } diff --git a/jong/src/stdb/lobby_setup_reducer.rs b/jong/src/stdb/lobby_setup_reducer.rs new file mode 100644 index 0000000..c54de7d --- /dev/null +++ b/jong/src/stdb/lobby_setup_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 LobbySetupArgs {} + +impl From for super::Reducer { + fn from(args: LobbySetupArgs) -> Self { + Self::LobbySetup + } +} + +impl __sdk::InModule for LobbySetupArgs { + type Module = super::RemoteModule; +} + +pub struct LobbySetupCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `lobby_setup`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait lobby_setup { + /// Request that the remote module invoke the reducer `lobby_setup` 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_lobby_setup`] callbacks. + fn lobby_setup(&self) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `lobby_setup`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`LobbySetupCallbackId`] can be passed to [`Self::remove_on_lobby_setup`] + /// to cancel the callback. + fn on_lobby_setup( + &self, + callback: impl FnMut(&super::ReducerEventContext) + Send + 'static, + ) -> LobbySetupCallbackId; + /// Cancel a callback previously registered by [`Self::on_lobby_setup`], + /// causing it not to run in the future. + fn remove_on_lobby_setup(&self, callback: LobbySetupCallbackId); +} + +impl lobby_setup for super::RemoteReducers { + fn lobby_setup(&self) -> __sdk::Result<()> { + self.imp.call_reducer("lobby_setup", LobbySetupArgs {}) + } + fn on_lobby_setup( + &self, + mut callback: impl FnMut(&super::ReducerEventContext) + Send + 'static, + ) -> LobbySetupCallbackId { + LobbySetupCallbackId(self.imp.on_reducer( + "lobby_setup", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::LobbySetup {}, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx) + }), + )) + } + fn remove_on_lobby_setup(&self, callback: LobbySetupCallbackId) { + self.imp.remove_on_reducer("lobby_setup", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `lobby_setup`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_lobby_setup { + /// Set the call-reducer flags for the reducer `lobby_setup` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn lobby_setup(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_lobby_setup for super::SetReducerFlags { + fn lobby_setup(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("lobby_setup", flags); + } +} diff --git a/jong/src/stdb/lobby_table.rs b/jong/src/stdb/lobby_table.rs index 45ee487..4249c91 100644 --- a/jong/src/stdb/lobby_table.rs +++ b/jong/src/stdb/lobby_table.rs @@ -83,7 +83,7 @@ impl<'ctx> __sdk::Table for LobbyTableHandle<'ctx> { pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { let _table = client_cache.get_or_make_table::("lobby"); _table.add_unique_constraint::("id", |row| &row.id); - _table.add_unique_constraint::("host_id", |row| &row.host_id); + _table.add_unique_constraint::("host_player_id", |row| &row.host_player_id); } pub struct LobbyUpdateCallbackId(__sdk::CallbackId); @@ -143,30 +143,30 @@ impl<'ctx> LobbyIdUnique<'ctx> { } } -/// Access to the `host_id` unique index on the table `lobby`, +/// Access to the `host_player_id` unique index on the table `lobby`, /// which allows point queries on the field of the same name -/// via the [`LobbyHostIdUnique::find`] method. +/// via the [`LobbyHostPlayerIdUnique::find`] method. /// /// Users are encouraged not to explicitly reference this type, /// but to directly chain method calls, -/// like `ctx.db.lobby().host_id().find(...)`. -pub struct LobbyHostIdUnique<'ctx> { +/// like `ctx.db.lobby().host_player_id().find(...)`. +pub struct LobbyHostPlayerIdUnique<'ctx> { imp: __sdk::UniqueConstraintHandle, phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, } impl<'ctx> LobbyTableHandle<'ctx> { - /// Get a handle on the `host_id` unique index on the table `lobby`. - pub fn host_id(&self) -> LobbyHostIdUnique<'ctx> { - LobbyHostIdUnique { - imp: self.imp.get_unique_constraint::("host_id"), + /// Get a handle on the `host_player_id` unique index on the table `lobby`. + pub fn host_player_id(&self) -> LobbyHostPlayerIdUnique<'ctx> { + LobbyHostPlayerIdUnique { + imp: self.imp.get_unique_constraint::("host_player_id"), phantom: std::marker::PhantomData, } } } -impl<'ctx> LobbyHostIdUnique<'ctx> { - /// Find the subscribed row whose `host_id` column value is equal to `col_val`, +impl<'ctx> LobbyHostPlayerIdUnique<'ctx> { + /// Find the subscribed row whose `host_player_id` column value is equal to `col_val`, /// if such a row is present in the client cache. pub fn find(&self, col_val: &u32) -> Option { self.imp.find(col_val) diff --git a/jong/src/stdb/lobby_type.rs b/jong/src/stdb/lobby_type.rs index 3b32b90..8d7b094 100644 --- a/jong/src/stdb/lobby_type.rs +++ b/jong/src/stdb/lobby_type.rs @@ -10,7 +10,7 @@ use super::game_state_type::GameState; #[sats(crate = __lib)] pub struct Lobby { pub id: u32, - pub host_id: u32, + pub host_player_id: u32, pub game_state: GameState, } diff --git a/jong/src/stdb/mod.rs b/jong/src/stdb/mod.rs index 68ddd4a..888635d 100644 --- a/jong/src/stdb/mod.rs +++ b/jong/src/stdb/mod.rs @@ -7,9 +7,10 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; pub mod add_bot_reducer; +pub mod bot_hand_type; pub mod bot_table; pub mod bot_type; -pub mod deal_hands_reducer; +pub mod bothand_table; pub mod dragon_type; pub mod game_state_type; pub mod hand_table; @@ -23,18 +24,20 @@ pub mod player_type; pub mod pond_table; pub mod pond_type; pub mod rank_type; -pub mod shuffle_wall_reducer; +pub mod setup_game_reducer; pub mod sort_hand_reducer; pub mod suit_type; pub mod tile_type; +pub mod view_player_hand_table; pub mod wall_table; pub mod wall_type; pub mod wind_type; pub use add_bot_reducer::{add_bot, set_flags_for_add_bot, AddBotCallbackId}; +pub use bot_hand_type::BotHand; pub use bot_table::*; pub use bot_type::Bot; -pub use deal_hands_reducer::{deal_hands, set_flags_for_deal_hands, DealHandsCallbackId}; +pub use bothand_table::*; pub use dragon_type::Dragon; pub use game_state_type::GameState; pub use hand_table::*; @@ -52,10 +55,11 @@ pub use player_type::Player; pub use pond_table::*; pub use pond_type::Pond; pub use rank_type::Rank; -pub use shuffle_wall_reducer::{set_flags_for_shuffle_wall, shuffle_wall, ShuffleWallCallbackId}; +pub use setup_game_reducer::{set_flags_for_setup_game, setup_game, SetupGameCallbackId}; pub use sort_hand_reducer::{set_flags_for_sort_hand, sort_hand, SortHandCallbackId}; pub use suit_type::Suit; pub use tile_type::Tile; +pub use view_player_hand_table::*; pub use wall_table::*; pub use wall_type::Wall; pub use wind_type::Wind; @@ -69,10 +73,9 @@ pub use wind_type::Wind; pub enum Reducer { AddBot { lobby_id: u32 }, - DealHands, - JoinOrCreateLobby { lobby: Option }, + JoinOrCreateLobby { lobby_id: u32 }, LoginOrAddPlayer, - ShuffleWall, + SetupGame { lobby_id: u32 }, SortHand, } @@ -84,10 +87,9 @@ impl __sdk::Reducer for Reducer { fn reducer_name(&self) -> &'static str { match self { Reducer::AddBot { .. } => "add_bot", - Reducer::DealHands => "deal_hands", Reducer::JoinOrCreateLobby { .. } => "join_or_create_lobby", Reducer::LoginOrAddPlayer => "login_or_add_player", - Reducer::ShuffleWall => "shuffle_wall", + Reducer::SetupGame { .. } => "setup_game", Reducer::SortHand => "sort_hand", _ => unreachable!(), } @@ -102,13 +104,6 @@ impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer { &value.args, )? .into()), - "deal_hands" => Ok( - __sdk::parse_reducer_args::( - "deal_hands", - &value.args, - )? - .into(), - ), "join_or_create_lobby" => Ok(__sdk::parse_reducer_args::< join_or_create_lobby_reducer::JoinOrCreateLobbyArgs, >("join_or_create_lobby", &value.args)? @@ -117,9 +112,9 @@ impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer { login_or_add_player_reducer::LoginOrAddPlayerArgs, >("login_or_add_player", &value.args)? .into()), - "shuffle_wall" => Ok( - __sdk::parse_reducer_args::( - "shuffle_wall", + "setup_game" => Ok( + __sdk::parse_reducer_args::( + "setup_game", &value.args, )? .into(), @@ -146,10 +141,12 @@ impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer { #[doc(hidden)] pub struct DbUpdate { bot: __sdk::TableUpdate, + bothand: __sdk::TableUpdate, hand: __sdk::TableUpdate, lobby: __sdk::TableUpdate, player: __sdk::TableUpdate, pond: __sdk::TableUpdate, + view_player_hand: __sdk::TableUpdate, wall: __sdk::TableUpdate, } @@ -162,6 +159,9 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate { "bot" => db_update .bot .append(bot_table::parse_table_update(table_update)?), + "bothand" => db_update + .bothand + .append(bothand_table::parse_table_update(table_update)?), "hand" => db_update .hand .append(hand_table::parse_table_update(table_update)?), @@ -174,6 +174,9 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate { "pond" => db_update .pond .append(pond_table::parse_table_update(table_update)?), + "view_player_hand" => db_update + .view_player_hand + .append(view_player_hand_table::parse_table_update(table_update)?), "wall" => db_update .wall .append(wall_table::parse_table_update(table_update)?), @@ -206,9 +209,12 @@ impl __sdk::DbUpdate for DbUpdate { diff.bot = cache .apply_diff_to_table::("bot", &self.bot) .with_updates_by_pk(|row| &row.id); + diff.bothand = cache + .apply_diff_to_table::("bothand", &self.bothand) + .with_updates_by_pk(|row| &row.bot_id); diff.hand = cache .apply_diff_to_table::("hand", &self.hand) - .with_updates_by_pk(|row| &row.player_ident); + .with_updates_by_pk(|row| &row.player_identity); diff.lobby = cache .apply_diff_to_table::("lobby", &self.lobby) .with_updates_by_pk(|row| &row.id); @@ -218,7 +224,9 @@ impl __sdk::DbUpdate for DbUpdate { diff.pond = cache.apply_diff_to_table::("pond", &self.pond); diff.wall = cache .apply_diff_to_table::("wall", &self.wall) - .with_updates_by_pk(|row| &row.id); + .with_updates_by_pk(|row| &row.lobby_id); + diff.view_player_hand = + cache.apply_diff_to_table::("view_player_hand", &self.view_player_hand); diff } @@ -229,10 +237,12 @@ impl __sdk::DbUpdate for DbUpdate { #[doc(hidden)] pub struct AppliedDiff<'r> { bot: __sdk::TableAppliedDiff<'r, Bot>, + bothand: __sdk::TableAppliedDiff<'r, BotHand>, hand: __sdk::TableAppliedDiff<'r, Hand>, lobby: __sdk::TableAppliedDiff<'r, Lobby>, player: __sdk::TableAppliedDiff<'r, Player>, pond: __sdk::TableAppliedDiff<'r, Pond>, + view_player_hand: __sdk::TableAppliedDiff<'r, Hand>, wall: __sdk::TableAppliedDiff<'r, Wall>, __unused: std::marker::PhantomData<&'r ()>, } @@ -248,10 +258,16 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { callbacks: &mut __sdk::DbCallbacks, ) { callbacks.invoke_table_row_callbacks::("bot", &self.bot, event); + callbacks.invoke_table_row_callbacks::("bothand", &self.bothand, event); callbacks.invoke_table_row_callbacks::("hand", &self.hand, event); callbacks.invoke_table_row_callbacks::("lobby", &self.lobby, event); callbacks.invoke_table_row_callbacks::("player", &self.player, event); callbacks.invoke_table_row_callbacks::("pond", &self.pond, event); + callbacks.invoke_table_row_callbacks::( + "view_player_hand", + &self.view_player_hand, + event, + ); callbacks.invoke_table_row_callbacks::("wall", &self.wall, event); } } @@ -973,10 +989,12 @@ impl __sdk::SpacetimeModule for RemoteModule { fn register_tables(client_cache: &mut __sdk::ClientCache) { bot_table::register_table(client_cache); + bothand_table::register_table(client_cache); hand_table::register_table(client_cache); lobby_table::register_table(client_cache); player_table::register_table(client_cache); pond_table::register_table(client_cache); + view_player_hand_table::register_table(client_cache); wall_table::register_table(client_cache); } } diff --git a/jong/src/stdb/setup_game_reducer.rs b/jong/src/stdb/setup_game_reducer.rs new file mode 100644 index 0000000..3380b89 --- /dev/null +++ b/jong/src/stdb/setup_game_reducer.rs @@ -0,0 +1,105 @@ +// 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 SetupGameArgs { + pub lobby_id: u32, +} + +impl From for super::Reducer { + fn from(args: SetupGameArgs) -> Self { + Self::SetupGame { + lobby_id: args.lobby_id, + } + } +} + +impl __sdk::InModule for SetupGameArgs { + type Module = super::RemoteModule; +} + +pub struct SetupGameCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `setup_game`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait setup_game { + /// Request that the remote module invoke the reducer `setup_game` 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_setup_game`] callbacks. + fn setup_game(&self, lobby_id: u32) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `setup_game`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`SetupGameCallbackId`] can be passed to [`Self::remove_on_setup_game`] + /// to cancel the callback. + fn on_setup_game( + &self, + callback: impl FnMut(&super::ReducerEventContext, &u32) + Send + 'static, + ) -> SetupGameCallbackId; + /// Cancel a callback previously registered by [`Self::on_setup_game`], + /// causing it not to run in the future. + fn remove_on_setup_game(&self, callback: SetupGameCallbackId); +} + +impl setup_game for super::RemoteReducers { + fn setup_game(&self, lobby_id: u32) -> __sdk::Result<()> { + self.imp + .call_reducer("setup_game", SetupGameArgs { lobby_id }) + } + fn on_setup_game( + &self, + mut callback: impl FnMut(&super::ReducerEventContext, &u32) + Send + 'static, + ) -> SetupGameCallbackId { + SetupGameCallbackId(self.imp.on_reducer( + "setup_game", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::SetupGame { lobby_id }, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx, lobby_id) + }), + )) + } + fn remove_on_setup_game(&self, callback: SetupGameCallbackId) { + self.imp.remove_on_reducer("setup_game", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `setup_game`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_setup_game { + /// Set the call-reducer flags for the reducer `setup_game` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn setup_game(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_setup_game for super::SetReducerFlags { + fn setup_game(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("setup_game", flags); + } +} diff --git a/jong/src/stdb/view_hand_table.rs b/jong/src/stdb/view_hand_table.rs new file mode 100644 index 0000000..a9927c9 --- /dev/null +++ b/jong/src/stdb/view_hand_table.rs @@ -0,0 +1,96 @@ +// 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 super::hand_type::Hand; +use super::tile_type::Tile; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `view_hand`. +/// +/// Obtain a handle from the [`ViewHandTableAccess::view_hand`] method on [`super::RemoteTables`], +/// like `ctx.db.view_hand()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.view_hand().on_insert(...)`. +pub struct ViewHandTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `view_hand`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ViewHandTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ViewHandTableHandle`], which mediates access to the table `view_hand`. + fn view_hand(&self) -> ViewHandTableHandle<'_>; +} + +impl ViewHandTableAccess for super::RemoteTables { + fn view_hand(&self) -> ViewHandTableHandle<'_> { + ViewHandTableHandle { + imp: self.imp.get_table::("view_hand"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ViewHandInsertCallbackId(__sdk::CallbackId); +pub struct ViewHandDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for ViewHandTableHandle<'ctx> { + type Row = Hand; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ViewHandInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ViewHandInsertCallbackId { + ViewHandInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ViewHandInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ViewHandDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ViewHandDeleteCallbackId { + ViewHandDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ViewHandDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("view_hand"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} diff --git a/jong/src/stdb/view_player_hand_table.rs b/jong/src/stdb/view_player_hand_table.rs new file mode 100644 index 0000000..37429bd --- /dev/null +++ b/jong/src/stdb/view_player_hand_table.rs @@ -0,0 +1,96 @@ +// 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 super::hand_type::Hand; +use super::tile_type::Tile; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `view_player_hand`. +/// +/// Obtain a handle from the [`ViewPlayerHandTableAccess::view_player_hand`] method on [`super::RemoteTables`], +/// like `ctx.db.view_player_hand()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.view_player_hand().on_insert(...)`. +pub struct ViewPlayerHandTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `view_player_hand`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ViewPlayerHandTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ViewPlayerHandTableHandle`], which mediates access to the table `view_player_hand`. + fn view_player_hand(&self) -> ViewPlayerHandTableHandle<'_>; +} + +impl ViewPlayerHandTableAccess for super::RemoteTables { + fn view_player_hand(&self) -> ViewPlayerHandTableHandle<'_> { + ViewPlayerHandTableHandle { + imp: self.imp.get_table::("view_player_hand"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ViewPlayerHandInsertCallbackId(__sdk::CallbackId); +pub struct ViewPlayerHandDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for ViewPlayerHandTableHandle<'ctx> { + type Row = Hand; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ViewPlayerHandInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ViewPlayerHandInsertCallbackId { + ViewPlayerHandInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ViewPlayerHandInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ViewPlayerHandDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ViewPlayerHandDeleteCallbackId { + ViewPlayerHandDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ViewPlayerHandDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("view_player_hand"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} diff --git a/jong/src/stdb/wall_table.rs b/jong/src/stdb/wall_table.rs index 4a06950..92a737e 100644 --- a/jong/src/stdb/wall_table.rs +++ b/jong/src/stdb/wall_table.rs @@ -82,7 +82,6 @@ impl<'ctx> __sdk::Table for WallTableHandle<'ctx> { #[doc(hidden)] pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { let _table = client_cache.get_or_make_table::("wall"); - _table.add_unique_constraint::("id", |row| &row.id); _table.add_unique_constraint::("lobby_id", |row| &row.lobby_id); } pub struct WallUpdateCallbackId(__sdk::CallbackId); @@ -113,36 +112,6 @@ pub(super) fn parse_table_update( }) } -/// Access to the `id` unique index on the table `wall`, -/// which allows point queries on the field of the same name -/// via the [`WallIdUnique::find`] method. -/// -/// Users are encouraged not to explicitly reference this type, -/// but to directly chain method calls, -/// like `ctx.db.wall().id().find(...)`. -pub struct WallIdUnique<'ctx> { - imp: __sdk::UniqueConstraintHandle, - phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, -} - -impl<'ctx> WallTableHandle<'ctx> { - /// Get a handle on the `id` unique index on the table `wall`. - pub fn id(&self) -> WallIdUnique<'ctx> { - WallIdUnique { - imp: self.imp.get_unique_constraint::("id"), - phantom: std::marker::PhantomData, - } - } -} - -impl<'ctx> WallIdUnique<'ctx> { - /// Find the subscribed row whose `id` column value is equal to `col_val`, - /// if such a row is present in the client cache. - pub fn find(&self, col_val: &u32) -> Option { - self.imp.find(col_val) - } -} - /// Access to the `lobby_id` unique index on the table `wall`, /// which allows point queries on the field of the same name /// via the [`WallLobbyIdUnique::find`] method. diff --git a/jong/src/stdb/wall_type.rs b/jong/src/stdb/wall_type.rs index 732dbea..2398ef1 100644 --- a/jong/src/stdb/wall_type.rs +++ b/jong/src/stdb/wall_type.rs @@ -9,7 +9,6 @@ use super::tile_type::Tile; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct Wall { - pub id: u32, pub lobby_id: u32, pub tiles: Vec, } diff --git a/spacetimedb/src/lib.rs b/spacetimedb/src/lib.rs index 4c96c59..efd08f2 100644 --- a/spacetimedb/src/lib.rs +++ b/spacetimedb/src/lib.rs @@ -1,5 +1,7 @@ -use log::info; -use spacetimedb::{Identity, ReducerContext, Table, rand::seq::SliceRandom, reducer, table}; +use log::{debug, info}; +use spacetimedb::{ + Identity, ReducerContext, Table, ViewContext, rand::seq::SliceRandom, reducer, table, view, +}; use jong_types::*; @@ -30,7 +32,7 @@ pub struct Bot { lobby_id: u32, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] #[table(name = lobby, public)] pub struct Lobby { #[primary_key] @@ -39,19 +41,18 @@ pub struct Lobby { #[index(direct)] #[unique] - host_id: u32, + host_player_id: u32, game_state: GameState, } #[table(name = wall)] pub struct Wall { + // #[auto_inc] + // id: u32, #[primary_key] - #[auto_inc] - id: u32, - - #[index(direct)] - #[unique] + // #[index(direct)] + // #[unique] lobby_id: u32, tiles: Vec, @@ -60,7 +61,15 @@ pub struct Wall { #[table(name = hand)] pub struct Hand { #[primary_key] - player_ident: Identity, + player_identity: Identity, + + tiles: Vec, +} + +#[table(name = bothand)] +pub struct BotHand { + #[primary_key] + bot_id: u32, tiles: Vec, } @@ -89,7 +98,7 @@ pub fn login_or_add_player(ctx: &ReducerContext) { } #[reducer] -pub fn join_or_create_lobby(ctx: &ReducerContext, lobby: Option) -> Result<(), String> { +pub fn join_or_create_lobby(ctx: &ReducerContext, mut lobby_id: u32) -> Result<(), String> { let mut player = ctx .db .player() @@ -97,16 +106,16 @@ pub fn join_or_create_lobby(ctx: &ReducerContext, lobby: Option) -> Result< .find(ctx.sender) .ok_or(format!("cannot find player {}", ctx.sender))?; - let lobby_id = lobby.unwrap_or_else(|| { + if lobby_id == 0 { let lobby = ctx.db.lobby().insert(Lobby { id: 0, - host_id: player.id, + host_player_id: player.id, game_state: GameState::None, }); info!("created lobby: {:?}", lobby); - lobby.id - }); + lobby_id = lobby.id; + } player.lobby_id = lobby_id; @@ -134,18 +143,64 @@ pub fn add_bot(ctx: &ReducerContext, lobby_id: u32) -> Result<(), String> { } #[reducer] -pub fn shuffle_wall(ctx: &ReducerContext) { - // if let Some(wall) = +pub fn setup_game(ctx: &ReducerContext, lobby_id: u32) { + debug!("lobby_id: {lobby_id}"); + let mut lobby = ctx.db.lobby().id().find(lobby_id).unwrap(); + lobby.game_state = GameState::Setup; - // let mut rng = ctx.rng(); - // let mut wall = tiles(); - // wall.shuffle(&mut rng); - todo!() + ctx.db.lobby().id().update(lobby); + + let tiles = new_shuffled_wall(ctx); + ctx.db.wall().insert(Wall { + // id: 0, + lobby_id, + tiles, + }); + + lobby.game_state = GameState::Deal; + ctx.db.lobby().id().update(lobby); + + deal_hands(ctx, lobby_id); + lobby.game_state = GameState::Play; + ctx.db.lobby().id().update(lobby); } -#[reducer] -pub fn deal_hands(ctx: &ReducerContext) { - todo!() +pub fn new_shuffled_wall(ctx: &ReducerContext) -> Vec { + let mut rng = ctx.rng(); + let mut wall = tiles(); + wall.shuffle(&mut rng); + + wall +} + +pub fn deal_hands(ctx: &ReducerContext, lobby_id: u32) { + let players = ctx.db.player().lobby_id().filter(lobby_id); + let bots = ctx.db.bot().lobby_id().filter(lobby_id); + + let mut wall = ctx.db.wall().lobby_id().find(lobby_id).unwrap(); + + // FIXME rectify deal orders + for player in players { + let tiles = wall.tiles.split_off(wall.tiles.len() - 13); + wall = ctx.db.wall().lobby_id().update(wall); + ctx.db.hand().insert(Hand { + player_identity: player.identity, + tiles, + }); + } + for bot in bots { + let tiles = wall.tiles.split_off(wall.tiles.len() - 13); + wall = ctx.db.wall().lobby_id().update(wall); + ctx.db.bothand().insert(BotHand { + bot_id: bot.id, + tiles, + }); + } +} + +#[view(name = view_player_hand, public)] +pub fn view_player_hand(ctx: &ViewContext) -> Option { + ctx.db.hand().player_identity().find(ctx.sender) } #[reducer]