diff --git a/packages/client/src/components/Game.tsx b/packages/client/src/components/Game.tsx index 8afcc36..be274d7 100644 --- a/packages/client/src/components/Game.tsx +++ b/packages/client/src/components/Game.tsx @@ -40,6 +40,12 @@ export default () => { {" "} turn + ); }; diff --git a/packages/client/src/components/Table.tsx b/packages/client/src/components/Table.tsx index a34e089..d2f7472 100644 --- a/packages/client/src/components/Table.tsx +++ b/packages/client/src/components/Table.tsx @@ -11,7 +11,7 @@ import { } from "solid-js"; import api, { fromWebsocket } from "~/api"; import { createObservable, createObservableWithInit, cx } from "~/fn"; -import { me } from "~/profile"; +import { me, mePromise } from "~/profile"; import Game from "./Game"; import Player from "./Player"; @@ -37,7 +37,7 @@ export default (props: { tableKey: string }) => { onCleanup(() => wsPromise.then((ws) => ws.close())); const presenceEvents = wsEvents.filter((evt) => evt.players != null); - const gameEvents = wsEvents.filter((evt) => evt.view != null); + const gameEvents = wsEvents.filter((evt) => evt.view !== undefined); const players = createObservableWithInit( presenceEvents.map((evt) => evt.players!), @@ -45,6 +45,15 @@ export default (props: { tableKey: string }) => { ); const [ready, setReady] = createSignal(false); + mePromise.then( + (me) => + me && + wsEvents + .filter((evt) => evt.playersReady !== undefined) + .map((evt) => evt.playersReady?.[me] ?? false) + .onValue(setReady) + ); + createEffect(() => sendWs({ ready: ready() })); const view = createObservable(gameEvents.map((evt) => evt.view)); diff --git a/packages/server/src/table.ts b/packages/server/src/table.ts index 2d7409a..ed8a2cf 100644 --- a/packages/server/src/table.ts +++ b/packages/server/src/table.ts @@ -12,7 +12,7 @@ import { transform } from "./kefir-extension"; export const WsOut = t.Object({ players: t.Optional(t.Array(t.String())), - playersReady: t.Optional(t.Record(t.String(), t.Boolean())), + playersReady: t.Optional(t.Nullable(t.Record(t.String(), t.Boolean()))), view: t.Optional(t.Any()), }); export type TWsOut = typeof WsOut.static; @@ -31,15 +31,15 @@ type TablePayload = { never >; - readys: TBus; - actions: TBus; - quits: TBus; + readys: TBus; + actions: TBus; + quits: TBus; }; outputs: { - playersPresent: Property; - playersReady: Property<{ [key: string]: boolean }, unknown>; - gameConfig: Property; - gameState: Property; + playersPresent: Property; + playersReady: Property<{ [key: string]: boolean } | null, any>; + gameConfig: Property; + gameState: Property; }; }; @@ -60,6 +60,7 @@ export const liveTable = (key: string) => { quits: Bus(), }; const { connectionChanges, readys, actions, quits } = inputs; + quits.log("quits"); // ======= const playersPresent = connectionChanges @@ -78,34 +79,45 @@ export const liveTable = (key: string) => { .map((counts) => Object.keys(counts)) .toProperty(); + const gameEnds = quits.map((_) => null); + + const gameStarts = pool(); const playersReady = transform( - {} as { [key: string]: boolean }, + null as { [key: string]: boolean } | null, [ playersPresent, (prev, players: string[]) => Object.fromEntries( - players.map((p) => [p, prev[p] ?? false]) + players.map((p) => [p, prev?.[p] ?? false]) ), ], [ readys, (prev, evt: { humanKey: string; ready: boolean }) => - prev[evt.humanKey] != null + prev?.[evt.humanKey] != null ? { ...prev, [evt.humanKey]: evt.ready } : prev, + ], + [gameStarts, () => null], + [ + combine([gameEnds], [playersPresent], (_, players) => players), + (_, players: string[]) => + Object.fromEntries(players.map((p) => [p, false])), ] ) .toProperty() .log("playersReady"); - const gameStarts = playersReady - .filter( - (pr) => - Object.values(pr).length > 0 && - Object.values(pr).every((ready) => ready) - ) - .map((_) => null) - .log("gameStarts"); + gameStarts.plug( + playersReady + .filter( + (pr) => + Object.values(pr ?? {}).length > 0 && + Object.values(pr!).every((ready) => ready) + ) + .map((_) => null) + .log("gameStarts") + ); const gameConfigPool = pool< { @@ -144,8 +156,12 @@ export const liveTable = (key: string) => { humanKey: evt.action.humanKey, }) : prev, - ] + ], + [quits, () => null] ).toProperty(); + gameState + .map((state) => JSON.stringify(state).substring(0, 10)) + .log("gameState"); const gameIsActive = gameState .map((gs) => gs != null) @@ -166,9 +182,9 @@ export const liveTable = (key: string) => { inputs, outputs: { playersPresent, - playersReady, - gameConfig: gameConfig as Property, - gameState: gameState as Property, + playersReady: playersReady.toProperty(), + gameConfig: gameConfig as Property, + gameState: gameState as Property, }, };