create a workspace
This commit is contained in:
parent
dedeb39304
commit
bcbaab6909
38 changed files with 771 additions and 905 deletions
28
jong/Cargo.toml
Normal file
28
jong/Cargo.toml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
[package]
|
||||
name = "jong"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
# license = "Source-First-1.1" # pending crates.io support
|
||||
license-file = "LICENSE.typ"
|
||||
description = "riichi mahjong"
|
||||
readme = false
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
bevy = { version = "0.17.3", features = ["dynamic_linking"] }
|
||||
# bevy_ratatui = { git = "https://github.com/kenianbei/bevy_ratatui.git", rev = "e4b022308e08ab360ef89eca8e9f8b1c969e9a56" }
|
||||
bevy_ratatui = { path = "/home/tao/clones/bevy_ratatui" }
|
||||
bevy_spacetimedb = "0.7"
|
||||
clap = { version = "4.5.54", features = ["derive"] }
|
||||
jongline = { version = "0.1.0", path = "../jongline" }
|
||||
log = { version = "0.4.29", features = [
|
||||
"release_max_level_error",
|
||||
"max_level_trace",
|
||||
] }
|
||||
rand = "0.9.2"
|
||||
ratatui = "0.30.0"
|
||||
strum = { version = "0.27.2", features = ["derive"] }
|
||||
tracing = "0.1.44"
|
||||
tracing-subscriber = "0.3.22"
|
||||
tui-logger = { version = "0.18.0", features = ["tracing-support", "crossterm"] }
|
||||
101
jong/src/game.rs
Normal file
101
jong/src/game.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
game::{
|
||||
hand::{Hand, Pond},
|
||||
player::{CurrentPlayer, MainPlayer},
|
||||
round::{TurnState, Wind},
|
||||
wall::Wall,
|
||||
},
|
||||
tile::{self},
|
||||
};
|
||||
|
||||
pub mod hand;
|
||||
pub mod player;
|
||||
pub mod round;
|
||||
pub mod wall;
|
||||
|
||||
#[derive(States, Default, Hash, Clone, Eq, Debug, PartialEq, Copy)]
|
||||
pub enum GameState {
|
||||
#[default]
|
||||
None,
|
||||
Setup,
|
||||
Deal,
|
||||
Play,
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
pub enum GameMessage {
|
||||
Discarded(Entity),
|
||||
CallPending,
|
||||
Called { player: Entity, calltype: Entity },
|
||||
}
|
||||
|
||||
impl GameMessage {
|
||||
pub(crate) fn is_called(&self) -> bool {
|
||||
match self {
|
||||
GameMessage::Called { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Riichi;
|
||||
impl Plugin for Riichi {
|
||||
fn build(&self, app: &mut App) {
|
||||
app
|
||||
// start stopper
|
||||
.init_state::<GameState>()
|
||||
.add_sub_state::<TurnState>()
|
||||
.init_resource::<round::MatchSettings>()
|
||||
.init_resource::<round::Compass>()
|
||||
.add_message::<GameMessage>()
|
||||
.add_systems(Startup, tile::init_tiles)
|
||||
.add_systems(OnEnter(GameState::Setup), setup)
|
||||
.add_systems(OnEnter(GameState::Deal), hand::shuffle_deal)
|
||||
.add_systems(Update, hand::sort_hands.run_if(in_state(GameState::Play)))
|
||||
.add_systems(OnEnter(TurnState::Tsumo), round::tsumo)
|
||||
.add_systems(OnEnter(TurnState::Menzen), round::menzen)
|
||||
.add_systems(Update, round::riichi_kan.run_if(in_state(TurnState::RiichiKan)))
|
||||
.add_systems(Update, round::discard.run_if(in_state(TurnState::Discard)))
|
||||
.add_systems(OnEnter(TurnState::RonChiiPonKan), round::notify_callable)
|
||||
.add_systems(Update, round::ron_chi_pon_kan.run_if(in_state(TurnState::RonChiiPonKan)).after(round::notify_callable))
|
||||
.add_systems(OnEnter(TurnState::End), round::end)
|
||||
// semicolon stopper
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn setup(
|
||||
mut commands: Commands,
|
||||
matchsettings: Res<round::MatchSettings>,
|
||||
// mut compass: ResMut<Compass>
|
||||
// tiles: Query<Entity, With<Tile>>,
|
||||
mut next_gamestate: ResMut<NextState<GameState>>,
|
||||
) {
|
||||
for i in 1..=matchsettings.player_count {
|
||||
let player = player::Player {
|
||||
name: format!("Player {}", i),
|
||||
};
|
||||
let points = player::Points(matchsettings.starting_points);
|
||||
|
||||
let bundle = (
|
||||
player,
|
||||
points,
|
||||
Hand,
|
||||
Pond,
|
||||
Wind::from_repr((i - 1) as usize).unwrap(),
|
||||
);
|
||||
|
||||
if i == 1 {
|
||||
let player = commands.spawn((bundle, MainPlayer, CurrentPlayer)).id();
|
||||
// commands.insert_resource(CurrentPlayer(player));
|
||||
} else {
|
||||
commands.spawn(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
commands.spawn(Wall);
|
||||
|
||||
next_gamestate.set(GameState::Deal);
|
||||
}
|
||||
84
jong/src/game/hand.rs
Normal file
84
jong/src/game/hand.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
use std::mem::discriminant;
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
game::{GameState, player::Player, wall::Wall},
|
||||
tile::Tile,
|
||||
};
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Hand;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Pond;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Drawn;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Discarded;
|
||||
|
||||
// #[derive(Component, Default)]
|
||||
// enum SortHand {
|
||||
// #[default]
|
||||
// Unsorted,
|
||||
// Sort,
|
||||
// Manual,
|
||||
// }
|
||||
|
||||
pub(crate) fn sort_hands(
|
||||
tiles: Query<&Tile>,
|
||||
hands: Query<&mut Children, (Changed<Children>, With<Hand>, Without<Player>)>,
|
||||
) -> Result {
|
||||
for mut hand in hands {
|
||||
hand.sort_unstable_by_key(|e| tiles.get(*e).unwrap().suit);
|
||||
debug!("sorted: {hand:?}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn shuffle_deal(
|
||||
mut commands: Commands,
|
||||
tiles: Populated<Entity, With<Tile>>,
|
||||
players: Populated<Entity, With<Player>>,
|
||||
wall_ent: Single<Entity, With<Wall>>,
|
||||
mut next_gamestate: ResMut<NextState<GameState>>,
|
||||
) {
|
||||
use rand::seq::SliceRandom;
|
||||
|
||||
let mut rng = rand::rng();
|
||||
let mut walltiles: Vec<_> = tiles.iter().collect();
|
||||
walltiles.shuffle(&mut rng);
|
||||
|
||||
for player_ent in players {
|
||||
let handtiles = walltiles.split_off(walltiles.len() - 13);
|
||||
let hand_ent = commands.spawn(Hand).add_children(&handtiles).id();
|
||||
commands.entity(player_ent).add_child(hand_ent);
|
||||
debug!("deal to player_ent {player_ent:?} {hand_ent:?}");
|
||||
}
|
||||
|
||||
// don't need to remove hands from wall if we don't insert to wall to begin with
|
||||
// TODO probably do this later on when animating the draw
|
||||
debug!("shuffled: {walltiles:?}");
|
||||
commands.entity(*wall_ent).replace_children(&walltiles);
|
||||
|
||||
next_gamestate.set(GameState::Play);
|
||||
}
|
||||
|
||||
/// assumes hand is sorted
|
||||
pub(crate) fn check_wincon(hand: &[Tile; 14], melds: &[&[Tile]]) -> bool {
|
||||
// 4x3 + pair
|
||||
// assume sorted
|
||||
//
|
||||
// let melds = hand.iter().array_chunks::<3>().all(|tiles| {
|
||||
// let suit = discriminant(&tiles[0].suit);
|
||||
// let starting_rank = tiles[0].suit
|
||||
// // tiles.iter().all(|t| discriminant(&t.suit) == suit) && tiles.iter().zip(tiles[0].suit.rank())
|
||||
// }) && melds.iter().all(|meld| todo!());
|
||||
// let eyeball = todo!();
|
||||
|
||||
todo!();
|
||||
|
||||
// melds && eyeball
|
||||
}
|
||||
21
jong/src/game/player.rs
Normal file
21
jong/src/game/player.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Component, Debug, PartialEq)]
|
||||
pub struct Player {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Points(pub isize);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct MainPlayer;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct CurrentPlayer;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Dealer;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Tsumo;
|
||||
252
jong/src/game/round.rs
Normal file
252
jong/src/game/round.rs
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
use std::rc::Weak;
|
||||
|
||||
use bevy::{platform::collections::HashMap, prelude::*};
|
||||
use strum::{EnumCount, FromRepr};
|
||||
|
||||
use crate::{
|
||||
EnumNextCycle,
|
||||
game::{
|
||||
GameMessage, GameState,
|
||||
hand::{Discarded, Drawn, Hand, Pond},
|
||||
player::{CurrentPlayer, Player},
|
||||
wall::Wall,
|
||||
},
|
||||
};
|
||||
|
||||
// #[derive(Resource)]
|
||||
// pub struct CurrentPlayer(pub Entity);
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct MatchSettings {
|
||||
pub(crate) starting_points: isize,
|
||||
pub(crate) player_count: u8,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct Dice(u8, u8);
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct Compass {
|
||||
pub(crate) prevalent_wind: Wind,
|
||||
pub(crate) round: u8,
|
||||
pub(crate) dealer_wind: Wind,
|
||||
pub(crate) riichi: usize,
|
||||
pub(crate) honba: usize,
|
||||
}
|
||||
|
||||
#[derive(Component, Clone, Copy, FromRepr, EnumCount, PartialEq)]
|
||||
pub enum Wind {
|
||||
Ton,
|
||||
Nan,
|
||||
Shaa,
|
||||
Pei,
|
||||
}
|
||||
|
||||
pub enum WindRelation {
|
||||
Shimocha,
|
||||
Toimen,
|
||||
Kamicha,
|
||||
}
|
||||
|
||||
#[derive(SubStates, Default, Clone, Copy, PartialEq, Eq, Hash, Debug, FromRepr, EnumCount)]
|
||||
#[source(GameState = GameState::Play)]
|
||||
pub(crate) enum TurnState {
|
||||
#[default]
|
||||
Tsumo,
|
||||
Menzen,
|
||||
RiichiKan,
|
||||
Discard,
|
||||
RonChiiPonKan,
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum CallType {
|
||||
Skip,
|
||||
Ron,
|
||||
Chii,
|
||||
Pon,
|
||||
Kan,
|
||||
}
|
||||
|
||||
impl Default for MatchSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
starting_points: 25000,
|
||||
player_count: 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Compass {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
prevalent_wind: Wind::Ton,
|
||||
round: 1,
|
||||
dealer_wind: Wind::Ton,
|
||||
riichi: 0,
|
||||
honba: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumNextCycle for Wind {
|
||||
fn next(&self) -> Self {
|
||||
if (*self as usize + 1) >= Self::COUNT {
|
||||
Self::from_repr(0).unwrap()
|
||||
} else {
|
||||
Self::from_repr(*self as usize + 1).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Wind {
|
||||
pub fn relate(&self, other: &Self) -> WindRelation {
|
||||
if self.next() == *other {
|
||||
WindRelation::Shimocha
|
||||
} else if other.next() == *self {
|
||||
WindRelation::Kamicha
|
||||
} else {
|
||||
WindRelation::Toimen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumNextCycle for TurnState {
|
||||
fn next(&self) -> Self {
|
||||
if (*self as usize + 1) >= Self::COUNT {
|
||||
Self::from_repr(0).unwrap()
|
||||
} else {
|
||||
Self::from_repr(*self as usize + 1).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tsumo(
|
||||
mut commands: Commands,
|
||||
|
||||
// curr_player: Res<CurrentPlayer>,
|
||||
curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||
wall: Single<Entity, With<Wall>>,
|
||||
walltiles: Single<&Children, With<Wall>>,
|
||||
|
||||
curr_turnstate: Res<State<TurnState>>,
|
||||
mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
) {
|
||||
let drawn = walltiles.last().unwrap();
|
||||
commands.entity(*wall).remove_child(*drawn);
|
||||
|
||||
let drawn = commands.entity(*drawn).insert(Drawn).id();
|
||||
commands.entity(*curr_player).add_child(drawn);
|
||||
|
||||
debug!("tsumo for: {:?}, tile: {:?}", *curr_player, drawn);
|
||||
next_turnstate.set(curr_turnstate.next());
|
||||
}
|
||||
|
||||
pub(crate) fn menzen(
|
||||
curr_turnstate: Res<State<TurnState>>,
|
||||
mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
) {
|
||||
trace!("menzen check");
|
||||
next_turnstate.set(curr_turnstate.next());
|
||||
}
|
||||
|
||||
pub(crate) fn riichi_kan(
|
||||
curr_turnstate: Res<State<TurnState>>,
|
||||
mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
) {
|
||||
trace!("riichi_kan");
|
||||
next_turnstate.set(curr_turnstate.next());
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments, irrefutable_let_patterns)]
|
||||
pub(crate) fn discard(
|
||||
mut commands: Commands,
|
||||
mut reader: MessageReader<GameMessage>,
|
||||
|
||||
curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||
players: Query<&Children, With<Player>>,
|
||||
mut hands: Query<(&Children, Entity), (With<Hand>, Without<Player>)>,
|
||||
drawn: Single<Entity, With<Drawn>>,
|
||||
|
||||
curr_turnstate: Res<State<TurnState>>,
|
||||
mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
) -> Result {
|
||||
// trace!("discard");
|
||||
let (handtiles, hand) = hands.get_mut(players.get(*curr_player)?.iter().next().unwrap())?;
|
||||
|
||||
let mut done = false;
|
||||
while let Some(message) = reader.read().next() {
|
||||
if let GameMessage::Discarded(discarded) = message {
|
||||
debug!("discarded: {discarded:?}");
|
||||
if *discarded == *drawn {
|
||||
} else if handtiles.contains(discarded) {
|
||||
commands
|
||||
.entity(hand)
|
||||
.remove_child(*discarded)
|
||||
.add_child(*drawn);
|
||||
} else {
|
||||
panic!("current hand nor drawn tile contains discarded tile")
|
||||
}
|
||||
commands.entity(*drawn).remove::<Drawn>();
|
||||
commands.entity(*discarded).insert(Discarded);
|
||||
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if done {
|
||||
next_turnstate.set(curr_turnstate.next());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct PendingCalls {
|
||||
eligible: Vec<Entity>,
|
||||
calls: HashMap<Entity, CallType>,
|
||||
}
|
||||
|
||||
pub(crate) fn notify_callable() {}
|
||||
|
||||
pub(crate) fn ron_chi_pon_kan(
|
||||
mut commands: Commands,
|
||||
mut reader: MessageReader<GameMessage>,
|
||||
|
||||
discarded: Single<Entity, With<Discarded>>,
|
||||
mut ponds: Query<(&Children, Entity), (With<Pond>, Without<Player>)>,
|
||||
calls: Query<&CallType>,
|
||||
|
||||
curr_turnstate: Res<State<TurnState>>,
|
||||
mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
) {
|
||||
// check if can call?
|
||||
// message players?
|
||||
// collect then prioritize
|
||||
|
||||
// let mut received = vec![];
|
||||
let mut received: Vec<_> = reader
|
||||
.read()
|
||||
.filter_map(|m| {
|
||||
if let GameMessage::Called { player, calltype } = m
|
||||
&& let Ok(calltype) = calls.get(*calltype)
|
||||
{
|
||||
Some((calltype, player))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
// received.sort_unstable_by_key(|(c, t)| c);
|
||||
// received.sort_unstable_by_key(|m| m.);
|
||||
|
||||
next_turnstate.set(curr_turnstate.next());
|
||||
}
|
||||
|
||||
pub(crate) fn end(
|
||||
curr_turnstate: Res<State<TurnState>>,
|
||||
mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
) {
|
||||
next_turnstate.set(curr_turnstate.next());
|
||||
}
|
||||
7
jong/src/game/wall.rs
Normal file
7
jong/src/game/wall.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Wall;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Dead;
|
||||
29
jong/src/gui/mod.rs
Normal file
29
jong/src/gui/mod.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use bevy::{color::palettes::css::GREEN, prelude::*};
|
||||
|
||||
pub(crate) fn init_environment(mut commands: Commands) {
|
||||
commands.spawn((
|
||||
DirectionalLight {
|
||||
shadows_enabled: true,
|
||||
..default()
|
||||
},
|
||||
// Transform::from_xyz(),
|
||||
));
|
||||
commands.spawn((
|
||||
Camera3d::default(),
|
||||
Transform::from_xyz(-200.5, 100., 0.).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
));
|
||||
}
|
||||
|
||||
pub(crate) fn init_table(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
let green: Color = GREEN.into();
|
||||
let table = Cuboid::new(1000., 5., 1000.);
|
||||
|
||||
commands.spawn((
|
||||
Mesh3d(meshes.add(table)),
|
||||
MeshMaterial3d(materials.add(green)),
|
||||
));
|
||||
}
|
||||
105
jong/src/jongline_bindings/create_player_reducer.rs
Normal file
105
jong/src/jongline_bindings/create_player_reducer.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub(super) struct CreatePlayerArgs {
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
impl From<CreatePlayerArgs> for super::Reducer {
|
||||
fn from(args: CreatePlayerArgs) -> Self {
|
||||
Self::CreatePlayer {
|
||||
username: args.username,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for CreatePlayerArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
pub struct CreatePlayerCallbackId(__sdk::CallbackId);
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the reducer `create_player`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteReducers`].
|
||||
pub trait create_player {
|
||||
/// Request that the remote module invoke the reducer `create_player` to run as soon as possible.
|
||||
///
|
||||
/// This method returns immediately, and errors only if we are unable to send the request.
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and its status can be observed by listening for [`Self::on_create_player`] callbacks.
|
||||
fn create_player(&self, username: String) -> __sdk::Result<()>;
|
||||
/// Register a callback to run whenever we are notified of an invocation of the reducer `create_player`.
|
||||
///
|
||||
/// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`]
|
||||
/// to determine the reducer's status.
|
||||
///
|
||||
/// The returned [`CreatePlayerCallbackId`] can be passed to [`Self::remove_on_create_player`]
|
||||
/// to cancel the callback.
|
||||
fn on_create_player(
|
||||
&self,
|
||||
callback: impl FnMut(&super::ReducerEventContext, &String) + Send + 'static,
|
||||
) -> CreatePlayerCallbackId;
|
||||
/// Cancel a callback previously registered by [`Self::on_create_player`],
|
||||
/// causing it not to run in the future.
|
||||
fn remove_on_create_player(&self, callback: CreatePlayerCallbackId);
|
||||
}
|
||||
|
||||
impl create_player for super::RemoteReducers {
|
||||
fn create_player(&self, username: String) -> __sdk::Result<()> {
|
||||
self.imp
|
||||
.call_reducer("create_player", CreatePlayerArgs { username })
|
||||
}
|
||||
fn on_create_player(
|
||||
&self,
|
||||
mut callback: impl FnMut(&super::ReducerEventContext, &String) + Send + 'static,
|
||||
) -> CreatePlayerCallbackId {
|
||||
CreatePlayerCallbackId(self.imp.on_reducer(
|
||||
"create_player",
|
||||
Box::new(move |ctx: &super::ReducerEventContext| {
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
let super::ReducerEventContext {
|
||||
event:
|
||||
__sdk::ReducerEvent {
|
||||
reducer: super::Reducer::CreatePlayer { username },
|
||||
..
|
||||
},
|
||||
..
|
||||
} = ctx
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
callback(ctx, username)
|
||||
}),
|
||||
))
|
||||
}
|
||||
fn remove_on_create_player(&self, callback: CreatePlayerCallbackId) {
|
||||
self.imp.remove_on_reducer("create_player", callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[doc(hidden)]
|
||||
/// Extension trait for setting the call-flags for the reducer `create_player`.
|
||||
///
|
||||
/// Implemented for [`super::SetReducerFlags`].
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
pub trait set_flags_for_create_player {
|
||||
/// Set the call-reducer flags for the reducer `create_player` to `flags`.
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
fn create_player(&self, flags: __ws::CallReducerFlags);
|
||||
}
|
||||
|
||||
impl set_flags_for_create_player for super::SetReducerFlags {
|
||||
fn create_player(&self, flags: __ws::CallReducerFlags) {
|
||||
self.imp.set_call_reducer_flags("create_player", flags);
|
||||
}
|
||||
}
|
||||
95
jong/src/jongline_bindings/drawn_table.rs
Normal file
95
jong/src/jongline_bindings/drawn_table.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// 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::drawn_type::Drawn;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `drawn`.
|
||||
///
|
||||
/// Obtain a handle from the [`DrawnTableAccess::drawn`] method on [`super::RemoteTables`],
|
||||
/// like `ctx.db.drawn()`.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.drawn().on_insert(...)`.
|
||||
pub struct DrawnTableHandle<'ctx> {
|
||||
imp: __sdk::TableHandle<Drawn>,
|
||||
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the table `drawn`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteTables`].
|
||||
pub trait DrawnTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Obtain a [`DrawnTableHandle`], which mediates access to the table `drawn`.
|
||||
fn drawn(&self) -> DrawnTableHandle<'_>;
|
||||
}
|
||||
|
||||
impl DrawnTableAccess for super::RemoteTables {
|
||||
fn drawn(&self) -> DrawnTableHandle<'_> {
|
||||
DrawnTableHandle {
|
||||
imp: self.imp.get_table::<Drawn>("drawn"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrawnInsertCallbackId(__sdk::CallbackId);
|
||||
pub struct DrawnDeleteCallbackId(__sdk::CallbackId);
|
||||
|
||||
impl<'ctx> __sdk::Table for DrawnTableHandle<'ctx> {
|
||||
type Row = Drawn;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = Drawn> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = DrawnInsertCallbackId;
|
||||
|
||||
fn on_insert(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> DrawnInsertCallbackId {
|
||||
DrawnInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_insert(&self, callback: DrawnInsertCallbackId) {
|
||||
self.imp.remove_on_insert(callback.0)
|
||||
}
|
||||
|
||||
type DeleteCallbackId = DrawnDeleteCallbackId;
|
||||
|
||||
fn on_delete(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> DrawnDeleteCallbackId {
|
||||
DrawnDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_delete(&self, callback: DrawnDeleteCallbackId) {
|
||||
self.imp.remove_on_delete(callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
let _table = client_cache.get_or_make_table::<Drawn>("drawn");
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::TableUpdate<__ws::BsatnFormat>,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<Drawn>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse("TableUpdate<Drawn>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
13
jong/src/jongline_bindings/drawn_type.rs
Normal file
13
jong/src/jongline_bindings/drawn_type.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// 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 Drawn {}
|
||||
|
||||
impl __sdk::InModule for Drawn {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
95
jong/src/jongline_bindings/hand_table.rs
Normal file
95
jong/src/jongline_bindings/hand_table.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use super::hand_type::Hand;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `hand`.
|
||||
///
|
||||
/// Obtain a handle from the [`HandTableAccess::hand`] method on [`super::RemoteTables`],
|
||||
/// like `ctx.db.hand()`.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.hand().on_insert(...)`.
|
||||
pub struct HandTableHandle<'ctx> {
|
||||
imp: __sdk::TableHandle<Hand>,
|
||||
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the table `hand`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteTables`].
|
||||
pub trait HandTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Obtain a [`HandTableHandle`], which mediates access to the table `hand`.
|
||||
fn hand(&self) -> HandTableHandle<'_>;
|
||||
}
|
||||
|
||||
impl HandTableAccess for super::RemoteTables {
|
||||
fn hand(&self) -> HandTableHandle<'_> {
|
||||
HandTableHandle {
|
||||
imp: self.imp.get_table::<Hand>("hand"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HandInsertCallbackId(__sdk::CallbackId);
|
||||
pub struct HandDeleteCallbackId(__sdk::CallbackId);
|
||||
|
||||
impl<'ctx> __sdk::Table for HandTableHandle<'ctx> {
|
||||
type Row = Hand;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = Hand> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = HandInsertCallbackId;
|
||||
|
||||
fn on_insert(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> HandInsertCallbackId {
|
||||
HandInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_insert(&self, callback: HandInsertCallbackId) {
|
||||
self.imp.remove_on_insert(callback.0)
|
||||
}
|
||||
|
||||
type DeleteCallbackId = HandDeleteCallbackId;
|
||||
|
||||
fn on_delete(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> HandDeleteCallbackId {
|
||||
HandDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_delete(&self, callback: HandDeleteCallbackId) {
|
||||
self.imp.remove_on_delete(callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
let _table = client_cache.get_or_make_table::<Hand>("hand");
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::TableUpdate<__ws::BsatnFormat>,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<Hand>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse("TableUpdate<Hand>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
13
jong/src/jongline_bindings/hand_type.rs
Normal file
13
jong/src/jongline_bindings/hand_type.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// 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 Hand {}
|
||||
|
||||
impl __sdk::InModule for Hand {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
101
jong/src/jongline_bindings/identity_connected_reducer.rs
Normal file
101
jong/src/jongline_bindings/identity_connected_reducer.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// 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 IdentityConnectedArgs {}
|
||||
|
||||
impl From<IdentityConnectedArgs> for super::Reducer {
|
||||
fn from(args: IdentityConnectedArgs) -> Self {
|
||||
Self::IdentityConnected
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for IdentityConnectedArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
pub struct IdentityConnectedCallbackId(__sdk::CallbackId);
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the reducer `identity_connected`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteReducers`].
|
||||
pub trait identity_connected {
|
||||
/// Request that the remote module invoke the reducer `identity_connected` to run as soon as possible.
|
||||
///
|
||||
/// This method returns immediately, and errors only if we are unable to send the request.
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and its status can be observed by listening for [`Self::on_identity_connected`] callbacks.
|
||||
fn identity_connected(&self) -> __sdk::Result<()>;
|
||||
/// Register a callback to run whenever we are notified of an invocation of the reducer `identity_connected`.
|
||||
///
|
||||
/// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`]
|
||||
/// to determine the reducer's status.
|
||||
///
|
||||
/// The returned [`IdentityConnectedCallbackId`] can be passed to [`Self::remove_on_identity_connected`]
|
||||
/// to cancel the callback.
|
||||
fn on_identity_connected(
|
||||
&self,
|
||||
callback: impl FnMut(&super::ReducerEventContext) + Send + 'static,
|
||||
) -> IdentityConnectedCallbackId;
|
||||
/// Cancel a callback previously registered by [`Self::on_identity_connected`],
|
||||
/// causing it not to run in the future.
|
||||
fn remove_on_identity_connected(&self, callback: IdentityConnectedCallbackId);
|
||||
}
|
||||
|
||||
impl identity_connected for super::RemoteReducers {
|
||||
fn identity_connected(&self) -> __sdk::Result<()> {
|
||||
self.imp
|
||||
.call_reducer("identity_connected", IdentityConnectedArgs {})
|
||||
}
|
||||
fn on_identity_connected(
|
||||
&self,
|
||||
mut callback: impl FnMut(&super::ReducerEventContext) + Send + 'static,
|
||||
) -> IdentityConnectedCallbackId {
|
||||
IdentityConnectedCallbackId(self.imp.on_reducer(
|
||||
"identity_connected",
|
||||
Box::new(move |ctx: &super::ReducerEventContext| {
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
let super::ReducerEventContext {
|
||||
event:
|
||||
__sdk::ReducerEvent {
|
||||
reducer: super::Reducer::IdentityConnected {},
|
||||
..
|
||||
},
|
||||
..
|
||||
} = ctx
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
callback(ctx)
|
||||
}),
|
||||
))
|
||||
}
|
||||
fn remove_on_identity_connected(&self, callback: IdentityConnectedCallbackId) {
|
||||
self.imp.remove_on_reducer("identity_connected", callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[doc(hidden)]
|
||||
/// Extension trait for setting the call-flags for the reducer `identity_connected`.
|
||||
///
|
||||
/// Implemented for [`super::SetReducerFlags`].
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
pub trait set_flags_for_identity_connected {
|
||||
/// Set the call-reducer flags for the reducer `identity_connected` to `flags`.
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
fn identity_connected(&self, flags: __ws::CallReducerFlags);
|
||||
}
|
||||
|
||||
impl set_flags_for_identity_connected for super::SetReducerFlags {
|
||||
fn identity_connected(&self, flags: __ws::CallReducerFlags) {
|
||||
self.imp.set_call_reducer_flags("identity_connected", flags);
|
||||
}
|
||||
}
|
||||
103
jong/src/jongline_bindings/identity_disconnected_reducer.rs
Normal file
103
jong/src/jongline_bindings/identity_disconnected_reducer.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
// 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 IdentityDisconnectedArgs {}
|
||||
|
||||
impl From<IdentityDisconnectedArgs> for super::Reducer {
|
||||
fn from(args: IdentityDisconnectedArgs) -> Self {
|
||||
Self::IdentityDisconnected
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for IdentityDisconnectedArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
pub struct IdentityDisconnectedCallbackId(__sdk::CallbackId);
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the reducer `identity_disconnected`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteReducers`].
|
||||
pub trait identity_disconnected {
|
||||
/// Request that the remote module invoke the reducer `identity_disconnected` to run as soon as possible.
|
||||
///
|
||||
/// This method returns immediately, and errors only if we are unable to send the request.
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and its status can be observed by listening for [`Self::on_identity_disconnected`] callbacks.
|
||||
fn identity_disconnected(&self) -> __sdk::Result<()>;
|
||||
/// Register a callback to run whenever we are notified of an invocation of the reducer `identity_disconnected`.
|
||||
///
|
||||
/// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`]
|
||||
/// to determine the reducer's status.
|
||||
///
|
||||
/// The returned [`IdentityDisconnectedCallbackId`] can be passed to [`Self::remove_on_identity_disconnected`]
|
||||
/// to cancel the callback.
|
||||
fn on_identity_disconnected(
|
||||
&self,
|
||||
callback: impl FnMut(&super::ReducerEventContext) + Send + 'static,
|
||||
) -> IdentityDisconnectedCallbackId;
|
||||
/// Cancel a callback previously registered by [`Self::on_identity_disconnected`],
|
||||
/// causing it not to run in the future.
|
||||
fn remove_on_identity_disconnected(&self, callback: IdentityDisconnectedCallbackId);
|
||||
}
|
||||
|
||||
impl identity_disconnected for super::RemoteReducers {
|
||||
fn identity_disconnected(&self) -> __sdk::Result<()> {
|
||||
self.imp
|
||||
.call_reducer("identity_disconnected", IdentityDisconnectedArgs {})
|
||||
}
|
||||
fn on_identity_disconnected(
|
||||
&self,
|
||||
mut callback: impl FnMut(&super::ReducerEventContext) + Send + 'static,
|
||||
) -> IdentityDisconnectedCallbackId {
|
||||
IdentityDisconnectedCallbackId(self.imp.on_reducer(
|
||||
"identity_disconnected",
|
||||
Box::new(move |ctx: &super::ReducerEventContext| {
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
let super::ReducerEventContext {
|
||||
event:
|
||||
__sdk::ReducerEvent {
|
||||
reducer: super::Reducer::IdentityDisconnected {},
|
||||
..
|
||||
},
|
||||
..
|
||||
} = ctx
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
callback(ctx)
|
||||
}),
|
||||
))
|
||||
}
|
||||
fn remove_on_identity_disconnected(&self, callback: IdentityDisconnectedCallbackId) {
|
||||
self.imp
|
||||
.remove_on_reducer("identity_disconnected", callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[doc(hidden)]
|
||||
/// Extension trait for setting the call-flags for the reducer `identity_disconnected`.
|
||||
///
|
||||
/// Implemented for [`super::SetReducerFlags`].
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
pub trait set_flags_for_identity_disconnected {
|
||||
/// Set the call-reducer flags for the reducer `identity_disconnected` to `flags`.
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
fn identity_disconnected(&self, flags: __ws::CallReducerFlags);
|
||||
}
|
||||
|
||||
impl set_flags_for_identity_disconnected for super::SetReducerFlags {
|
||||
fn identity_disconnected(&self, flags: __ws::CallReducerFlags) {
|
||||
self.imp
|
||||
.set_call_reducer_flags("identity_disconnected", flags);
|
||||
}
|
||||
}
|
||||
916
jong/src/jongline_bindings/mod.rs
Normal file
916
jong/src/jongline_bindings/mod.rs
Normal file
|
|
@ -0,0 +1,916 @@
|
|||
// 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 1.11.3 (commit 02449737ca3b29e7e39679fccbef541a50f32094).
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
pub mod create_player_reducer;
|
||||
pub mod drawn_table;
|
||||
pub mod drawn_type;
|
||||
pub mod hand_table;
|
||||
pub mod hand_type;
|
||||
pub mod identity_connected_reducer;
|
||||
pub mod identity_disconnected_reducer;
|
||||
pub mod player_table;
|
||||
pub mod player_type;
|
||||
pub mod table_table;
|
||||
pub mod table_type;
|
||||
pub mod wall_table;
|
||||
pub mod wall_type;
|
||||
|
||||
pub use create_player_reducer::{
|
||||
create_player, set_flags_for_create_player, CreatePlayerCallbackId,
|
||||
};
|
||||
pub use drawn_table::*;
|
||||
pub use drawn_type::Drawn;
|
||||
pub use hand_table::*;
|
||||
pub use hand_type::Hand;
|
||||
pub use identity_connected_reducer::{
|
||||
identity_connected, set_flags_for_identity_connected, IdentityConnectedCallbackId,
|
||||
};
|
||||
pub use identity_disconnected_reducer::{
|
||||
identity_disconnected, set_flags_for_identity_disconnected, IdentityDisconnectedCallbackId,
|
||||
};
|
||||
pub use player_table::*;
|
||||
pub use player_type::Player;
|
||||
pub use table_table::*;
|
||||
pub use table_type::Table;
|
||||
pub use wall_table::*;
|
||||
pub use wall_type::Wall;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
|
||||
/// One of the reducers defined by this module.
|
||||
///
|
||||
/// Contained within a [`__sdk::ReducerEvent`] in [`EventContext`]s for reducer events
|
||||
/// to indicate which reducer caused the event.
|
||||
|
||||
pub enum Reducer {
|
||||
CreatePlayer { username: String },
|
||||
IdentityConnected,
|
||||
IdentityDisconnected,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for Reducer {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
impl __sdk::Reducer for Reducer {
|
||||
fn reducer_name(&self) -> &'static str {
|
||||
match self {
|
||||
Reducer::CreatePlayer { .. } => "create_player",
|
||||
Reducer::IdentityConnected => "identity_connected",
|
||||
Reducer::IdentityDisconnected => "identity_disconnected",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer {
|
||||
type Error = __sdk::Error;
|
||||
fn try_from(value: __ws::ReducerCallInfo<__ws::BsatnFormat>) -> __sdk::Result<Self> {
|
||||
match &value.reducer_name[..] {
|
||||
"create_player" => Ok(__sdk::parse_reducer_args::<
|
||||
create_player_reducer::CreatePlayerArgs,
|
||||
>("create_player", &value.args)?
|
||||
.into()),
|
||||
"identity_connected" => Ok(__sdk::parse_reducer_args::<
|
||||
identity_connected_reducer::IdentityConnectedArgs,
|
||||
>("identity_connected", &value.args)?
|
||||
.into()),
|
||||
"identity_disconnected" => Ok(__sdk::parse_reducer_args::<
|
||||
identity_disconnected_reducer::IdentityDisconnectedArgs,
|
||||
>("identity_disconnected", &value.args)?
|
||||
.into()),
|
||||
unknown => {
|
||||
Err(
|
||||
__sdk::InternalError::unknown_name("reducer", unknown, "ReducerCallInfo")
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[allow(non_snake_case)]
|
||||
#[doc(hidden)]
|
||||
pub struct DbUpdate {
|
||||
drawn: __sdk::TableUpdate<Drawn>,
|
||||
hand: __sdk::TableUpdate<Hand>,
|
||||
player: __sdk::TableUpdate<Player>,
|
||||
table: __sdk::TableUpdate<Table>,
|
||||
wall: __sdk::TableUpdate<Wall>,
|
||||
}
|
||||
|
||||
impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate {
|
||||
type Error = __sdk::Error;
|
||||
fn try_from(raw: __ws::DatabaseUpdate<__ws::BsatnFormat>) -> Result<Self, Self::Error> {
|
||||
let mut db_update = DbUpdate::default();
|
||||
for table_update in raw.tables {
|
||||
match &table_update.table_name[..] {
|
||||
"drawn" => db_update
|
||||
.drawn
|
||||
.append(drawn_table::parse_table_update(table_update)?),
|
||||
"hand" => db_update
|
||||
.hand
|
||||
.append(hand_table::parse_table_update(table_update)?),
|
||||
"player" => db_update
|
||||
.player
|
||||
.append(player_table::parse_table_update(table_update)?),
|
||||
"table" => db_update
|
||||
.table
|
||||
.append(table_table::parse_table_update(table_update)?),
|
||||
"wall" => db_update
|
||||
.wall
|
||||
.append(wall_table::parse_table_update(table_update)?),
|
||||
|
||||
unknown => {
|
||||
return Err(__sdk::InternalError::unknown_name(
|
||||
"table",
|
||||
unknown,
|
||||
"DatabaseUpdate",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(db_update)
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for DbUpdate {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
impl __sdk::DbUpdate for DbUpdate {
|
||||
fn apply_to_client_cache(
|
||||
&self,
|
||||
cache: &mut __sdk::ClientCache<RemoteModule>,
|
||||
) -> AppliedDiff<'_> {
|
||||
let mut diff = AppliedDiff::default();
|
||||
|
||||
diff.drawn = cache.apply_diff_to_table::<Drawn>("drawn", &self.drawn);
|
||||
diff.hand = cache.apply_diff_to_table::<Hand>("hand", &self.hand);
|
||||
diff.player = cache.apply_diff_to_table::<Player>("player", &self.player);
|
||||
diff.table = cache.apply_diff_to_table::<Table>("table", &self.table);
|
||||
diff.wall = cache.apply_diff_to_table::<Wall>("wall", &self.wall);
|
||||
|
||||
diff
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[allow(non_snake_case)]
|
||||
#[doc(hidden)]
|
||||
pub struct AppliedDiff<'r> {
|
||||
drawn: __sdk::TableAppliedDiff<'r, Drawn>,
|
||||
hand: __sdk::TableAppliedDiff<'r, Hand>,
|
||||
player: __sdk::TableAppliedDiff<'r, Player>,
|
||||
table: __sdk::TableAppliedDiff<'r, Table>,
|
||||
wall: __sdk::TableAppliedDiff<'r, Wall>,
|
||||
__unused: std::marker::PhantomData<&'r ()>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AppliedDiff<'_> {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
|
||||
fn invoke_row_callbacks(
|
||||
&self,
|
||||
event: &EventContext,
|
||||
callbacks: &mut __sdk::DbCallbacks<RemoteModule>,
|
||||
) {
|
||||
callbacks.invoke_table_row_callbacks::<Drawn>("drawn", &self.drawn, event);
|
||||
callbacks.invoke_table_row_callbacks::<Hand>("hand", &self.hand, event);
|
||||
callbacks.invoke_table_row_callbacks::<Player>("player", &self.player, event);
|
||||
callbacks.invoke_table_row_callbacks::<Table>("table", &self.table, event);
|
||||
callbacks.invoke_table_row_callbacks::<Wall>("wall", &self.wall, event);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct RemoteModule;
|
||||
|
||||
impl __sdk::InModule for RemoteModule {
|
||||
type Module = Self;
|
||||
}
|
||||
|
||||
/// The `reducers` field of [`EventContext`] and [`DbConnection`],
|
||||
/// with methods provided by extension traits for each reducer defined by the module.
|
||||
pub struct RemoteReducers {
|
||||
imp: __sdk::DbContextImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for RemoteReducers {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
/// The `procedures` field of [`DbConnection`] and other [`DbContext`] types,
|
||||
/// with methods provided by extension traits for each procedure defined by the module.
|
||||
pub struct RemoteProcedures {
|
||||
imp: __sdk::DbContextImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for RemoteProcedures {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// The `set_reducer_flags` field of [`DbConnection`],
|
||||
/// with methods provided by extension traits for each reducer defined by the module.
|
||||
/// Each method sets the flags for the reducer with the same name.
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
pub struct SetReducerFlags {
|
||||
imp: __sdk::DbContextImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for SetReducerFlags {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
/// The `db` field of [`EventContext`] and [`DbConnection`],
|
||||
/// with methods provided by extension traits for each table defined by the module.
|
||||
pub struct RemoteTables {
|
||||
imp: __sdk::DbContextImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for RemoteTables {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
/// A connection to a remote module, including a materialized view of a subset of the database.
|
||||
///
|
||||
/// Connect to a remote module by calling [`DbConnection::builder`]
|
||||
/// and using the [`__sdk::DbConnectionBuilder`] builder-pattern constructor.
|
||||
///
|
||||
/// You must explicitly advance the connection by calling any one of:
|
||||
///
|
||||
/// - [`DbConnection::frame_tick`].
|
||||
/// - [`DbConnection::run_threaded`].
|
||||
/// - [`DbConnection::run_async`].
|
||||
/// - [`DbConnection::advance_one_message`].
|
||||
/// - [`DbConnection::advance_one_message_blocking`].
|
||||
/// - [`DbConnection::advance_one_message_async`].
|
||||
///
|
||||
/// Which of these methods you should call depends on the specific needs of your application,
|
||||
/// but you must call one of them, or else the connection will never progress.
|
||||
pub struct DbConnection {
|
||||
/// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].
|
||||
pub db: RemoteTables,
|
||||
/// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].
|
||||
pub reducers: RemoteReducers,
|
||||
#[doc(hidden)]
|
||||
/// Access to setting the call-flags of each reducer defined for each reducer defined by the module
|
||||
/// via extension traits implemented for [`SetReducerFlags`].
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
pub set_reducer_flags: SetReducerFlags,
|
||||
|
||||
/// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].
|
||||
pub procedures: RemoteProcedures,
|
||||
|
||||
imp: __sdk::DbContextImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for DbConnection {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
impl __sdk::DbContext for DbConnection {
|
||||
type DbView = RemoteTables;
|
||||
type Reducers = RemoteReducers;
|
||||
type Procedures = RemoteProcedures;
|
||||
type SetReducerFlags = SetReducerFlags;
|
||||
|
||||
fn db(&self) -> &Self::DbView {
|
||||
&self.db
|
||||
}
|
||||
fn reducers(&self) -> &Self::Reducers {
|
||||
&self.reducers
|
||||
}
|
||||
fn procedures(&self) -> &Self::Procedures {
|
||||
&self.procedures
|
||||
}
|
||||
fn set_reducer_flags(&self) -> &Self::SetReducerFlags {
|
||||
&self.set_reducer_flags
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.imp.is_active()
|
||||
}
|
||||
|
||||
fn disconnect(&self) -> __sdk::Result<()> {
|
||||
self.imp.disconnect()
|
||||
}
|
||||
|
||||
type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;
|
||||
|
||||
fn subscription_builder(&self) -> Self::SubscriptionBuilder {
|
||||
__sdk::SubscriptionBuilder::new(&self.imp)
|
||||
}
|
||||
|
||||
fn try_identity(&self) -> Option<__sdk::Identity> {
|
||||
self.imp.try_identity()
|
||||
}
|
||||
fn connection_id(&self) -> __sdk::ConnectionId {
|
||||
self.imp.connection_id()
|
||||
}
|
||||
fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {
|
||||
self.imp.try_connection_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl DbConnection {
|
||||
/// Builder-pattern constructor for a connection to a remote module.
|
||||
///
|
||||
/// See [`__sdk::DbConnectionBuilder`] for required and optional configuration for the new connection.
|
||||
pub fn builder() -> __sdk::DbConnectionBuilder<RemoteModule> {
|
||||
__sdk::DbConnectionBuilder::new()
|
||||
}
|
||||
|
||||
/// If any WebSocket messages are waiting, process one of them.
|
||||
///
|
||||
/// Returns `true` if a message was processed, or `false` if the queue is empty.
|
||||
/// Callers should invoke this message in a loop until it returns `false`
|
||||
/// or for as much time is available to process messages.
|
||||
///
|
||||
/// Returns an error if the connection is disconnected.
|
||||
/// If the disconnection in question was normal,
|
||||
/// i.e. the result of a call to [`__sdk::DbContext::disconnect`],
|
||||
/// the returned error will be downcastable to [`__sdk::DisconnectedError`].
|
||||
///
|
||||
/// This is a low-level primitive exposed for power users who need significant control over scheduling.
|
||||
/// Most applications should call [`Self::frame_tick`] each frame
|
||||
/// to fully exhaust the queue whenever time is available.
|
||||
pub fn advance_one_message(&self) -> __sdk::Result<bool> {
|
||||
self.imp.advance_one_message()
|
||||
}
|
||||
|
||||
/// Process one WebSocket message, potentially blocking the current thread until one is received.
|
||||
///
|
||||
/// Returns an error if the connection is disconnected.
|
||||
/// If the disconnection in question was normal,
|
||||
/// i.e. the result of a call to [`__sdk::DbContext::disconnect`],
|
||||
/// the returned error will be downcastable to [`__sdk::DisconnectedError`].
|
||||
///
|
||||
/// This is a low-level primitive exposed for power users who need significant control over scheduling.
|
||||
/// Most applications should call [`Self::run_threaded`] to spawn a thread
|
||||
/// which advances the connection automatically.
|
||||
pub fn advance_one_message_blocking(&self) -> __sdk::Result<()> {
|
||||
self.imp.advance_one_message_blocking()
|
||||
}
|
||||
|
||||
/// Process one WebSocket message, `await`ing until one is received.
|
||||
///
|
||||
/// Returns an error if the connection is disconnected.
|
||||
/// If the disconnection in question was normal,
|
||||
/// i.e. the result of a call to [`__sdk::DbContext::disconnect`],
|
||||
/// the returned error will be downcastable to [`__sdk::DisconnectedError`].
|
||||
///
|
||||
/// This is a low-level primitive exposed for power users who need significant control over scheduling.
|
||||
/// Most applications should call [`Self::run_async`] to run an `async` loop
|
||||
/// which advances the connection when polled.
|
||||
pub async fn advance_one_message_async(&self) -> __sdk::Result<()> {
|
||||
self.imp.advance_one_message_async().await
|
||||
}
|
||||
|
||||
/// Process all WebSocket messages waiting in the queue,
|
||||
/// then return without `await`ing or blocking the current thread.
|
||||
pub fn frame_tick(&self) -> __sdk::Result<()> {
|
||||
self.imp.frame_tick()
|
||||
}
|
||||
|
||||
/// Spawn a thread which processes WebSocket messages as they are received.
|
||||
pub fn run_threaded(&self) -> std::thread::JoinHandle<()> {
|
||||
self.imp.run_threaded()
|
||||
}
|
||||
|
||||
/// Run an `async` loop which processes WebSocket messages when polled.
|
||||
pub async fn run_async(&self) -> __sdk::Result<()> {
|
||||
self.imp.run_async().await
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::DbConnection for DbConnection {
|
||||
fn new(imp: __sdk::DbContextImpl<RemoteModule>) -> Self {
|
||||
Self {
|
||||
db: RemoteTables { imp: imp.clone() },
|
||||
reducers: RemoteReducers { imp: imp.clone() },
|
||||
procedures: RemoteProcedures { imp: imp.clone() },
|
||||
set_reducer_flags: SetReducerFlags { imp: imp.clone() },
|
||||
imp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle on a subscribed query.
|
||||
// TODO: Document this better after implementing the new subscription API.
|
||||
#[derive(Clone)]
|
||||
pub struct SubscriptionHandle {
|
||||
imp: __sdk::SubscriptionHandleImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for SubscriptionHandle {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
impl __sdk::SubscriptionHandle for SubscriptionHandle {
|
||||
fn new(imp: __sdk::SubscriptionHandleImpl<RemoteModule>) -> Self {
|
||||
Self { imp }
|
||||
}
|
||||
|
||||
/// Returns true if this subscription has been terminated due to an unsubscribe call or an error.
|
||||
fn is_ended(&self) -> bool {
|
||||
self.imp.is_ended()
|
||||
}
|
||||
|
||||
/// Returns true if this subscription has been applied and has not yet been unsubscribed.
|
||||
fn is_active(&self) -> bool {
|
||||
self.imp.is_active()
|
||||
}
|
||||
|
||||
/// Unsubscribe from the query controlled by this `SubscriptionHandle`,
|
||||
/// then run `on_end` when its rows are removed from the client cache.
|
||||
fn unsubscribe_then(self, on_end: __sdk::OnEndedCallback<RemoteModule>) -> __sdk::Result<()> {
|
||||
self.imp.unsubscribe_then(Some(on_end))
|
||||
}
|
||||
|
||||
fn unsubscribe(self) -> __sdk::Result<()> {
|
||||
self.imp.unsubscribe_then(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Alias trait for a [`__sdk::DbContext`] connected to this module,
|
||||
/// with that trait's associated types bounded to this module's concrete types.
|
||||
///
|
||||
/// Users can use this trait as a boundary on definitions which should accept
|
||||
/// either a [`DbConnection`] or an [`EventContext`] and operate on either.
|
||||
pub trait RemoteDbContext:
|
||||
__sdk::DbContext<
|
||||
DbView = RemoteTables,
|
||||
Reducers = RemoteReducers,
|
||||
SetReducerFlags = SetReducerFlags,
|
||||
SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,
|
||||
>
|
||||
{
|
||||
}
|
||||
impl<
|
||||
Ctx: __sdk::DbContext<
|
||||
DbView = RemoteTables,
|
||||
Reducers = RemoteReducers,
|
||||
SetReducerFlags = SetReducerFlags,
|
||||
SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,
|
||||
>,
|
||||
> RemoteDbContext for Ctx
|
||||
{
|
||||
}
|
||||
|
||||
/// An [`__sdk::DbContext`] augmented with a [`__sdk::Event`],
|
||||
/// passed to [`__sdk::Table::on_insert`], [`__sdk::Table::on_delete`] and [`__sdk::TableWithPrimaryKey::on_update`] callbacks.
|
||||
pub struct EventContext {
|
||||
/// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].
|
||||
pub db: RemoteTables,
|
||||
/// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].
|
||||
pub reducers: RemoteReducers,
|
||||
/// Access to setting the call-flags of each reducer defined for each reducer defined by the module
|
||||
/// via extension traits implemented for [`SetReducerFlags`].
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
pub set_reducer_flags: SetReducerFlags,
|
||||
/// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].
|
||||
pub procedures: RemoteProcedures,
|
||||
/// The event which caused these callbacks to run.
|
||||
pub event: __sdk::Event<Reducer>,
|
||||
imp: __sdk::DbContextImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::AbstractEventContext for EventContext {
|
||||
type Event = __sdk::Event<Reducer>;
|
||||
fn event(&self) -> &Self::Event {
|
||||
&self.event
|
||||
}
|
||||
fn new(imp: __sdk::DbContextImpl<RemoteModule>, event: Self::Event) -> Self {
|
||||
Self {
|
||||
db: RemoteTables { imp: imp.clone() },
|
||||
reducers: RemoteReducers { imp: imp.clone() },
|
||||
set_reducer_flags: SetReducerFlags { imp: imp.clone() },
|
||||
procedures: RemoteProcedures { imp: imp.clone() },
|
||||
event,
|
||||
imp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for EventContext {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
impl __sdk::DbContext for EventContext {
|
||||
type DbView = RemoteTables;
|
||||
type Reducers = RemoteReducers;
|
||||
type Procedures = RemoteProcedures;
|
||||
type SetReducerFlags = SetReducerFlags;
|
||||
|
||||
fn db(&self) -> &Self::DbView {
|
||||
&self.db
|
||||
}
|
||||
fn reducers(&self) -> &Self::Reducers {
|
||||
&self.reducers
|
||||
}
|
||||
fn procedures(&self) -> &Self::Procedures {
|
||||
&self.procedures
|
||||
}
|
||||
fn set_reducer_flags(&self) -> &Self::SetReducerFlags {
|
||||
&self.set_reducer_flags
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.imp.is_active()
|
||||
}
|
||||
|
||||
fn disconnect(&self) -> __sdk::Result<()> {
|
||||
self.imp.disconnect()
|
||||
}
|
||||
|
||||
type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;
|
||||
|
||||
fn subscription_builder(&self) -> Self::SubscriptionBuilder {
|
||||
__sdk::SubscriptionBuilder::new(&self.imp)
|
||||
}
|
||||
|
||||
fn try_identity(&self) -> Option<__sdk::Identity> {
|
||||
self.imp.try_identity()
|
||||
}
|
||||
fn connection_id(&self) -> __sdk::ConnectionId {
|
||||
self.imp.connection_id()
|
||||
}
|
||||
fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {
|
||||
self.imp.try_connection_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::EventContext for EventContext {}
|
||||
|
||||
/// An [`__sdk::DbContext`] augmented with a [`__sdk::ReducerEvent`],
|
||||
/// passed to on-reducer callbacks.
|
||||
pub struct ReducerEventContext {
|
||||
/// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].
|
||||
pub db: RemoteTables,
|
||||
/// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].
|
||||
pub reducers: RemoteReducers,
|
||||
/// Access to setting the call-flags of each reducer defined for each reducer defined by the module
|
||||
/// via extension traits implemented for [`SetReducerFlags`].
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
pub set_reducer_flags: SetReducerFlags,
|
||||
/// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].
|
||||
pub procedures: RemoteProcedures,
|
||||
/// The event which caused these callbacks to run.
|
||||
pub event: __sdk::ReducerEvent<Reducer>,
|
||||
imp: __sdk::DbContextImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::AbstractEventContext for ReducerEventContext {
|
||||
type Event = __sdk::ReducerEvent<Reducer>;
|
||||
fn event(&self) -> &Self::Event {
|
||||
&self.event
|
||||
}
|
||||
fn new(imp: __sdk::DbContextImpl<RemoteModule>, event: Self::Event) -> Self {
|
||||
Self {
|
||||
db: RemoteTables { imp: imp.clone() },
|
||||
reducers: RemoteReducers { imp: imp.clone() },
|
||||
set_reducer_flags: SetReducerFlags { imp: imp.clone() },
|
||||
procedures: RemoteProcedures { imp: imp.clone() },
|
||||
event,
|
||||
imp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for ReducerEventContext {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
impl __sdk::DbContext for ReducerEventContext {
|
||||
type DbView = RemoteTables;
|
||||
type Reducers = RemoteReducers;
|
||||
type Procedures = RemoteProcedures;
|
||||
type SetReducerFlags = SetReducerFlags;
|
||||
|
||||
fn db(&self) -> &Self::DbView {
|
||||
&self.db
|
||||
}
|
||||
fn reducers(&self) -> &Self::Reducers {
|
||||
&self.reducers
|
||||
}
|
||||
fn procedures(&self) -> &Self::Procedures {
|
||||
&self.procedures
|
||||
}
|
||||
fn set_reducer_flags(&self) -> &Self::SetReducerFlags {
|
||||
&self.set_reducer_flags
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.imp.is_active()
|
||||
}
|
||||
|
||||
fn disconnect(&self) -> __sdk::Result<()> {
|
||||
self.imp.disconnect()
|
||||
}
|
||||
|
||||
type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;
|
||||
|
||||
fn subscription_builder(&self) -> Self::SubscriptionBuilder {
|
||||
__sdk::SubscriptionBuilder::new(&self.imp)
|
||||
}
|
||||
|
||||
fn try_identity(&self) -> Option<__sdk::Identity> {
|
||||
self.imp.try_identity()
|
||||
}
|
||||
fn connection_id(&self) -> __sdk::ConnectionId {
|
||||
self.imp.connection_id()
|
||||
}
|
||||
fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {
|
||||
self.imp.try_connection_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::ReducerEventContext for ReducerEventContext {}
|
||||
|
||||
/// An [`__sdk::DbContext`] passed to procedure callbacks.
|
||||
pub struct ProcedureEventContext {
|
||||
/// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].
|
||||
pub db: RemoteTables,
|
||||
/// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].
|
||||
pub reducers: RemoteReducers,
|
||||
/// Access to setting the call-flags of each reducer defined for each reducer defined by the module
|
||||
/// via extension traits implemented for [`SetReducerFlags`].
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
pub set_reducer_flags: SetReducerFlags,
|
||||
/// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].
|
||||
pub procedures: RemoteProcedures,
|
||||
imp: __sdk::DbContextImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::AbstractEventContext for ProcedureEventContext {
|
||||
type Event = ();
|
||||
fn event(&self) -> &Self::Event {
|
||||
&()
|
||||
}
|
||||
fn new(imp: __sdk::DbContextImpl<RemoteModule>, _event: Self::Event) -> Self {
|
||||
Self {
|
||||
db: RemoteTables { imp: imp.clone() },
|
||||
reducers: RemoteReducers { imp: imp.clone() },
|
||||
procedures: RemoteProcedures { imp: imp.clone() },
|
||||
set_reducer_flags: SetReducerFlags { imp: imp.clone() },
|
||||
imp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for ProcedureEventContext {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
impl __sdk::DbContext for ProcedureEventContext {
|
||||
type DbView = RemoteTables;
|
||||
type Reducers = RemoteReducers;
|
||||
type Procedures = RemoteProcedures;
|
||||
type SetReducerFlags = SetReducerFlags;
|
||||
|
||||
fn db(&self) -> &Self::DbView {
|
||||
&self.db
|
||||
}
|
||||
fn reducers(&self) -> &Self::Reducers {
|
||||
&self.reducers
|
||||
}
|
||||
fn procedures(&self) -> &Self::Procedures {
|
||||
&self.procedures
|
||||
}
|
||||
fn set_reducer_flags(&self) -> &Self::SetReducerFlags {
|
||||
&self.set_reducer_flags
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.imp.is_active()
|
||||
}
|
||||
|
||||
fn disconnect(&self) -> __sdk::Result<()> {
|
||||
self.imp.disconnect()
|
||||
}
|
||||
|
||||
type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;
|
||||
|
||||
fn subscription_builder(&self) -> Self::SubscriptionBuilder {
|
||||
__sdk::SubscriptionBuilder::new(&self.imp)
|
||||
}
|
||||
|
||||
fn try_identity(&self) -> Option<__sdk::Identity> {
|
||||
self.imp.try_identity()
|
||||
}
|
||||
fn connection_id(&self) -> __sdk::ConnectionId {
|
||||
self.imp.connection_id()
|
||||
}
|
||||
fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {
|
||||
self.imp.try_connection_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::ProcedureEventContext for ProcedureEventContext {}
|
||||
|
||||
/// An [`__sdk::DbContext`] passed to [`__sdk::SubscriptionBuilder::on_applied`] and [`SubscriptionHandle::unsubscribe_then`] callbacks.
|
||||
pub struct SubscriptionEventContext {
|
||||
/// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].
|
||||
pub db: RemoteTables,
|
||||
/// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].
|
||||
pub reducers: RemoteReducers,
|
||||
/// Access to setting the call-flags of each reducer defined for each reducer defined by the module
|
||||
/// via extension traits implemented for [`SetReducerFlags`].
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
pub set_reducer_flags: SetReducerFlags,
|
||||
/// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].
|
||||
pub procedures: RemoteProcedures,
|
||||
imp: __sdk::DbContextImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::AbstractEventContext for SubscriptionEventContext {
|
||||
type Event = ();
|
||||
fn event(&self) -> &Self::Event {
|
||||
&()
|
||||
}
|
||||
fn new(imp: __sdk::DbContextImpl<RemoteModule>, _event: Self::Event) -> Self {
|
||||
Self {
|
||||
db: RemoteTables { imp: imp.clone() },
|
||||
reducers: RemoteReducers { imp: imp.clone() },
|
||||
procedures: RemoteProcedures { imp: imp.clone() },
|
||||
set_reducer_flags: SetReducerFlags { imp: imp.clone() },
|
||||
imp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for SubscriptionEventContext {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
impl __sdk::DbContext for SubscriptionEventContext {
|
||||
type DbView = RemoteTables;
|
||||
type Reducers = RemoteReducers;
|
||||
type Procedures = RemoteProcedures;
|
||||
type SetReducerFlags = SetReducerFlags;
|
||||
|
||||
fn db(&self) -> &Self::DbView {
|
||||
&self.db
|
||||
}
|
||||
fn reducers(&self) -> &Self::Reducers {
|
||||
&self.reducers
|
||||
}
|
||||
fn procedures(&self) -> &Self::Procedures {
|
||||
&self.procedures
|
||||
}
|
||||
fn set_reducer_flags(&self) -> &Self::SetReducerFlags {
|
||||
&self.set_reducer_flags
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.imp.is_active()
|
||||
}
|
||||
|
||||
fn disconnect(&self) -> __sdk::Result<()> {
|
||||
self.imp.disconnect()
|
||||
}
|
||||
|
||||
type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;
|
||||
|
||||
fn subscription_builder(&self) -> Self::SubscriptionBuilder {
|
||||
__sdk::SubscriptionBuilder::new(&self.imp)
|
||||
}
|
||||
|
||||
fn try_identity(&self) -> Option<__sdk::Identity> {
|
||||
self.imp.try_identity()
|
||||
}
|
||||
fn connection_id(&self) -> __sdk::ConnectionId {
|
||||
self.imp.connection_id()
|
||||
}
|
||||
fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {
|
||||
self.imp.try_connection_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::SubscriptionEventContext for SubscriptionEventContext {}
|
||||
|
||||
/// An [`__sdk::DbContext`] augmented with a [`__sdk::Error`],
|
||||
/// passed to [`__sdk::DbConnectionBuilder::on_disconnect`], [`__sdk::DbConnectionBuilder::on_connect_error`] and [`__sdk::SubscriptionBuilder::on_error`] callbacks.
|
||||
pub struct ErrorContext {
|
||||
/// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].
|
||||
pub db: RemoteTables,
|
||||
/// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].
|
||||
pub reducers: RemoteReducers,
|
||||
/// Access to setting the call-flags of each reducer defined for each reducer defined by the module
|
||||
/// via extension traits implemented for [`SetReducerFlags`].
|
||||
///
|
||||
/// This type is currently unstable and may be removed without a major version bump.
|
||||
pub set_reducer_flags: SetReducerFlags,
|
||||
/// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].
|
||||
pub procedures: RemoteProcedures,
|
||||
/// The event which caused these callbacks to run.
|
||||
pub event: Option<__sdk::Error>,
|
||||
imp: __sdk::DbContextImpl<RemoteModule>,
|
||||
}
|
||||
|
||||
impl __sdk::AbstractEventContext for ErrorContext {
|
||||
type Event = Option<__sdk::Error>;
|
||||
fn event(&self) -> &Self::Event {
|
||||
&self.event
|
||||
}
|
||||
fn new(imp: __sdk::DbContextImpl<RemoteModule>, event: Self::Event) -> Self {
|
||||
Self {
|
||||
db: RemoteTables { imp: imp.clone() },
|
||||
reducers: RemoteReducers { imp: imp.clone() },
|
||||
set_reducer_flags: SetReducerFlags { imp: imp.clone() },
|
||||
procedures: RemoteProcedures { imp: imp.clone() },
|
||||
event,
|
||||
imp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for ErrorContext {
|
||||
type Module = RemoteModule;
|
||||
}
|
||||
|
||||
impl __sdk::DbContext for ErrorContext {
|
||||
type DbView = RemoteTables;
|
||||
type Reducers = RemoteReducers;
|
||||
type Procedures = RemoteProcedures;
|
||||
type SetReducerFlags = SetReducerFlags;
|
||||
|
||||
fn db(&self) -> &Self::DbView {
|
||||
&self.db
|
||||
}
|
||||
fn reducers(&self) -> &Self::Reducers {
|
||||
&self.reducers
|
||||
}
|
||||
fn procedures(&self) -> &Self::Procedures {
|
||||
&self.procedures
|
||||
}
|
||||
fn set_reducer_flags(&self) -> &Self::SetReducerFlags {
|
||||
&self.set_reducer_flags
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.imp.is_active()
|
||||
}
|
||||
|
||||
fn disconnect(&self) -> __sdk::Result<()> {
|
||||
self.imp.disconnect()
|
||||
}
|
||||
|
||||
type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;
|
||||
|
||||
fn subscription_builder(&self) -> Self::SubscriptionBuilder {
|
||||
__sdk::SubscriptionBuilder::new(&self.imp)
|
||||
}
|
||||
|
||||
fn try_identity(&self) -> Option<__sdk::Identity> {
|
||||
self.imp.try_identity()
|
||||
}
|
||||
fn connection_id(&self) -> __sdk::ConnectionId {
|
||||
self.imp.connection_id()
|
||||
}
|
||||
fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {
|
||||
self.imp.try_connection_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::ErrorContext for ErrorContext {}
|
||||
|
||||
impl __sdk::SpacetimeModule for RemoteModule {
|
||||
type DbConnection = DbConnection;
|
||||
type EventContext = EventContext;
|
||||
type ReducerEventContext = ReducerEventContext;
|
||||
type ProcedureEventContext = ProcedureEventContext;
|
||||
type SubscriptionEventContext = SubscriptionEventContext;
|
||||
type ErrorContext = ErrorContext;
|
||||
type Reducer = Reducer;
|
||||
type DbView = RemoteTables;
|
||||
type Reducers = RemoteReducers;
|
||||
type SetReducerFlags = SetReducerFlags;
|
||||
type DbUpdate = DbUpdate;
|
||||
type AppliedDiff<'r> = AppliedDiff<'r>;
|
||||
type SubscriptionHandle = SubscriptionHandle;
|
||||
|
||||
fn register_tables(client_cache: &mut __sdk::ClientCache<Self>) {
|
||||
drawn_table::register_table(client_cache);
|
||||
hand_table::register_table(client_cache);
|
||||
player_table::register_table(client_cache);
|
||||
table_table::register_table(client_cache);
|
||||
wall_table::register_table(client_cache);
|
||||
}
|
||||
}
|
||||
95
jong/src/jongline_bindings/player_table.rs
Normal file
95
jong/src/jongline_bindings/player_table.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// 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<Player>,
|
||||
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>("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<Item = Player> + '_ {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
let _table = client_cache.get_or_make_table::<Player>("player");
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::TableUpdate<__ws::BsatnFormat>,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<Player>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse("TableUpdate<Player>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
17
jong/src/jongline_bindings/player_type.rs
Normal file
17
jong/src/jongline_bindings/player_type.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// 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 username: String,
|
||||
pub identity: __sdk::Identity,
|
||||
pub connection_id: __sdk::ConnectionId,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for Player {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
97
jong/src/jongline_bindings/table_table.rs
Normal file
97
jong/src/jongline_bindings/table_table.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use super::hand_type::Hand;
|
||||
use super::player_type::Player;
|
||||
use super::table_type::Table;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `table`.
|
||||
///
|
||||
/// Obtain a handle from the [`TableTableAccess::table`] method on [`super::RemoteTables`],
|
||||
/// like `ctx.db.table()`.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.table().on_insert(...)`.
|
||||
pub struct TableTableHandle<'ctx> {
|
||||
imp: __sdk::TableHandle<Table>,
|
||||
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the table `table`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteTables`].
|
||||
pub trait TableTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Obtain a [`TableTableHandle`], which mediates access to the table `table`.
|
||||
fn table(&self) -> TableTableHandle<'_>;
|
||||
}
|
||||
|
||||
impl TableTableAccess for super::RemoteTables {
|
||||
fn table(&self) -> TableTableHandle<'_> {
|
||||
TableTableHandle {
|
||||
imp: self.imp.get_table::<Table>("table"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TableInsertCallbackId(__sdk::CallbackId);
|
||||
pub struct TableDeleteCallbackId(__sdk::CallbackId);
|
||||
|
||||
impl<'ctx> __sdk::Table for TableTableHandle<'ctx> {
|
||||
type Row = Table;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = Table> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = TableInsertCallbackId;
|
||||
|
||||
fn on_insert(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> TableInsertCallbackId {
|
||||
TableInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_insert(&self, callback: TableInsertCallbackId) {
|
||||
self.imp.remove_on_insert(callback.0)
|
||||
}
|
||||
|
||||
type DeleteCallbackId = TableDeleteCallbackId;
|
||||
|
||||
fn on_delete(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> TableDeleteCallbackId {
|
||||
TableDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_delete(&self, callback: TableDeleteCallbackId) {
|
||||
self.imp.remove_on_delete(callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
let _table = client_cache.get_or_make_table::<Table>("table");
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::TableUpdate<__ws::BsatnFormat>,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<Table>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse("TableUpdate<Table>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
19
jong/src/jongline_bindings/table_type.rs
Normal file
19
jong/src/jongline_bindings/table_type.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::hand_type::Hand;
|
||||
use super::player_type::Player;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct Table {
|
||||
pub players: Player,
|
||||
pub hands: Vec<Hand>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for Table {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
95
jong/src/jongline_bindings/wall_table.rs
Normal file
95
jong/src/jongline_bindings/wall_table.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// 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::wall_type::Wall;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `wall`.
|
||||
///
|
||||
/// Obtain a handle from the [`WallTableAccess::wall`] method on [`super::RemoteTables`],
|
||||
/// like `ctx.db.wall()`.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.wall().on_insert(...)`.
|
||||
pub struct WallTableHandle<'ctx> {
|
||||
imp: __sdk::TableHandle<Wall>,
|
||||
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the table `wall`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteTables`].
|
||||
pub trait WallTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Obtain a [`WallTableHandle`], which mediates access to the table `wall`.
|
||||
fn wall(&self) -> WallTableHandle<'_>;
|
||||
}
|
||||
|
||||
impl WallTableAccess for super::RemoteTables {
|
||||
fn wall(&self) -> WallTableHandle<'_> {
|
||||
WallTableHandle {
|
||||
imp: self.imp.get_table::<Wall>("wall"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WallInsertCallbackId(__sdk::CallbackId);
|
||||
pub struct WallDeleteCallbackId(__sdk::CallbackId);
|
||||
|
||||
impl<'ctx> __sdk::Table for WallTableHandle<'ctx> {
|
||||
type Row = Wall;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = Wall> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = WallInsertCallbackId;
|
||||
|
||||
fn on_insert(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> WallInsertCallbackId {
|
||||
WallInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_insert(&self, callback: WallInsertCallbackId) {
|
||||
self.imp.remove_on_insert(callback.0)
|
||||
}
|
||||
|
||||
type DeleteCallbackId = WallDeleteCallbackId;
|
||||
|
||||
fn on_delete(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> WallDeleteCallbackId {
|
||||
WallDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_delete(&self, callback: WallDeleteCallbackId) {
|
||||
self.imp.remove_on_delete(callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
let _table = client_cache.get_or_make_table::<Wall>("wall");
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::TableUpdate<__ws::BsatnFormat>,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<Wall>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse("TableUpdate<Wall>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
13
jong/src/jongline_bindings/wall_type.rs
Normal file
13
jong/src/jongline_bindings/wall_type.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// 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 Wall {}
|
||||
|
||||
impl __sdk::InModule for Wall {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
17
jong/src/lib.rs
Normal file
17
jong/src/lib.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#![feature(stmt_expr_attributes, iter_array_chunks)]
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_spacetimedb::StdbConnection;
|
||||
|
||||
mod jongline_bindings;
|
||||
use jongline_bindings::*;
|
||||
|
||||
pub mod game;
|
||||
pub mod tile;
|
||||
pub mod yakus;
|
||||
|
||||
trait EnumNextCycle {
|
||||
fn next(&self) -> Self;
|
||||
}
|
||||
|
||||
pub type SpacetimeDb<'a> = Res<'a, StdbConnection<DbConnection>>;
|
||||
61
jong/src/main.rs
Normal file
61
jong/src/main.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
use bevy::{log::LogPlugin, prelude::*};
|
||||
use bevy_spacetimedb::{StdbConnection, StdbPlugin, TableMessages};
|
||||
use clap::{Parser, Subcommand};
|
||||
use tracing::Level;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
mod gui;
|
||||
mod tui;
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
#[command(subcommand)]
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Mode {
|
||||
RunGui,
|
||||
RunTui,
|
||||
}
|
||||
|
||||
const FILTERSTRING: &str = "warn,jong=trace";
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
let mut app = App::new();
|
||||
let app = match args.mode {
|
||||
Mode::RunGui => {
|
||||
app.add_plugins(DefaultPlugins.set(LogPlugin {
|
||||
filter: FILTERSTRING.into(),
|
||||
level: Level::TRACE,
|
||||
// custom_layer: todo!(),
|
||||
// fmt_layer: todo!(),
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
Mode::RunTui => {
|
||||
tracing_subscriber::registry()
|
||||
.with(tui_logger::TuiTracingSubscriberLayer)
|
||||
.init();
|
||||
tui_logger::init_logger(tui_logger::LevelFilter::Trace).unwrap();
|
||||
tui_logger::set_env_filter_from_string(FILTERSTRING);
|
||||
|
||||
// app.add_plugins(tui::RiichiTui)
|
||||
app.add_plugins(tui::TuiPlugin)
|
||||
}
|
||||
};
|
||||
|
||||
app.add_plugins(
|
||||
StdbPlugin::default()
|
||||
.with_uri("http://localhost:3000")
|
||||
.with_module_name("jongline")
|
||||
.with_run_fn(DbConnection::run_threaded)
|
||||
.add_partial_table(RemoteTables::players, TableMessages::no_update()),
|
||||
);
|
||||
|
||||
app.add_plugins(jong::game::Riichi);
|
||||
|
||||
app.run();
|
||||
}
|
||||
77
jong/src/tile.rs
Normal file
77
jong/src/tile.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
use bevy::prelude::*;
|
||||
use strum::FromRepr;
|
||||
|
||||
#[derive(Component, Debug, Clone, Copy)]
|
||||
pub struct Tile {
|
||||
pub suit: Suit,
|
||||
}
|
||||
|
||||
#[derive(/* MapEntities, */ Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy)]
|
||||
pub enum Suit {
|
||||
Man(Rank),
|
||||
Pin(Rank),
|
||||
Sou(Rank),
|
||||
Wind(Wind),
|
||||
Dragon(Dragon),
|
||||
}
|
||||
|
||||
impl Suit {
|
||||
pub fn rank(&self) -> Option<Rank> {
|
||||
match self {
|
||||
Suit::Man(rank) => Some(*rank),
|
||||
Suit::Pin(rank) => Some(*rank),
|
||||
Suit::Sou(rank) => Some(*rank),
|
||||
// Suit::Wind(wind) | Suit::Dragon(dragon) => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy)]
|
||||
pub struct Rank(pub u8);
|
||||
|
||||
#[derive(FromRepr, Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy)]
|
||||
pub enum Wind {
|
||||
Ton,
|
||||
Nan,
|
||||
Shaa,
|
||||
Pei,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromRepr, PartialEq, PartialOrd, Eq, Ord, Clone, Copy)]
|
||||
pub enum Dragon {
|
||||
Haku,
|
||||
Hatsu,
|
||||
Chun,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Dora;
|
||||
|
||||
pub fn init_tiles(mut commands: Commands) {
|
||||
let mut tiles = vec![];
|
||||
for _ in 0..4 {
|
||||
for i in 1..=9 {
|
||||
tiles.push(Tile {
|
||||
suit: Suit::Pin(Rank(i)),
|
||||
});
|
||||
tiles.push(Tile {
|
||||
suit: Suit::Sou(Rank(i)),
|
||||
});
|
||||
tiles.push(Tile {
|
||||
suit: Suit::Man(Rank(i)),
|
||||
});
|
||||
}
|
||||
for i in 0..4 {
|
||||
tiles.push(Tile {
|
||||
suit: Suit::Wind(Wind::from_repr(i).unwrap()),
|
||||
});
|
||||
}
|
||||
for i in 0..3 {
|
||||
tiles.push(Tile {
|
||||
suit: Suit::Dragon(Dragon::from_repr(i).unwrap()),
|
||||
});
|
||||
}
|
||||
}
|
||||
commands.spawn_batch(tiles);
|
||||
}
|
||||
122
jong/src/tui.rs
Normal file
122
jong/src/tui.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin};
|
||||
use bevy_ratatui::RatatuiPlugins;
|
||||
use jong::game::{
|
||||
GameMessage, GameState,
|
||||
hand::{Drawn, Hand},
|
||||
player::{CurrentPlayer, Player},
|
||||
};
|
||||
use tui_logger::TuiWidgetState;
|
||||
|
||||
use crate::tui::{input::ConfirmSelect, states::ConsoleWidget};
|
||||
|
||||
mod input;
|
||||
mod layout;
|
||||
mod render;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TuiPlugin;
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, SystemSet)]
|
||||
pub enum TuiSet {
|
||||
Input,
|
||||
Layout,
|
||||
Render,
|
||||
}
|
||||
|
||||
impl Plugin for TuiPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_plugins((
|
||||
MinimalPlugins.set(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f32(
|
||||
1. / 60.,
|
||||
))),
|
||||
RatatuiPlugins {
|
||||
// enable_kitty_protocol: todo!(),
|
||||
enable_mouse_capture: true,
|
||||
enable_input_forwarding: true,
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
.add_plugins(StatesPlugin)
|
||||
.init_resource::<layout::Overlays>()
|
||||
.insert_resource(ConsoleWidget {
|
||||
state: TuiWidgetState::new(),
|
||||
open: true,
|
||||
})
|
||||
.init_state::<states::TuiState>()
|
||||
.configure_sets(
|
||||
Update,
|
||||
(TuiSet::Input, TuiSet::Layout, TuiSet::Render).chain(),
|
||||
)
|
||||
.add_message::<ConfirmSelect>()
|
||||
.add_systems(
|
||||
Update,
|
||||
(input::keyboard, input::mouse).in_set(TuiSet::Input),
|
||||
)
|
||||
.add_systems(Update, layout::layout.in_set(TuiSet::Layout))
|
||||
.add_systems(Update, discard_tile.run_if(in_state(GameState::Play)))
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
render::render_hands.run_if(in_state(GameState::Play)),
|
||||
render::render,
|
||||
)
|
||||
.chain()
|
||||
.in_set(TuiSet::Render),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn discard_tile(
|
||||
mut writer: MessageWriter<GameMessage>,
|
||||
mut selected: MessageReader<ConfirmSelect>,
|
||||
|
||||
drawn: Single<Entity, With<Drawn>>,
|
||||
curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||
player_hands: Populated<(&Player, &Children), With<Hand>>,
|
||||
hands: Populated<&Children, (With<Hand>, Without<Player>)>,
|
||||
) {
|
||||
// trace!("discard_tile");
|
||||
|
||||
let hand_ent = player_hands
|
||||
.get(*curr_player)
|
||||
.unwrap()
|
||||
.1
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap();
|
||||
let hand = hands.get(hand_ent).unwrap();
|
||||
|
||||
while let Some(message) = selected.read().next()
|
||||
&& (message.0 == *drawn || hand.contains(&message.0))
|
||||
{
|
||||
writer.write(GameMessage::Discarded(message.0));
|
||||
}
|
||||
}
|
||||
|
||||
mod states {
|
||||
use bevy::prelude::*;
|
||||
use tui_logger::TuiWidgetState;
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct ConsoleWidget {
|
||||
pub(crate) state: TuiWidgetState,
|
||||
pub(crate) open: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default)]
|
||||
pub(crate) enum TuiState {
|
||||
#[default]
|
||||
MainMenu,
|
||||
InGame,
|
||||
}
|
||||
|
||||
// #[derive(SubStates, Default, Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
// #[source(TuiState = TuiState::MainMenu)]
|
||||
// pub(crate) enum ZenState {
|
||||
// #[default]
|
||||
// Menu,
|
||||
// Zen,
|
||||
// }
|
||||
}
|
||||
16
jong/src/tui/input.rs
Normal file
16
jong/src/tui/input.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
pub(crate) use keyboard::keyboard;
|
||||
pub(crate) use mouse::mouse;
|
||||
|
||||
pub(crate) mod keyboard;
|
||||
pub(crate) mod mouse;
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct Hovered;
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct StartSelect;
|
||||
|
||||
#[derive(Message, Debug)]
|
||||
pub(crate) struct ConfirmSelect(pub(crate) Entity);
|
||||
85
jong/src/tui/input/keyboard.rs
Normal file
85
jong/src/tui/input/keyboard.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy_ratatui::event::KeyMessage;
|
||||
use ratatui::crossterm::event::KeyCode;
|
||||
use tui_logger::TuiWidgetEvent;
|
||||
|
||||
use jong::game::GameState;
|
||||
|
||||
use crate::tui::layout::Overlays;
|
||||
use crate::tui::states::ConsoleWidget;
|
||||
use crate::tui::states::TuiState;
|
||||
|
||||
pub(crate) fn keyboard(
|
||||
mut messages: MessageReader<KeyMessage>,
|
||||
mut overlays: ResMut<Overlays>,
|
||||
mut consolewidget: ResMut<ConsoleWidget>,
|
||||
mut exit: MessageWriter<AppExit>,
|
||||
|
||||
curr_gamestate: Res<State<GameState>>,
|
||||
curr_tuistate: Res<State<TuiState>>,
|
||||
|
||||
mut next_gamestate: ResMut<NextState<GameState>>,
|
||||
mut next_tuistate: ResMut<NextState<TuiState>>,
|
||||
) {
|
||||
'message: for message in messages.read() {
|
||||
let key = message.code;
|
||||
if consolewidget.handle_keyboard(key) {
|
||||
continue 'message;
|
||||
}
|
||||
for overlay in overlays.stack.iter().rev() {
|
||||
let consumed = match overlay {
|
||||
_ => false,
|
||||
};
|
||||
if consumed {
|
||||
continue 'message;
|
||||
}
|
||||
}
|
||||
|
||||
match curr_tuistate.get() {
|
||||
TuiState::MainMenu => match key {
|
||||
KeyCode::Char('p') => {
|
||||
next_tuistate.set(TuiState::InGame);
|
||||
next_gamestate.set(GameState::Setup);
|
||||
}
|
||||
KeyCode::Char('z') => {
|
||||
// if let Some(ref curr_zenstate) = curr_zenstate {
|
||||
// match curr_zenstate.get() {
|
||||
// ZenState::Menu => next_zenstate.set(ZenState::Zen),
|
||||
// ZenState::Zen => next_zenstate.set(ZenState::Menu),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
KeyCode::Char('q') => {
|
||||
exit.write_default();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
TuiState::InGame => { /* debug!("unhandled keyboard event") */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleWidget {
|
||||
pub(crate) fn handle_keyboard(&mut self, key: KeyCode) -> bool {
|
||||
if key == KeyCode::Char('`') {
|
||||
self.open = !self.open;
|
||||
return true;
|
||||
}
|
||||
if self.open {
|
||||
match key {
|
||||
KeyCode::Up => self.state.transition(TuiWidgetEvent::UpKey),
|
||||
KeyCode::Down => self.state.transition(TuiWidgetEvent::DownKey),
|
||||
// KeyCode::Home => self.state.transition(TuiWidgetEvent::),
|
||||
// KeyCode::End => self.state.transition(TuiWidgetEvent::),
|
||||
KeyCode::PageUp => self.state.transition(TuiWidgetEvent::PrevPageKey),
|
||||
KeyCode::PageDown => self.state.transition(TuiWidgetEvent::NextPageKey),
|
||||
KeyCode::Esc => {
|
||||
self.open = false;
|
||||
return true;
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
self.open
|
||||
}
|
||||
}
|
||||
83
jong/src/tui/input/mouse.rs
Normal file
83
jong/src/tui/input/mouse.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy_ratatui::event::MouseMessage;
|
||||
use ratatui::layout::Position;
|
||||
|
||||
use crate::tui::{
|
||||
input::{ConfirmSelect, Hovered, StartSelect},
|
||||
render::PickRegion,
|
||||
};
|
||||
|
||||
pub(crate) fn mouse(
|
||||
mut commands: Commands,
|
||||
mut mouse_reader: MessageReader<MouseMessage>,
|
||||
mut event_writer: MessageWriter<ConfirmSelect>,
|
||||
entities: Query<(Entity, &PickRegion)>,
|
||||
hovered: Query<(Entity, &PickRegion), With<Hovered>>,
|
||||
startselected: Query<(Entity, &PickRegion), With<StartSelect>>,
|
||||
) {
|
||||
for message in mouse_reader.read() {
|
||||
let event = message.0;
|
||||
// let term_size = context.size().unwrap();
|
||||
let position = Position::new(event.column, event.row);
|
||||
match event.kind {
|
||||
#[allow(clippy::single_match)]
|
||||
ratatui::crossterm::event::MouseEventKind::Down(mouse_button) => match mouse_button {
|
||||
ratatui::crossterm::event::MouseButton::Left => {
|
||||
for (entity, region) in &entities {
|
||||
if region.area.contains(position) {
|
||||
commands.entity(entity).insert(StartSelect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ratatui::crossterm::event::MouseButton::Right => debug!("unhandled mouse event"),
|
||||
// ratatui::crossterm::event::MouseButton::Middle => debug!("unhandled mouse event"),
|
||||
_ => {}
|
||||
},
|
||||
ratatui::crossterm::event::MouseEventKind::Moved => {
|
||||
for (entity, region) in &hovered {
|
||||
if !region.area.contains(position) {
|
||||
commands.entity(entity).remove::<Hovered>();
|
||||
}
|
||||
}
|
||||
for (entity, region) in &entities {
|
||||
if region.area.contains(position) {
|
||||
commands.entity(entity).insert(Hovered);
|
||||
}
|
||||
}
|
||||
}
|
||||
ratatui::crossterm::event::MouseEventKind::Up(mouse_button) => match mouse_button {
|
||||
ratatui::crossterm::event::MouseButton::Left => {
|
||||
for (entity, region) in &startselected {
|
||||
if region.area.contains(position) {
|
||||
commands.entity(entity).remove::<StartSelect>();
|
||||
event_writer.write(ConfirmSelect(entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
ratatui::crossterm::event::MouseButton::Right => {
|
||||
for (entity, _) in &startselected {
|
||||
commands.entity(entity).remove::<StartSelect>();
|
||||
}
|
||||
}
|
||||
// ratatui::crossterm::event::MouseButton::Middle => debug!("unhandled mouse event"),
|
||||
_ => {}
|
||||
},
|
||||
ratatui::crossterm::event::MouseEventKind::Drag(mouse_button) => {
|
||||
// debug!("unhandled mouse event")
|
||||
}
|
||||
ratatui::crossterm::event::MouseEventKind::ScrollDown => {
|
||||
// debug!("unhandled mouse event")
|
||||
}
|
||||
ratatui::crossterm::event::MouseEventKind::ScrollUp => {
|
||||
// debug!("unhandled mouse event")
|
||||
}
|
||||
ratatui::crossterm::event::MouseEventKind::ScrollLeft => {
|
||||
// debug!("unhandled mouse event")
|
||||
}
|
||||
ratatui::crossterm::event::MouseEventKind::ScrollRight => {
|
||||
// debug!("unhandled mouse event")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
82
jong/src/tui/layout.rs
Normal file
82
jong/src/tui/layout.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy_ratatui::RatatuiContext;
|
||||
use ratatui::{
|
||||
layout::{Constraint, Layout, Margin},
|
||||
prelude::Rect,
|
||||
};
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub(crate) struct Overlays {
|
||||
pub(crate) stack: Vec<Overlay>,
|
||||
}
|
||||
|
||||
pub(crate) enum Overlay {}
|
||||
|
||||
#[derive(Resource, Clone, Copy)]
|
||||
pub(crate) struct HandLayouts {
|
||||
pub(crate) left_pond: Rect,
|
||||
pub(crate) cross_pond: Rect,
|
||||
pub(crate) right_pond: Rect,
|
||||
pub(crate) this_pond: Rect,
|
||||
pub(crate) left_hand: Rect,
|
||||
pub(crate) cross_hand: Rect,
|
||||
pub(crate) right_hand: Rect,
|
||||
pub(crate) this_hand: Rect,
|
||||
}
|
||||
|
||||
pub(crate) enum LayoutError {
|
||||
TerminalTooSmall,
|
||||
}
|
||||
|
||||
pub(crate) fn layout(mut commands: Commands, mut tui: ResMut<RatatuiContext>) -> Result {
|
||||
tui.autoresize()?;
|
||||
let frame = tui.get_frame();
|
||||
let term_area = frame.area();
|
||||
|
||||
let tiles = tiles_areas(term_area);
|
||||
commands.insert_resource(tiles);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO make fallible to warn area too small
|
||||
fn tiles_areas(term_area: Rect) -> HandLayouts {
|
||||
let constraints = [Constraint::Fill(3), Constraint::Fill(18)];
|
||||
let horizontal_slicer_right = Layout::horizontal(constraints);
|
||||
let vertical_slicer_bottom = Layout::vertical(constraints);
|
||||
let horizontal_slicer_left = Layout::horizontal(constraints.iter().rev());
|
||||
let vertical_slicer_top = Layout::vertical(constraints.iter().rev());
|
||||
let [left_hand, this_hand] = horizontal_slicer_right.areas::<2>(term_area);
|
||||
let [_, left_hand] = vertical_slicer_bottom.areas::<2>(left_hand);
|
||||
let [_, this_hand] = vertical_slicer_top.areas::<2>(this_hand);
|
||||
let [cross_hand, right_hand] = horizontal_slicer_left.areas::<2>(term_area);
|
||||
let [right_hand, _] = vertical_slicer_top.areas::<2>(right_hand);
|
||||
let [cross_hand, _] = vertical_slicer_bottom.areas::<2>(cross_hand);
|
||||
|
||||
let margin = Margin::new(
|
||||
(left_hand.width + right_hand.width) / 2,
|
||||
(cross_hand.height + this_hand.height) / 2,
|
||||
);
|
||||
let pond_area = term_area.inner(margin);
|
||||
let all_pond = Layout::horizontal([Constraint::Fill(1); 3]);
|
||||
let cross_pond =
|
||||
Layout::vertical([Constraint::Fill(1), Constraint::Max(1), Constraint::Fill(1)]);
|
||||
let [mut left_pond, center, mut right_pond] = all_pond.areas::<3>(pond_area);
|
||||
let [cross_pond, _compass, this_pond] = cross_pond.areas::<3>(center);
|
||||
// let shift = left_pond.height - cross_pond.height;
|
||||
left_pond.height = cross_pond.height;
|
||||
left_pond.y += cross_pond.height / 2;
|
||||
right_pond.height = cross_pond.height;
|
||||
right_pond.y += cross_pond.height / 2;
|
||||
|
||||
HandLayouts {
|
||||
left_pond,
|
||||
cross_pond,
|
||||
right_pond,
|
||||
this_pond,
|
||||
left_hand,
|
||||
cross_hand,
|
||||
right_hand,
|
||||
this_hand,
|
||||
}
|
||||
}
|
||||
209
jong/src/tui/render.rs
Normal file
209
jong/src/tui/render.rs
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
use std::io::Write as _;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_ratatui::RatatuiContext;
|
||||
use ratatui::layout::{Constraint, Flex, Layout, Offset, Rect, Size};
|
||||
use ratatui::style::{Modifier, Stylize};
|
||||
use ratatui::widgets::{Block, Borders, Clear, Paragraph};
|
||||
|
||||
use jong::game::hand::{Drawn, Hand};
|
||||
use jong::game::player::{CurrentPlayer, MainPlayer, Player};
|
||||
use jong::game::round::Wind;
|
||||
use jong::tile::Tile;
|
||||
|
||||
use crate::tui::input::Hovered;
|
||||
use crate::tui::layout::*;
|
||||
use crate::tui::states::ConsoleWidget;
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct PickRegion {
|
||||
pub(crate) area: Rect,
|
||||
}
|
||||
|
||||
fn render_tile(tile: &Tile, hovered: bool) -> Paragraph<'_> {
|
||||
let block = ratatui::widgets::Block::bordered();
|
||||
let mut widget = Paragraph::new(match &tile.suit {
|
||||
jong::tile::Suit::Pin(rank) => format!("{}\np", rank.0),
|
||||
jong::tile::Suit::Sou(rank) => format!("{}\ns", rank.0),
|
||||
jong::tile::Suit::Man(rank) => format!("{}\nm", rank.0),
|
||||
jong::tile::Suit::Wind(wind) => (match wind {
|
||||
jong::tile::Wind::Ton => "e\nw",
|
||||
jong::tile::Wind::Nan => "s\nw",
|
||||
jong::tile::Wind::Shaa => "w\nw",
|
||||
jong::tile::Wind::Pei => "n\nw",
|
||||
})
|
||||
.into(),
|
||||
jong::tile::Suit::Dragon(dragon) => (match dragon {
|
||||
jong::tile::Dragon::Haku => "w\nd",
|
||||
jong::tile::Dragon::Hatsu => "g\nd",
|
||||
jong::tile::Dragon::Chun => "r\nd",
|
||||
})
|
||||
.into(),
|
||||
})
|
||||
.block(block)
|
||||
.centered();
|
||||
|
||||
if hovered {
|
||||
widget = widget.add_modifier(Modifier::BOLD);
|
||||
}
|
||||
|
||||
widget
|
||||
}
|
||||
|
||||
fn debug_blocks(layouts: HandLayouts, frame: &mut ratatui::Frame<'_>) {
|
||||
let debug_block = Block::new().borders(Borders::ALL);
|
||||
frame.render_widget(debug_block.clone(), layouts.this_hand);
|
||||
frame.render_widget(debug_block.clone(), layouts.left_hand);
|
||||
frame.render_widget(debug_block.clone(), layouts.cross_hand);
|
||||
frame.render_widget(debug_block.clone(), layouts.right_hand);
|
||||
frame.render_widget(debug_block.clone(), layouts.this_pond);
|
||||
frame.render_widget(debug_block.clone(), layouts.left_pond);
|
||||
frame.render_widget(debug_block.clone(), layouts.cross_pond);
|
||||
frame.render_widget(debug_block.clone(), layouts.right_pond);
|
||||
}
|
||||
|
||||
pub(crate) fn render(
|
||||
mut tui: ResMut<RatatuiContext>,
|
||||
layouts: Res<HandLayouts>,
|
||||
overlays: Res<Overlays>,
|
||||
consolewidget: Res<ConsoleWidget>,
|
||||
) -> Result {
|
||||
let mut frame = tui.get_frame();
|
||||
|
||||
for overlay in overlays.stack.iter() {
|
||||
match overlay {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if consolewidget.open {
|
||||
let block = Block::bordered().title("console [press esc to close]");
|
||||
frame.render_widget(Clear, frame.area());
|
||||
frame.render_widget(
|
||||
tui_logger::TuiLoggerWidget::default()
|
||||
.block(block)
|
||||
.state(&consolewidget.state),
|
||||
frame.area(), /* .inner(Margin { horizontal: 8, vertical: 8 }) */
|
||||
);
|
||||
}
|
||||
|
||||
tui.hide_cursor()?;
|
||||
tui.flush()?;
|
||||
tui.swap_buffers();
|
||||
tui.backend_mut().flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
||||
pub(crate) fn render_hands(
|
||||
mut commands: Commands,
|
||||
mut tui: ResMut<RatatuiContext>,
|
||||
|
||||
hovered: Query<Entity, With<Hovered>>,
|
||||
layouts: Res<HandLayouts>,
|
||||
|
||||
tiles: Query<&Tile>,
|
||||
main_player: Single<(&Player, Entity, &Wind), With<MainPlayer>>,
|
||||
curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||
players: Query<(&Player, Entity, &Children)>,
|
||||
hands: Query<(&Children, Entity), (With<Hand>, Without<Player>)>,
|
||||
drawn_tile: Single<Entity, With<Drawn>>,
|
||||
) -> Result {
|
||||
let mut frame = tui.get_frame();
|
||||
debug_blocks(*layouts, &mut frame);
|
||||
|
||||
for (hand, hand_ent) in hands {
|
||||
// debug!("{hand:?}");
|
||||
let (player, player_ent, _) = players
|
||||
.iter()
|
||||
.find(|(_, e, c)| c.contains(&hand_ent))
|
||||
.unwrap();
|
||||
let hand: Vec<_> = hand
|
||||
.iter()
|
||||
.map(|entity| -> Result<_> {
|
||||
let tile = tiles.get(entity).unwrap_or_else(|_| panic!("{entity:?}"));
|
||||
let hovered = hovered.contains(entity);
|
||||
let widget = render_tile(tile, hovered);
|
||||
|
||||
Ok((entity, widget, hovered))
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
if player == main_player.0 {
|
||||
// split main box into thirds
|
||||
let mut this_hand = layouts.this_hand;
|
||||
// let this_drawer = drawn_tile..is_some_and(|dt| dt.0 == player);
|
||||
let this_drawer = player_ent == *curr_player;
|
||||
let tile_drawn = if this_drawer { 7 } else { 0 };
|
||||
let hand_draw_meld = Layout::horizontal([
|
||||
Constraint::Max(hand.len() as u16 * 5),
|
||||
Constraint::Max(tile_drawn),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.flex(Flex::SpaceBetween);
|
||||
this_hand = this_hand.offset(Offset {
|
||||
x: 0,
|
||||
y: this_hand.height.abs_diff(5) as i32 + 1,
|
||||
});
|
||||
this_hand = this_hand.resize(Size {
|
||||
width: this_hand.width,
|
||||
height: 4,
|
||||
});
|
||||
let [hand_area, drawn_area, meld_area] = hand_draw_meld.areas::<3>(this_hand);
|
||||
|
||||
// split hand area into tile areas
|
||||
let mut constraints = vec![Constraint::Max(5); hand.len()];
|
||||
constraints.push(Constraint::Fill(1));
|
||||
let layout = Layout::horizontal(constraints).flex(Flex::Start);
|
||||
let tile_areas = layout.split(hand_area);
|
||||
|
||||
for ((entity, widget, hovered), mut area) in
|
||||
hand.into_iter().zip(tile_areas.iter().copied())
|
||||
{
|
||||
if hovered {
|
||||
area = area.offset(Offset { x: 0, y: -1 });
|
||||
let mut hitbox = area.as_size();
|
||||
hitbox.height += 1;
|
||||
commands.entity(entity).insert(PickRegion {
|
||||
area: area.resize(hitbox),
|
||||
});
|
||||
} else {
|
||||
commands.entity(entity).insert(PickRegion { area });
|
||||
}
|
||||
frame.render_widget(widget, area);
|
||||
}
|
||||
|
||||
// tsumo tile
|
||||
if this_drawer {
|
||||
// trace!("this_drawer");
|
||||
let mut area = drawn_area.resize(Size {
|
||||
width: 5,
|
||||
height: 4,
|
||||
});
|
||||
area = area.offset(Offset { x: 2, y: 0 });
|
||||
let hovered = hovered.contains(*drawn_tile);
|
||||
let widget = render_tile(tiles.get(*drawn_tile)?, hovered);
|
||||
if hovered {
|
||||
area = area.offset(Offset { x: 0, y: -1 });
|
||||
let mut hitbox = area.as_size();
|
||||
hitbox.height += 1;
|
||||
commands.entity(*drawn_tile).insert(PickRegion {
|
||||
area: area.resize(hitbox),
|
||||
});
|
||||
} else {
|
||||
commands.entity(*drawn_tile).insert(PickRegion { area });
|
||||
}
|
||||
frame.render_widget(widget, area);
|
||||
}
|
||||
// TODO draw melds
|
||||
} else {
|
||||
// match mainplayer.1.relate(wind) {
|
||||
// jong::game::round::WindRelation::Shimocha => todo!(),
|
||||
// jong::game::round::WindRelation::Toimen => todo!(),
|
||||
// jong::game::round::WindRelation::Kamicha => todo!(),
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
1
jong/src/yakus.rs
Normal file
1
jong/src/yakus.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
// const TSUMO;
|
||||
Loading…
Add table
Add a link
Reference in a new issue