fix multi tab presence

This commit is contained in:
2025-08-24 15:09:58 -04:00
parent 6c64886f2a
commit 4bcf071668
6 changed files with 59 additions and 18 deletions

View File

@@ -22,11 +22,13 @@
"elysia-rate-limit": "^4.4.0",
"kefir": "^3.8.8",
"kefir-bus": "^2.3.1",
"lodash": "^4.17.21",
"object-hash": "^3.0.0"
},
"devDependencies": {
"@types/bun": "latest",
"@types/kefir": "^3.8.11",
"@types/lodash": "^4.17.20",
"concurrently": "^9.2.0",
"prisma": "6.13.0",
"ts-xor": "^1.3.0"

View File

@@ -10,6 +10,7 @@ import dayjs from "dayjs";
import db from "./db";
import { liveTable, WsOut, WsIn } from "./table";
import { Human } from "@prisma/client";
import _ from "lodash";
const api = new Elysia({ prefix: "/api" })
.post("/whoami", async ({ cookie: { token } }) => {
@@ -74,9 +75,9 @@ const api = new Elysia({ prefix: "/api" })
}) {
const table = liveTable<SimpleGameState, SimpleAction>(tableKey);
table.outputs.playersPresent.onValue((players) =>
send({ players })
);
table.outputs.playersPresent
.skipDuplicates((p1, p2) => _.isEqual(new Set(p1), new Set(p2)))
.onValue((players) => send({ players }));
table.outputs.gameState.onValue((gameState) =>
send({
view:
@@ -84,7 +85,10 @@ const api = new Elysia({ prefix: "/api" })
getView(getKnowledge(gameState, humanKey), humanKey),
})
);
table.inputs.presenceChanges.emit({ humanKey, presence: "joined" });
table.inputs.connectionChanges.emit({
humanKey,
presence: "joined",
});
},
response: WsOut,
@@ -118,7 +122,7 @@ const api = new Elysia({ prefix: "/api" })
humanKey,
},
}) {
liveTable(tableKey).inputs.presenceChanges.emit({
liveTable(tableKey).inputs.connectionChanges.emit({
humanKey,
presence: "left",
});

View File

@@ -24,7 +24,7 @@ export type TWsIn = typeof WsIn.static;
type Attributed = { humanKey: string };
type TablePayload<GameState, GameAction> = {
inputs: {
presenceChanges: TBus<
connectionChanges: TBus<
Attributed & { presence: "joined" | "left" },
never
>;
@@ -45,23 +45,30 @@ const tables: {
export const liveTable = <GameState, GameAction>(key: string) => {
if (!(key in tables)) {
const inputs: TablePayload<GameState, GameAction>["inputs"] = {
presenceChanges: Bus(),
connectionChanges: Bus(),
gameProposals: Bus(),
gameStarts: Bus(),
gameActions: Bus(),
};
const { presenceChanges, gameProposals, gameStarts, gameActions } =
const { connectionChanges, gameProposals, gameStarts, gameActions } =
inputs;
// =======
const playersPresent = presenceChanges.scan((prev, evt) => {
if (evt.presence == "joined") {
prev.push(evt.humanKey);
} else if (evt.presence == "left") {
prev.splice(prev.indexOf(evt.humanKey), 1);
const playerConnectionCounts = connectionChanges.scan((prev, evt) => {
if (evt.presence == "left" && prev[evt.humanKey] == 1) {
const { [evt.humanKey]: _, ...rest } = prev;
return rest;
}
return prev;
}, [] as string[]);
return {
...prev,
[evt.humanKey]:
(prev[evt.humanKey] ?? 0) +
(evt.presence == "joined" ? 1 : -1),
};
}, {} as { [key: string]: number });
const playersPresent = playerConnectionCounts.map((counts) =>
Object.keys(counts)
);
const gameState = transform(
null as SimpleGameState | null,