cooking with kefir
This commit is contained in:
66
packages/server/src/table.ts
Normal file
66
packages/server/src/table.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { XOR } from "ts-xor";
|
||||
import Bus, { type Bus as TBus } from "kefir-bus";
|
||||
import { merge, Observable, Property, Stream } from "kefir";
|
||||
import { Game } from "@prisma/client";
|
||||
|
||||
type TableInputEvent<GameAction> = { humanKey: string } & XOR<
|
||||
{ presence: "joined" | "left" },
|
||||
{ proposeGame: string },
|
||||
{ startGame: true },
|
||||
{ action: GameAction }
|
||||
>;
|
||||
|
||||
type InMemoryTable<GameState> = {
|
||||
playersPresent: string[];
|
||||
gameState: GameState | null;
|
||||
};
|
||||
|
||||
type TableOutputEvents<GameState> = {
|
||||
playersPresent: Property<string[], never>;
|
||||
gameState: Property<GameState | null, never>;
|
||||
};
|
||||
|
||||
type TablePayload<GameAction, GameState> = {
|
||||
input: TBus<TableInputEvent<GameAction>, never>;
|
||||
outputs: TableOutputEvents<GameState>;
|
||||
};
|
||||
|
||||
const tables: {
|
||||
[key: string]: TablePayload<unknown, unknown>;
|
||||
} = {};
|
||||
|
||||
export const liveTable = <GameState, GameAction>(key: string) => {
|
||||
if (!(key in tables)) {
|
||||
const inputEvents = Bus<TableInputEvent<GameAction>, never>();
|
||||
|
||||
tables[key] = {
|
||||
input: inputEvents,
|
||||
outputs: {
|
||||
playersPresent: inputEvents
|
||||
.filter((evt) => Boolean(evt.presence))
|
||||
.scan((prev, evt) => {
|
||||
if (evt.presence == "joined") {
|
||||
prev.push(evt.humanKey);
|
||||
} else if (evt.presence == "left") {
|
||||
prev.splice(prev.indexOf(evt.humanKey), 1);
|
||||
}
|
||||
return prev;
|
||||
}, [] as string[]),
|
||||
gameState: inputEvents
|
||||
.filter((evt) => Boolean(evt.startGame || evt.action))
|
||||
.scan((prev, evt) => {
|
||||
return prev;
|
||||
}, null as GameState | null),
|
||||
},
|
||||
};
|
||||
|
||||
// cleanup
|
||||
tables[key].outputs.playersPresent
|
||||
.slidingWindow(2)
|
||||
.filter(([prev, curr]) => prev.length > 0 && curr.length == 0)
|
||||
.onValue((_) => {
|
||||
delete tables[key];
|
||||
});
|
||||
}
|
||||
return tables[key] as TablePayload<GameAction, GameState>;
|
||||
};
|
||||
Reference in New Issue
Block a user