various smol changes

This commit is contained in:
Tao Tien 2026-03-02 20:27:56 -08:00
parent bf23dbaf80
commit 0713d6869b
13 changed files with 108 additions and 93 deletions

View file

@ -1,2 +1,2 @@
jong-db jong-db/src/db
bevy_spacetimedb bevy_spacetimedb

View file

@ -4,6 +4,7 @@
#![allow(unused, clippy::all)] #![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::db_tile_type::DbTile;
use super::player_or_bot_type::PlayerOrBot; use super::player_or_bot_type::PlayerOrBot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[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 struct HandView {
pub player: PlayerOrBot, pub player: PlayerOrBot,
pub hand_length: u8, pub hand_length: u8,
pub pond: Vec<DbTile>,
pub drawn: bool, pub drawn: bool,
} }
@ -24,6 +26,7 @@ impl __sdk::InModule for HandView {
pub struct HandViewCols { pub struct HandViewCols {
pub player: __sdk::__query_builder::Col<HandView, PlayerOrBot>, pub player: __sdk::__query_builder::Col<HandView, PlayerOrBot>,
pub hand_length: __sdk::__query_builder::Col<HandView, u8>, pub hand_length: __sdk::__query_builder::Col<HandView, u8>,
pub pond: __sdk::__query_builder::Col<HandView, Vec<DbTile>>,
pub drawn: __sdk::__query_builder::Col<HandView, bool>, pub drawn: __sdk::__query_builder::Col<HandView, bool>,
} }
@ -33,6 +36,7 @@ impl __sdk::__query_builder::HasCols for HandView {
HandViewCols { HandViewCols {
player: __sdk::__query_builder::Col::new(table_name, "player"), player: __sdk::__query_builder::Col::new(table_name, "player"),
hand_length: __sdk::__query_builder::Col::new(table_name, "hand_length"), 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"), drawn: __sdk::__query_builder::Col::new(table_name, "drawn"),
} }
} }

View file

@ -2,6 +2,7 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)] #![allow(unused, clippy::all)]
use super::db_tile_type::DbTile;
use super::hand_view_type::HandView; use super::hand_view_type::HandView;
use super::player_or_bot_type::PlayerOrBot; use super::player_or_bot_type::PlayerOrBot;
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};

View file

@ -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(); let mut hand = ctx.db.player_hand().player_id().find(player_id).unwrap();
match hand.turn_state { match hand.turn_state {
TurnState::None => { TurnState::None => {
trace!("draw a tile"); // trace!("draw a tile");
if let Some(mut wall) = ctx.db.wall().lobby_id().find(lobby.id) if let Some(mut wall) = ctx.db.wall().lobby_id().find(lobby.id)
&& let Some(tile) = wall.tiles.pop() && let Some(tile) = wall.tiles.pop()
{ {

View file

@ -68,7 +68,7 @@ pub fn add_bot(ctx: &ReducerContext, lobby_id: u32) -> Result<(), String> {
info!("added bot {} to lobby {}", bot.id, lobby_id); info!("added bot {} to lobby {}", bot.id, lobby_id);
Ok(()) Ok(())
} else { } else {
Err("lobby doesn't exist".into()) Err(format!("lobby {lobby_id} doesn't exist or is full"))
} }
} }

View file

@ -129,11 +129,12 @@ fn view_hand(ctx: &ViewContext) -> Option<PlayerHand> {
.and_then(|p| ctx.db.player_hand().player_id().find(p.id)) .and_then(|p| ctx.db.player_hand().player_id().find(p.id))
} }
#[derive(SpacetimeType, Clone, Copy)] #[derive(SpacetimeType, Clone)]
pub struct HandView { pub struct HandView {
pub player: PlayerOrBot, pub player: PlayerOrBot,
pub hand_length: u8, pub hand_length: u8,
// pub melds: u8, // pub melds: u8,
pub pond: Vec<DbTile>,
pub drawn: bool, pub drawn: bool,
} }
@ -151,6 +152,7 @@ fn view_closed_hands(ctx: &ViewContext) -> Vec<HandView> {
Some(HandView { Some(HandView {
player, player,
hand_length: player_hand.hand.len() as u8, hand_length: player_hand.hand.len() as u8,
pond: player_hand.pond,
drawn: player_hand.turn_state == TurnState::Tsumo drawn: player_hand.turn_state == TurnState::Tsumo
&& player_hand.working_tile.is_some(), && player_hand.working_tile.is_some(),
}) })
@ -163,6 +165,7 @@ fn view_closed_hands(ctx: &ViewContext) -> Vec<HandView> {
Some(HandView { Some(HandView {
player, player,
hand_length: bot.hand.len() as u8, hand_length: bot.hand.len() as u8,
pond: bot.pond,
drawn: bot.turn_state == TurnState::Tsumo && bot.working_tile.is_some(), drawn: bot.turn_state == TurnState::Tsumo && bot.working_tile.is_some(),
}) })
} else { } else {

View file

@ -1,6 +1,7 @@
use std::time::Duration; use std::time::Duration;
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::render_resource::AsBindGroupShaderType;
use bevy_spacetimedb::{ReadInsertMessage, ReadInsertUpdateMessage, ReadUpdateMessage, StdbPlugin}; use bevy_spacetimedb::{ReadInsertMessage, ReadInsertUpdateMessage, ReadUpdateMessage, StdbPlugin};
use jong_db::{ use jong_db::{
@ -47,13 +48,18 @@ impl Plugin for Riichi {
.add_sub_state::<jong_types::states::TurnState>() .add_sub_state::<jong_types::states::TurnState>()
.add_message::<SyncPlayer>() .add_message::<SyncPlayer>()
.add_message::<SyncOpenHand>() .add_message::<SyncOpenHand>()
.add_message::<SyncBot>()
.add_systems(Startup, subscriptions) .add_systems(Startup, subscriptions)
.add_systems(Update, (connection::on_connect, connection::on_disconnect)) .add_systems(Update, (connection::on_connect, connection::on_disconnect))
.add_systems( .add_systems(
Update, 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( .add_systems(
@ -112,6 +118,9 @@ fn subscriptions(stdb: SpacetimeDB, mut commands: Commands) {
#[derive(Message)] #[derive(Message)]
struct SyncPlayer(u32); struct SyncPlayer(u32);
#[derive(Message)]
struct SyncBot(u32);
#[derive(Message)] #[derive(Message)]
struct SyncOpenHand(u32); struct SyncOpenHand(u32);
@ -153,6 +162,14 @@ fn sync_player(
} }
} }
fn sync_bot(
stdb: SpacetimeDB,
mut messages: MessageReader<SyncBot>,
mut commands: Commands,
players: Query<(Entity, &Player)>,
) {
}
fn sync_open_hand( fn sync_open_hand(
stdb: SpacetimeDB, stdb: SpacetimeDB,
@ -183,7 +200,7 @@ fn sync_open_hand(
}); });
let pond_ent = ponds let pond_ent = ponds
.iter() .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(|| { .unwrap_or_else(|| {
commands commands
.spawn(Pond { .spawn(Pond {
@ -230,12 +247,52 @@ fn sync_open_hand(
fn sync_closed_hand( fn sync_closed_hand(
stdb: SpacetimeDB, stdb: SpacetimeDB,
mut events: MessageReader<SyncClosedHand>, mut messages: MessageReader<SyncClosedHand>,
mut commands: Commands, mut commands: Commands,
hands: Query<&mut Closed, With<Hand>>, tiles: Query<(Entity, &TileId)>,
ponds: Query<&mut Children, With<Pond>>, 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<Entity> = 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() {} 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<jong_db::Bot>,
mut writer: MessageWriter<SyncBot>,
) {
for msg in db_messages.read() {
writer.write(SyncBot(msg.new.id));
}
}
#[derive(Resource)]
struct Lobby {
players: Vec<PlayerOrBot>,
}
fn on_lobby_insert_update( fn on_lobby_insert_update(
stdb: SpacetimeDB, stdb: SpacetimeDB,
@ -272,10 +341,10 @@ fn on_lobby_insert_update(
match msg.new.game_state { match msg.new.game_state {
jong_db::GameState::None => { jong_db::GameState::None => {
trace!("game entered none"); // trace!("game entered none");
} }
jong_db::GameState::Lobby => { jong_db::GameState::Lobby => {
trace!("game entered lobby"); // trace!("game entered lobby");
if !player.ready { if !player.ready {
for _ in 0..3 { for _ in 0..3 {
stdb.reducers().add_bot(player.lobby_id).unwrap(); stdb.reducers().add_bot(player.lobby_id).unwrap();
@ -285,16 +354,16 @@ fn on_lobby_insert_update(
} }
} }
jong_db::GameState::Setup => { jong_db::GameState::Setup => {
trace!("game entered setup"); // trace!("game entered setup");
} }
jong_db::GameState::Deal => { jong_db::GameState::Deal => {
trace!("game entered deal"); // trace!("game entered deal");
} }
jong_db::GameState::Play => { jong_db::GameState::Play => {
trace!("game entered play"); // trace!("game entered play");
} }
jong_db::GameState::Exit => { 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( fn on_view_hand_update(
mut messages: ReadUpdateMessage<jong_db::PlayerHand>, mut messages: ReadUpdateMessage<jong_db::PlayerHand>,
mut writer: MessageWriter<SyncOpenHand>, mut writer: MessageWriter<SyncOpenHand>,
// mut commands: Commands,
// tiles: Query<(Entity, &TileId)>,
// main_player: Single<(Entity, &Children), With<MainPlayer>>,
// hand: Query<Entity, With<Hand>>,
// pond: Query<Entity, With<Pond>>,
// // drawn: Option<Single<Entity, With<Drawn>>>,
// mut next_turnstate: ResMut<NextState<jong_types::states::TurnState>>,
) { ) {
// TODO can this and similar run at startup or on play/reconnect?
for msg in messages.read() { for msg in messages.read() {
trace!("on_view_hand_update"); trace!("on_view_hand_update");
writer.write(SyncOpenHand(msg.new.player_id)); 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());
// }
} }

View file

@ -23,7 +23,6 @@ pub struct Hand {
#[derive(Component)] #[derive(Component)]
pub struct Closed { pub struct Closed {
pub(crate) owner: PlayerOrBot,
pub(crate) length: u8, pub(crate) length: u8,
} }
@ -34,6 +33,3 @@ pub struct Pond {
#[derive(Component)] #[derive(Component)]
pub struct Drawn; pub struct Drawn;
#[derive(Component)]
pub struct Discarded;

View file

@ -104,13 +104,12 @@ fn discard_tile(
mut commands: Commands, mut commands: Commands,
// main_player: Single<&Children, With<MainPlayer>>, // main_player: Single<&Children, With<MainPlayer>>,
// only main player will have a Drawn tile? // only main player will have a Drawn tile with TileId?
drawn: Single<(Entity, &TileId), With<Drawn>>, drawn: Single<(Entity, &TileId), With<Drawn>>,
tiles: Query<&TileId>, tiles: Query<&TileId>,
) { ) {
// TODO disable this when we're not current player? // TODO disable this when we're not current player?
if let Ok(tile_id) = tiles.get(selected.0) { if let Ok(tile_id) = tiles.get(selected.0) {
trace!("{:?}, {tile_id:?}", selected.0);
stdb.reducers().discard_tile(tile_id.0).unwrap(); stdb.reducers().discard_tile(tile_id.0).unwrap();
stdb.reducers().advance_game().unwrap(); stdb.reducers().advance_game().unwrap();
commands.entity(drawn.0).remove::<Drawn>(); commands.entity(drawn.0).remove::<Drawn>();

View file

@ -1,11 +1,12 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_ratatui::crossterm::event::KeyCode; use bevy_ratatui::crossterm::event::KeyCode;
use bevy_ratatui::event::KeyMessage; use bevy_ratatui::event::KeyMessage;
use jong_db::PlayerTableAccess;
use jong_db::join_or_create_lobby;
use tui_logger::TuiWidgetEvent; use tui_logger::TuiWidgetEvent;
use jong::SpacetimeDB; 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::layout::Overlays;
use crate::tui::states::ConsoleWidget; use crate::tui::states::ConsoleWidget;
@ -38,6 +39,11 @@ pub(crate) fn keyboard(
} }
} }
if *curr_gamestate.get() != GameState::None {
next_tuistate.set(TuiState::InGame);
continue;
}
match curr_tuistate.get() { match curr_tuistate.get() {
TuiState::MainMenu => match key { TuiState::MainMenu => match key {
KeyCode::Char('p') => { KeyCode::Char('p') => {

View file

@ -152,17 +152,17 @@ pub(crate) fn render_main_hand(
main_player: Single<&Player, With<MainPlayer>>, main_player: Single<&Player, With<MainPlayer>>,
hand: Query<(&Hand, &Children)>, hands: Query<(&Hand, &Children)>,
drawn_tile: Option<Single<Entity, With<Drawn>>>, drawn_tile: Option<Single<Entity, With<Drawn>>>,
) -> Result { ) -> Result {
let mut frame = tui.get_frame(); let mut frame = tui.get_frame();
debug_blocks(layouts.clone(), &mut frame); debug_blocks(layouts.clone(), &mut frame);
if hand.is_empty() { if hands.is_empty() {
return Ok(()); return Ok(());
} }
let hand: Vec<_> = hand let hand: Vec<_> = hands
.iter() .iter()
.find_map(|(h, c)| (main_player.id == h.owner).then_some(c)) .find_map(|(h, c)| (main_player.id == h.owner).then_some(c))
.unwrap() .unwrap()

View file

@ -18,7 +18,7 @@ spacetime:
devenv up devenv up
spacetime_dev: 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-bindings:
spacetime generate --lang rust --out-dir jong-db/src/db --module-path jong-line spacetime generate --lang rust --out-dir jong-db/src/db --module-path jong-line

View file

@ -2,7 +2,6 @@
"dev": { "dev": {
"run": "" "run": ""
}, },
"_source-config": "spacetime.local.json",
"module-path": "jong-line", "module-path": "jong-line",
"server": "local", "server": "local",
"database": "jong-line" "database": "jong-line"