From 0713d6869b990f3131e788bd84b7f86af33a0634 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Mon, 2 Mar 2026 20:27:56 -0800 Subject: [PATCH] various smol changes --- .helix/ignore | 2 +- jong-db/src/db/hand_view_type.rs | 4 + jong-db/src/db/view_closed_hands_table.rs | 1 + jong-line/src/reducers.rs | 2 +- jong-line/src/reducers/lobby.rs | 2 +- jong-line/src/tables.rs | 5 +- jong/src/riichi.rs | 157 +++++++++++----------- jong/src/riichi/player.rs | 4 - jong/src/tui.rs | 3 +- jong/src/tui/input/keyboard.rs | 12 +- jong/src/tui/render.rs | 6 +- justfile | 2 +- spacetime.json | 1 - 13 files changed, 108 insertions(+), 93 deletions(-) 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/jong-db/src/db/hand_view_type.rs b/jong-db/src/db/hand_view_type.rs index 93935a6..3fe49b3 100644 --- a/jong-db/src/db/hand_view_type.rs +++ b/jong-db/src/db/hand_view_type.rs @@ -4,6 +4,7 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; +use super::db_tile_type::DbTile; use super::player_or_bot_type::PlayerOrBot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] @@ -11,6 +12,7 @@ use super::player_or_bot_type::PlayerOrBot; pub struct HandView { pub player: PlayerOrBot, pub hand_length: u8, + pub pond: Vec, pub drawn: bool, } @@ -24,6 +26,7 @@ impl __sdk::InModule for HandView { pub struct HandViewCols { pub player: __sdk::__query_builder::Col, pub hand_length: __sdk::__query_builder::Col, + pub pond: __sdk::__query_builder::Col>, pub drawn: __sdk::__query_builder::Col, } @@ -33,6 +36,7 @@ impl __sdk::__query_builder::HasCols for HandView { HandViewCols { player: __sdk::__query_builder::Col::new(table_name, "player"), 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/view_closed_hands_table.rs b/jong-db/src/db/view_closed_hands_table.rs index b2d7c0f..ab1b296 100644 --- a/jong-db/src/db/view_closed_hands_table.rs +++ b/jong-db/src/db/view_closed_hands_table.rs @@ -2,6 +2,7 @@ // 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}; diff --git a/jong-line/src/reducers.rs b/jong-line/src/reducers.rs index 0f3dda9..2328667 100644 --- a/jong-line/src/reducers.rs +++ b/jong-line/src/reducers.rs @@ -121,7 +121,7 @@ pub fn advance_game_private(ctx: &ReducerContext, mut game_timer: GameTimer) -> let mut hand = ctx.db.player_hand().player_id().find(player_id).unwrap(); match hand.turn_state { TurnState::None => { - trace!("draw a tile"); + // trace!("draw a tile"); if let Some(mut wall) = ctx.db.wall().lobby_id().find(lobby.id) && let Some(tile) = wall.tiles.pop() { diff --git a/jong-line/src/reducers/lobby.rs b/jong-line/src/reducers/lobby.rs index 17b658a..61f165f 100644 --- a/jong-line/src/reducers/lobby.rs +++ b/jong-line/src/reducers/lobby.rs @@ -68,7 +68,7 @@ pub fn add_bot(ctx: &ReducerContext, lobby_id: u32) -> Result<(), String> { 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")) } } diff --git a/jong-line/src/tables.rs b/jong-line/src/tables.rs index fdf75d5..a8664aa 100644 --- a/jong-line/src/tables.rs +++ b/jong-line/src/tables.rs @@ -129,11 +129,12 @@ fn view_hand(ctx: &ViewContext) -> Option { .and_then(|p| ctx.db.player_hand().player_id().find(p.id)) } -#[derive(SpacetimeType, Clone, Copy)] +#[derive(SpacetimeType, Clone)] pub struct HandView { pub player: PlayerOrBot, pub hand_length: u8, // pub melds: u8, + pub pond: Vec, pub drawn: bool, } @@ -151,6 +152,7 @@ fn view_closed_hands(ctx: &ViewContext) -> Vec { Some(HandView { player, hand_length: player_hand.hand.len() as u8, + pond: player_hand.pond, drawn: player_hand.turn_state == TurnState::Tsumo && player_hand.working_tile.is_some(), }) @@ -163,6 +165,7 @@ fn view_closed_hands(ctx: &ViewContext) -> Vec { Some(HandView { player, hand_length: bot.hand.len() as u8, + pond: bot.pond, drawn: bot.turn_state == TurnState::Tsumo && bot.working_tile.is_some(), }) } else { diff --git a/jong/src/riichi.rs b/jong/src/riichi.rs index 14a6e0d..2f99ad9 100644 --- a/jong/src/riichi.rs +++ b/jong/src/riichi.rs @@ -1,6 +1,7 @@ use std::time::Duration; use bevy::prelude::*; +use bevy::render::render_resource::AsBindGroupShaderType; use bevy_spacetimedb::{ReadInsertMessage, ReadInsertUpdateMessage, ReadUpdateMessage, StdbPlugin}; use jong_db::{ @@ -47,13 +48,18 @@ impl Plugin for Riichi { .add_sub_state::() .add_message::() .add_message::() + .add_message::() .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_lobby_insert_update, + on_player_insert_update, + on_bot_insert_update, + ), + (sync_player, sync_bot), ), ) .add_systems( @@ -112,6 +118,9 @@ fn subscriptions(stdb: SpacetimeDB, mut commands: Commands) { #[derive(Message)] struct SyncPlayer(u32); +#[derive(Message)] +struct SyncBot(u32); + #[derive(Message)] struct SyncOpenHand(u32); @@ -153,6 +162,14 @@ fn sync_player( } } +fn sync_bot( + stdb: SpacetimeDB, + mut messages: MessageReader, + mut commands: Commands, + players: Query<(Entity, &Player)>, +) { +} + fn sync_open_hand( stdb: SpacetimeDB, @@ -183,7 +200,7 @@ fn sync_open_hand( }); let pond_ent = ponds .iter() - .find_map(|(e, h)| (h.owner == PlayerOrBot::Player { id: *id }).then_some(e)) + .find_map(|(e, p)| (p.owner == PlayerOrBot::Player { id: *id }).then_some(e)) .unwrap_or_else(|| { commands .spawn(Pond { @@ -230,12 +247,52 @@ fn sync_open_hand( fn sync_closed_hand( stdb: SpacetimeDB, - mut events: MessageReader, + mut messages: MessageReader, mut commands: Commands, - hands: Query<&mut Closed, With>, - ponds: Query<&mut Children, With>, + 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); + } + } } fn sync_player_clock() {} @@ -251,7 +308,19 @@ fn on_player_insert_update( } } -fn on_bot_insert_update() {} +fn on_bot_insert_update( + mut db_messages: ReadInsertUpdateMessage, + mut writer: MessageWriter, +) { + for msg in db_messages.read() { + writer.write(SyncBot(msg.new.id)); + } +} + +#[derive(Resource)] +struct Lobby { + players: Vec, +} fn on_lobby_insert_update( stdb: SpacetimeDB, @@ -272,10 +341,10 @@ fn on_lobby_insert_update( 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"); + // trace!("game entered lobby"); if !player.ready { for _ in 0..3 { stdb.reducers().add_bot(player.lobby_id).unwrap(); @@ -285,16 +354,16 @@ 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"); } } @@ -315,71 +384,9 @@ fn on_view_hand_insert( 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>, ) { - // 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)); } - // 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()); - // } } diff --git a/jong/src/riichi/player.rs b/jong/src/riichi/player.rs index a2dc497..8b6225d 100644 --- a/jong/src/riichi/player.rs +++ b/jong/src/riichi/player.rs @@ -23,7 +23,6 @@ pub struct Hand { #[derive(Component)] pub struct Closed { - pub(crate) owner: PlayerOrBot, pub(crate) length: u8, } @@ -34,6 +33,3 @@ pub struct Pond { #[derive(Component)] pub struct Drawn; - -#[derive(Component)] -pub struct Discarded; diff --git a/jong/src/tui.rs b/jong/src/tui.rs index 1db3695..18a47d2 100644 --- a/jong/src/tui.rs +++ b/jong/src/tui.rs @@ -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..da9ce40 100644 --- a/jong/src/tui/input/keyboard.rs +++ b/jong/src/tui/input/keyboard.rs @@ -1,11 +1,12 @@ 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 tui_logger::TuiWidgetEvent; use jong::SpacetimeDB; +use jong_db::PlayerTableAccess; +use jong_db::join_or_create_lobby; +use jong_types::GameState; use crate::tui::layout::Overlays; use crate::tui::states::ConsoleWidget; @@ -38,10 +39,15 @@ 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 + // TODO this should be a msg/signal and handle the correct lobby stdb.reducers().join_or_create_lobby(0).unwrap(); next_tuistate.set(TuiState::InGame); } diff --git a/jong/src/tui/render.rs b/jong/src/tui/render.rs index 566c2d4..8361571 100644 --- a/jong/src/tui/render.rs +++ b/jong/src/tui/render.rs @@ -152,17 +152,17 @@ 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)) .unwrap() diff --git a/justfile b/justfile index 4467e36..a73beca 100644 --- a/justfile +++ b/justfile @@ -18,7 +18,7 @@ spacetime: devenv up spacetime_dev: - spacetime dev --module-bindings-path jong-db/src/db jong-line --delete-data=always --yes --server-only + spacetime dev --module-bindings-path jong-db/src/db jong-line --delete-data=on-conflict --yes --server-only spacetime_generate-bindings: spacetime generate --lang rust --out-dir jong-db/src/db --module-path jong-line 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"