bugs bashed
This commit is contained in:
@@ -6,11 +6,9 @@ import { Stylable } from "./toolbox";
|
|||||||
export default (props: { playerKey: string } & Stylable) => {
|
export default (props: { playerKey: string } & Stylable) => {
|
||||||
const table = useContext(TableContext);
|
const table = useContext(TableContext);
|
||||||
|
|
||||||
onMount(() => console.log("Player mounted"));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={(e) => table?.setPlayers(props.playerKey, "ref", e)}
|
ref={(e) => table?.setPlayers(props.playerKey, { ref: e })}
|
||||||
style={{
|
style={{
|
||||||
...props.style,
|
...props.style,
|
||||||
"background-color": playerColor(props.playerKey),
|
"background-color": playerColor(props.playerKey),
|
||||||
|
|||||||
@@ -52,36 +52,40 @@ export default (props: { tableKey: string }) => {
|
|||||||
|
|
||||||
// #region inbound table properties
|
// #region inbound table properties
|
||||||
const [players, setPlayers] = createStore<PlayerStore>({});
|
const [players, setPlayers] = createStore<PlayerStore>({});
|
||||||
const playerKeys = () => Object.keys(players);
|
|
||||||
wsEvents
|
wsEvents
|
||||||
.thru(extractProperty("playersPresent"))
|
.thru(extractProperty("playersPresent"))
|
||||||
.onValue((P) =>
|
.onValue((P) =>
|
||||||
P.filter((p) => !(p in players)).forEach((p) =>
|
setPlayers(
|
||||||
setPlayers(p, { name: "", ready: false })
|
Object.fromEntries(
|
||||||
|
P.map((p) => [
|
||||||
|
p,
|
||||||
|
p in players ? players[p] : { name: "", ready: false },
|
||||||
|
])
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
wsEvents
|
wsEvents.thru(extractProperty("playerNames")).onValue((P) =>
|
||||||
.thru(extractProperty("playerNames"))
|
Object.entries(P)
|
||||||
.onValue((P) =>
|
.filter(([player]) => player in players)
|
||||||
Object.entries(P).map(([player, name]) =>
|
.map(([player, name]) => setPlayers(player, "name", name))
|
||||||
setPlayers(player, "name", name)
|
);
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
wsEvents
|
wsEvents.thru(extractProperty("playersReady")).onValue((P) =>
|
||||||
.thru(extractProperty("playersReady"))
|
Object.entries(P)
|
||||||
.onValue((P) =>
|
.filter(([player]) => player in players)
|
||||||
Object.entries(P).map(([player, ready]) =>
|
.map(([player, ready]) => setPlayers(player, "ready", ready))
|
||||||
setPlayers(player, "ready", ready)
|
);
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region inbound game properties
|
// #region inbound game properties
|
||||||
const [gameConfig, setGameConfig] = createSynced({
|
const [gameConfig, setGameConfig] = createSynced({
|
||||||
ws: wsEvents.thru(extractProperty("gameConfig")) as Property<any, any>,
|
ws: wsEvents.thru(extractProperty("gameConfig")) as Property<
|
||||||
|
{ game: string; players: string[] },
|
||||||
|
any
|
||||||
|
>,
|
||||||
sendWs: (gameConfig) => sendWs({ gameConfig }),
|
sendWs: (gameConfig) => sendWs({ gameConfig }),
|
||||||
});
|
});
|
||||||
const view = wsEvents.thru(extractProperty("view")).thru(createObservable);
|
const view = wsEvents.thru(extractProperty("view")).thru(createObservable);
|
||||||
@@ -94,6 +98,7 @@ export default (props: { tableKey: string }) => {
|
|||||||
ws.on("open", () => {
|
ws.on("open", () => {
|
||||||
wsEvents.plug(fromWebsocket<TWsOut>(ws));
|
wsEvents.plug(fromWebsocket<TWsOut>(ws));
|
||||||
|
|
||||||
|
// TODO: these need to be in a tracking scope to be disposed
|
||||||
createEffect(() => sendWs({ ready: ready() }));
|
createEffect(() => sendWs({ ready: ready() }));
|
||||||
createEffect(() => sendWs({ name: name() }));
|
createEffect(() => sendWs({ name: name() }));
|
||||||
});
|
});
|
||||||
@@ -103,7 +108,7 @@ export default (props: { tableKey: string }) => {
|
|||||||
const GamePicker = () => {
|
const GamePicker = () => {
|
||||||
return (
|
return (
|
||||||
<div class="absolute tc mt-8 flex gap-4">
|
<div class="absolute tc mt-8 flex gap-4">
|
||||||
<select>
|
<select value={gameConfig()?.game}>
|
||||||
<For each={Object.entries(games)}>
|
<For each={Object.entries(games)}>
|
||||||
{([gameId]) => <option value={gameId}>{gameId}</option>}
|
{([gameId]) => <option value={gameId}>{gameId}</option>}
|
||||||
</For>
|
</For>
|
||||||
@@ -133,10 +138,10 @@ export default (props: { tableKey: string }) => {
|
|||||||
>
|
>
|
||||||
{/* Player avatars around the table */}
|
{/* Player avatars around the table */}
|
||||||
<div class="flex justify-around p-t-14">
|
<div class="flex justify-around p-t-14">
|
||||||
<For each={playerKeys().filter((p) => p != me())}>
|
<For each={gameConfig()?.players.filter((p) => p != me())}>
|
||||||
{(player, i) => {
|
{(player, i) => {
|
||||||
const verticalOffset = () => {
|
const verticalOffset = () => {
|
||||||
const N = playerKeys().length - 1;
|
const N = gameConfig()!.players.length - 1;
|
||||||
const x = Math.abs((2 * i() + 1) / (N * 2) - 0.5);
|
const x = Math.abs((2 * i() + 1) / (N * 2) - 0.5);
|
||||||
const y = Math.sqrt(1 - x * x);
|
const y = Math.sqrt(1 - x * x);
|
||||||
return 1 - y;
|
return 1 - y;
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ export default () => {
|
|||||||
const table = useContext(TableContext)!;
|
const table = useContext(TableContext)!;
|
||||||
const view = table.view as Accessor<SimplePlayerView>;
|
const view = table.view as Accessor<SimplePlayerView>;
|
||||||
|
|
||||||
createEffect(() => console.log(table.gameConfig()));
|
|
||||||
|
|
||||||
const Configuration = () => (
|
const Configuration = () => (
|
||||||
<Show when={view() == null}>
|
<Show when={view() == null}>
|
||||||
<Portal mount={table.tableRef}>
|
<Portal mount={table.tableRef}>
|
||||||
@@ -52,7 +50,7 @@ export default () => {
|
|||||||
onChange={(evt) =>
|
onChange={(evt) =>
|
||||||
table.setGameConfig({
|
table.setGameConfig({
|
||||||
...table.gameConfig(),
|
...table.gameConfig(),
|
||||||
"cards to win": evt.target.value,
|
"cards to win": Number.parseInt(evt.target.value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { createLatest } from "@solid-primitives/memo";
|
|||||||
import { Observable, Property, Stream } from "kefir";
|
import { Observable, Property, Stream } from "kefir";
|
||||||
import { Accessor, createEffect, createSignal } from "solid-js";
|
import { Accessor, createEffect, createSignal } from "solid-js";
|
||||||
import { createStore } from "solid-js/store";
|
import { createStore } from "solid-js/store";
|
||||||
|
import type { ExtractPropertyType, UnionKeys } from "@games/shared/types";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Array<T> {
|
interface Array<T> {
|
||||||
@@ -48,13 +49,6 @@ export const createObservableStore =
|
|||||||
return store;
|
return store;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UnionKeys<T> = T extends any ? keyof T : never;
|
|
||||||
type ExtractPropertyType<T, P extends string | number | symbol> = T extends {
|
|
||||||
[K in P]: any;
|
|
||||||
}
|
|
||||||
? T[P]
|
|
||||||
: never;
|
|
||||||
|
|
||||||
export const extractProperty =
|
export const extractProperty =
|
||||||
<T extends object, P extends UnionKeys<T>>(property: P) =>
|
<T extends object, P extends UnionKeys<T>>(property: P) =>
|
||||||
(obs: Observable<T, any>): Property<ExtractPropertyType<T, P>, any> =>
|
(obs: Observable<T, any>): Property<ExtractPropertyType<T, P>, any> =>
|
||||||
|
|||||||
@@ -1,21 +1,9 @@
|
|||||||
import { Game } from "@games/shared/games";
|
|
||||||
import { Human } from "@prisma/client";
|
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { Elysia, t } from "elysia";
|
import { Elysia } from "elysia";
|
||||||
import { combine } from "kefir";
|
import { generateTokenAndKey, resolveToken } from "./human";
|
||||||
import Bus from "kefir-bus";
|
|
||||||
import { liveTable, WsIn, WsOut } from "./table";
|
|
||||||
import { err } from "./logging";
|
import { err } from "./logging";
|
||||||
import { generateTokenAndKey, resolveToken, tokenExists } from "./human";
|
import { liveTable, WsIn, WsOut } from "./table";
|
||||||
|
import type { ExtractPropertyType, UnionKeys } from "@games/shared/types";
|
||||||
export const WS = Bus<
|
|
||||||
{
|
|
||||||
type: "open" | "message" | "error" | "close";
|
|
||||||
humanKey: string;
|
|
||||||
tableKey: string;
|
|
||||||
},
|
|
||||||
unknown
|
|
||||||
>();
|
|
||||||
|
|
||||||
const api = new Elysia({ prefix: "/api" })
|
const api = new Elysia({ prefix: "/api" })
|
||||||
.post("/whoami", async ({ cookie: { token } }) => {
|
.post("/whoami", async ({ cookie: { token } }) => {
|
||||||
@@ -57,6 +45,7 @@ const api = new Elysia({ prefix: "/api" })
|
|||||||
...table.outputs.global,
|
...table.outputs.global,
|
||||||
...(table.outputs.player[humanKey] ?? {}),
|
...(table.outputs.player[humanKey] ?? {}),
|
||||||
}).forEach(([type, stream]) =>
|
}).forEach(([type, stream]) =>
|
||||||
|
// @ts-ignore
|
||||||
stream.onValue((v) => send({ [type]: v }))
|
stream.onValue((v) => send({ [type]: v }))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ new Elysia()
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
.onError(({ error }) => console.error(error))
|
// .onError(({ error }) => console.error(error))
|
||||||
|
|
||||||
.get("/ping", () => "pong")
|
.get("/ping", () => "pong")
|
||||||
.use(api)
|
.use(api)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const log = (value: unknown) => LogBus.emit(value);
|
|||||||
export const err = (value: unknown) =>
|
export const err = (value: unknown) =>
|
||||||
LogBus.emitEvent({ type: "error", value });
|
LogBus.emitEvent({ type: "error", value });
|
||||||
|
|
||||||
LogStream.log();
|
// LogStream.log();
|
||||||
LogStream.onError((err) => {
|
// LogStream.onError((err) => {
|
||||||
console.error(err);
|
// console.error(err);
|
||||||
});
|
// });
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import { t } from "elysia";
|
|||||||
import { combine, constant, merge, Observable, pool, Property } from "kefir";
|
import { combine, constant, merge, Observable, pool, Property } from "kefir";
|
||||||
import Bus, { type Bus as TBus } from "kefir-bus";
|
import Bus, { type Bus as TBus } from "kefir-bus";
|
||||||
import { log } from "./logging";
|
import { log } from "./logging";
|
||||||
|
import simple from "@games/shared/games/simple";
|
||||||
|
|
||||||
|
const DEFAULT_GAME_CONFIG = simple.defaultConfig;
|
||||||
|
|
||||||
export const WsOut = t.Union([
|
export const WsOut = t.Union([
|
||||||
t.Object({ playersPresent: t.Array(t.String()) }),
|
t.Object({ playersPresent: t.Array(t.String()) }),
|
||||||
@@ -270,10 +273,7 @@ export const liveTable = <
|
|||||||
|
|
||||||
gameConfigPool.plug(
|
gameConfigPool.plug(
|
||||||
multiScan(
|
multiScan(
|
||||||
{
|
DEFAULT_GAME_CONFIG,
|
||||||
game: "simple",
|
|
||||||
players: [] as string[],
|
|
||||||
},
|
|
||||||
[
|
[
|
||||||
playersPresent.filterBy(gameIsActive.thru(invert)),
|
playersPresent.filterBy(gameIsActive.thru(invert)),
|
||||||
(prev, players) => ({
|
(prev, players) => ({
|
||||||
@@ -282,11 +282,9 @@ export const liveTable = <
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
clientGameConfigs
|
clientGameConfigs.filterBy(gameIsActive.thru(invert)),
|
||||||
// .filterBy(gameIsActive.thru(invert))
|
|
||||||
.map(({ gameConfig }) => gameConfig),
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
(prev, config) => ({ ...config, players: prev.players }),
|
(prev, { gameConfig }) => ({ ...gameConfig, players: prev.players }),
|
||||||
]
|
]
|
||||||
) as unknown as Observable<GameConfig, any>
|
) as unknown as Observable<GameConfig, any>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export default {
|
|||||||
resolveQuit: () => null,
|
resolveQuit: () => null,
|
||||||
getResult: (state) =>
|
getResult: (state) =>
|
||||||
Object.entries(state.playerHands).find(
|
Object.entries(state.playerHands).find(
|
||||||
([_, hand]) => hand.length === 52
|
([_, hand]) => hand.length === config["cards to win"]
|
||||||
)?.[0],
|
)?.[0],
|
||||||
}),
|
}),
|
||||||
} satisfies Game<
|
} satisfies Game<
|
||||||
|
|||||||
9
pkg/shared/types.ts
Normal file
9
pkg/shared/types.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export type UnionKeys<T> = T extends any ? keyof T : never;
|
||||||
|
export type ExtractPropertyType<
|
||||||
|
T,
|
||||||
|
P extends string | number | symbol
|
||||||
|
> = T extends {
|
||||||
|
[K in P]: any;
|
||||||
|
}
|
||||||
|
? T[P]
|
||||||
|
: never;
|
||||||
Reference in New Issue
Block a user