From 8f606543f56b56f108cc6e7556fe80a9ee35c1ee Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Sun, 8 Mar 2026 19:57:39 -0700 Subject: [PATCH 1/2] order views after player anyways --- devenv.nix | 2 +- jong/src/riichi.rs | 155 ++++++++++++++++++++++------------ jong/src/riichi/connection.rs | 2 +- jong/src/riichi/player.rs | 9 +- jong/src/tui/render.rs | 16 ++-- 5 files changed, 113 insertions(+), 71 deletions(-) diff --git a/devenv.nix b/devenv.nix index 782205c..3e83f98 100644 --- a/devenv.nix +++ b/devenv.nix @@ -45,7 +45,7 @@ ]; env.LD_LIBRARY_PATH = lib.makeLibraryPath packages; env.RUST_LOG = "jong=trace"; - env.RUST_BACKTRACE = "full"; + # env.RUST_BACKTRACE = "full"; # https://devenv.sh/languages/ languages.rust = { diff --git a/jong/src/riichi.rs b/jong/src/riichi.rs index 7da69e4..c255b43 100644 --- a/jong/src/riichi.rs +++ b/jong/src/riichi.rs @@ -5,9 +5,9 @@ use bevy::render::render_resource::AsBindGroupShaderType; use bevy_spacetimedb::{ReadInsertMessage, ReadInsertUpdateMessage, ReadUpdateMessage, StdbPlugin}; use jong_db::{ - self, PlayerClockTableAccess, PlayerConfigTableAccess, UserTableAccess, add_bot, advance_game, - join_or_create_lobby, lobbyQueryTableAccess, player_configQueryTableAccess, set_ready, - userQueryTableAccess, + self, HandView, PlayerClockTableAccess, PlayerConfigTableAccess, UserTableAccess, add_bot, + advance_game, join_or_create_lobby, lobbyQueryTableAccess, player_configQueryTableAccess, + set_ready, userQueryTableAccess, }; use jong_db::{ BotTableAccess, DbConnection, LobbyTableAccess, PlayerHand, RemoteTables, @@ -61,7 +61,13 @@ impl Plugin for Riichi { ) .add_systems( Update, - (on_view_hand_insert, on_view_hand_update) + ( + on_view_hand_insert, + on_view_hand_update, + // on_view_closed_insert, + // on_view_closed_update, + ) + .after(on_player_config_insert_update) .run_if(in_state(GameState::Play).or(in_state(GameState::Deal))), ); } @@ -165,13 +171,16 @@ fn on_player_config_insert_update( players: Query<(Entity, &Player)>, ) { for msg in messages.read() { - trace!("on_player_insert_update"); + trace!("on_player_config_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()); + .unwrap_or_else(|| { + trace!("spawn_player"); + commands.spawn(Player { id: player.id }).id() + }); if player.id == stdb @@ -191,6 +200,7 @@ fn on_player_config_insert_update( fn on_view_hand_insert( mut messages: ReadInsertMessage, mut commands: Commands, + players: Populated<(Entity, &Player, Option<&Children>)>, tiles: Query<(Entity, &TileId)>, hands: Query<(Entity, &Hand)>, ponds: Query<(Entity, &Pond)>, @@ -200,6 +210,7 @@ fn on_view_hand_insert( trace!("on_view_hand_insert"); sync_open_hand( &mut commands, + &players, tiles, hands, ponds, @@ -212,6 +223,7 @@ fn on_view_hand_insert( fn on_view_hand_update( mut messages: ReadUpdateMessage, mut commands: Commands, + players: Populated<(Entity, &Player, Option<&Children>)>, tiles: Query<(Entity, &TileId)>, hands: Query<(Entity, &Hand)>, ponds: Query<(Entity, &Pond)>, @@ -221,6 +233,7 @@ fn on_view_hand_update( trace!("on_view_hand_update"); sync_open_hand( &mut commands, + &players, tiles, hands, ponds, @@ -232,6 +245,7 @@ fn on_view_hand_update( fn sync_open_hand( commands: &mut Commands, + players: &Populated<(Entity, &Player, Option<&Children>)>, tiles: Query<(Entity, &TileId)>, hands: Query<(Entity, &Hand)>, ponds: Query<(Entity, &Pond)>, @@ -239,14 +253,28 @@ fn sync_open_hand( player_hand: &PlayerHand, ) { let player_id = player_hand.player_id; + + let (player_ent, player_children) = players + .iter() + .find_map(|(e, p, c)| (p.id == player_id).then_some((e, c))) + .unwrap(); + 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()); + .find_map(|(e, _)| player_children.is_some_and(|c| c.contains(&e)).then_some(e)) + .unwrap_or_else(|| { + let id = commands.spawn(Hand).id(); + commands.entity(player_ent).add_child(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()); + .find_map(|(e, _)| player_children.is_some_and(|c| c.contains(&e)).then_some(e)) + .unwrap_or_else(|| { + let id = commands.spawn(Pond).id(); + commands.entity(player_ent).add_child(id); + id + }); // hand and pond both still need ability to spawn for the reconnect case let hand: Vec = player_hand @@ -276,60 +304,77 @@ fn sync_open_hand( 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))); + let id = commands + .spawn((Drawn, Tile::from(&dbt.tile), TileId(dbt.id))) + .id(); + commands.entity(player_ent).add_child(id); + } else { + // TODO clear Drawn from auto-discarded tile } next_turnstate.set(player_hand.turn_state.into()); } -// fn sync_closed_hand( -// stdb: SpacetimeDB, - -// mut messages: MessageReader, +// fn on_view_closed_insert( +// mut messages: ReadInsertMessage, // mut commands: Commands, - // tiles: Query<(Entity, &TileId)>, -// hands: Query<(Entity, &mut Closed, &Hand)>, +// hands: Query<(Entity, &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); -// } +// for msg in messages.read() { +// sync_closed_hand(&mut commands, tiles, hands, ponds, &msg.row); // } // } +// fn on_view_closed_update( +// mut messages: ReadUpdateMessage, +// mut commands: Commands, +// tiles: Query<(Entity, &TileId)>, +// hands: Query<(Entity, &Hand)>, +// ponds: Query<(Entity, &Pond)>, +// ) { +// for msg in messages.read() { +// sync_closed_hand(&mut commands, tiles, hands, ponds, &msg.new); +// } +// } + +// fn sync_closed_hand( +// commands: &mut Commands, +// tiles: Query<(Entity, &TileId)>, +// hands: Query<(Entity, &Hand)>, +// ponds: Query<(Entity, &Pond)>, +// hand_view: &HandView, +// ) { +// let player_id = hand_view.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()); + +// 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); +// } else { +// // TODO clear Drawn from auto-discarded tile +// } +// } diff --git a/jong/src/riichi/connection.rs b/jong/src/riichi/connection.rs index b869105..f33d96f 100644 --- a/jong/src/riichi/connection.rs +++ b/jong/src/riichi/connection.rs @@ -8,7 +8,7 @@ pub(crate) fn on_connect(stdb: SpacetimeDB, mut messages: ReadStdbConnectedMessa for msg in messages.read() { info!("you're now jongline"); - debug!("with identity: {}", stdb.identity()); + // debug!("with identity: {}", stdb.identity()); creds_store() .save(&msg.access_token) .expect("i/o error saving token"); diff --git a/jong/src/riichi/player.rs b/jong/src/riichi/player.rs index bb90d87..a489ad6 100644 --- a/jong/src/riichi/player.rs +++ b/jong/src/riichi/player.rs @@ -22,20 +22,15 @@ pub struct LeftLord; pub struct TileId(pub u32); #[derive(Component)] -pub struct Hand { - pub player_id: u32, -} +pub struct Hand; #[derive(Component)] pub struct Closed { - pub player_id: u32, pub(crate) length: u8, } #[derive(Component)] -pub struct Pond { - pub player_id: u32, -} +pub struct Pond; #[derive(Component)] pub struct Drawn; diff --git a/jong/src/tui/render.rs b/jong/src/tui/render.rs index 2d40ff6..d8a48f7 100644 --- a/jong/src/tui/render.rs +++ b/jong/src/tui/render.rs @@ -150,9 +150,9 @@ pub(crate) fn render_main_hand( tiles: Query<&jong_types::Tile>, hovered: Query>, - main_player: Single<&Player, With>, + main_player: Single<(&Player, &Children), With>, - hands: Query<(&Hand, &Children)>, + hands: Query<(Entity, &Hand, &Children)>, drawn_tile: Option>>, ) -> Result { let mut frame = tui.get_frame(); @@ -164,7 +164,7 @@ pub(crate) fn render_main_hand( let hand: Vec<_> = hands .iter() - .find_map(|(h, c)| (main_player.id == h.player_id).then_some(c)) + .find_map(|(e, h, c)| (main_player.1.contains(&e)).then_some(c)) .unwrap() .iter() .map(|entity| -> Result<_> { @@ -217,7 +217,9 @@ pub(crate) fn render_main_hand( } // tsumo tile - if let Some(drawn_tile) = drawn_tile { + if let Some(drawn_tile) = drawn_tile + && main_player.1.contains(&*drawn_tile) + { let mut area = drawn_area.resize(Size { width: 5, height: 4, @@ -259,9 +261,9 @@ pub(crate) fn render_main_pond( tiles: Query<&Tile>, hovered: Query>, - main_player: Single<&Player, With>, + main_player: Single<(&Player, &Children), With>, - pond: Query<(&Pond, &Children)>, + pond: Query<(Entity, &Pond, &Children)>, ) -> Result { let mut frame = tui.get_frame(); @@ -271,7 +273,7 @@ pub(crate) fn render_main_pond( let pond: Vec<_> = pond .iter() - .find_map(|(p, c)| (main_player.id == p.player_id).then_some(c)) + .find_map(|(e, p, c)| (main_player.1.contains(&e)).then_some(c)) .unwrap() .iter() .map(|entity| -> Result<_> { From 1b5b7c9e7733b8889e61993003e915c44c7c7457 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Mon, 9 Mar 2026 03:33:20 -0700 Subject: [PATCH 2/2] start render other --- devenv.nix | 5 +- jong-db/src/db/bot_table.rs | 31 ++++++++ jong-db/src/db/bot_type.rs | 2 + jong-line/src/tables.rs | 26 +++--- jong/src/riichi.rs | 140 +++++++++++++++++++-------------- jong/src/tui.rs | 4 +- jong/src/tui/input/keyboard.rs | 14 +++- jong/src/tui/render.rs | 51 +++++++++++- 8 files changed, 193 insertions(+), 80 deletions(-) diff --git a/devenv.nix b/devenv.nix index 3e83f98..72ac11b 100644 --- a/devenv.nix +++ b/devenv.nix @@ -3,6 +3,9 @@ lib, ... }: rec { + env.RUST_LOG = "jong=trace"; + env.RUST_BACKTRACE = "1"; + # https://devenv.sh/processes/ processes.lspmux.exec = "lspmux server"; processes.spacetimedb_start.exec = "spacetime start"; @@ -44,8 +47,6 @@ wayland ]; 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 73bc154..be072da 100644 --- a/jong-db/src/db/bot_table.rs +++ b/jong-db/src/db/bot_table.rs @@ -125,10 +125,41 @@ impl<'ctx> BotIdUnique<'ctx> { } } +/// Access to the `config_id` unique index on the table `bot`, +/// which allows point queries on the field of the same name +/// via the [`BotConfigIdUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.bot().config_id().find(...)`. +pub struct BotConfigIdUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> BotTableHandle<'ctx> { + /// Get a handle on the `config_id` unique index on the table `bot`. + pub fn config_id(&self) -> BotConfigIdUnique<'ctx> { + BotConfigIdUnique { + imp: self.imp.get_unique_constraint::("config_id"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> BotConfigIdUnique<'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::("bot"); _table.add_unique_constraint::("id", |row| &row.id); + _table.add_unique_constraint::("config_id", |row| &row.config_id); } #[doc(hidden)] diff --git a/jong-db/src/db/bot_type.rs b/jong-db/src/db/bot_type.rs index a49650a..dcb1bb8 100644 --- a/jong-db/src/db/bot_type.rs +++ b/jong-db/src/db/bot_type.rs @@ -40,6 +40,7 @@ impl __sdk::__query_builder::HasCols for Bot { /// /// Provides typed access to indexed columns for query building. pub struct BotIxCols { + pub config_id: __sdk::__query_builder::IxCol, pub id: __sdk::__query_builder::IxCol, pub lobby_id: __sdk::__query_builder::IxCol, } @@ -48,6 +49,7 @@ impl __sdk::__query_builder::HasIxCols for Bot { type IxCols = BotIxCols; fn ix_cols(table_name: &'static str) -> Self::IxCols { BotIxCols { + config_id: __sdk::__query_builder::IxCol::new(table_name, "config_id"), id: __sdk::__query_builder::IxCol::new(table_name, "id"), lobby_id: __sdk::__query_builder::IxCol::new(table_name, "lobby_id"), } diff --git a/jong-line/src/tables.rs b/jong-line/src/tables.rs index 0b09f6c..4c22804 100644 --- a/jong-line/src/tables.rs +++ b/jong-line/src/tables.rs @@ -72,6 +72,7 @@ pub struct Bot { #[index(btree)] pub lobby_id: u32, + #[unique] pub config_id: u32, } @@ -149,16 +150,21 @@ fn view_closed_hands(ctx: &ViewContext) -> Vec { .players .iter() .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(), - }) + if *id == this_player.config_id { + None + } else { + 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/src/riichi.rs b/jong/src/riichi.rs index c255b43..bb4b9fd 100644 --- a/jong/src/riichi.rs +++ b/jong/src/riichi.rs @@ -64,8 +64,8 @@ impl Plugin for Riichi { ( on_view_hand_insert, on_view_hand_update, - // on_view_closed_insert, - // on_view_closed_update, + on_view_closed_insert, + on_view_closed_update, ) .after(on_player_config_insert_update) .run_if(in_state(GameState::Play).or(in_state(GameState::Deal))), @@ -315,66 +315,86 @@ fn sync_open_hand( next_turnstate.set(player_hand.turn_state.into()); } -// fn on_view_closed_insert( -// mut messages: ReadInsertMessage, -// mut commands: Commands, -// tiles: Query<(Entity, &TileId)>, -// hands: Query<(Entity, &Hand)>, -// ponds: Query<(Entity, &Pond)>, -// ) { -// for msg in messages.read() { -// sync_closed_hand(&mut commands, tiles, hands, ponds, &msg.row); -// } -// } +fn on_view_closed_insert( + mut messages: ReadInsertMessage, + mut commands: Commands, + players: Populated<(Entity, &Player, Option<&Children>)>, + tiles: Query<(Entity, &TileId)>, + hands: Query<(Entity, &Hand)>, + ponds: Query<(Entity, &Pond)>, +) { + for msg in messages.read() { + trace!("on_view_closed_insert"); + sync_closed_hand(&mut commands, &players, tiles, hands, ponds, &msg.row); + } +} -// fn on_view_closed_update( -// mut messages: ReadUpdateMessage, -// mut commands: Commands, -// tiles: Query<(Entity, &TileId)>, -// hands: Query<(Entity, &Hand)>, -// ponds: Query<(Entity, &Pond)>, -// ) { -// for msg in messages.read() { -// sync_closed_hand(&mut commands, tiles, hands, ponds, &msg.new); -// } -// } +fn on_view_closed_update( + mut messages: ReadUpdateMessage, + mut commands: Commands, + players: Populated<(Entity, &Player, Option<&Children>)>, + tiles: Query<(Entity, &TileId)>, + hands: Query<(Entity, &Hand)>, + ponds: Query<(Entity, &Pond)>, +) { + for msg in messages.read() { + trace!("on_view_closed_update"); + sync_closed_hand(&mut commands, &players, tiles, hands, ponds, &msg.new); + } +} -// fn sync_closed_hand( -// commands: &mut Commands, -// tiles: Query<(Entity, &TileId)>, -// hands: Query<(Entity, &Hand)>, -// ponds: Query<(Entity, &Pond)>, -// hand_view: &HandView, -// ) { -// let player_id = hand_view.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()); +fn sync_closed_hand( + commands: &mut Commands, + players: &Populated<(Entity, &Player, Option<&Children>)>, + tiles: Query<(Entity, &TileId)>, + hands: Query<(Entity, &Hand)>, + ponds: Query<(Entity, &Pond)>, + hand_view: &HandView, +) { + let player_id = hand_view.player_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(); + let (player_ent, player_children) = players + .iter() + .find_map(|(e, p, c)| (p.id == player_id).then_some((e, c))) + .unwrap(); -// commands.entity(hand_ent).insert(Closed { -// length: hand_view.hand_length, -// }); -// commands.entity(pond_ent).replace_children(&pond); + let hand_ent = hands + .iter() + .find_map(|(e, _)| player_children.is_some_and(|c| c.contains(&e)).then_some(e)) + .unwrap_or_else(|| { + let id = commands.spawn(Hand).id(); + commands.entity(player_ent).add_child(id); + id + }); + let pond_ent = ponds + .iter() + .find_map(|(e, _)| player_children.is_some_and(|c| c.contains(&e)).then_some(e)) + .unwrap_or_else(|| { + let id = commands.spawn(Pond).id(); + commands.entity(player_ent).add_child(id); + id + }); -// if hand_view.drawn { -// commands.spawn(Drawn); -// } else { -// // TODO clear Drawn from auto-discarded tile -// } -// } + 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 { + let id = commands.spawn(Drawn).id(); + commands.entity(player_ent).add_child(id); + } else { + // TODO clear Drawn from auto-discarded tile + } +} diff --git a/jong/src/tui.rs b/jong/src/tui.rs index fa38da2..db0267b 100644 --- a/jong/src/tui.rs +++ b/jong/src/tui.rs @@ -1,5 +1,3 @@ -#![allow(unused)] - use std::time::Duration; use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin}; @@ -84,7 +82,7 @@ impl Plugin for TuiPlugin { .add_systems( Update, ( - (render::render_main_hand, render::render_main_pond) + (render::render_main_hand, render::render_main_pond, render::render_other_hands) .run_if(in_state(GameState::Play).or(in_state(GameState::Deal))), render::render, ) diff --git a/jong/src/tui/input/keyboard.rs b/jong/src/tui/input/keyboard.rs index 64685d6..88c5c36 100644 --- a/jong/src/tui/input/keyboard.rs +++ b/jong/src/tui/input/keyboard.rs @@ -7,9 +7,9 @@ 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, + LobbyTableAccess, UserTableAccess, botQueryTableAccess, join_or_create_lobby, + lobbyQueryTableAccess, player_clockQueryTableAccess, player_configQueryTableAccess, + view_closed_handsQueryTableAccess, view_handQueryTableAccess, }; use jong_types::GameState; @@ -78,6 +78,14 @@ pub(crate) fn keyboard( .player_config() .r#where(|pc| pc.id.eq(user.config_id)) }) + .add_query(|q| { + q.from + .bot() + .r#where(|b| b.lobby_id.eq(lobby_id)) + .right_semijoin(q.from.player_config(), |b, pc| { + b.config_id.eq(pc.id) + }) + }) .add_query(|q| { q.from .player_clock() diff --git a/jong/src/tui/render.rs b/jong/src/tui/render.rs index d8a48f7..83aa271 100644 --- a/jong/src/tui/render.rs +++ b/jong/src/tui/render.rs @@ -7,7 +7,7 @@ use ratatui::style::{Color, Modifier, Style, Stylize}; use ratatui::text::{Line, Span, Text}; use ratatui::widgets::{Block, Borders, Clear, Paragraph}; -use jong::riichi::player::*; +use jong::riichi::{Lobby, player::*}; use jong_types::*; use crate::tui::input::Hovered; @@ -291,4 +291,51 @@ pub(crate) fn render_main_pond( Ok(()) } -pub(crate) fn render_other_hands() {} +pub(crate) fn render_other_hands( + mut tui: ResMut, + layouts: Res, + + lobby: Res, + + main_player: Single<&Player, With>, + players: Query<(&Player, &Children), Without>, + hands: Query<(Entity, &Closed)>, + ponds: Query<(Entity, &Children), With>, + drawn: Option>>, +) { + let mut frame = tui.get_frame(); + + let main_idx = lobby + .players + .iter() + .position(|id| *id == main_player.id) + .unwrap(); + + let mut player_it = lobby.players.iter().copied().cycle().skip(main_idx + 1); + + let right_id = player_it.next().unwrap(); + let top_id = player_it.next().unwrap(); + let left_id = player_it.next().unwrap(); + + { + let children = players + .iter() + .find_map(|(p, c)| (p.id == right_id).then_some(c)) + .unwrap(); + let hand = hands + .iter() + .find_map(|(e, h)| children.contains(&e).then_some(h)) + .unwrap(); + // let pond = ponds + // .iter() + // .find_map(|(e, c)| children.contains(&e).then_some(c)) + // .unwrap(); + let drawn = drawn.is_some_and(|e| children.contains(&*e)); + + // let hand_draw_meld = Layout::vertical([ + // Constraint::() + // ]) + } + {} + {} +}