cooking with websockets
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
-- 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;
|
||||
@@ -32,6 +32,6 @@ model Instance {
|
||||
|
||||
players Human[]
|
||||
|
||||
game Game @relation(fields: [gameKey], references: [key])
|
||||
gameState Json
|
||||
game Game @relation(fields: [gameKey], references: [key])
|
||||
gameState Json?
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
"elysia": "^1.3.8",
|
||||
"elysia-ip": "^1.0.10",
|
||||
"elysia-rate-limit": "^4.4.0",
|
||||
"kefir": "^3.8.8",
|
||||
"kefir-bus": "^2.3.1",
|
||||
"object-hash": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/kefir": "^3.8.11",
|
||||
"concurrently": "^9.2.0",
|
||||
"prisma": "6.13.0"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ import { heq } from "@games/shared/utils";
|
||||
import { Elysia, t } from "elysia";
|
||||
import db from "../db";
|
||||
import { human } from "../human";
|
||||
import { ElysiaWS } from "elysia/dist/ws";
|
||||
import K, { Stream } from "kefir";
|
||||
import Bus, { type Bus as TBus } from "kefir-bus";
|
||||
|
||||
// omniscient game state
|
||||
export type GameState = {
|
||||
@@ -104,13 +107,18 @@ export const resolveAction = (
|
||||
};
|
||||
};
|
||||
|
||||
const instanceEvents: {
|
||||
[instanceId: string]: TBus<
|
||||
{ view?: PlayerView; players?: string[] },
|
||||
never
|
||||
>;
|
||||
} = {};
|
||||
|
||||
export const simpleApi = new Elysia({ prefix: "/simple" })
|
||||
.use(human)
|
||||
.post("/newGame", ({ humanKey }) => {
|
||||
console.log("KEY", humanKey);
|
||||
return db.instance.create({
|
||||
data: {
|
||||
gameState: newGame([humanKey]),
|
||||
gameKey: "simple",
|
||||
createdByKey: humanKey,
|
||||
players: {
|
||||
@@ -119,25 +127,96 @@ export const simpleApi = new Elysia({ prefix: "/simple" })
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
.group("/:instanceId", (app) =>
|
||||
app
|
||||
.get("/", ({ params: { instanceId }, humanKey }) =>
|
||||
db.instance
|
||||
.findUnique({
|
||||
.ws("/", {
|
||||
async open(ws) {
|
||||
console.log("Got ws connection");
|
||||
ws.send("Hello!");
|
||||
|
||||
// send initial state
|
||||
const instanceId = ws.data.params.instanceId;
|
||||
const humanKey = ws.data.humanKey;
|
||||
|
||||
const instance = await db.instance.update({
|
||||
data: {
|
||||
players: {
|
||||
connect: { key: humanKey },
|
||||
},
|
||||
},
|
||||
where: {
|
||||
id: instanceId,
|
||||
},
|
||||
})
|
||||
.then((game) =>
|
||||
getView(
|
||||
getKnowledge(
|
||||
game!.gameState as GameState,
|
||||
select: {
|
||||
createdByKey: true,
|
||||
gameState: true,
|
||||
players: {
|
||||
select: {
|
||||
key: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (instance == null) {
|
||||
ws.close(1011, "no such instance");
|
||||
return;
|
||||
}
|
||||
|
||||
// register this socket as a listener for events of this instance
|
||||
if (!instanceEvents[instanceId]) {
|
||||
instanceEvents[instanceId] = Bus();
|
||||
}
|
||||
// @ts-ignore
|
||||
ws.data.cb = instanceEvents[instanceId].onValue((evt) =>
|
||||
ws.send(evt)
|
||||
);
|
||||
ws.send({ creator: instance.createdByKey });
|
||||
if (instance.gameState != null) {
|
||||
ws.send(
|
||||
getView(
|
||||
getKnowledge(
|
||||
instance.gameState as GameState,
|
||||
humanKey
|
||||
),
|
||||
humanKey
|
||||
),
|
||||
humanKey
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
instanceEvents[instanceId]?.emit({
|
||||
players: instance.players.map((p) => p.key),
|
||||
});
|
||||
},
|
||||
close(ws) {
|
||||
console.log("Got ws close");
|
||||
const instanceId = ws.data.params.instanceId;
|
||||
// @ts-ignore
|
||||
instanceEvents[instanceId]?.offValue(ws.data.cb);
|
||||
db.instance
|
||||
.update({
|
||||
where: {
|
||||
id: instanceId,
|
||||
},
|
||||
data: {
|
||||
players: {
|
||||
disconnect: { key: ws.data.humanKey },
|
||||
},
|
||||
},
|
||||
select: {
|
||||
players: {
|
||||
select: {
|
||||
key: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((instance) => {
|
||||
instanceEvents[instanceId]?.emit({
|
||||
players: instance.players.map((p) => p.key),
|
||||
});
|
||||
});
|
||||
},
|
||||
})
|
||||
.post(
|
||||
"/",
|
||||
({ params: { instanceId }, body: { action }, humanKey }) =>
|
||||
|
||||
@@ -9,7 +9,7 @@ const port = env.PORT || 5001;
|
||||
const app = new Elysia()
|
||||
.use(
|
||||
cors({
|
||||
origin: ["localhost:3000", "games.drm.dev"],
|
||||
origin: ["http://localhost:3000", "https://games.drm.dev"],
|
||||
})
|
||||
)
|
||||
.onRequest(({ request }) => {
|
||||
|
||||
Reference in New Issue
Block a user