diff --git a/.helix/ignore b/.helix/ignore index 10f3355..a928bbf 100644 --- a/.helix/ignore +++ b/.helix/ignore @@ -1,2 +1,2 @@ -jong-db +jong-db/src/db bevy_spacetimedb diff --git a/devenv.nix b/devenv.nix index 2848ba6..782205c 100644 --- a/devenv.nix +++ b/devenv.nix @@ -45,6 +45,7 @@ ]; env.LD_LIBRARY_PATH = lib.makeLibraryPath packages; env.RUST_LOG = "jong=trace"; + env.RUST_BACKTRACE = "full"; # https://devenv.sh/languages/ languages.rust = { diff --git a/jong-db/src/db/bot_table.rs b/jong-db/src/db/bot_table.rs index 0c81cf5..73bc154 100644 --- a/jong-db/src/db/bot_table.rs +++ b/jong-db/src/db/bot_table.rs @@ -3,8 +3,6 @@ #![allow(unused, clippy::all)] use super::bot_type::Bot; -use super::db_tile_type::DbTile; -use super::turn_state_type::TurnState; use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; /// Table handle for the table `bot`. diff --git a/jong-db/src/db/bot_type.rs b/jong-db/src/db/bot_type.rs index 5981868..a49650a 100644 --- a/jong-db/src/db/bot_type.rs +++ b/jong-db/src/db/bot_type.rs @@ -4,18 +4,12 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; -use super::db_tile_type::DbTile; -use super::turn_state_type::TurnState; - #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct Bot { pub id: u32, pub lobby_id: u32, - pub turn_state: TurnState, - pub hand: Vec, - pub pond: Vec, - pub working_tile: Option, + pub config_id: u32, } impl __sdk::InModule for Bot { @@ -28,10 +22,7 @@ impl __sdk::InModule for Bot { pub struct BotCols { pub id: __sdk::__query_builder::Col, pub lobby_id: __sdk::__query_builder::Col, - pub turn_state: __sdk::__query_builder::Col, - pub hand: __sdk::__query_builder::Col>, - pub pond: __sdk::__query_builder::Col>, - pub working_tile: __sdk::__query_builder::Col>, + pub config_id: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for Bot { @@ -40,10 +31,7 @@ impl __sdk::__query_builder::HasCols for Bot { BotCols { id: __sdk::__query_builder::Col::new(table_name, "id"), lobby_id: __sdk::__query_builder::Col::new(table_name, "lobby_id"), - turn_state: __sdk::__query_builder::Col::new(table_name, "turn_state"), - hand: __sdk::__query_builder::Col::new(table_name, "hand"), - pond: __sdk::__query_builder::Col::new(table_name, "pond"), - working_tile: __sdk::__query_builder::Col::new(table_name, "working_tile"), + config_id: __sdk::__query_builder::Col::new(table_name, "config_id"), } } } diff --git a/jong-db/src/db/clear_all_reducer.rs b/jong-db/src/db/clear_all_reducer.rs deleted file mode 100644 index 49a6454..0000000 --- a/jong-db/src/db/clear_all_reducer.rs +++ /dev/null @@ -1,62 +0,0 @@ -// 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 ClearAllArgs {} - -impl From for super::Reducer { - fn from(args: ClearAllArgs) -> Self { - Self::ClearAll - } -} - -impl __sdk::InModule for ClearAllArgs { - type Module = super::RemoteModule; -} - -#[allow(non_camel_case_types)] -/// Extension trait for access to the reducer `clear_all`. -/// -/// Implemented for [`super::RemoteReducers`]. -pub trait clear_all { - /// Request that the remote module invoke the reducer `clear_all` 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 this method provides no way to listen for its completion status. - /// /// Use [`clear_all:clear_all_then`] to run a callback after the reducer completes. - fn clear_all(&self) -> __sdk::Result<()> { - self.clear_all_then(|_, _| {}) - } - - /// Request that the remote module invoke the reducer `clear_all` to run as soon as possible, - /// registering `callback` to run when we are notified that the reducer completed. - /// - /// 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 with the `callback`. - fn clear_all_then( - &self, - - callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) - + Send - + 'static, - ) -> __sdk::Result<()>; -} - -impl clear_all for super::RemoteReducers { - fn clear_all_then( - &self, - - callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) - + Send - + 'static, - ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(ClearAllArgs {}, callback) - } -} diff --git a/jong-db/src/db/game_timer_table.rs b/jong-db/src/db/game_timer_table.rs deleted file mode 100644 index 2aa5ed2..0000000 --- a/jong-db/src/db/game_timer_table.rs +++ /dev/null @@ -1,190 +0,0 @@ -// 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::game_timer_type::GameTimer; -use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; - -/// Table handle for the table `game_timer`. -/// -/// Obtain a handle from the [`GameTimerTableAccess::game_timer`] method on [`super::RemoteTables`], -/// like `ctx.db.game_timer()`. -/// -/// Users are encouraged not to explicitly reference this type, -/// but to directly chain method calls, -/// like `ctx.db.game_timer().on_insert(...)`. -pub struct GameTimerTableHandle<'ctx> { - imp: __sdk::TableHandle, - ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, -} - -#[allow(non_camel_case_types)] -/// Extension trait for access to the table `game_timer`. -/// -/// Implemented for [`super::RemoteTables`]. -pub trait GameTimerTableAccess { - #[allow(non_snake_case)] - /// Obtain a [`GameTimerTableHandle`], which mediates access to the table `game_timer`. - fn game_timer(&self) -> GameTimerTableHandle<'_>; -} - -impl GameTimerTableAccess for super::RemoteTables { - fn game_timer(&self) -> GameTimerTableHandle<'_> { - GameTimerTableHandle { - imp: self.imp.get_table::("game_timer"), - ctx: std::marker::PhantomData, - } - } -} - -pub struct GameTimerInsertCallbackId(__sdk::CallbackId); -pub struct GameTimerDeleteCallbackId(__sdk::CallbackId); - -impl<'ctx> __sdk::Table for GameTimerTableHandle<'ctx> { - type Row = GameTimer; - type EventContext = super::EventContext; - - fn count(&self) -> u64 { - self.imp.count() - } - fn iter(&self) -> impl Iterator + '_ { - self.imp.iter() - } - - type InsertCallbackId = GameTimerInsertCallbackId; - - fn on_insert( - &self, - callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, - ) -> GameTimerInsertCallbackId { - GameTimerInsertCallbackId(self.imp.on_insert(Box::new(callback))) - } - - fn remove_on_insert(&self, callback: GameTimerInsertCallbackId) { - self.imp.remove_on_insert(callback.0) - } - - type DeleteCallbackId = GameTimerDeleteCallbackId; - - fn on_delete( - &self, - callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, - ) -> GameTimerDeleteCallbackId { - GameTimerDeleteCallbackId(self.imp.on_delete(Box::new(callback))) - } - - fn remove_on_delete(&self, callback: GameTimerDeleteCallbackId) { - self.imp.remove_on_delete(callback.0) - } -} - -pub struct GameTimerUpdateCallbackId(__sdk::CallbackId); - -impl<'ctx> __sdk::TableWithPrimaryKey for GameTimerTableHandle<'ctx> { - type UpdateCallbackId = GameTimerUpdateCallbackId; - - fn on_update( - &self, - callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static, - ) -> GameTimerUpdateCallbackId { - GameTimerUpdateCallbackId(self.imp.on_update(Box::new(callback))) - } - - fn remove_on_update(&self, callback: GameTimerUpdateCallbackId) { - self.imp.remove_on_update(callback.0) - } -} - -/// Access to the `id` unique index on the table `game_timer`, -/// which allows point queries on the field of the same name -/// via the [`GameTimerIdUnique::find`] method. -/// -/// Users are encouraged not to explicitly reference this type, -/// but to directly chain method calls, -/// like `ctx.db.game_timer().id().find(...)`. -pub struct GameTimerIdUnique<'ctx> { - imp: __sdk::UniqueConstraintHandle, - phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, -} - -impl<'ctx> GameTimerTableHandle<'ctx> { - /// Get a handle on the `id` unique index on the table `game_timer`. - pub fn id(&self) -> GameTimerIdUnique<'ctx> { - GameTimerIdUnique { - imp: self.imp.get_unique_constraint::("id"), - phantom: std::marker::PhantomData, - } - } -} - -impl<'ctx> GameTimerIdUnique<'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: &u64) -> Option { - self.imp.find(col_val) - } -} - -/// Access to the `lobby_id` unique index on the table `game_timer`, -/// which allows point queries on the field of the same name -/// via the [`GameTimerLobbyIdUnique::find`] method. -/// -/// Users are encouraged not to explicitly reference this type, -/// but to directly chain method calls, -/// like `ctx.db.game_timer().lobby_id().find(...)`. -pub struct GameTimerLobbyIdUnique<'ctx> { - imp: __sdk::UniqueConstraintHandle, - phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, -} - -impl<'ctx> GameTimerTableHandle<'ctx> { - /// Get a handle on the `lobby_id` unique index on the table `game_timer`. - pub fn lobby_id(&self) -> GameTimerLobbyIdUnique<'ctx> { - GameTimerLobbyIdUnique { - imp: self.imp.get_unique_constraint::("lobby_id"), - phantom: std::marker::PhantomData, - } - } -} - -impl<'ctx> GameTimerLobbyIdUnique<'ctx> { - /// Find the subscribed row whose `lobby_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) - } -} - -#[doc(hidden)] -pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { - let _table = client_cache.get_or_make_table::("game_timer"); - _table.add_unique_constraint::("id", |row| &row.id); - _table.add_unique_constraint::("lobby_id", |row| &row.lobby_id); -} - -#[doc(hidden)] -pub(super) fn parse_table_update( - raw_updates: __ws::v2::TableUpdate, -) -> __sdk::Result<__sdk::TableUpdate> { - __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { - __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") - .with_cause(e) - .into() - }) -} - -#[allow(non_camel_case_types)] -/// Extension trait for query builder access to the table `GameTimer`. -/// -/// Implemented for [`__sdk::QueryTableAccessor`]. -pub trait game_timerQueryTableAccess { - #[allow(non_snake_case)] - /// Get a query builder for the table `GameTimer`. - fn game_timer(&self) -> __sdk::__query_builder::Table; -} - -impl game_timerQueryTableAccess for __sdk::QueryTableAccessor { - fn game_timer(&self) -> __sdk::__query_builder::Table { - __sdk::__query_builder::Table::new("game_timer") - } -} diff --git a/jong-db/src/db/game_timer_type.rs b/jong-db/src/db/game_timer_type.rs deleted file mode 100644 index 31d3fa3..0000000 --- a/jong-db/src/db/game_timer_type.rs +++ /dev/null @@ -1,57 +0,0 @@ -// 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 struct GameTimer { - pub id: u64, - pub lobby_id: u32, - pub scheduled_at: __sdk::ScheduleAt, -} - -impl __sdk::InModule for GameTimer { - type Module = super::RemoteModule; -} - -/// Column accessor struct for the table `GameTimer`. -/// -/// Provides typed access to columns for query building. -pub struct GameTimerCols { - pub id: __sdk::__query_builder::Col, - pub lobby_id: __sdk::__query_builder::Col, - pub scheduled_at: __sdk::__query_builder::Col, -} - -impl __sdk::__query_builder::HasCols for GameTimer { - type Cols = GameTimerCols; - fn cols(table_name: &'static str) -> Self::Cols { - GameTimerCols { - id: __sdk::__query_builder::Col::new(table_name, "id"), - lobby_id: __sdk::__query_builder::Col::new(table_name, "lobby_id"), - scheduled_at: __sdk::__query_builder::Col::new(table_name, "scheduled_at"), - } - } -} - -/// Indexed column accessor struct for the table `GameTimer`. -/// -/// Provides typed access to indexed columns for query building. -pub struct GameTimerIxCols { - pub id: __sdk::__query_builder::IxCol, - pub lobby_id: __sdk::__query_builder::IxCol, -} - -impl __sdk::__query_builder::HasIxCols for GameTimer { - type IxCols = GameTimerIxCols; - fn ix_cols(table_name: &'static str) -> Self::IxCols { - GameTimerIxCols { - id: __sdk::__query_builder::IxCol::new(table_name, "id"), - lobby_id: __sdk::__query_builder::IxCol::new(table_name, "lobby_id"), - } - } -} - -impl __sdk::__query_builder::CanBeLookupTable for GameTimer {} diff --git a/jong-db/src/db/hand_view_type.rs b/jong-db/src/db/hand_view_type.rs index 93935a6..bc75638 100644 --- a/jong-db/src/db/hand_view_type.rs +++ b/jong-db/src/db/hand_view_type.rs @@ -4,13 +4,14 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; -use super::player_or_bot_type::PlayerOrBot; +use super::db_tile_type::DbTile; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct HandView { - pub player: PlayerOrBot, + pub player_id: u32, pub hand_length: u8, + pub pond: Vec, pub drawn: bool, } @@ -22,8 +23,9 @@ impl __sdk::InModule for HandView { /// /// Provides typed access to columns for query building. pub struct HandViewCols { - pub player: __sdk::__query_builder::Col, + pub player_id: __sdk::__query_builder::Col, pub hand_length: __sdk::__query_builder::Col, + pub pond: __sdk::__query_builder::Col>, pub drawn: __sdk::__query_builder::Col, } @@ -31,8 +33,9 @@ impl __sdk::__query_builder::HasCols for HandView { type Cols = HandViewCols; fn cols(table_name: &'static str) -> Self::Cols { HandViewCols { - player: __sdk::__query_builder::Col::new(table_name, "player"), + player_id: __sdk::__query_builder::Col::new(table_name, "player_id"), hand_length: __sdk::__query_builder::Col::new(table_name, "hand_length"), + pond: __sdk::__query_builder::Col::new(table_name, "pond"), drawn: __sdk::__query_builder::Col::new(table_name, "drawn"), } } diff --git a/jong-db/src/db/lobby_table.rs b/jong-db/src/db/lobby_table.rs index 696c723..02144a5 100644 --- a/jong-db/src/db/lobby_table.rs +++ b/jong-db/src/db/lobby_table.rs @@ -4,7 +4,6 @@ #![allow(unused, clippy::all)] use super::game_state_type::GameState; use super::lobby_type::Lobby; -use super::player_or_bot_type::PlayerOrBot; use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; /// Table handle for the table `lobby`. diff --git a/jong-db/src/db/lobby_timer_type.rs b/jong-db/src/db/lobby_timer_type.rs new file mode 100644 index 0000000..beb8419 --- /dev/null +++ b/jong-db/src/db/lobby_timer_type.rs @@ -0,0 +1,57 @@ +// 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 struct LobbyTimer { + pub lobby_id: u32, + pub scheduled_id: u64, + pub scheduled_at: __sdk::ScheduleAt, +} + +impl __sdk::InModule for LobbyTimer { + type Module = super::RemoteModule; +} + +/// Column accessor struct for the table `LobbyTimer`. +/// +/// Provides typed access to columns for query building. +pub struct LobbyTimerCols { + pub lobby_id: __sdk::__query_builder::Col, + pub scheduled_id: __sdk::__query_builder::Col, + pub scheduled_at: __sdk::__query_builder::Col, +} + +impl __sdk::__query_builder::HasCols for LobbyTimer { + type Cols = LobbyTimerCols; + fn cols(table_name: &'static str) -> Self::Cols { + LobbyTimerCols { + lobby_id: __sdk::__query_builder::Col::new(table_name, "lobby_id"), + scheduled_id: __sdk::__query_builder::Col::new(table_name, "scheduled_id"), + scheduled_at: __sdk::__query_builder::Col::new(table_name, "scheduled_at"), + } + } +} + +/// Indexed column accessor struct for the table `LobbyTimer`. +/// +/// Provides typed access to indexed columns for query building. +pub struct LobbyTimerIxCols { + pub lobby_id: __sdk::__query_builder::IxCol, + pub scheduled_id: __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for LobbyTimer { + type IxCols = LobbyTimerIxCols; + fn ix_cols(table_name: &'static str) -> Self::IxCols { + LobbyTimerIxCols { + lobby_id: __sdk::__query_builder::IxCol::new(table_name, "lobby_id"), + scheduled_id: __sdk::__query_builder::IxCol::new(table_name, "scheduled_id"), + } + } +} + +impl __sdk::__query_builder::CanBeLookupTable for LobbyTimer {} diff --git a/jong-db/src/db/lobby_type.rs b/jong-db/src/db/lobby_type.rs index 942a58a..f442ff8 100644 --- a/jong-db/src/db/lobby_type.rs +++ b/jong-db/src/db/lobby_type.rs @@ -5,13 +5,12 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; use super::game_state_type::GameState; -use super::player_or_bot_type::PlayerOrBot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct Lobby { pub id: u32, - pub players: Vec, + pub players: Vec, pub dealer_idx: u8, pub current_idx: u8, pub game_state: GameState, @@ -26,7 +25,7 @@ impl __sdk::InModule for Lobby { /// Provides typed access to columns for query building. pub struct LobbyCols { pub id: __sdk::__query_builder::Col, - pub players: __sdk::__query_builder::Col>, + pub players: __sdk::__query_builder::Col>, pub dealer_idx: __sdk::__query_builder::Col, pub current_idx: __sdk::__query_builder::Col, pub game_state: __sdk::__query_builder::Col, diff --git a/jong-db/src/db/mod.rs b/jong-db/src/db/mod.rs index 83e4053..e5c0c6e 100644 --- a/jong-db/src/db/mod.rs +++ b/jong-db/src/db/mod.rs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 2.0.1 (commit a4d29daec8ed35ce4913a335b7210b9ae3933d00). +// This was generated using spacetimedb cli version 2.0.2 (commit bc4fcec6f33f607fb46f61ae66c479eecf5a6e74). #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; @@ -10,62 +10,60 @@ pub mod add_bot_reducer; pub mod advance_game_reducer; pub mod bot_table; pub mod bot_type; -pub mod clear_all_reducer; pub mod db_tile_type; -pub mod db_wall_type; pub mod discard_tile_reducer; pub mod dragon_type; pub mod game_state_type; -pub mod game_timer_table; -pub mod game_timer_type; pub mod hand_view_type; pub mod join_or_create_lobby_reducer; pub mod lobby_table; +pub mod lobby_timer_type; pub mod lobby_type; pub mod player_clock_table; pub mod player_clock_type; +pub mod player_config_table; +pub mod player_config_type; pub mod player_hand_type; -pub mod player_or_bot_type; -pub mod player_table; -pub mod player_type; pub mod rank_type; pub mod set_ready_reducer; pub mod suit_type; pub mod tile_type; pub mod turn_state_type; +pub mod user_table; +pub mod user_type; pub mod view_closed_hands_table; pub mod view_hand_table; +pub mod wall_type; pub mod wind_type; pub use add_bot_reducer::add_bot; pub use advance_game_reducer::advance_game; pub use bot_table::*; pub use bot_type::Bot; -pub use clear_all_reducer::clear_all; pub use db_tile_type::DbTile; -pub use db_wall_type::DbWall; pub use discard_tile_reducer::discard_tile; pub use dragon_type::Dragon; pub use game_state_type::GameState; -pub use game_timer_table::*; -pub use game_timer_type::GameTimer; pub use hand_view_type::HandView; pub use join_or_create_lobby_reducer::join_or_create_lobby; pub use lobby_table::*; +pub use lobby_timer_type::LobbyTimer; pub use lobby_type::Lobby; pub use player_clock_table::*; pub use player_clock_type::PlayerClock; +pub use player_config_table::*; +pub use player_config_type::PlayerConfig; pub use player_hand_type::PlayerHand; -pub use player_or_bot_type::PlayerOrBot; -pub use player_table::*; -pub use player_type::Player; pub use rank_type::Rank; pub use set_ready_reducer::set_ready; pub use suit_type::Suit; pub use tile_type::Tile; pub use turn_state_type::TurnState; +pub use user_table::*; +pub use user_type::User; pub use view_closed_hands_table::*; pub use view_hand_table::*; +pub use wall_type::Wall; pub use wind_type::Wind; #[derive(Clone, PartialEq, Debug)] @@ -78,7 +76,6 @@ pub use wind_type::Wind; pub enum Reducer { AddBot { lobby_id: u32 }, AdvanceGame, - ClearAll, DiscardTile { tile_id: u32 }, JoinOrCreateLobby { lobby_id: u32 }, SetReady { ready: bool }, @@ -93,7 +90,6 @@ impl __sdk::Reducer for Reducer { match self { Reducer::AddBot { .. } => "add_bot", Reducer::AdvanceGame => "advance_game", - Reducer::ClearAll => "clear_all", Reducer::DiscardTile { .. } => "discard_tile", Reducer::JoinOrCreateLobby { .. } => "join_or_create_lobby", Reducer::SetReady { .. } => "set_ready", @@ -109,7 +105,6 @@ impl __sdk::Reducer for Reducer { Reducer::AdvanceGame => { __sats::bsatn::to_vec(&advance_game_reducer::AdvanceGameArgs {}) } - Reducer::ClearAll => __sats::bsatn::to_vec(&clear_all_reducer::ClearAllArgs {}), Reducer::DiscardTile { tile_id } => { __sats::bsatn::to_vec(&discard_tile_reducer::DiscardTileArgs { tile_id: tile_id.clone(), @@ -135,10 +130,10 @@ impl __sdk::Reducer for Reducer { #[doc(hidden)] pub struct DbUpdate { bot: __sdk::TableUpdate, - game_timer: __sdk::TableUpdate, lobby: __sdk::TableUpdate, - player: __sdk::TableUpdate, player_clock: __sdk::TableUpdate, + player_config: __sdk::TableUpdate, + user: __sdk::TableUpdate, view_closed_hands: __sdk::TableUpdate, view_hand: __sdk::TableUpdate, } @@ -152,18 +147,18 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate { "bot" => db_update .bot .append(bot_table::parse_table_update(table_update)?), - "game_timer" => db_update - .game_timer - .append(game_timer_table::parse_table_update(table_update)?), "lobby" => db_update .lobby .append(lobby_table::parse_table_update(table_update)?), - "player" => db_update - .player - .append(player_table::parse_table_update(table_update)?), "player_clock" => db_update .player_clock .append(player_clock_table::parse_table_update(table_update)?), + "player_config" => db_update + .player_config + .append(player_config_table::parse_table_update(table_update)?), + "user" => db_update + .user + .append(user_table::parse_table_update(table_update)?), "view_closed_hands" => db_update .view_closed_hands .append(view_closed_hands_table::parse_table_update(table_update)?), @@ -199,18 +194,18 @@ impl __sdk::DbUpdate for DbUpdate { diff.bot = cache .apply_diff_to_table::("bot", &self.bot) .with_updates_by_pk(|row| &row.id); - diff.game_timer = cache - .apply_diff_to_table::("game_timer", &self.game_timer) - .with_updates_by_pk(|row| &row.id); diff.lobby = cache .apply_diff_to_table::("lobby", &self.lobby) .with_updates_by_pk(|row| &row.id); - diff.player = cache - .apply_diff_to_table::("player", &self.player) - .with_updates_by_pk(|row| &row.identity); diff.player_clock = cache .apply_diff_to_table::("player_clock", &self.player_clock) + .with_updates_by_pk(|row| &row.player_id); + diff.player_config = cache + .apply_diff_to_table::("player_config", &self.player_config) .with_updates_by_pk(|row| &row.id); + diff.user = cache + .apply_diff_to_table::("user", &self.user) + .with_updates_by_pk(|row| &row.identity); diff.view_closed_hands = cache.apply_diff_to_table::("view_closed_hands", &self.view_closed_hands); diff.view_hand = cache.apply_diff_to_table::("view_hand", &self.view_hand); @@ -224,18 +219,18 @@ impl __sdk::DbUpdate for DbUpdate { "bot" => db_update .bot .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), - "game_timer" => db_update - .game_timer - .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), "lobby" => db_update .lobby .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), - "player" => db_update - .player - .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), "player_clock" => db_update .player_clock .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), + "player_config" => db_update + .player_config + .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), + "user" => db_update + .user + .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), "view_closed_hands" => db_update .view_closed_hands .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), @@ -258,18 +253,18 @@ impl __sdk::DbUpdate for DbUpdate { "bot" => db_update .bot .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), - "game_timer" => db_update - .game_timer - .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), "lobby" => db_update .lobby .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), - "player" => db_update - .player - .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), "player_clock" => db_update .player_clock .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), + "player_config" => db_update + .player_config + .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), + "user" => db_update + .user + .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), "view_closed_hands" => db_update .view_closed_hands .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), @@ -292,10 +287,10 @@ impl __sdk::DbUpdate for DbUpdate { #[doc(hidden)] pub struct AppliedDiff<'r> { bot: __sdk::TableAppliedDiff<'r, Bot>, - game_timer: __sdk::TableAppliedDiff<'r, GameTimer>, lobby: __sdk::TableAppliedDiff<'r, Lobby>, - player: __sdk::TableAppliedDiff<'r, Player>, player_clock: __sdk::TableAppliedDiff<'r, PlayerClock>, + player_config: __sdk::TableAppliedDiff<'r, PlayerConfig>, + user: __sdk::TableAppliedDiff<'r, User>, view_closed_hands: __sdk::TableAppliedDiff<'r, HandView>, view_hand: __sdk::TableAppliedDiff<'r, PlayerHand>, __unused: std::marker::PhantomData<&'r ()>, @@ -312,14 +307,18 @@ 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::("game_timer", &self.game_timer, event); callbacks.invoke_table_row_callbacks::("lobby", &self.lobby, event); - callbacks.invoke_table_row_callbacks::("player", &self.player, event); callbacks.invoke_table_row_callbacks::( "player_clock", &self.player_clock, event, ); + callbacks.invoke_table_row_callbacks::( + "player_config", + &self.player_config, + event, + ); + callbacks.invoke_table_row_callbacks::("user", &self.user, event); callbacks.invoke_table_row_callbacks::( "view_closed_hands", &self.view_closed_hands, @@ -971,19 +970,19 @@ impl __sdk::SpacetimeModule for RemoteModule { fn register_tables(client_cache: &mut __sdk::ClientCache) { bot_table::register_table(client_cache); - game_timer_table::register_table(client_cache); lobby_table::register_table(client_cache); - player_table::register_table(client_cache); player_clock_table::register_table(client_cache); + player_config_table::register_table(client_cache); + user_table::register_table(client_cache); view_closed_hands_table::register_table(client_cache); view_hand_table::register_table(client_cache); } const ALL_TABLE_NAMES: &'static [&'static str] = &[ "bot", - "game_timer", "lobby", - "player", "player_clock", + "player_config", + "user", "view_closed_hands", "view_hand", ]; diff --git a/jong-db/src/db/player_clock_table.rs b/jong-db/src/db/player_clock_table.rs index 82093ad..5f1983b 100644 --- a/jong-db/src/db/player_clock_table.rs +++ b/jong-db/src/db/player_clock_table.rs @@ -95,36 +95,6 @@ impl<'ctx> __sdk::TableWithPrimaryKey for PlayerClockTableHandle<'ctx> { } } -/// Access to the `id` unique index on the table `player_clock`, -/// which allows point queries on the field of the same name -/// via the [`PlayerClockIdUnique::find`] method. -/// -/// Users are encouraged not to explicitly reference this type, -/// but to directly chain method calls, -/// like `ctx.db.player_clock().id().find(...)`. -pub struct PlayerClockIdUnique<'ctx> { - imp: __sdk::UniqueConstraintHandle, - phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, -} - -impl<'ctx> PlayerClockTableHandle<'ctx> { - /// Get a handle on the `id` unique index on the table `player_clock`. - pub fn id(&self) -> PlayerClockIdUnique<'ctx> { - PlayerClockIdUnique { - imp: self.imp.get_unique_constraint::("id"), - phantom: std::marker::PhantomData, - } - } -} - -impl<'ctx> PlayerClockIdUnique<'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 `player_id` unique index on the table `player_clock`, /// which allows point queries on the field of the same name /// via the [`PlayerClockPlayerIdUnique::find`] method. @@ -158,7 +128,6 @@ impl<'ctx> PlayerClockPlayerIdUnique<'ctx> { #[doc(hidden)] pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { let _table = client_cache.get_or_make_table::("player_clock"); - _table.add_unique_constraint::("id", |row| &row.id); _table.add_unique_constraint::("player_id", |row| &row.player_id); } diff --git a/jong-db/src/db/player_clock_type.rs b/jong-db/src/db/player_clock_type.rs index 59998c7..791ee13 100644 --- a/jong-db/src/db/player_clock_type.rs +++ b/jong-db/src/db/player_clock_type.rs @@ -7,7 +7,6 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct PlayerClock { - pub id: u32, pub player_id: u32, pub renewable: u16, pub total: u16, @@ -21,7 +20,6 @@ impl __sdk::InModule for PlayerClock { /// /// Provides typed access to columns for query building. pub struct PlayerClockCols { - pub id: __sdk::__query_builder::Col, pub player_id: __sdk::__query_builder::Col, pub renewable: __sdk::__query_builder::Col, pub total: __sdk::__query_builder::Col, @@ -31,7 +29,6 @@ impl __sdk::__query_builder::HasCols for PlayerClock { type Cols = PlayerClockCols; fn cols(table_name: &'static str) -> Self::Cols { PlayerClockCols { - id: __sdk::__query_builder::Col::new(table_name, "id"), player_id: __sdk::__query_builder::Col::new(table_name, "player_id"), renewable: __sdk::__query_builder::Col::new(table_name, "renewable"), total: __sdk::__query_builder::Col::new(table_name, "total"), @@ -43,7 +40,6 @@ impl __sdk::__query_builder::HasCols for PlayerClock { /// /// Provides typed access to indexed columns for query building. pub struct PlayerClockIxCols { - pub id: __sdk::__query_builder::IxCol, pub player_id: __sdk::__query_builder::IxCol, } @@ -51,7 +47,6 @@ impl __sdk::__query_builder::HasIxCols for PlayerClock { type IxCols = PlayerClockIxCols; fn ix_cols(table_name: &'static str) -> Self::IxCols { PlayerClockIxCols { - id: __sdk::__query_builder::IxCol::new(table_name, "id"), player_id: __sdk::__query_builder::IxCol::new(table_name, "player_id"), } } diff --git a/jong-db/src/db/player_config_table.rs b/jong-db/src/db/player_config_table.rs new file mode 100644 index 0000000..a7baa2e --- /dev/null +++ b/jong-db/src/db/player_config_table.rs @@ -0,0 +1,159 @@ +// 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::player_config_type::PlayerConfig; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `player_config`. +/// +/// Obtain a handle from the [`PlayerConfigTableAccess::player_config`] method on [`super::RemoteTables`], +/// like `ctx.db.player_config()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.player_config().on_insert(...)`. +pub struct PlayerConfigTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `player_config`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait PlayerConfigTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`PlayerConfigTableHandle`], which mediates access to the table `player_config`. + fn player_config(&self) -> PlayerConfigTableHandle<'_>; +} + +impl PlayerConfigTableAccess for super::RemoteTables { + fn player_config(&self) -> PlayerConfigTableHandle<'_> { + PlayerConfigTableHandle { + imp: self.imp.get_table::("player_config"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct PlayerConfigInsertCallbackId(__sdk::CallbackId); +pub struct PlayerConfigDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for PlayerConfigTableHandle<'ctx> { + type Row = PlayerConfig; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = PlayerConfigInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> PlayerConfigInsertCallbackId { + PlayerConfigInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: PlayerConfigInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = PlayerConfigDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> PlayerConfigDeleteCallbackId { + PlayerConfigDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: PlayerConfigDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +pub struct PlayerConfigUpdateCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::TableWithPrimaryKey for PlayerConfigTableHandle<'ctx> { + type UpdateCallbackId = PlayerConfigUpdateCallbackId; + + fn on_update( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static, + ) -> PlayerConfigUpdateCallbackId { + PlayerConfigUpdateCallbackId(self.imp.on_update(Box::new(callback))) + } + + fn remove_on_update(&self, callback: PlayerConfigUpdateCallbackId) { + self.imp.remove_on_update(callback.0) + } +} + +/// Access to the `id` unique index on the table `player_config`, +/// which allows point queries on the field of the same name +/// via the [`PlayerConfigIdUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.player_config().id().find(...)`. +pub struct PlayerConfigIdUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> PlayerConfigTableHandle<'ctx> { + /// Get a handle on the `id` unique index on the table `player_config`. + pub fn id(&self) -> PlayerConfigIdUnique<'ctx> { + PlayerConfigIdUnique { + imp: self.imp.get_unique_constraint::("id"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> PlayerConfigIdUnique<'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) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("player_config"); + _table.add_unique_constraint::("id", |row| &row.id); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::v2::TableUpdate, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} + +#[allow(non_camel_case_types)] +/// Extension trait for query builder access to the table `PlayerConfig`. +/// +/// Implemented for [`__sdk::QueryTableAccessor`]. +pub trait player_configQueryTableAccess { + #[allow(non_snake_case)] + /// Get a query builder for the table `PlayerConfig`. + fn player_config(&self) -> __sdk::__query_builder::Table; +} + +impl player_configQueryTableAccess for __sdk::QueryTableAccessor { + fn player_config(&self) -> __sdk::__query_builder::Table { + __sdk::__query_builder::Table::new("player_config") + } +} diff --git a/jong-db/src/db/player_config_type.rs b/jong-db/src/db/player_config_type.rs new file mode 100644 index 0000000..7ddd007 --- /dev/null +++ b/jong-db/src/db/player_config_type.rs @@ -0,0 +1,58 @@ +// 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 struct PlayerConfig { + pub id: u32, + pub name: String, + pub ready: bool, + pub sort: bool, +} + +impl __sdk::InModule for PlayerConfig { + type Module = super::RemoteModule; +} + +/// Column accessor struct for the table `PlayerConfig`. +/// +/// Provides typed access to columns for query building. +pub struct PlayerConfigCols { + pub id: __sdk::__query_builder::Col, + pub name: __sdk::__query_builder::Col, + pub ready: __sdk::__query_builder::Col, + pub sort: __sdk::__query_builder::Col, +} + +impl __sdk::__query_builder::HasCols for PlayerConfig { + type Cols = PlayerConfigCols; + fn cols(table_name: &'static str) -> Self::Cols { + PlayerConfigCols { + id: __sdk::__query_builder::Col::new(table_name, "id"), + name: __sdk::__query_builder::Col::new(table_name, "name"), + ready: __sdk::__query_builder::Col::new(table_name, "ready"), + sort: __sdk::__query_builder::Col::new(table_name, "sort"), + } + } +} + +/// Indexed column accessor struct for the table `PlayerConfig`. +/// +/// Provides typed access to indexed columns for query building. +pub struct PlayerConfigIxCols { + pub id: __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for PlayerConfig { + type IxCols = PlayerConfigIxCols; + fn ix_cols(table_name: &'static str) -> Self::IxCols { + PlayerConfigIxCols { + id: __sdk::__query_builder::IxCol::new(table_name, "id"), + } + } +} + +impl __sdk::__query_builder::CanBeLookupTable for PlayerConfig {} diff --git a/jong-db/src/db/player_hand_type.rs b/jong-db/src/db/player_hand_type.rs index 6715afa..658116c 100644 --- a/jong-db/src/db/player_hand_type.rs +++ b/jong-db/src/db/player_hand_type.rs @@ -10,11 +10,10 @@ use super::turn_state_type::TurnState; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct PlayerHand { - pub id: u32, pub player_id: u32, pub turn_state: TurnState, - pub pond: Vec, pub hand: Vec, + pub pond: Vec, pub working_tile: Option, } @@ -26,11 +25,10 @@ impl __sdk::InModule for PlayerHand { /// /// Provides typed access to columns for query building. pub struct PlayerHandCols { - pub id: __sdk::__query_builder::Col, pub player_id: __sdk::__query_builder::Col, pub turn_state: __sdk::__query_builder::Col, - pub pond: __sdk::__query_builder::Col>, pub hand: __sdk::__query_builder::Col>, + pub pond: __sdk::__query_builder::Col>, pub working_tile: __sdk::__query_builder::Col>, } @@ -38,11 +36,10 @@ impl __sdk::__query_builder::HasCols for PlayerHand { type Cols = PlayerHandCols; fn cols(table_name: &'static str) -> Self::Cols { PlayerHandCols { - id: __sdk::__query_builder::Col::new(table_name, "id"), player_id: __sdk::__query_builder::Col::new(table_name, "player_id"), turn_state: __sdk::__query_builder::Col::new(table_name, "turn_state"), - pond: __sdk::__query_builder::Col::new(table_name, "pond"), hand: __sdk::__query_builder::Col::new(table_name, "hand"), + pond: __sdk::__query_builder::Col::new(table_name, "pond"), working_tile: __sdk::__query_builder::Col::new(table_name, "working_tile"), } } @@ -52,7 +49,6 @@ impl __sdk::__query_builder::HasCols for PlayerHand { /// /// Provides typed access to indexed columns for query building. pub struct PlayerHandIxCols { - pub id: __sdk::__query_builder::IxCol, pub player_id: __sdk::__query_builder::IxCol, } @@ -60,7 +56,6 @@ impl __sdk::__query_builder::HasIxCols for PlayerHand { type IxCols = PlayerHandIxCols; fn ix_cols(table_name: &'static str) -> Self::IxCols { PlayerHandIxCols { - id: __sdk::__query_builder::IxCol::new(table_name, "id"), player_id: __sdk::__query_builder::IxCol::new(table_name, "player_id"), } } diff --git a/jong-db/src/db/player_or_bot_type.rs b/jong-db/src/db/player_or_bot_type.rs deleted file mode 100644 index 538d940..0000000 --- a/jong-db/src/db/player_or_bot_type.rs +++ /dev/null @@ -1,17 +0,0 @@ -// 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 enum PlayerOrBot { - Player(u32), - - Bot(u32), -} - -impl __sdk::InModule for PlayerOrBot { - type Module = super::RemoteModule; -} diff --git a/jong-db/src/db/player_table.rs b/jong-db/src/db/player_table.rs deleted file mode 100644 index 7972a6e..0000000 --- a/jong-db/src/db/player_table.rs +++ /dev/null @@ -1,192 +0,0 @@ -// 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::player_type::Player; -use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; - -/// Table handle for the table `player`. -/// -/// Obtain a handle from the [`PlayerTableAccess::player`] method on [`super::RemoteTables`], -/// like `ctx.db.player()`. -/// -/// Users are encouraged not to explicitly reference this type, -/// but to directly chain method calls, -/// like `ctx.db.player().on_insert(...)`. -pub struct PlayerTableHandle<'ctx> { - imp: __sdk::TableHandle, - ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, -} - -#[allow(non_camel_case_types)] -/// Extension trait for access to the table `player`. -/// -/// Implemented for [`super::RemoteTables`]. -pub trait PlayerTableAccess { - #[allow(non_snake_case)] - /// Obtain a [`PlayerTableHandle`], which mediates access to the table `player`. - fn player(&self) -> PlayerTableHandle<'_>; -} - -impl PlayerTableAccess for super::RemoteTables { - fn player(&self) -> PlayerTableHandle<'_> { - PlayerTableHandle { - imp: self.imp.get_table::("player"), - ctx: std::marker::PhantomData, - } - } -} - -pub struct PlayerInsertCallbackId(__sdk::CallbackId); -pub struct PlayerDeleteCallbackId(__sdk::CallbackId); - -impl<'ctx> __sdk::Table for PlayerTableHandle<'ctx> { - type Row = Player; - type EventContext = super::EventContext; - - fn count(&self) -> u64 { - self.imp.count() - } - fn iter(&self) -> impl Iterator + '_ { - self.imp.iter() - } - - type InsertCallbackId = PlayerInsertCallbackId; - - fn on_insert( - &self, - callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, - ) -> PlayerInsertCallbackId { - PlayerInsertCallbackId(self.imp.on_insert(Box::new(callback))) - } - - fn remove_on_insert(&self, callback: PlayerInsertCallbackId) { - self.imp.remove_on_insert(callback.0) - } - - type DeleteCallbackId = PlayerDeleteCallbackId; - - fn on_delete( - &self, - callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, - ) -> PlayerDeleteCallbackId { - PlayerDeleteCallbackId(self.imp.on_delete(Box::new(callback))) - } - - fn remove_on_delete(&self, callback: PlayerDeleteCallbackId) { - self.imp.remove_on_delete(callback.0) - } -} - -pub struct PlayerUpdateCallbackId(__sdk::CallbackId); - -impl<'ctx> __sdk::TableWithPrimaryKey for PlayerTableHandle<'ctx> { - type UpdateCallbackId = PlayerUpdateCallbackId; - - fn on_update( - &self, - callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static, - ) -> PlayerUpdateCallbackId { - PlayerUpdateCallbackId(self.imp.on_update(Box::new(callback))) - } - - fn remove_on_update(&self, callback: PlayerUpdateCallbackId) { - self.imp.remove_on_update(callback.0) - } -} - -/// Access to the `id` unique index on the table `player`, -/// which allows point queries on the field of the same name -/// via the [`PlayerIdUnique::find`] method. -/// -/// Users are encouraged not to explicitly reference this type, -/// but to directly chain method calls, -/// like `ctx.db.player().id().find(...)`. -pub struct PlayerIdUnique<'ctx> { - imp: __sdk::UniqueConstraintHandle, - phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, -} - -impl<'ctx> PlayerTableHandle<'ctx> { - /// Get a handle on the `id` unique index on the table `player`. - pub fn id(&self) -> PlayerIdUnique<'ctx> { - PlayerIdUnique { - imp: self.imp.get_unique_constraint::("id"), - phantom: std::marker::PhantomData, - } - } -} - -impl<'ctx> PlayerIdUnique<'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 `identity` unique index on the table `player`, -/// which allows point queries on the field of the same name -/// via the [`PlayerIdentityUnique::find`] method. -/// -/// Users are encouraged not to explicitly reference this type, -/// but to directly chain method calls, -/// like `ctx.db.player().identity().find(...)`. -pub struct PlayerIdentityUnique<'ctx> { - imp: __sdk::UniqueConstraintHandle, - phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, -} - -impl<'ctx> PlayerTableHandle<'ctx> { - /// Get a handle on the `identity` unique index on the table `player`. - pub fn identity(&self) -> PlayerIdentityUnique<'ctx> { - PlayerIdentityUnique { - imp: self - .imp - .get_unique_constraint::<__sdk::Identity>("identity"), - phantom: std::marker::PhantomData, - } - } -} - -impl<'ctx> PlayerIdentityUnique<'ctx> { - /// Find the subscribed row whose `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) - } -} - -#[doc(hidden)] -pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { - let _table = client_cache.get_or_make_table::("player"); - _table.add_unique_constraint::("id", |row| &row.id); - _table.add_unique_constraint::<__sdk::Identity>("identity", |row| &row.identity); -} - -#[doc(hidden)] -pub(super) fn parse_table_update( - raw_updates: __ws::v2::TableUpdate, -) -> __sdk::Result<__sdk::TableUpdate> { - __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { - __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") - .with_cause(e) - .into() - }) -} - -#[allow(non_camel_case_types)] -/// Extension trait for query builder access to the table `Player`. -/// -/// Implemented for [`__sdk::QueryTableAccessor`]. -pub trait playerQueryTableAccess { - #[allow(non_snake_case)] - /// Get a query builder for the table `Player`. - fn player(&self) -> __sdk::__query_builder::Table; -} - -impl playerQueryTableAccess for __sdk::QueryTableAccessor { - fn player(&self) -> __sdk::__query_builder::Table { - __sdk::__query_builder::Table::new("player") - } -} diff --git a/jong-db/src/db/player_type.rs b/jong-db/src/db/player_type.rs deleted file mode 100644 index a4b8634..0000000 --- a/jong-db/src/db/player_type.rs +++ /dev/null @@ -1,68 +0,0 @@ -// 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 struct Player { - pub id: u32, - pub identity: __sdk::Identity, - pub name: Option, - pub lobby_id: u32, - pub ready: bool, - pub sort: bool, -} - -impl __sdk::InModule for Player { - type Module = super::RemoteModule; -} - -/// Column accessor struct for the table `Player`. -/// -/// Provides typed access to columns for query building. -pub struct PlayerCols { - pub id: __sdk::__query_builder::Col, - pub identity: __sdk::__query_builder::Col, - pub name: __sdk::__query_builder::Col>, - pub lobby_id: __sdk::__query_builder::Col, - pub ready: __sdk::__query_builder::Col, - pub sort: __sdk::__query_builder::Col, -} - -impl __sdk::__query_builder::HasCols for Player { - type Cols = PlayerCols; - fn cols(table_name: &'static str) -> Self::Cols { - PlayerCols { - id: __sdk::__query_builder::Col::new(table_name, "id"), - identity: __sdk::__query_builder::Col::new(table_name, "identity"), - name: __sdk::__query_builder::Col::new(table_name, "name"), - lobby_id: __sdk::__query_builder::Col::new(table_name, "lobby_id"), - ready: __sdk::__query_builder::Col::new(table_name, "ready"), - sort: __sdk::__query_builder::Col::new(table_name, "sort"), - } - } -} - -/// Indexed column accessor struct for the table `Player`. -/// -/// Provides typed access to indexed columns for query building. -pub struct PlayerIxCols { - pub id: __sdk::__query_builder::IxCol, - pub identity: __sdk::__query_builder::IxCol, - pub lobby_id: __sdk::__query_builder::IxCol, -} - -impl __sdk::__query_builder::HasIxCols for Player { - type IxCols = PlayerIxCols; - fn ix_cols(table_name: &'static str) -> Self::IxCols { - PlayerIxCols { - id: __sdk::__query_builder::IxCol::new(table_name, "id"), - identity: __sdk::__query_builder::IxCol::new(table_name, "identity"), - lobby_id: __sdk::__query_builder::IxCol::new(table_name, "lobby_id"), - } - } -} - -impl __sdk::__query_builder::CanBeLookupTable for Player {} diff --git a/jong-db/src/db/user_table.rs b/jong-db/src/db/user_table.rs new file mode 100644 index 0000000..fe60dce --- /dev/null +++ b/jong-db/src/db/user_table.rs @@ -0,0 +1,192 @@ +// 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::user_type::User; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `user`. +/// +/// Obtain a handle from the [`UserTableAccess::user`] method on [`super::RemoteTables`], +/// like `ctx.db.user()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.user().on_insert(...)`. +pub struct UserTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `user`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait UserTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`UserTableHandle`], which mediates access to the table `user`. + fn user(&self) -> UserTableHandle<'_>; +} + +impl UserTableAccess for super::RemoteTables { + fn user(&self) -> UserTableHandle<'_> { + UserTableHandle { + imp: self.imp.get_table::("user"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct UserInsertCallbackId(__sdk::CallbackId); +pub struct UserDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for UserTableHandle<'ctx> { + type Row = User; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = UserInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> UserInsertCallbackId { + UserInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: UserInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = UserDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> UserDeleteCallbackId { + UserDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: UserDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +pub struct UserUpdateCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::TableWithPrimaryKey for UserTableHandle<'ctx> { + type UpdateCallbackId = UserUpdateCallbackId; + + fn on_update( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static, + ) -> UserUpdateCallbackId { + UserUpdateCallbackId(self.imp.on_update(Box::new(callback))) + } + + fn remove_on_update(&self, callback: UserUpdateCallbackId) { + self.imp.remove_on_update(callback.0) + } +} + +/// Access to the `identity` unique index on the table `user`, +/// which allows point queries on the field of the same name +/// via the [`UserIdentityUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.user().identity().find(...)`. +pub struct UserIdentityUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> UserTableHandle<'ctx> { + /// Get a handle on the `identity` unique index on the table `user`. + pub fn identity(&self) -> UserIdentityUnique<'ctx> { + UserIdentityUnique { + imp: self + .imp + .get_unique_constraint::<__sdk::Identity>("identity"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> UserIdentityUnique<'ctx> { + /// Find the subscribed row whose `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) + } +} + +/// Access to the `config_id` unique index on the table `user`, +/// which allows point queries on the field of the same name +/// via the [`UserConfigIdUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.user().config_id().find(...)`. +pub struct UserConfigIdUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> UserTableHandle<'ctx> { + /// Get a handle on the `config_id` unique index on the table `user`. + pub fn config_id(&self) -> UserConfigIdUnique<'ctx> { + UserConfigIdUnique { + imp: self.imp.get_unique_constraint::("config_id"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> UserConfigIdUnique<'ctx> { + /// Find the subscribed row whose `config_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) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("user"); + _table.add_unique_constraint::<__sdk::Identity>("identity", |row| &row.identity); + _table.add_unique_constraint::("config_id", |row| &row.config_id); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::v2::TableUpdate, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} + +#[allow(non_camel_case_types)] +/// Extension trait for query builder access to the table `User`. +/// +/// Implemented for [`__sdk::QueryTableAccessor`]. +pub trait userQueryTableAccess { + #[allow(non_snake_case)] + /// Get a query builder for the table `User`. + fn user(&self) -> __sdk::__query_builder::Table; +} + +impl userQueryTableAccess for __sdk::QueryTableAccessor { + fn user(&self) -> __sdk::__query_builder::Table { + __sdk::__query_builder::Table::new("user") + } +} diff --git a/jong-db/src/db/user_type.rs b/jong-db/src/db/user_type.rs new file mode 100644 index 0000000..0f99ca8 --- /dev/null +++ b/jong-db/src/db/user_type.rs @@ -0,0 +1,62 @@ +// 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 struct User { + pub identity: __sdk::Identity, + pub name: String, + pub lobby_id: u32, + pub config_id: u32, +} + +impl __sdk::InModule for User { + type Module = super::RemoteModule; +} + +/// Column accessor struct for the table `User`. +/// +/// Provides typed access to columns for query building. +pub struct UserCols { + pub identity: __sdk::__query_builder::Col, + pub name: __sdk::__query_builder::Col, + pub lobby_id: __sdk::__query_builder::Col, + pub config_id: __sdk::__query_builder::Col, +} + +impl __sdk::__query_builder::HasCols for User { + type Cols = UserCols; + fn cols(table_name: &'static str) -> Self::Cols { + UserCols { + identity: __sdk::__query_builder::Col::new(table_name, "identity"), + name: __sdk::__query_builder::Col::new(table_name, "name"), + lobby_id: __sdk::__query_builder::Col::new(table_name, "lobby_id"), + config_id: __sdk::__query_builder::Col::new(table_name, "config_id"), + } + } +} + +/// Indexed column accessor struct for the table `User`. +/// +/// Provides typed access to indexed columns for query building. +pub struct UserIxCols { + pub config_id: __sdk::__query_builder::IxCol, + pub identity: __sdk::__query_builder::IxCol, + pub lobby_id: __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for User { + type IxCols = UserIxCols; + fn ix_cols(table_name: &'static str) -> Self::IxCols { + UserIxCols { + config_id: __sdk::__query_builder::IxCol::new(table_name, "config_id"), + identity: __sdk::__query_builder::IxCol::new(table_name, "identity"), + lobby_id: __sdk::__query_builder::IxCol::new(table_name, "lobby_id"), + } + } +} + +impl __sdk::__query_builder::CanBeLookupTable for User {} diff --git a/jong-db/src/db/view_closed_hands_table.rs b/jong-db/src/db/view_closed_hands_table.rs index b2d7c0f..613dbee 100644 --- a/jong-db/src/db/view_closed_hands_table.rs +++ b/jong-db/src/db/view_closed_hands_table.rs @@ -2,8 +2,8 @@ // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. #![allow(unused, clippy::all)] +use super::db_tile_type::DbTile; use super::hand_view_type::HandView; -use super::player_or_bot_type::PlayerOrBot; use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; /// Table handle for the table `view_closed_hands`. diff --git a/jong-db/src/db/db_wall_type.rs b/jong-db/src/db/wall_type.rs similarity index 60% rename from jong-db/src/db/db_wall_type.rs rename to jong-db/src/db/wall_type.rs index 6d46d7f..c173096 100644 --- a/jong-db/src/db/db_wall_type.rs +++ b/jong-db/src/db/wall_type.rs @@ -8,47 +8,47 @@ use super::db_tile_type::DbTile; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -pub struct DbWall { +pub struct Wall { pub lobby_id: u32, pub tiles: Vec, } -impl __sdk::InModule for DbWall { +impl __sdk::InModule for Wall { type Module = super::RemoteModule; } -/// Column accessor struct for the table `DbWall`. +/// Column accessor struct for the table `Wall`. /// /// Provides typed access to columns for query building. -pub struct DbWallCols { - pub lobby_id: __sdk::__query_builder::Col, - pub tiles: __sdk::__query_builder::Col>, +pub struct WallCols { + pub lobby_id: __sdk::__query_builder::Col, + pub tiles: __sdk::__query_builder::Col>, } -impl __sdk::__query_builder::HasCols for DbWall { - type Cols = DbWallCols; +impl __sdk::__query_builder::HasCols for Wall { + type Cols = WallCols; fn cols(table_name: &'static str) -> Self::Cols { - DbWallCols { + WallCols { lobby_id: __sdk::__query_builder::Col::new(table_name, "lobby_id"), tiles: __sdk::__query_builder::Col::new(table_name, "tiles"), } } } -/// Indexed column accessor struct for the table `DbWall`. +/// Indexed column accessor struct for the table `Wall`. /// /// Provides typed access to indexed columns for query building. -pub struct DbWallIxCols { - pub lobby_id: __sdk::__query_builder::IxCol, +pub struct WallIxCols { + pub lobby_id: __sdk::__query_builder::IxCol, } -impl __sdk::__query_builder::HasIxCols for DbWall { - type IxCols = DbWallIxCols; +impl __sdk::__query_builder::HasIxCols for Wall { + type IxCols = WallIxCols; fn ix_cols(table_name: &'static str) -> Self::IxCols { - DbWallIxCols { + WallIxCols { lobby_id: __sdk::__query_builder::IxCol::new(table_name, "lobby_id"), } } } -impl __sdk::__query_builder::CanBeLookupTable for DbWall {} +impl __sdk::__query_builder::CanBeLookupTable for Wall {} diff --git a/jong-db/src/lib.rs b/jong-db/src/lib.rs index 46735ab..0d3befb 100644 --- a/jong-db/src/lib.rs +++ b/jong-db/src/lib.rs @@ -55,13 +55,4 @@ mod conversions { Self::from_repr(value as usize).unwrap() } } - - impl From<&crate::db::PlayerOrBot> for jong_types::PlayerOrBot { - fn from(value: &crate::db::PlayerOrBot) -> Self { - match value { - crate::PlayerOrBot::Player(id) => Self::Player { id: *id }, - crate::PlayerOrBot::Bot(id) => Self::Bot { id: *id }, - } - } - } } diff --git a/jong-line/src/lib.rs b/jong-line/src/lib.rs index 2161226..07c9b33 100644 --- a/jong-line/src/lib.rs +++ b/jong-line/src/lib.rs @@ -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(()) } diff --git a/jong-line/src/reducers.rs b/jong-line/src/reducers.rs index 0f3dda9..500c4a1 100644 --- a/jong-line/src/reducers.rs +++ b/jong-line/src/reducers.rs @@ -5,11 +5,14 @@ use spacetimedb::{ ReducerContext, ScheduleAt::Interval, Table as _, rand::seq::SliceRandom, reducer, }; -use crate::tables::{ - DbTile, DbWall, GameTimer, Lobby, PlayerClock, PlayerHand, bot, game_timer, lobby as _, player, - player_clock, player_hand, tile as _, wall, +use crate::{ + reducers::hand::{discard_tile, discard_tile_private}, + tables::{ + 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 +25,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 +46,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 +55,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 +73,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 +88,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 +118,52 @@ 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 + discard_tile_private( + ctx, + lobby.id, + ctx.db.player_config().id().find(curr_player).unwrap(), + hand.working_tile.unwrap().id, + ) + .unwrap(); + return Ok(()); } - 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 +174,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 ))?; } diff --git a/jong-line/src/reducers/hand.rs b/jong-line/src/reducers/hand.rs index da67516..afba4ac 100644 --- a/jong-line/src/reducers/hand.rs +++ b/jong-line/src/reducers/hand.rs @@ -8,57 +8,61 @@ 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 config = ctx.db.player_config().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); + discard_tile_private(ctx, player.lobby_id, config, tile_id)?; - 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 - )); + Ok(()) +} + +pub(crate) fn discard_tile_private( + ctx: &ReducerContext, + lobby_id: u32, + player_config: PlayerConfig, + tile_id: u32, +) -> Result<(), String> { + let mut hand = ctx + .db + .player_hand() + .player_id() + .find(player_config.id) + .unwrap(); + + let dealt = ctx.db.tile().id().find(tile_id).unwrap(); + let drawn = hand.working_tile.unwrap(); + + 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 player_config.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(); - clock.renew(); - ctx.db.player_clock().id().update(clock); + if let Some(mut clock) = ctx.db.player_clock().player_id().find(player_config.id) { + clock.renew(); + ctx.db.player_clock().player_id().update(clock); + } - let mut lobby = ctx.db.lobby().id().find(player.lobby_id).unwrap(); + let mut lobby = ctx.db.lobby().id().find(lobby_id).unwrap(); lobby.next_player(); ctx.db.lobby().id().update(lobby); - debug!("player {} discarded tile {:?}", player.id, dealt_tile.tile); - Ok(()) } diff --git a/jong-line/src/reducers/lobby.rs b/jong-line/src/reducers/lobby.rs index 17b658a..13bbfc7 100644 --- a/jong-line/src/reducers/lobby.rs +++ b/jong-line/src/reducers/lobby.rs @@ -1,48 +1,96 @@ use std::time::Duration; -use log::info; +use log::{debug, 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); + + Ok(()) +} + +#[reducer] +pub fn set_ready(ctx: &ReducerContext, ready: bool) -> Result<(), String> { + let mut user = ctx.db.user().identity().find(ctx.sender()).unwrap(); + let mut player = ctx.db.player_config().id().find(user.config_id).unwrap(); + + player.ready = ready; + let player = ctx.db.player_config().id().update(player); + + if player.ready { + debug!("player readied"); + } else { + debug!("player unreadied"); + } + + if let Some(mut lobby) = ctx.db.lobby().id().find(user.lobby_id) + && lobby.players.len() == 4 + && lobby.players.iter().all(|id| { + ctx.db + .player_config() + .id() + .find(id) + .is_some_and(|p| p.ready) + }) + && ctx.db.game_timer().lobby_id().find(user.lobby_id).is_none() + { + lobby.game_state = jong_types::states::GameState::Setup; + 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(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 { + // TODO if lobby doesn't exist, reset player state? + // FIXME return error here + } - info!("player {} joined lobby {}", player.id, lobby_id); Ok(()) } @@ -51,57 +99,26 @@ 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: false, + }); 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(()) } else { - Err("lobby doesn't exist".into()) + Err(format!("lobby {lobby_id} doesn't exist or is full")) } } - -#[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? - let game_timer = ctx.db.game_timer().insert(GameTimer { - id: 0, - lobby_id: lobby.id, - 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); - - return Err(format!("couldn't find lobby with id: {}", player.lobby_id)); - } - - Ok(()) -} diff --git a/jong-line/src/tables.rs b/jong-line/src/tables.rs index fdf75d5..0b09f6c 100644 --- a/jong-line/src/tables.rs +++ b/jong-line/src/tables.rs @@ -1,13 +1,27 @@ -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, public)] +#[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 +29,7 @@ pub struct Lobby { #[auto_inc] pub id: u32, - pub players: Vec, + pub players: Vec, pub dealer_idx: u8, pub current_idx: u8, @@ -23,8 +37,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 +63,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, - - #[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, - pub hand: Vec, - - /// drawn or callable tile - pub working_tile: Option, -} - #[table(accessor = bot, public)] pub struct Bot { #[primary_key] @@ -100,75 +72,93 @@ 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, pub pond: Vec, + /// drawn or callable tile pub working_tile: Option, } -#[table(accessor = game_timer, scheduled(advance_game_private), public)] -pub struct GameTimer { - #[primary_key] - #[auto_inc] - pub id: u64, - +#[table(accessor = game_timer, scheduled(advance_game_private))] +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 { 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, Copy)] +#[derive(SpacetimeType, Clone)] pub struct HandView { - pub player: PlayerOrBot, + pub player_id: u32, pub hand_length: u8, // pub melds: u8, + pub pond: Vec, pub drawn: bool, } #[view(accessor = view_closed_hands, public)] fn view_closed_hands(ctx: &ViewContext) -> Vec { - 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, - 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, - 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 { diff --git a/jong-types/src/lib.rs b/jong-types/src/lib.rs index 6eecb7e..9932bee 100644 --- a/jong-types/src/lib.rs +++ b/jong-types/src/lib.rs @@ -17,9 +17,3 @@ pub mod tiles; pub use states::*; pub use tiles::*; -#[derive(Debug, ..Copy, ..Eq, Hash)] -#[derive(SpacetimeType)] -pub enum PlayerOrBot { - Player { id: u32 }, - Bot { id: u32 }, -} diff --git a/jong/src/riichi.rs b/jong/src/riichi.rs index d7a817e..7da69e4 100644 --- a/jong/src/riichi.rs +++ b/jong/src/riichi.rs @@ -1,11 +1,16 @@ +use std::time::Duration; + use bevy::prelude::*; +use bevy::render::render_resource::AsBindGroupShaderType; use bevy_spacetimedb::{ReadInsertMessage, ReadInsertUpdateMessage, ReadUpdateMessage, StdbPlugin}; use jong_db::{ - self, GameTimerTableAccess, PlayerClockTableAccess, add_bot, advance_game, set_ready, + self, PlayerClockTableAccess, PlayerConfigTableAccess, UserTableAccess, add_bot, advance_game, + join_or_create_lobby, lobbyQueryTableAccess, player_configQueryTableAccess, set_ready, + userQueryTableAccess, }; use jong_db::{ - BotTableAccess, DbConnection, LobbyTableAccess, PlayerHand, PlayerTableAccess, RemoteTables, + BotTableAccess, DbConnection, LobbyTableAccess, PlayerHand, RemoteTables, ViewClosedHandsTableAccess, ViewHandTableAccess, }; use jong_types::*; @@ -23,15 +28,14 @@ impl Plugin for Riichi { .with_uri("http://localhost:3000") .with_module_name("jong-line") .with_run_fn(DbConnection::run_threaded) + .add_table(RemoteTables::user) .add_table(RemoteTables::lobby) - .add_table(RemoteTables::player) .add_table(RemoteTables::bot) + .add_table(RemoteTables::player_config) .add_table(RemoteTables::player_clock) // TODO check bevy_spacetimedb PR status - .add_view_with_pk(RemoteTables::view_hand, |p| p.id) - .add_view_with_pk(RemoteTables::view_closed_hands, |p| { - PlayerOrBot::from(&p.player) - }); + .add_view_with_pk(RemoteTables::view_hand, |p| p.player_id) + .add_view_with_pk(RemoteTables::view_closed_hands, |p| p.player_id); let plugins = if let Some(token) = creds_store().load().expect("i/o error loading credentials") { // FIXME patch plugin so this takes Option? @@ -43,37 +47,31 @@ impl Plugin for Riichi { app.add_plugins(plugins) .init_state::() .add_sub_state::() - .add_message::() - .add_message::() + .init_resource::() .add_systems(Startup, subscriptions) .add_systems(Update, (connection::on_connect, connection::on_disconnect)) .add_systems( Update, ( - (on_player_insert_update, on_lobby_insert_update), - (sync_player), + on_user_insert_update, + on_lobby_insert_update, + on_bot_insert_update, + on_player_config_insert_update, ), ) .add_systems( Update, - ((on_view_hand_insert, on_view_hand_update), (sync_open_hand)) + (on_view_hand_insert, on_view_hand_update) .run_if(in_state(GameState::Play).or(in_state(GameState::Deal))), ); } } -/// on subscribe we need to check: -/// if we're in a game already -/// spawn (restore) all current game state -/// spawn all players and hands -/// else -/// spawn self player -/// then -/// wait for lobbies -/// spawn other players -/// spawn all hands and ponds - fn subscriptions(stdb: SpacetimeDB, mut commands: Commands) { + while stdb.try_identity().is_none() { + bevy::platform::thread::sleep(Duration::from_secs(1)); + } + let (tx, rx) = std::sync::mpsc::channel(); stdb.subscription_builder() .on_applied(move |_| { @@ -83,194 +81,51 @@ fn subscriptions(stdb: SpacetimeDB, mut commands: Commands) { .on_error(|_, err| { error!("subs failed: {err}"); }) - .subscribe([ - // TODO change these to sub/unsub based on being in lobby and some such - format!( - "SELECT p.* FROM player p WHERE p.identity = '{}'", - stdb.identity() - ), - // TODO add filter for lobby id for all of these later - "SELECT l.* FROM lobby l JOIN player p ON l.id = p.lobby_id".to_string(), - "SELECT p.* FROM player p JOIN lobby l ON p.lobby_id = l.id".to_string(), - "SELECT c.* FROM player_clock c JOIN player p ON c.player_id = p.id".to_string(), - "SELECT b.* FROM bot b JOIN lobby l ON l.id = b.lobby_id".to_string(), - "SELECT * FROM view_hand".to_string(), - "SELECT * FROM view_closed_hands".to_string(), - ]); + .add_query(|q| q.from.user().r#where(|u| u.identity.eq(stdb.identity()))) + .subscribe(); + // TODO more sub/unsub based on being in lobby and some such, current done in tui::keybaord while let Ok(()) = rx.recv() { // todo!() } } -#[derive(Message)] -struct SyncPlayer(u32); +fn on_user_insert_update() {} -#[derive(Message)] -struct SyncOpenHand(u32); - -#[derive(Message)] -struct SyncClosedHand(PlayerOrBot); - -#[derive(Message)] -struct SyncPlayerClock(u32); - -fn sync_player( - stdb: SpacetimeDB, - - mut messages: MessageReader, - mut commands: Commands, - - players: Query<(Entity, &Player)>, -) { - for SyncPlayer(id) in messages.read() { - trace!("sync_player"); - let Some(player) = stdb.db().player().id().find(id) else { - todo!() - }; - - let player_ent = players - .iter() - .find_map(|(e, p)| (p.id == PlayerOrBot::Player { id: player.id }).then_some(e)) - .unwrap_or_else(|| { - commands - .spawn(Player { - id: PlayerOrBot::Player { id: player.id }, - }) - .id() - }); - - if player.identity == stdb.identity() { - commands.entity(player_ent).insert(MainPlayer); - } else { - } - } +#[derive(Resource, Default)] +pub struct Lobby { + pub players: Vec, } -fn sync_open_hand( - stdb: SpacetimeDB, - - mut messages: MessageReader, - mut commands: Commands, - - tiles: Query<(Entity, &TileId)>, - hands: Query<(Entity, &Hand)>, - ponds: Query<(Entity, &Pond)>, - - mut next_turnstate: ResMut>, -) { - for SyncOpenHand(id) in messages.read() { - trace!("sync_open_hand"); - let Some(player_hand) = stdb.db().view_hand().iter().find(|hand| hand.id == *id) else { - todo!() - }; - - let hand_ent = hands - .iter() - .find_map(|(e, h)| (h.owner == PlayerOrBot::Player { id: *id }).then_some(e)) - .unwrap_or_else(|| { - commands - .spawn(Hand { - owner: PlayerOrBot::Player { id: *id }, - }) - .id() - }); - let pond_ent = ponds - .iter() - .find_map(|(e, h)| (h.owner == PlayerOrBot::Player { id: *id }).then_some(e)) - .unwrap_or_else(|| { - commands - .spawn(Pond { - owner: PlayerOrBot::Player { id: *id }, - }) - .id() - }); - - // hand and pond both still need ability to spawn for the reconnect case - let hand: Vec = player_hand - .hand - .iter() - .map(|dbt| { - tiles - .iter() - .find_map(|(e, i)| (i.0 == dbt.id).then_some(e)) - .unwrap_or_else(|| commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id))).id()) - }) - .collect(); - let pond: Vec = player_hand - .pond - .iter() - .map(|dbt| { - tiles - .iter() - .find_map(|(e, i)| (i.0 == dbt.id).then_some(e)) - .unwrap_or_else(|| commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id))).id()) - }) - .collect(); - - commands.entity(hand_ent).replace_children(&hand); - commands.entity(pond_ent).replace_children(&pond); - - if let Some(dbt) = player_hand.working_tile - && player_hand.turn_state == jong_db::TurnState::Tsumo - { - commands.spawn((Drawn, Tile::from(&dbt.tile), TileId(dbt.id))); - } - - next_turnstate.set(player_hand.turn_state.into()); - } -} - -fn sync_closed_hand( - stdb: SpacetimeDB, - - mut events: MessageReader, - mut commands: Commands, - - hands: Query<&mut Closed, With>, - ponds: Query<&mut Children, With>, -) { -} - -fn sync_player_clock() {} - -fn on_player_insert_update( - mut db_messages: ReadInsertUpdateMessage, - - mut writer: MessageWriter, -) { - for msg in db_messages.read() { - trace!("on_player_insert_update"); - writer.write(SyncPlayer(msg.new.id)); - } -} - -fn on_bot_insert_update() {} - fn on_lobby_insert_update( stdb: SpacetimeDB, mut messages: ReadInsertUpdateMessage, mut commands: Commands, + + mut lobby: ResMut, + players: Query<(Entity, &Player)>, + mut next_gamestate: ResMut>, ) { for msg in messages.read() { // trace!("on_lobby_insert_update msg:\n{:#?}", msg.new); - let player = stdb + let player = stdb.db().user().identity().find(&stdb.identity()).unwrap(); + let player_config = stdb .db() - .player() - .identity() - .find(&stdb.identity()) + .player_config() + .id() + .find(&player.config_id) .unwrap(); match msg.new.game_state { jong_db::GameState::None => { - trace!("game entered none"); + // trace!("game entered none"); } jong_db::GameState::Lobby => { - trace!("game entered lobby"); - if !player.ready { + // trace!("game entered lobby"); + if !player_config.ready { for _ in 0..3 { stdb.reducers().add_bot(player.lobby_id).unwrap(); } @@ -279,101 +134,202 @@ fn on_lobby_insert_update( } } jong_db::GameState::Setup => { - trace!("game entered setup"); + // trace!("game entered setup"); } jong_db::GameState::Deal => { - trace!("game entered deal"); + // trace!("game entered deal"); } jong_db::GameState::Play => { - trace!("game entered play"); + // trace!("game entered play"); } jong_db::GameState::Exit => { - trace!("game enetered exit"); + // trace!("game enetered exit"); } } + lobby.players = msg.new.players.clone(); next_gamestate.set(msg.new.game_state.into()); } } +fn on_bot_insert_update(stdb: SpacetimeDB, mut messages: ReadInsertUpdateMessage) { + for msg in messages.read() {} +} + +fn on_player_config_insert_update( + stdb: SpacetimeDB, + mut messages: ReadInsertUpdateMessage, + + mut commands: Commands, + + players: Query<(Entity, &Player)>, +) { + for msg in messages.read() { + trace!("on_player_insert_update"); + let player = &msg.new; + + let player_ent = players + .iter() + .find_map(|(e, p)| (p.id == player.id).then_some(e)) + .unwrap_or_else(|| commands.spawn(Player { id: player.id }).id()); + + if player.id + == stdb + .db() + .user() + .identity() + .find(&stdb.identity()) + .unwrap() + .config_id + { + commands.entity(player_ent).insert(MainPlayer); + } else { + } + } +} + fn on_view_hand_insert( mut messages: ReadInsertMessage, - mut writer: MessageWriter, + mut commands: Commands, + tiles: Query<(Entity, &TileId)>, + hands: Query<(Entity, &Hand)>, + ponds: Query<(Entity, &Pond)>, + mut next_turnstate: ResMut>, ) { for msg in messages.read() { trace!("on_view_hand_insert"); - writer.write(SyncOpenHand(msg.row.id)); + sync_open_hand( + &mut commands, + tiles, + hands, + ponds, + &mut next_turnstate, + &msg.row, + ); } } fn on_view_hand_update( mut messages: ReadUpdateMessage, - mut writer: MessageWriter, - // mut commands: Commands, - // tiles: Query<(Entity, &TileId)>, - - // main_player: Single<(Entity, &Children), With>, - - // hand: Query>, - // pond: Query>, - // // drawn: Option>>, - // mut next_turnstate: ResMut>, + mut commands: Commands, + tiles: Query<(Entity, &TileId)>, + hands: Query<(Entity, &Hand)>, + ponds: Query<(Entity, &Pond)>, + mut next_turnstate: ResMut>, ) { - // TODO can this and similar run at startup or on play/reconnect? for msg in messages.read() { trace!("on_view_hand_update"); - writer.write(SyncOpenHand(msg.new.player_id)); + sync_open_hand( + &mut commands, + tiles, + hands, + ponds, + &mut next_turnstate, + &msg.new, + ); } - // let hand_tiles: Vec<_> = msg - // .new - // .hand - // .iter() - // .map(|dbt| { - // tiles - // .iter() - // .find_map(|(e, t)| if *t == TileId(dbt.id) { Some(e) } else { None }) - // .unwrap_or_else(|| commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id))).id()) - // }) - // .collect(); - // let pond_tiles: Vec<_> = msg - // .new - // .pond - // .iter() - // .map(|dbt| { - // tiles - // .iter() - // .find_map(|(e, t)| if *t == TileId(dbt.id) { Some(e) } else { None }) - // .unwrap_or_else(|| commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id))).id()) - // }) - // .collect(); - - // commands - // .entity(hand.iter().find(|e| main_player.1.contains(e)).unwrap()) - // .replace_children(&hand_tiles); - // commands - // .entity(pond.iter().find(|e| main_player.1.contains(e)).unwrap()) - // .replace_children(&pond_tiles); - - // match msg.new.turn_state { - // jong_db::TurnState::None => { - // trace!("turnstate none"); - // // TODO do we reconcile hand state here or in ::End? - // } - // jong_db::TurnState::Tsumo => { - // trace!("turnstate tsumo"); - // let dbt = msg - // .new - // .working_tile - // .as_ref() - // .expect("entered tsumo without a drawn tile"); - // commands.spawn((Drawn, Tile::from(&dbt.tile), TileId(dbt.id))); - // } - // jong_db::TurnState::Menzen => todo!(), - // jong_db::TurnState::RiichiKan => todo!(), - // jong_db::TurnState::RonChiiPonKan => todo!(), - // jong_db::TurnState::End => todo!(), - // } - - // next_turnstate.set(msg.new.turn_state.into()); - // } } + +fn sync_open_hand( + commands: &mut Commands, + tiles: Query<(Entity, &TileId)>, + hands: Query<(Entity, &Hand)>, + ponds: Query<(Entity, &Pond)>, + next_turnstate: &mut ResMut>, + player_hand: &PlayerHand, +) { + let player_id = player_hand.player_id; + let hand_ent = hands + .iter() + .find_map(|(e, h)| (h.player_id == player_id).then_some(e)) + .unwrap_or_else(|| commands.spawn(Hand { player_id }).id()); + let pond_ent = ponds + .iter() + .find_map(|(e, p)| (p.player_id == player_id).then_some(e)) + .unwrap_or_else(|| commands.spawn(Pond { player_id }).id()); + + // hand and pond both still need ability to spawn for the reconnect case + let hand: Vec = player_hand + .hand + .iter() + .map(|dbt| { + tiles + .iter() + .find_map(|(e, i)| (i.0 == dbt.id).then_some(e)) + .unwrap_or_else(|| commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id))).id()) + }) + .collect(); + let pond: Vec = player_hand + .pond + .iter() + .map(|dbt| { + tiles + .iter() + .find_map(|(e, i)| (i.0 == dbt.id).then_some(e)) + .unwrap_or_else(|| commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id))).id()) + }) + .collect(); + + commands.entity(hand_ent).replace_children(&hand); + commands.entity(pond_ent).replace_children(&pond); + + if let Some(dbt) = &player_hand.working_tile + && player_hand.turn_state == jong_db::TurnState::Tsumo + { + commands.spawn((Drawn, Tile::from(&dbt.tile), TileId(dbt.id))); + } + + next_turnstate.set(player_hand.turn_state.into()); +} + +// fn sync_closed_hand( +// stdb: SpacetimeDB, + +// mut messages: MessageReader, +// mut commands: Commands, + +// tiles: Query<(Entity, &TileId)>, +// hands: Query<(Entity, &mut Closed, &Hand)>, +// ponds: Query<(Entity, &Pond)>, +// ) { +// for SyncClosedHand(id) in messages.read() { +// let Some(hand_view) = stdb +// .db() +// .view_closed_hands() +// .iter() +// .find(|hand| PlayerOrBot::from(&hand.player) == *id) +// else { +// todo!() +// }; + +// let hand_ent = hands +// .iter() +// .find_map(|(e, c, h)| (h.owner == *id).then_some(e)) +// .unwrap_or_else(|| commands.spawn(Hand { owner: *id }).id()); +// let pond_ent = ponds +// .iter() +// .find_map(|(e, p)| (p.owner == *id).then_some(e)) +// .unwrap_or_else(|| commands.spawn(Pond { owner: *id }).id()); + +// let pond: Vec = hand_view +// .pond +// .iter() +// .map(|dbt| { +// tiles +// .iter() +// .find_map(|(e, i)| (i.0 == dbt.id).then_some(e)) +// .unwrap_or_else(|| commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id))).id()) +// }) +// .collect(); + +// commands.entity(hand_ent).insert(Closed { +// length: hand_view.hand_length, +// }); +// commands.entity(pond_ent).replace_children(&pond); + +// if hand_view.drawn { +// commands.spawn(Drawn); +// } +// } +// } + diff --git a/jong/src/riichi/connection.rs b/jong/src/riichi/connection.rs index c6d220b..b869105 100644 --- a/jong/src/riichi/connection.rs +++ b/jong/src/riichi/connection.rs @@ -8,9 +8,6 @@ pub(crate) fn on_connect(stdb: SpacetimeDB, mut messages: ReadStdbConnectedMessa for msg in messages.read() { info!("you're now jongline"); - // FIXME hack that doesn't work for startup crash? - while stdb.try_identity().is_none() {} - debug!("with identity: {}", stdb.identity()); creds_store() .save(&msg.access_token) diff --git a/jong/src/riichi/player.rs b/jong/src/riichi/player.rs index a2dc497..bb90d87 100644 --- a/jong/src/riichi/player.rs +++ b/jong/src/riichi/player.rs @@ -1,10 +1,8 @@ use bevy::prelude::*; -use jong_types::PlayerOrBot; - #[derive(Component)] pub struct Player { - pub id: PlayerOrBot, + pub id: u32, } #[derive(Component)] @@ -13,27 +11,31 @@ pub struct MainPlayer; #[derive(Component)] pub struct CurrentPlayer; +#[derive(Component)] +pub struct TopLord; +#[derive(Component)] +pub struct RightLord; +#[derive(Component)] +pub struct LeftLord; + #[derive(Component, PartialEq, Eq, Debug)] pub struct TileId(pub u32); #[derive(Component)] pub struct Hand { - pub owner: PlayerOrBot, + pub player_id: u32, } #[derive(Component)] pub struct Closed { - pub(crate) owner: PlayerOrBot, + pub player_id: u32, pub(crate) length: u8, } #[derive(Component)] pub struct Pond { - pub owner: PlayerOrBot, + pub player_id: u32, } #[derive(Component)] pub struct Drawn; - -#[derive(Component)] -pub struct Discarded; diff --git a/jong/src/tui.rs b/jong/src/tui.rs index 1db3695..fa38da2 100644 --- a/jong/src/tui.rs +++ b/jong/src/tui.rs @@ -8,7 +8,7 @@ use spacetimedb_sdk::Table; use tui_logger::TuiWidgetState; use jong::{SpacetimeDB, riichi::player::*}; -use jong_db::{self, GameTimerTableAccess, advance_game, discard_tile as _}; +use jong_db::{self, advance_game, discard_tile as _}; use jong_types::states::{GameState, TurnState}; mod input; @@ -104,13 +104,12 @@ fn discard_tile( mut commands: Commands, // main_player: Single<&Children, With>, - // only main player will have a Drawn tile? + // only main player will have a Drawn tile with TileId? drawn: Single<(Entity, &TileId), With>, tiles: Query<&TileId>, ) { // TODO disable this when we're not current player? if let Ok(tile_id) = tiles.get(selected.0) { - trace!("{:?}, {tile_id:?}", selected.0); stdb.reducers().discard_tile(tile_id.0).unwrap(); stdb.reducers().advance_game().unwrap(); commands.entity(drawn.0).remove::(); diff --git a/jong/src/tui/input/keyboard.rs b/jong/src/tui/input/keyboard.rs index 556e17d..64685d6 100644 --- a/jong/src/tui/input/keyboard.rs +++ b/jong/src/tui/input/keyboard.rs @@ -1,11 +1,17 @@ use bevy::prelude::*; use bevy_ratatui::crossterm::event::KeyCode; use bevy_ratatui::event::KeyMessage; -use jong_db::PlayerTableAccess; -use jong_db::join_or_create_lobby; +use spacetimedb_sdk::DbContext; +use spacetimedb_sdk::Table; use tui_logger::TuiWidgetEvent; use jong::SpacetimeDB; +use jong_db::{ + LobbyTableAccess, UserTableAccess, botQueryTableAccess, + join_or_create_lobby, lobbyQueryTableAccess, player_clockQueryTableAccess, + player_configQueryTableAccess, view_closed_handsQueryTableAccess, view_handQueryTableAccess, +}; +use jong_types::GameState; use crate::tui::layout::Overlays; use crate::tui::states::ConsoleWidget; @@ -38,11 +44,51 @@ pub(crate) fn keyboard( } } + if *curr_gamestate.get() != GameState::None { + next_tuistate.set(TuiState::InGame); + continue; + } + match curr_tuistate.get() { TuiState::MainMenu => match key { KeyCode::Char('p') => { - // TODO this should be a msg/signal and handle the correct lobby - stdb.reducers().join_or_create_lobby(0).unwrap(); + // TODO this should be a msg/signal and handle the correct lobby + stdb.reducers() + .join_or_create_lobby_then(0, |ctx, res| { + if res.is_ok() { + let user = + ctx.db().user().identity().find(&ctx.identity()).unwrap(); + let lobby_id = user.lobby_id; + + ctx.subscription_builder() + .on_applied(|ctx| { + // TODO subscribe other player configs? + // ctx.subscription_builder() + // .add_query(|q| { + // q.from.player_config().left_semijoin(right, on) + // }) + // .subscribe(); + }) + .add_query(|q| q.from.lobby().r#where(|l| l.id.eq(lobby_id))) + .add_query(|q| { + q.from.bot().r#where(|b| b.lobby_id.eq(lobby_id)) + }) + .add_query(|q| { + q.from + .player_config() + .r#where(|pc| pc.id.eq(user.config_id)) + }) + .add_query(|q| { + q.from + .player_clock() + .r#where(|pk| pk.player_id.eq(user.config_id)) + }) + .add_query(|q| q.from.view_hand()) + .add_query(|q| q.from.view_closed_hands()) + .subscribe(); + } + }) + .unwrap(); next_tuistate.set(TuiState::InGame); } KeyCode::Char('z') => { diff --git a/jong/src/tui/render.rs b/jong/src/tui/render.rs index 566c2d4..2d40ff6 100644 --- a/jong/src/tui/render.rs +++ b/jong/src/tui/render.rs @@ -152,19 +152,19 @@ pub(crate) fn render_main_hand( main_player: Single<&Player, With>, - hand: Query<(&Hand, &Children)>, + hands: Query<(&Hand, &Children)>, drawn_tile: Option>>, ) -> Result { let mut frame = tui.get_frame(); debug_blocks(layouts.clone(), &mut frame); - if hand.is_empty() { + if hands.is_empty() { return Ok(()); } - let hand: Vec<_> = hand + let hand: Vec<_> = hands .iter() - .find_map(|(h, c)| (main_player.id == h.owner).then_some(c)) + .find_map(|(h, c)| (main_player.id == h.player_id).then_some(c)) .unwrap() .iter() .map(|entity| -> Result<_> { @@ -271,7 +271,7 @@ pub(crate) fn render_main_pond( let pond: Vec<_> = pond .iter() - .find_map(|(p, c)| (main_player.id == p.owner).then_some(c)) + .find_map(|(p, c)| (main_player.id == p.player_id).then_some(c)) .unwrap() .iter() .map(|entity| -> Result<_> { diff --git a/spacetime.json b/spacetime.json index 339050e..865cbce 100644 --- a/spacetime.json +++ b/spacetime.json @@ -2,7 +2,6 @@ "dev": { "run": "" }, - "_source-config": "spacetime.local.json", "module-path": "jong-line", "server": "local", "database": "jong-line"