diff --git a/package.json b/package.json
index 98157f9..cd7e366 100644
--- a/package.json
+++ b/package.json
@@ -12,11 +12,7 @@
"object-hash": "^3.0.0"
},
"onlyBuiltDependencies": [
- "@parcel/watcher",
- "@prisma/client",
- "@prisma/engines",
- "esbuild",
- "prisma"
+ "esbuild"
]
},
"devDependencies": {
diff --git a/pkg/client/package.json b/pkg/client/package.json
index 227f358..35a2a13 100644
--- a/pkg/client/package.json
+++ b/pkg/client/package.json
@@ -9,6 +9,7 @@
"dependencies": {
"@elysiajs/eden": "^1.3.2",
"@solid-primitives/scheduled": "^1.5.2",
+ "@solid-primitives/storage": "^4.3.3",
"@solidjs/router": "^0.15.3",
"js-cookie": "^3.0.5",
"kefir": "^3.8.8",
diff --git a/pkg/client/src/app.tsx b/pkg/client/src/app.tsx
index fdd834a..2bdc667 100644
--- a/pkg/client/src/app.tsx
+++ b/pkg/client/src/app.tsx
@@ -1,17 +1,14 @@
+import { makePersisted } from "@solid-primitives/storage";
import { Route, Router } from "@solidjs/router";
-import { createResource, lazy, Suspense } from "solid-js";
+import pkg from "^/package.json";
+import { createSignal, lazy, Suspense } from "solid-js";
import { render } from "solid-js/web";
import "virtual:uno.css";
-import pkg from "^/package.json";
import "./style.css";
-import api from "./api";
-import { mePromise } from "./profile";
+import { name, setName } from "./profile";
const Profile = () => {
let dialogRef!: HTMLDialogElement;
- const [profile] = createResource(() =>
- mePromise.then(() => api.profile.get())
- );
return (
<>
@@ -24,10 +21,10 @@ const Profile = () => {
Name:{" "}
{
dialogRef.close();
- void api.setName.post({ name: e.target.value });
+ setName(e.target.value);
}}
class="bg-emerald-200 border-1.5 rounded-full px-4"
/>
diff --git a/pkg/client/src/components/Game.tsx b/pkg/client/src/components/Game.tsx
index 3216ebc..84c0eaf 100644
--- a/pkg/client/src/components/Game.tsx
+++ b/pkg/client/src/components/Game.tsx
@@ -4,7 +4,7 @@ import type {
SimplePlayerView,
SimpleResult,
} from "@games/shared/games/simple";
-import { me, profile } from "~/profile";
+import { me } from "~/profile";
import Hand from "./Hand";
import Pile from "./Pile";
import { TableContext } from "./Table";
@@ -39,7 +39,7 @@ export default () => {
{view().playerTurn == me()
? "your"
- : profile(view().playerTurn)()?.name + "'s"}
+ : table.playerNames[view().playerTurn] + "'s"}
{" "}
turn
diff --git a/pkg/client/src/components/Player.tsx b/pkg/client/src/components/Player.tsx
index 4989e8d..01f76df 100644
--- a/pkg/client/src/components/Player.tsx
+++ b/pkg/client/src/components/Player.tsx
@@ -1,5 +1,5 @@
import { createSignal, useContext } from "solid-js";
-import { playerColor, profile } from "~/profile";
+import { playerColor } from "~/profile";
import { TableContext } from "./Table";
import { Stylable } from "./toolbox";
import { createObservable, createObservableWithInit } from "~/fn";
@@ -31,7 +31,7 @@ export default (props: { playerKey: string } & Stylable) => {
class={`${props.class} w-20 h-20 rounded-full flex justify-center items-center`}
>
- {profile(props.playerKey)()?.name}
+ {table?.playerNames[props.playerKey]}
);
diff --git a/pkg/client/src/components/Table.tsx b/pkg/client/src/components/Table.tsx
index 160c16f..7c8e4a7 100644
--- a/pkg/client/src/components/Table.tsx
+++ b/pkg/client/src/components/Table.tsx
@@ -10,16 +10,24 @@ import {
Show,
} from "solid-js";
import api, { fromWebsocket } from "~/api";
-import { createObservable, createObservableWithInit, cx } from "~/fn";
-import { me, mePromise, profile } from "~/profile";
+import {
+ createObservable,
+ createObservableStore,
+ createObservableWithInit,
+ cx,
+} from "~/fn";
+import { me, mePromise } from "~/profile";
import Game from "./Game";
import Player from "./Player";
import games from "@games/shared/games/index";
+import { createStore, Store } from "solid-js/store";
+import { name } from "~/profile";
export const TableContext = createContext<{
view: Accessor;
sendWs: (msg: TWsIn) => void;
wsEvents: Stream;
+ playerNames: Store<{ [key: string]: string }>;
}>();
export default (props: { tableKey: string }) => {
@@ -37,7 +45,9 @@ export default (props: { tableKey: string }) => {
);
onCleanup(() => wsPromise.then((ws) => ws.close()));
- const presenceEvents = wsEvents.filter((evt) => evt.playersPresent != null);
+ const presenceEvents = wsEvents.filter(
+ (evt) => evt.playersPresent !== undefined
+ );
const gameEvents = wsEvents.filter((evt) => evt.view !== undefined);
const resultEvents = wsEvents.filter((evt) => evt.results !== undefined);
@@ -45,6 +55,13 @@ export default (props: { tableKey: string }) => {
presenceEvents.map((evt) => evt.playersPresent!),
[]
);
+ const playerNames = createObservableStore(
+ wsEvents
+ .filter((evt) => evt.playerNames != null)
+ .map(({ playerNames }) => playerNames!)
+ .toProperty(),
+ {}
+ );
const [ready, setReady] = createSignal(false);
mePromise.then(
@@ -57,8 +74,9 @@ export default (props: { tableKey: string }) => {
);
createEffect(() => sendWs({ ready: ready() }));
+ createEffect(() => sendWs({ name: name() }));
const view = createObservable(gameEvents.map((evt) => evt.view));
- const results = createObservable(
+ const results = createObservable(
merge([
gameEvents
.filter((evt) => "view" in evt && evt.view == null)
@@ -73,6 +91,7 @@ export default (props: { tableKey: string }) => {
sendWs,
wsEvents,
view,
+ playerNames,
}}
>
@@ -138,7 +157,7 @@ export default (props: { tableKey: string }) => {
- {profile(results())()?.name} won!
+ {playerNames[results()!]} won!
diff --git a/pkg/client/src/fn.ts b/pkg/client/src/fn.ts
index 968e092..069f7bb 100644
--- a/pkg/client/src/fn.ts
+++ b/pkg/client/src/fn.ts
@@ -1,5 +1,6 @@
import { Observable } from "kefir";
import { Accessor, createSignal } from "solid-js";
+import { createStore } from "solid-js/store";
declare global {
interface Array
{
@@ -37,3 +38,12 @@ export const createObservableWithInit = (
};
export const cx = (...classes: string[]) => classes.join(" ");
+
+export const createObservableStore = (
+ obs: Observable,
+ init: T
+) => {
+ const [store, setStore] = createStore(init);
+ obs.onValue((val) => setStore(val));
+ return store;
+};
diff --git a/pkg/client/src/profile.ts b/pkg/client/src/profile.ts
index 1af5b36..b6b2cf1 100644
--- a/pkg/client/src/profile.ts
+++ b/pkg/client/src/profile.ts
@@ -1,26 +1,16 @@
-import { createResource, Resource } from "solid-js";
+import { createEffect, createResource, createSignal, Resource } from "solid-js";
import { ApiType } from "./fn";
import api from "./api";
import hash from "object-hash";
+import { makePersisted } from "@solid-primitives/storage";
export const mePromise = api.whoami.post().then((r) => r.data);
export const [me] = createResource(() => mePromise);
-
-const playerProfiles: {
- [humanKey: string]: Resource>;
-} = {};
-
-export const profile = (humanKey: string) => {
- if (!(humanKey in playerProfiles)) {
- playerProfiles[humanKey] = createResource(() =>
- api.profile
- .get({ query: { otherHumanKey: humanKey } })
- .then((r) => r.data)
- )[0];
- }
-
- return playerProfiles[humanKey];
-};
+createEffect(() => console.log(me()));
export const playerColor = (humanKey: string) =>
"#" + hash(humanKey).substring(0, 6);
+
+export const [name, setName] = makePersisted(createSignal("__name__"), {
+ name: "name",
+});
diff --git a/pkg/server/db/migrations/20250805231347_init/migration.sql b/pkg/server/db/migrations/20250805231347_init/migration.sql
deleted file mode 100644
index ef9aabb..0000000
--- a/pkg/server/db/migrations/20250805231347_init/migration.sql
+++ /dev/null
@@ -1,19 +0,0 @@
--- CreateTable
-CREATE TABLE "Game" (
- "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
- "updatedAt" DATETIME NOT NULL,
- "name" TEXT NOT NULL,
- "rules" TEXT
-);
-
--- CreateTable
-CREATE TABLE "Instance" (
- "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- "gameId" INTEGER NOT NULL,
- "gameState" JSONB NOT NULL,
- CONSTRAINT "Instance_gameId_fkey" FOREIGN KEY ("gameId") REFERENCES "Game" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
-);
-
--- CreateIndex
-CREATE UNIQUE INDEX "Game_name_key" ON "Game"("name");
diff --git a/pkg/server/db/migrations/20250809034803_strings_are_just_better/migration.sql b/pkg/server/db/migrations/20250809034803_strings_are_just_better/migration.sql
deleted file mode 100644
index a33e2b2..0000000
--- a/pkg/server/db/migrations/20250809034803_strings_are_just_better/migration.sql
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- Warnings:
-
- - The primary key for the `Game` table will be changed. If it partially fails, the table could be left without primary key constraint.
- - You are about to drop the column `id` on the `Game` table. All the data in the column will be lost.
- - You are about to drop the column `gameId` on the `Instance` table. All the data in the column will be lost.
- - Added the required column `key` to the `Game` table without a default value. This is not possible if the table is not empty.
- - Added the required column `gameKey` to the `Instance` table without a default value. This is not possible if the table is not empty.
-
-*/
--- RedefineTables
-PRAGMA defer_foreign_keys=ON;
-PRAGMA foreign_keys=OFF;
-CREATE TABLE "new_Game" (
- "key" TEXT NOT NULL PRIMARY KEY,
- "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
- "updatedAt" DATETIME NOT NULL,
- "name" TEXT NOT NULL,
- "rules" TEXT
-);
-INSERT INTO "new_Game" ("createdAt", "name", "rules", "updatedAt") SELECT "createdAt", "name", "rules", "updatedAt" FROM "Game";
-DROP TABLE "Game";
-ALTER TABLE "new_Game" RENAME TO "Game";
-CREATE TABLE "new_Instance" (
- "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- "gameKey" TEXT NOT NULL,
- "gameState" JSONB NOT NULL,
- CONSTRAINT "Instance_gameKey_fkey" FOREIGN KEY ("gameKey") REFERENCES "Game" ("key") ON DELETE RESTRICT ON UPDATE CASCADE
-);
-INSERT INTO "new_Instance" ("gameState", "id") SELECT "gameState", "id" FROM "Instance";
-DROP TABLE "Instance";
-ALTER TABLE "new_Instance" RENAME TO "Instance";
-PRAGMA foreign_keys=ON;
-PRAGMA defer_foreign_keys=OFF;
diff --git a/pkg/server/db/migrations/20250809191236_strings/migration.sql b/pkg/server/db/migrations/20250809191236_strings/migration.sql
deleted file mode 100644
index 0701d21..0000000
--- a/pkg/server/db/migrations/20250809191236_strings/migration.sql
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- Warnings:
-
- - The primary key for the `Instance` table will be changed. If it partially fails, the table could be left without primary key constraint.
-
-*/
--- RedefineTables
-PRAGMA defer_foreign_keys=ON;
-PRAGMA foreign_keys=OFF;
-CREATE TABLE "new_Instance" (
- "id" TEXT NOT NULL PRIMARY KEY,
- "gameKey" TEXT NOT NULL,
- "gameState" JSONB NOT NULL,
- CONSTRAINT "Instance_gameKey_fkey" FOREIGN KEY ("gameKey") REFERENCES "Game" ("key") ON DELETE RESTRICT ON UPDATE CASCADE
-);
-INSERT INTO "new_Instance" ("gameKey", "gameState", "id") SELECT "gameKey", "gameState", "id" FROM "Instance";
-DROP TABLE "Instance";
-ALTER TABLE "new_Instance" RENAME TO "Instance";
-PRAGMA foreign_keys=ON;
-PRAGMA defer_foreign_keys=OFF;
diff --git a/pkg/server/db/migrations/20250809192322_humans/migration.sql b/pkg/server/db/migrations/20250809192322_humans/migration.sql
deleted file mode 100644
index d7d52cc..0000000
--- a/pkg/server/db/migrations/20250809192322_humans/migration.sql
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- Warnings:
-
- - Added the required column `createdByKey` to the `Instance` table without a default value. This is not possible if the table is not empty.
-
-*/
--- CreateTable
-CREATE TABLE "Human" (
- "key" TEXT NOT NULL PRIMARY KEY,
- "name" TEXT NOT NULL
-);
-
--- RedefineTables
-PRAGMA defer_foreign_keys=ON;
-PRAGMA foreign_keys=OFF;
-CREATE TABLE "new_Instance" (
- "id" TEXT NOT NULL PRIMARY KEY,
- "createdByKey" TEXT NOT NULL,
- "gameKey" TEXT NOT NULL,
- "gameState" JSONB NOT NULL,
- CONSTRAINT "Instance_createdByKey_fkey" FOREIGN KEY ("createdByKey") REFERENCES "Human" ("key") ON DELETE RESTRICT ON UPDATE CASCADE,
- CONSTRAINT "Instance_gameKey_fkey" FOREIGN KEY ("gameKey") REFERENCES "Game" ("key") ON DELETE RESTRICT ON UPDATE CASCADE
-);
-INSERT INTO "new_Instance" ("gameKey", "gameState", "id") SELECT "gameKey", "gameState", "id" FROM "Instance";
-DROP TABLE "Instance";
-ALTER TABLE "new_Instance" RENAME TO "Instance";
-PRAGMA foreign_keys=ON;
-PRAGMA defer_foreign_keys=OFF;
diff --git a/pkg/server/db/migrations/20250813041445_names_optional/migration.sql b/pkg/server/db/migrations/20250813041445_names_optional/migration.sql
deleted file mode 100644
index 2df6ca0..0000000
--- a/pkg/server/db/migrations/20250813041445_names_optional/migration.sql
+++ /dev/null
@@ -1,12 +0,0 @@
--- RedefineTables
-PRAGMA defer_foreign_keys=ON;
-PRAGMA foreign_keys=OFF;
-CREATE TABLE "new_Human" (
- "key" TEXT NOT NULL PRIMARY KEY,
- "name" TEXT NOT NULL DEFAULT ''
-);
-INSERT INTO "new_Human" ("key", "name") SELECT "key", "name" FROM "Human";
-DROP TABLE "Human";
-ALTER TABLE "new_Human" RENAME TO "Human";
-PRAGMA foreign_keys=ON;
-PRAGMA defer_foreign_keys=OFF;
diff --git a/pkg/server/db/migrations/20250818213152_tokens/migration.sql b/pkg/server/db/migrations/20250818213152_tokens/migration.sql
deleted file mode 100644
index 117242a..0000000
--- a/pkg/server/db/migrations/20250818213152_tokens/migration.sql
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- Warnings:
-
- - The required column `token` was added to the `Human` table with a prisma-level default value. This is not possible if the table is not empty. Please add this column as optional, then populate it before making it required.
-
-*/
--- CreateTable
-CREATE TABLE "_HumanToInstance" (
- "A" TEXT NOT NULL,
- "B" TEXT NOT NULL,
- CONSTRAINT "_HumanToInstance_A_fkey" FOREIGN KEY ("A") REFERENCES "Human" ("key") ON DELETE CASCADE ON UPDATE CASCADE,
- CONSTRAINT "_HumanToInstance_B_fkey" FOREIGN KEY ("B") REFERENCES "Instance" ("id") ON DELETE CASCADE ON UPDATE CASCADE
-);
-
--- RedefineTables
-PRAGMA defer_foreign_keys=ON;
-PRAGMA foreign_keys=OFF;
-CREATE TABLE "new_Human" (
- "key" TEXT NOT NULL PRIMARY KEY,
- "token" TEXT NOT NULL,
- "name" TEXT NOT NULL DEFAULT '__name__',
- "lastActive" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
-);
-INSERT INTO "new_Human" ("key", "name") SELECT "key", "name" FROM "Human";
-DROP TABLE "Human";
-ALTER TABLE "new_Human" RENAME TO "Human";
-CREATE UNIQUE INDEX "Human_token_key" ON "Human"("token");
-CREATE TABLE "new_Instance" (
- "id" TEXT NOT NULL PRIMARY KEY,
- "createdByKey" TEXT NOT NULL,
- "gameKey" TEXT NOT NULL,
- "gameState" JSONB NOT NULL,
- CONSTRAINT "Instance_gameKey_fkey" FOREIGN KEY ("gameKey") REFERENCES "Game" ("key") ON DELETE RESTRICT ON UPDATE CASCADE
-);
-INSERT INTO "new_Instance" ("createdByKey", "gameKey", "gameState", "id") SELECT "createdByKey", "gameKey", "gameState", "id" FROM "Instance";
-DROP TABLE "Instance";
-ALTER TABLE "new_Instance" RENAME TO "Instance";
-PRAGMA foreign_keys=ON;
-PRAGMA defer_foreign_keys=OFF;
-
--- CreateIndex
-CREATE UNIQUE INDEX "_HumanToInstance_AB_unique" ON "_HumanToInstance"("A", "B");
-
--- CreateIndex
-CREATE INDEX "_HumanToInstance_B_index" ON "_HumanToInstance"("B");
diff --git a/pkg/server/db/migrations/20250819011115_gamestate_nullable/migration.sql b/pkg/server/db/migrations/20250819011115_gamestate_nullable/migration.sql
deleted file mode 100644
index 99b6453..0000000
--- a/pkg/server/db/migrations/20250819011115_gamestate_nullable/migration.sql
+++ /dev/null
@@ -1,15 +0,0 @@
--- RedefineTables
-PRAGMA defer_foreign_keys=ON;
-PRAGMA foreign_keys=OFF;
-CREATE TABLE "new_Instance" (
- "id" TEXT NOT NULL PRIMARY KEY,
- "createdByKey" TEXT NOT NULL,
- "gameKey" TEXT NOT NULL,
- "gameState" JSONB,
- CONSTRAINT "Instance_gameKey_fkey" FOREIGN KEY ("gameKey") REFERENCES "Game" ("key") ON DELETE RESTRICT ON UPDATE CASCADE
-);
-INSERT INTO "new_Instance" ("createdByKey", "gameKey", "gameState", "id") SELECT "createdByKey", "gameKey", "gameState", "id" FROM "Instance";
-DROP TABLE "Instance";
-ALTER TABLE "new_Instance" RENAME TO "Instance";
-PRAGMA foreign_keys=ON;
-PRAGMA defer_foreign_keys=OFF;
diff --git a/pkg/server/db/migrations/migration_lock.toml b/pkg/server/db/migrations/migration_lock.toml
deleted file mode 100644
index 2a5a444..0000000
--- a/pkg/server/db/migrations/migration_lock.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-# Please do not edit this file manually
-# It should be added in your version-control system (e.g., Git)
-provider = "sqlite"
diff --git a/pkg/server/db/schema.prisma b/pkg/server/db/schema.prisma
deleted file mode 100644
index 14d9f80..0000000
--- a/pkg/server/db/schema.prisma
+++ /dev/null
@@ -1,37 +0,0 @@
-generator client {
- provider = "prisma-client-js"
-}
-
-datasource db {
- provider = "sqlite"
- url = "file:./dev.db"
-}
-
-model Game {
- key String @id
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- name String
- rules String?
- instances Instance[]
-}
-
-model Human {
- key String @id @default(cuid())
- token String @unique @default(cuid())
- name String @default("__name__")
- lastActive DateTime @default(now())
-
- playingInstances Instance[]
-}
-
-model Instance {
- id String @id @default(cuid())
- createdByKey String
- gameKey String
-
- players Human[]
-
- game Game @relation(fields: [gameKey], references: [key])
- gameState Json?
-}
diff --git a/pkg/server/package.json b/pkg/server/package.json
index f760194..cbacffb 100644
--- a/pkg/server/package.json
+++ b/pkg/server/package.json
@@ -1,15 +1,8 @@
{
"name": "@games/server",
"scripts": {
- "dev": "concurrently 'pnpm run devserver' 'pnpm run dbstudio'",
- "devserver": "NODE_ENV=development PORT=5001 bun run --hot src/index.ts",
- "dbstudio": "pnpm dlx prisma studio --browser none",
- "dbdeploy": "pnpm dlx prisma migrate deploy",
- "dbtypes": "pnpm dlx prisma generate",
- "dbsync": "pnpm dlx prisma migrate dev",
- "dbwipe": "pnpm dlx prisma migrate reset",
- "prod": "NODE_ENV=production bun run src/index.ts",
- "start": "concurrently 'pnpm run prod' 'pnpm run dbstudio'"
+ "dev": "NODE_ENV=development PORT=5001 bun run --hot src/index.ts",
+ "start": "NODE_ENV=production bun run src/index.ts"
},
"dependencies": {
"@elysiajs/cors": "^1.3.3",
diff --git a/pkg/server/prisma.config.ts b/pkg/server/prisma.config.ts
deleted file mode 100644
index 25b7c97..0000000
--- a/pkg/server/prisma.config.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import path from "node:path";
-import { defineConfig } from "prisma/config";
-
-export default defineConfig({
- schema: path.join("db", "schema.prisma"),
-});
diff --git a/pkg/server/src/api.ts b/pkg/server/src/api.ts
index 2ec4da6..dd9823e 100644
--- a/pkg/server/src/api.ts
+++ b/pkg/server/src/api.ts
@@ -4,9 +4,9 @@ import dayjs from "dayjs";
import { Elysia, t } from "elysia";
import { combine } from "kefir";
import Bus from "kefir-bus";
-import db from "./db";
import { liveTable, WsIn, WsOut } from "./table";
import { err } from "./logging";
+import { generateTokenAndKey, resolveToken, tokenExists } from "./human";
export const WS = Bus<
{
@@ -19,63 +19,23 @@ export const WS = Bus<
const api = new Elysia({ prefix: "/api" })
.post("/whoami", async ({ cookie: { token } }) => {
- let human: Human | null;
- if (
- token.value == null ||
- (human = await db.human.findUnique({
- where: {
- token: token.value,
- },
- })) == null
- ) {
- human = await db.human.create({
- data: {},
- });
+ console.log("WHOAMI");
+ let key: string | undefined;
+ if (token.value == null || (key = resolveToken(token.value)) == null) {
+ const [newToken, newKey] = generateTokenAndKey();
token.set({
- value: human.token,
+ value: newToken,
expires: dayjs().add(1, "year").toDate(),
httpOnly: true,
});
+ return newKey;
}
-
- return human.key;
+ return key;
})
.derive(async ({ cookie: { token }, status }) => {
- const humanKey = await db.human
- .findUnique({
- where: { token: token.value },
- })
- .then((human) => human?.key);
+ const humanKey = token.value && resolveToken(token.value);
return humanKey != null ? { humanKey } : status(401);
})
- .post(
- "/setName",
- ({ body: { name }, humanKey }) =>
- db.human.update({
- where: {
- key: humanKey,
- },
- data: {
- name,
- },
- }),
- {
- body: t.Object({
- name: t.String(),
- }),
- }
- )
- .get("/profile", ({ humanKey, query: { otherHumanKey } }) =>
- db.human
- .findFirst({ where: { key: otherHumanKey ?? humanKey } })
- .then((human) => {
- if (human == null) {
- return null;
- }
- const { token, ...safeProfile } = human;
- return safeProfile;
- })
- )
.ws("/ws/:tableKey", {
body: WsIn,
response: WsOut,
diff --git a/pkg/server/src/db.ts b/pkg/server/src/db.ts
deleted file mode 100644
index 3cfc482..0000000
--- a/pkg/server/src/db.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-"use server";
-
-import { PrismaClient } from "@prisma/client";
-
-export default new PrismaClient();
diff --git a/pkg/server/src/human.ts b/pkg/server/src/human.ts
new file mode 100644
index 0000000..b1608ff
--- /dev/null
+++ b/pkg/server/src/human.ts
@@ -0,0 +1,17 @@
+const tokenToHumanKey: { [token: string]: string } = {};
+const playerKeys: Set = new Set();
+
+export const generateTokenAndKey = () => {
+ let token: string, key: string;
+ do {
+ [token, key] = [crypto.randomUUID(), crypto.randomUUID()];
+ tokenToHumanKey[token] = key;
+ playerKeys.add(key);
+ } while (!(token in tokenToHumanKey || playerKeys.has(key)));
+ return [token, key];
+};
+
+export const resolveToken = (token: string) =>
+ tokenToHumanKey[token] as string | undefined;
+export const tokenExists = (token: string) => token in tokenToHumanKey;
+export const keyExists = (key: string) => playerKeys.has(key);
diff --git a/pkg/server/src/table.ts b/pkg/server/src/table.ts
index 1a07e36..19319ea 100644
--- a/pkg/server/src/table.ts
+++ b/pkg/server/src/table.ts
@@ -15,6 +15,7 @@ import { log } from "./logging";
export const WsOut = t.Object({
playersPresent: t.Optional(t.Array(t.String())),
+ playerNames: t.Optional(t.Record(t.String(), t.String())),
playersReady: t.Optional(t.Nullable(t.Record(t.String(), t.Boolean()))),
gameConfig: t.Optional(t.Any()),
view: t.Optional(t.Any()),
@@ -22,6 +23,7 @@ export const WsOut = t.Object({
});
export type TWsOut = typeof WsOut.static;
export const WsIn = t.Union([
+ t.Object({ name: t.String() }),
t.Object({ ready: t.Boolean() }),
t.Object({ action: t.Any() }),
t.Object({ quit: t.Literal(true) }),
@@ -55,6 +57,7 @@ type TablePayload<
>;
gameConfig: Property;
results: Property;
+ playerNames: Property<{ [key: string]: string }, any>;
};
player: {
[key: string]: {
@@ -133,11 +136,12 @@ export const liveTable = <
});
});
- const { ready, action, quit } = partition(
- ["ready", "action", "quit"],
+ const { name, ready, action, quit } = partition(
+ ["name", "ready", "action", "quit"],
messages
) as unknown as {
// yuck
+ name: Observable;
ready: Observable;
action: Observable;
quit: Observable;
@@ -145,17 +149,23 @@ export const liveTable = <
const gameEnds = quit.map((_) => null);
+ const gameIsActivePool = pool();
+ const gameIsActive = merge([
+ constant(false),
+ gameIsActivePool,
+ ]).toProperty();
+
const playersReady = multiScan(
null as {
[key: string]: boolean;
} | null,
[
- playersPresent, // TODO: filter to only outside active games
+ playersPresent.filterBy(invert(gameIsActive)),
(prev, players: ValueWithin) =>
Object.fromEntries(players.map((p) => [p, prev?.[p] ?? false])),
],
[
- ready, // TODO: filter to only outside active games
+ ready.filterBy(invert(gameIsActive)),
(prev, evt: ValueWithin) =>
prev?.[evt.humanKey] != null
? {
@@ -172,6 +182,13 @@ export const liveTable = <
]
).toProperty();
+ const playerNames = name
+ .scan(
+ (prev, n) => ({ ...prev, [n.humanKey]: n.name }),
+ {} as { [key: string]: string }
+ )
+ .toProperty();
+
const gameStarts = playersReady
.filter(
(pr) =>
@@ -194,12 +211,6 @@ export const liveTable = <
const resultsPool = pool();
const results = merge([constant(null), resultsPool]).toProperty();
- const gameIsActivePool = pool();
- const gameIsActive = merge([
- constant(false),
- gameIsActivePool,
- ]).toProperty();
-
const gameState = multiScan(
null as GameState | null,
[
@@ -270,6 +281,7 @@ export const liveTable = <
playersReady,
gameConfig,
results,
+ playerNames,
},
player: playerStreams,
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2aace19..5babdb3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,6 +23,9 @@ importers:
'@solid-primitives/scheduled':
specifier: ^1.5.2
version: 1.5.2(solid-js@1.9.9)
+ '@solid-primitives/storage':
+ specifier: ^4.3.3
+ version: 4.3.3(solid-js@1.9.9)
'@solidjs/router':
specifier: ^0.15.3
version: 0.15.3(solid-js@1.9.9)
@@ -592,6 +595,23 @@ packages:
peerDependencies:
solid-js: ^1.6.12
+ '@solid-primitives/storage@4.3.3':
+ resolution: {integrity: sha512-ACbNwMZ1s8VAvld6EUXkDkX/US3IhtlPLxg6+B2s9MwNUugwdd51I98LPEaHrdLpqPmyzqgoJe0TxEFlf3Dqrw==}
+ peerDependencies:
+ '@tauri-apps/plugin-store': '*'
+ solid-js: ^1.6.12
+ solid-start: '*'
+ peerDependenciesMeta:
+ '@tauri-apps/plugin-store':
+ optional: true
+ solid-start:
+ optional: true
+
+ '@solid-primitives/utils@6.3.2':
+ resolution: {integrity: sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
'@solidjs/router@0.15.3':
resolution: {integrity: sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw==}
peerDependencies:
@@ -2811,6 +2831,15 @@ snapshots:
dependencies:
solid-js: 1.9.9
+ '@solid-primitives/storage@4.3.3(solid-js@1.9.9)':
+ dependencies:
+ '@solid-primitives/utils': 6.3.2(solid-js@1.9.9)
+ solid-js: 1.9.9
+
+ '@solid-primitives/utils@6.3.2(solid-js@1.9.9)':
+ dependencies:
+ solid-js: 1.9.9
+
'@solidjs/router@0.15.3(solid-js@1.9.9)':
dependencies:
solid-js: 1.9.9