import { Elysia, t } from "elysia"; import { getSimplePlayerView, SimpleAction, SimpleConfiguration, SimpleGameState, } from "./games/simple"; import { human } from "./human"; import dayjs from "dayjs"; import db from "./db"; import { liveTable, WsOut, WsIn } from "./table"; import { Human } from "@prisma/client"; import { combine } from "kefir"; 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: {}, }); token.set({ value: human.token, expires: dayjs().add(1, "year").toDate(), httpOnly: true, }); } return human.key; }) .use(human) .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; }) ) .get("/games", () => [{ key: "simple", name: "simple" }]) .ws("/ws/:tableKey", { async open({ data: { params: { tableKey }, humanKey, }, send, }) { const table = liveTable< SimpleConfiguration, SimpleGameState, SimpleAction >(tableKey); table.inputs.connectionChanges.emit({ humanKey, presence: "joined", }); table.outputs.playersPresent.onValue((players) => send({ players }) ); table.outputs.playersReady .skipDuplicates() .onValue((readys) => send({ playersReady: readys })); combine( [table.outputs.gameState], [table.outputs.gameConfig], (state, config) => state && config && getSimplePlayerView(config, state, humanKey) ) .toProperty() .onValue((view) => send({ view })); }, response: WsOut, body: WsIn, message( { data: { humanKey, params: { tableKey }, }, }, body ) { const { inputs: { readys, actions, quits }, } = liveTable(tableKey); if ("ready" in body) { readys.emit({ humanKey, ...body }); } else if ("action" in body) { actions.emit({ humanKey, ...body.action }); } else if ("quit" in body) { quits.emit({ humanKey }); } }, async close({ data: { params: { tableKey }, humanKey, }, }) { liveTable(tableKey).inputs.connectionChanges.emit({ humanKey, presence: "left", }); }, }); export default api; export type Api = typeof api;