@edv4h/usketch-plugin-croupier 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +84 -2
- package/dist/awareness-presence.d.ts +22 -0
- package/dist/awareness-presence.d.ts.map +1 -0
- package/dist/awareness-presence.js +57 -0
- package/dist/awareness-presence.js.map +1 -0
- package/dist/croupier-service.d.ts +68 -0
- package/dist/croupier-service.d.ts.map +1 -0
- package/dist/croupier-service.js +133 -0
- package/dist/croupier-service.js.map +1 -0
- package/dist/game-state.d.ts +44 -0
- package/dist/game-state.d.ts.map +1 -0
- package/dist/game-state.js +55 -0
- package/dist/game-state.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +59 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +256 -0
- package/dist/plugin.js.map +1 -0
- package/dist/table-ops.d.ts +105 -0
- package/dist/table-ops.d.ts.map +1 -0
- package/dist/table-ops.js +521 -0
- package/dist/table-ops.js.map +1 -0
- package/dist/table-shape.d.ts +59 -0
- package/dist/table-shape.d.ts.map +1 -0
- package/dist/table-shape.js +185 -0
- package/dist/table-shape.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/package.json +49 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EdV4H
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,85 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @edv4h/usketch-plugin-croupier
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Play [croupier](https://github.com/EdV4H/croupier) games (`@edv4h/croupier-core`)
|
|
4
|
+
on the board. A **table** shape holds the game and its engine snapshot; every
|
|
5
|
+
player who joins gets a **seat** shape on the table. The plugin knows no rules:
|
|
6
|
+
the host app passes the games (e.g. Values Card) and the plugin runs them
|
|
7
|
+
through the normal shape sync — no game server needed.
|
|
8
|
+
|
|
9
|
+
**Turn-based games only.** Every move rewrites the table shape, and a shape is
|
|
10
|
+
synced as one value (last writer wins). Two players acting at the same moment
|
|
11
|
+
would overwrite each other, so games where several players act at once (e.g.
|
|
12
|
+
Planning Poker voting) are not supported.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createValuesCardConfig } from "@edv4h/croupier-plugin-values-card";
|
|
18
|
+
import { createCroupierPlugin, defineCroupierGame } from "@edv4h/usketch-plugin-croupier";
|
|
19
|
+
|
|
20
|
+
createCroupierPlugin({
|
|
21
|
+
player: { userId, name, avatarUrl },
|
|
22
|
+
games: [
|
|
23
|
+
defineCroupierGame({
|
|
24
|
+
id: "values-card",
|
|
25
|
+
label: "Values Card",
|
|
26
|
+
createConfig: (options) => createValuesCardConfig({ theme: String(options.theme ?? "") }),
|
|
27
|
+
defaultOptions: { theme: "大事にしたいこと" },
|
|
28
|
+
maxPlayers: 8,
|
|
29
|
+
// Buttons of the built-in look: one per card for actions that take a card.
|
|
30
|
+
actionChoices: (action, view, viewer) =>
|
|
31
|
+
action === "discardCard"
|
|
32
|
+
? view.players[viewer].hand.map((c) => ({ label: c.name, payload: { cardId: c.id } }))
|
|
33
|
+
: [{ label: action }],
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
presence: createAwarenessPresence(wsProvider.awareness, () => userId), // optional, see "Absent players"
|
|
37
|
+
gameState: createMapGameState(doc.getMap("croupier")), // recommended, see "How it syncs"
|
|
38
|
+
now: serverClock.now, // optional: shared clock for turn timing
|
|
39
|
+
renderTable, // optional: your own look (see below)
|
|
40
|
+
renderSeat,
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## How it syncs
|
|
45
|
+
|
|
46
|
+
A board shape is synced as one value (last writer wins), so each write has one owner:
|
|
47
|
+
|
|
48
|
+
| Shape | `meta` | Written by |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| `croupier-table` | game id, options, host, phase (`lobby` / `playing` / `finished`) | the host (lobby, start, reset), and whoever ends the game |
|
|
51
|
+
| `gameState` entry (keyed by table id) | engine snapshot (`CroupierCore#toSnapshot`), turn start time | the player whose turn it is |
|
|
52
|
+
| `croupier-seat` (child of the table) | user id, name, join time, result note | the seated player |
|
|
53
|
+
|
|
54
|
+
- **Joining** adds a seat shape instead of editing a shared list, so players joining at the same moment never drop each other. Seat order = turn order.
|
|
55
|
+
- **Playing**: an action restores the engine from the stored snapshot (`CroupierCore.fromSnapshot`), dispatches it and writes the new snapshot back. The snapshot log is capped (`SNAPSHOT_LOG_LIMIT`).
|
|
56
|
+
- **Where the snapshot lives**: pass `gameState: createMapGameState(doc.getMap("croupier"))` (any `Y.Map`-like map synced with the board). Without it the snapshot (a few KB) sits in the table's `meta`; the board rewrites a shape as a whole on every change, so every move — and every frame of dragging the table — adds the whole snapshot to the board's history.
|
|
57
|
+
- **Hidden information** is hidden by the game's `playerView` only: renderers get `getPlayerView(localPlayer)`, but the snapshot itself is readable by every client. Fine for social games; a game that must be cheat-proof needs a server (see `@croupier/server`).
|
|
58
|
+
- **Absent players**: with a `presence` adapter (`{ isOnline(userId), subscribe(listener) }`; `createAwarenessPresence` builds one from a Yjs awareness), the host's client plays the turn of a player who has been offline for `awayGraceMs` (default 20 s) with the game's bot — only that player's turn, never one of a connected player. Once a turn exceeds the game's own `turnTimeoutMs`, the bot plays it for whoever is due. An offline host is replaced by the first connected player. `awayGraceMs: false` turns this off.
|
|
59
|
+
- **Copy / delete**: a pasted copy of a table becomes a fresh lobby (same host), its copied seats are dropped, and seats of a table that was deleted are removed (`repairTables`, run on every change). A seat whose table has not loaded yet is kept.
|
|
60
|
+
|
|
61
|
+
Undo: the plugin writes game moves with plain `store.updateShape`. If your app's undo history records every store write (e.g. a Yjs `UndoManager` over the shapes map), keep the types in `CROUPIER_SHAPE_TYPES` out of it so Ctrl+Z cannot roll a move back.
|
|
62
|
+
|
|
63
|
+
## API
|
|
64
|
+
|
|
65
|
+
Operations are plain functions over the store (`joinTable`, `startGame`, `dispatchAction`, `takeOverTurn`, …, all exported) and are bundled as a service bound to the local player:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { croupierService } from "@edv4h/usketch-plugin-croupier";
|
|
69
|
+
|
|
70
|
+
const croupier = croupierService.get(app.services)!;
|
|
71
|
+
const tableId = croupier.createTable("values-card", { x: 0, y: 0 }, { theme: "仕事で大事なこと" });
|
|
72
|
+
croupier.join(tableId);
|
|
73
|
+
croupier.start(tableId); // host only
|
|
74
|
+
croupier.dispatch(tableId, "drawFromDeck"); // current player only
|
|
75
|
+
croupier.dispatch(tableId, "discardCard", { cardId });
|
|
76
|
+
const view = croupier.getView(tableId); // TableView of the local player
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`TableView` carries the phase, seats, the masked `gameView`, `currentPlayers`, `isMyTurn`, the `allowedActions` of the current stage, the final `result` (only once finished) and the masked log.
|
|
80
|
+
|
|
81
|
+
The Debug HUD gets a "Croupier" group (place a table per game; join / leave / start / reset the selected table), and the `croupier-table-draw` tool places a table of `api.getPendingGame()` on click.
|
|
82
|
+
|
|
83
|
+
## Custom look
|
|
84
|
+
|
|
85
|
+
`renderTable({ shape, view, api })` and `renderSeat({ seat, view, api, isCurrent, isOnline })` replace the built-in visuals (a plain panel with lobby buttons and the raw view). Seats are laid out by the table's container `layout`, so the host app needs the container plugin (otherwise seats keep where they were placed and may overlap). The table is a container shape, so its wrapper has `pointer-events: none`: give in-shape controls `pointerEvents: "auto"` and spread `stopCanvas` on them so a click does not start a drag.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CroupierPresence } from "./croupier-service.js";
|
|
2
|
+
/** The Yjs Awareness subset used here (same shape as y-protocols' `Awareness`). */
|
|
3
|
+
export interface PresenceAwareness {
|
|
4
|
+
setLocalStateField(field: string, value: unknown): void;
|
|
5
|
+
getLocalState(): Record<string, unknown> | null;
|
|
6
|
+
getStates(): Map<number, Record<string, unknown>>;
|
|
7
|
+
on(event: "change", listener: () => void): void;
|
|
8
|
+
off(event: "change", listener: () => void): void;
|
|
9
|
+
}
|
|
10
|
+
/** Awareness field this adapter publishes the local user id under. */
|
|
11
|
+
export declare const CROUPIER_AWARENESS_FIELD = "croupier";
|
|
12
|
+
/**
|
|
13
|
+
* Build a {@link CroupierPresence} from a Yjs awareness: publishes the local user
|
|
14
|
+
* id, and reports a user online while any connected client publishes theirs.
|
|
15
|
+
* Listeners only fire when the set of online users changes (not on every cursor
|
|
16
|
+
* move), so table views are not rebuilt needlessly. Pass a getter for `userId` when
|
|
17
|
+
* the user is known only later (e.g. after sign-in): the published id follows it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createAwarenessPresence(awareness: PresenceAwareness, userId: string | (() => string)): CroupierPresence & {
|
|
20
|
+
dispose(): void;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=awareness-presence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"awareness-presence.d.ts","sourceRoot":"","sources":["../src/awareness-presence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,mFAAmF;AACnF,MAAM,WAAW,iBAAiB;IACjC,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACxD,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAChD,SAAS,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAClD,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAChD,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CACjD;AAED,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACtC,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,GAC7B,gBAAgB,GAAG;IAAE,OAAO,IAAI,IAAI,CAAA;CAAE,CAgDxC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Awareness field this adapter publishes the local user id under. */
|
|
2
|
+
export const CROUPIER_AWARENESS_FIELD = "croupier";
|
|
3
|
+
/**
|
|
4
|
+
* Build a {@link CroupierPresence} from a Yjs awareness: publishes the local user
|
|
5
|
+
* id, and reports a user online while any connected client publishes theirs.
|
|
6
|
+
* Listeners only fire when the set of online users changes (not on every cursor
|
|
7
|
+
* move), so table views are not rebuilt needlessly. Pass a getter for `userId` when
|
|
8
|
+
* the user is known only later (e.g. after sign-in): the published id follows it.
|
|
9
|
+
*/
|
|
10
|
+
export function createAwarenessPresence(awareness, userId) {
|
|
11
|
+
const currentId = () => (typeof userId === "function" ? userId() : userId);
|
|
12
|
+
const publish = () => {
|
|
13
|
+
const id = currentId();
|
|
14
|
+
const field = awareness.getLocalState()?.[CROUPIER_AWARENESS_FIELD];
|
|
15
|
+
if (field?.userId !== id)
|
|
16
|
+
awareness.setLocalStateField(CROUPIER_AWARENESS_FIELD, { userId: id });
|
|
17
|
+
};
|
|
18
|
+
publish();
|
|
19
|
+
const readOnline = () => {
|
|
20
|
+
const ids = new Set();
|
|
21
|
+
for (const state of awareness.getStates().values()) {
|
|
22
|
+
const field = state[CROUPIER_AWARENESS_FIELD];
|
|
23
|
+
if (typeof field?.userId === "string")
|
|
24
|
+
ids.add(field.userId);
|
|
25
|
+
}
|
|
26
|
+
return ids;
|
|
27
|
+
};
|
|
28
|
+
let online = readOnline();
|
|
29
|
+
const key = (ids) => [...ids].sort().join("\n");
|
|
30
|
+
let lastKey = key(online);
|
|
31
|
+
const listeners = new Set();
|
|
32
|
+
const onChange = () => {
|
|
33
|
+
publish();
|
|
34
|
+
const next = readOnline();
|
|
35
|
+
const nextKey = key(next);
|
|
36
|
+
online = next;
|
|
37
|
+
if (nextKey === lastKey)
|
|
38
|
+
return;
|
|
39
|
+
lastKey = nextKey;
|
|
40
|
+
for (const listener of listeners)
|
|
41
|
+
listener();
|
|
42
|
+
};
|
|
43
|
+
awareness.on("change", onChange);
|
|
44
|
+
return {
|
|
45
|
+
isOnline: (id) => online.has(id),
|
|
46
|
+
subscribe(listener) {
|
|
47
|
+
listeners.add(listener);
|
|
48
|
+
return () => listeners.delete(listener);
|
|
49
|
+
},
|
|
50
|
+
dispose() {
|
|
51
|
+
awareness.off("change", onChange);
|
|
52
|
+
listeners.clear();
|
|
53
|
+
awareness.setLocalStateField(CROUPIER_AWARENESS_FIELD, null);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=awareness-presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"awareness-presence.js","sourceRoot":"","sources":["../src/awareness-presence.ts"],"names":[],"mappings":"AAWA,sEAAsE;AACtE,MAAM,CAAC,MAAM,wBAAwB,GAAG,UAAU,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACtC,SAA4B,EAC5B,MAA+B;IAE/B,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,CAAC,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAG,GAAG,EAAE;QACpB,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,EAAE,EAAE,CAAC,wBAAwB,CAEtD,CAAC;QACb,IAAI,KAAK,EAAE,MAAM,KAAK,EAAE;YACvB,SAAS,CAAC,kBAAkB,CAAC,wBAAwB,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;IACzE,CAAC,CAAC;IACF,OAAO,EAAE,CAAC;IAEV,MAAM,UAAU,GAAG,GAAgB,EAAE;QACpC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;QAC9B,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC;YACpD,MAAM,KAAK,GAAG,KAAK,CAAC,wBAAwB,CAAqC,CAAC;YAClF,IAAI,OAAO,KAAK,EAAE,MAAM,KAAK,QAAQ;gBAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC,CAAC;IACF,IAAI,MAAM,GAAG,UAAU,EAAE,CAAC;IAC1B,MAAM,GAAG,GAAG,CAAC,GAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7D,IAAI,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;IAE1B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;IACxC,MAAM,QAAQ,GAAG,GAAG,EAAE;QACrB,OAAO,EAAE,CAAC;QACV,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,GAAG,IAAI,CAAC;QACd,IAAI,OAAO,KAAK,OAAO;YAAE,OAAO;QAChC,OAAO,GAAG,OAAO,CAAC;QAClB,KAAK,MAAM,QAAQ,IAAI,SAAS;YAAE,QAAQ,EAAE,CAAC;IAC9C,CAAC,CAAC;IACF,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAEjC,OAAO;QACN,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,SAAS,CAAC,QAAQ;YACjB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC;QACD,OAAO;YACN,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAClC,SAAS,CAAC,KAAK,EAAE,CAAC;YAClB,SAAS,CAAC,kBAAkB,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC;QAC9D,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type BoardStore } from "@edv4h/usketch-shared";
|
|
2
|
+
import { type GameStateStore } from "./game-state.js";
|
|
3
|
+
import { type GameRegistry } from "./table-ops.js";
|
|
4
|
+
import type { CroupierGameDefinition, CroupierPlayer, GameOptions, OpResult, TableShape, TableView } from "./types.js";
|
|
5
|
+
export interface CroupierApi {
|
|
6
|
+
/** The local player. */
|
|
7
|
+
me(): CroupierPlayer;
|
|
8
|
+
/** Games available on this board, in registration order. */
|
|
9
|
+
games(): CroupierGameDefinition[];
|
|
10
|
+
/**
|
|
11
|
+
* Create a table centered on a world point. `addShape` is the host's way to
|
|
12
|
+
* add it (default: `store.addShape`; pass an undoable command runner if wanted).
|
|
13
|
+
* Returns the new table id, or `null` for an unknown game.
|
|
14
|
+
*/
|
|
15
|
+
createTable(game: string, center: {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
}, options?: GameOptions): string | null;
|
|
19
|
+
/** Game used by the `croupier-table-draw` tool. Defaults to the first game. */
|
|
20
|
+
getPendingGame(): string | undefined;
|
|
21
|
+
setPendingGame(game: string): void;
|
|
22
|
+
listTables(): TableShape[];
|
|
23
|
+
/** The table as the local player sees it. Stable while nothing relevant changed. */
|
|
24
|
+
getView(tableId: string): TableView | undefined;
|
|
25
|
+
/** Fires on any change that may affect a table view (shapes or presence). */
|
|
26
|
+
subscribe(listener: () => void): () => void;
|
|
27
|
+
join(tableId: string): OpResult;
|
|
28
|
+
leave(tableId: string): OpResult;
|
|
29
|
+
kick(tableId: string, userId: string): OpResult;
|
|
30
|
+
claimHost(tableId: string): OpResult;
|
|
31
|
+
setOptions(tableId: string, options: GameOptions): OpResult;
|
|
32
|
+
start(tableId: string): OpResult;
|
|
33
|
+
dispatch(tableId: string, action: string, payload?: unknown): OpResult;
|
|
34
|
+
/** Play the current turn with the game's bot (host only), for `players` or everyone due. */
|
|
35
|
+
takeOver(tableId: string, players?: readonly string[]): Promise<OpResult>;
|
|
36
|
+
endGame(tableId: string): OpResult;
|
|
37
|
+
reset(tableId: string): OpResult;
|
|
38
|
+
setNote(tableId: string, note: string): OpResult;
|
|
39
|
+
/** Whether a player is connected (from the host's presence adapter; `true` without one). */
|
|
40
|
+
isOnline(userId: string): boolean;
|
|
41
|
+
}
|
|
42
|
+
/** Typed service handle. Provided in `setup`; get it with `croupierService.get(ctx.services)`. */
|
|
43
|
+
export declare const croupierService: import("@edv4h/usketch-shared").ServiceHandle<CroupierApi>;
|
|
44
|
+
export interface CroupierPresence {
|
|
45
|
+
isOnline(userId: string): boolean;
|
|
46
|
+
subscribe(listener: () => void): () => void;
|
|
47
|
+
}
|
|
48
|
+
export interface CreateCroupierApiParams {
|
|
49
|
+
store: BoardStore;
|
|
50
|
+
registry: GameRegistry;
|
|
51
|
+
/** The local player, or a getter when it is known only later (e.g. after sign-in). */
|
|
52
|
+
player: CroupierPlayer | (() => CroupierPlayer);
|
|
53
|
+
presence?: CroupierPresence;
|
|
54
|
+
/** Where running games are kept (bound to `store` by the caller); its changes refresh views. */
|
|
55
|
+
gameState?: GameStateStore;
|
|
56
|
+
now?: () => number;
|
|
57
|
+
addShape?: (shape: TableShape) => void;
|
|
58
|
+
tableSize?: {
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** The API plus the teardown of its store / presence subscriptions (called by the plugin). */
|
|
64
|
+
export type CroupierApiHandle = CroupierApi & {
|
|
65
|
+
dispose(): void;
|
|
66
|
+
};
|
|
67
|
+
export declare function createCroupierApi(params: CreateCroupierApiParams): CroupierApiHandle;
|
|
68
|
+
//# sourceMappingURL=croupier-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"croupier-service.d.ts","sourceRoot":"","sources":["../src/croupier-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Host-facing API of the croupier plugin, published on `ctx.services` (see
|
|
2
|
+
// docs/plugin-system-design.md). Every method is a thin binding of a table-ops
|
|
3
|
+
// function to the board store and the local player.
|
|
4
|
+
import { defineService, generateId } from "@edv4h/usketch-shared";
|
|
5
|
+
import { boundGameState } from "./game-state.js";
|
|
6
|
+
import { claimHost, DEFAULT_TABLE_SIZE, dispatchAction, endGame, getTable, getTableView, isSeat, joinTable, kickFromTable, leaveTable, listTables, makeTableShape, resetTable, setSeatNote, setTableOptions, startGame, takeOverTurn, } from "./table-ops.js";
|
|
7
|
+
/** Typed service handle. Provided in `setup`; get it with `croupierService.get(ctx.services)`. */
|
|
8
|
+
export const croupierService = defineService("usketch-plugin-croupier");
|
|
9
|
+
export function createCroupierApi(params) {
|
|
10
|
+
const { store, registry, presence } = params;
|
|
11
|
+
const me = () => typeof params.player === "function" ? params.player() : params.player;
|
|
12
|
+
const now = params.now ?? Date.now;
|
|
13
|
+
const addShape = params.addShape ?? ((shape) => store.addShape(shape));
|
|
14
|
+
const tableSize = params.tableSize ?? DEFAULT_TABLE_SIZE;
|
|
15
|
+
let pendingGame;
|
|
16
|
+
// View cache: rebuilt only when the table, one of its seats or presence changed,
|
|
17
|
+
// so `useSyncExternalStore` gets a stable snapshot.
|
|
18
|
+
let presenceVersion = 0;
|
|
19
|
+
const cache = new Map();
|
|
20
|
+
// Seats by table, built once per shapes snapshot: getView runs on every render,
|
|
21
|
+
// so it must not scan the whole board each time.
|
|
22
|
+
let seatIndexOf = null;
|
|
23
|
+
let seatIndex = new Map();
|
|
24
|
+
const seatsOf = (tableId) => {
|
|
25
|
+
const shapes = store.getShapes();
|
|
26
|
+
if (shapes !== seatIndexOf) {
|
|
27
|
+
seatIndexOf = shapes;
|
|
28
|
+
seatIndex = new Map();
|
|
29
|
+
for (const shape of shapes.values()) {
|
|
30
|
+
if (!isSeat(shape))
|
|
31
|
+
continue;
|
|
32
|
+
const list = seatIndex.get(shape.parentId);
|
|
33
|
+
if (list)
|
|
34
|
+
list.push(shape);
|
|
35
|
+
else
|
|
36
|
+
seatIndex.set(shape.parentId, [shape]);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return seatIndex.get(tableId) ?? [];
|
|
40
|
+
};
|
|
41
|
+
const keyFor = (tableId) => [
|
|
42
|
+
presenceVersion,
|
|
43
|
+
me().userId,
|
|
44
|
+
store.getShapes().get(tableId),
|
|
45
|
+
boundGameState(store)?.get(tableId),
|
|
46
|
+
...seatsOf(tableId),
|
|
47
|
+
];
|
|
48
|
+
const sameKey = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
49
|
+
const listeners = new Set();
|
|
50
|
+
const notify = () => {
|
|
51
|
+
for (const listener of listeners)
|
|
52
|
+
listener();
|
|
53
|
+
};
|
|
54
|
+
let lastShapes = store.getShapes();
|
|
55
|
+
const offStore = store.subscribe(() => {
|
|
56
|
+
const shapes = store.getShapes();
|
|
57
|
+
if (shapes === lastShapes)
|
|
58
|
+
return; // pan / zoom / selection only
|
|
59
|
+
lastShapes = shapes;
|
|
60
|
+
notify();
|
|
61
|
+
});
|
|
62
|
+
// Games kept outside the shapes (see `bindGameState`).
|
|
63
|
+
const offGameState = params.gameState?.subscribe(notify);
|
|
64
|
+
const offPresence = presence?.subscribe(() => {
|
|
65
|
+
presenceVersion++;
|
|
66
|
+
notify();
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
dispose() {
|
|
70
|
+
offStore();
|
|
71
|
+
offGameState?.();
|
|
72
|
+
offPresence?.();
|
|
73
|
+
listeners.clear();
|
|
74
|
+
cache.clear();
|
|
75
|
+
},
|
|
76
|
+
me,
|
|
77
|
+
games: () => [...registry.values()],
|
|
78
|
+
createTable(game, center, options) {
|
|
79
|
+
if (!registry.has(game))
|
|
80
|
+
return null;
|
|
81
|
+
const id = generateId();
|
|
82
|
+
addShape(makeTableShape(registry, {
|
|
83
|
+
id,
|
|
84
|
+
game,
|
|
85
|
+
x: center.x - tableSize.width / 2,
|
|
86
|
+
y: center.y - tableSize.height / 2,
|
|
87
|
+
host: me(),
|
|
88
|
+
options,
|
|
89
|
+
size: tableSize,
|
|
90
|
+
}));
|
|
91
|
+
return id;
|
|
92
|
+
},
|
|
93
|
+
getPendingGame: () => pendingGame ?? registry.keys().next().value,
|
|
94
|
+
setPendingGame(game) {
|
|
95
|
+
pendingGame = game;
|
|
96
|
+
},
|
|
97
|
+
listTables: () => listTables(store),
|
|
98
|
+
getView(tableId) {
|
|
99
|
+
const key = keyFor(tableId);
|
|
100
|
+
const hit = cache.get(tableId);
|
|
101
|
+
if (hit && sameKey(hit.key, key))
|
|
102
|
+
return hit.view;
|
|
103
|
+
const view = getTableView(store, registry, tableId, me().userId);
|
|
104
|
+
// Drop entries of deleted tables so the cache does not grow with the board's history.
|
|
105
|
+
if (view)
|
|
106
|
+
cache.set(tableId, { key, view });
|
|
107
|
+
else
|
|
108
|
+
cache.delete(tableId);
|
|
109
|
+
return view;
|
|
110
|
+
},
|
|
111
|
+
subscribe(listener) {
|
|
112
|
+
listeners.add(listener);
|
|
113
|
+
return () => listeners.delete(listener);
|
|
114
|
+
},
|
|
115
|
+
join: (tableId) => joinTable(store, registry, tableId, me(), now()),
|
|
116
|
+
leave: (tableId) => leaveTable(store, tableId, me().userId),
|
|
117
|
+
kick: (tableId, userId) => kickFromTable(store, tableId, me().userId, userId),
|
|
118
|
+
claimHost(tableId) {
|
|
119
|
+
const table = getTable(store, tableId);
|
|
120
|
+
const hostAway = table ? !(presence?.isOnline(table.meta.hostUserId) ?? true) : false;
|
|
121
|
+
return claimHost(store, tableId, me().userId, hostAway);
|
|
122
|
+
},
|
|
123
|
+
setOptions: (tableId, options) => setTableOptions(store, tableId, me().userId, options),
|
|
124
|
+
start: (tableId) => startGame(store, registry, tableId, me().userId, now()),
|
|
125
|
+
dispatch: (tableId, action, payload) => dispatchAction(store, registry, tableId, me().userId, action, payload, now()),
|
|
126
|
+
takeOver: (tableId, players) => takeOverTurn(store, registry, tableId, me().userId, now, players),
|
|
127
|
+
endGame: (tableId) => endGame(store, registry, tableId, me().userId, now()),
|
|
128
|
+
reset: (tableId) => resetTable(store, tableId, me().userId),
|
|
129
|
+
setNote: (tableId, note) => setSeatNote(store, tableId, me().userId, note),
|
|
130
|
+
isOnline: (userId) => presence?.isOnline(userId) ?? true,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=croupier-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"croupier-service.js","sourceRoot":"","sources":["../src/croupier-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { CroupierSnapshot } from "@edv4h/croupier-core";
|
|
2
|
+
import type { BoardStore } from "@edv4h/usketch-shared";
|
|
3
|
+
import type { TableShape } from "./types.js";
|
|
4
|
+
/** A table's running game: the engine snapshot and when the current turn started. */
|
|
5
|
+
export interface TableGameState {
|
|
6
|
+
snapshot: CroupierSnapshot;
|
|
7
|
+
/** When the current turn started (host clock), for absent-player takeover. */
|
|
8
|
+
turnStartedAt: number | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Where running games are kept, keyed by table id. Without one, the snapshot lives
|
|
12
|
+
* in the table shape's meta, which the board rewrites as a whole on every change
|
|
13
|
+
* (including each frame of a drag), so a long game bloats the board's history.
|
|
14
|
+
* Keep it in a document map beside the shapes instead ({@link createMapGameState}).
|
|
15
|
+
*/
|
|
16
|
+
export interface GameStateStore {
|
|
17
|
+
get(tableId: string): TableGameState | undefined;
|
|
18
|
+
/** `null` removes the entry. */
|
|
19
|
+
set(tableId: string, state: TableGameState | null): void;
|
|
20
|
+
subscribe(listener: () => void): () => void;
|
|
21
|
+
}
|
|
22
|
+
/** The `Y.Map` subset {@link createMapGameState} uses. */
|
|
23
|
+
export interface GameStateMap {
|
|
24
|
+
get(key: string): unknown;
|
|
25
|
+
set(key: string, value: unknown): unknown;
|
|
26
|
+
delete(key: string): void;
|
|
27
|
+
observe(listener: () => void): void;
|
|
28
|
+
unobserve(listener: () => void): void;
|
|
29
|
+
}
|
|
30
|
+
/** A {@link GameStateStore} over a synced map, e.g. `doc.getMap("croupier")`. */
|
|
31
|
+
export declare function createMapGameState(map: GameStateMap): GameStateStore;
|
|
32
|
+
/**
|
|
33
|
+
* Keep the running games of `store`'s tables in `gameState` instead of the table
|
|
34
|
+
* meta. The plugin does this for its `gameState` option; call it yourself when you
|
|
35
|
+
* use the table operations directly. Returns the unbind function.
|
|
36
|
+
*/
|
|
37
|
+
export declare function bindGameState(store: BoardStore, gameState: GameStateStore): () => void;
|
|
38
|
+
/** The store `store` is bound to, if any. */
|
|
39
|
+
export declare function boundGameState(store: BoardStore): GameStateStore | undefined;
|
|
40
|
+
/** The running game of a table, wherever it is kept. */
|
|
41
|
+
export declare function readGameState(store: BoardStore, table: TableShape): TableGameState | null;
|
|
42
|
+
/** Store (or with `null`, clear) a table's game. Returns the meta fields to write on the table. */
|
|
43
|
+
export declare function writeGameState(store: BoardStore, table: TableShape, state: TableGameState | null): Pick<TableShape["meta"], "snapshot" | "turnStartedAt">;
|
|
44
|
+
//# sourceMappingURL=game-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-state.d.ts","sourceRoot":"","sources":["../src/game-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,qFAAqF;AACrF,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,8EAA8E;IAC9E,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC9B,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IACjD,gCAAgC;IAChC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC;IACzD,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC5C;AAED,0DAA0D;AAC1D,MAAM,WAAW,YAAY;IAC5B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACpC,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CACtC;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,GAAG,cAAc,CAgBpE;AAID;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,GAAG,MAAM,IAAI,CAKtF;AAED,6CAA6C;AAC7C,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,GAAG,SAAS,CAE5E;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAKzF;AAED,mGAAmG;AACnG,wBAAgB,cAAc,CAC7B,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,cAAc,GAAG,IAAI,GAC1B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,eAAe,CAAC,CAOxD"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** A {@link GameStateStore} over a synced map, e.g. `doc.getMap("croupier")`. */
|
|
2
|
+
export function createMapGameState(map) {
|
|
3
|
+
return {
|
|
4
|
+
get(tableId) {
|
|
5
|
+
const value = map.get(tableId);
|
|
6
|
+
return value && typeof value === "object" && value.snapshot ? value : undefined;
|
|
7
|
+
},
|
|
8
|
+
set(tableId, state) {
|
|
9
|
+
if (state)
|
|
10
|
+
map.set(tableId, state);
|
|
11
|
+
else if (map.get(tableId) !== undefined)
|
|
12
|
+
map.delete(tableId);
|
|
13
|
+
},
|
|
14
|
+
subscribe(listener) {
|
|
15
|
+
const observer = () => listener();
|
|
16
|
+
map.observe(observer);
|
|
17
|
+
return () => map.unobserve(observer);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const bound = new WeakMap();
|
|
22
|
+
/**
|
|
23
|
+
* Keep the running games of `store`'s tables in `gameState` instead of the table
|
|
24
|
+
* meta. The plugin does this for its `gameState` option; call it yourself when you
|
|
25
|
+
* use the table operations directly. Returns the unbind function.
|
|
26
|
+
*/
|
|
27
|
+
export function bindGameState(store, gameState) {
|
|
28
|
+
bound.set(store, gameState);
|
|
29
|
+
return () => {
|
|
30
|
+
if (bound.get(store) === gameState)
|
|
31
|
+
bound.delete(store);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** The store `store` is bound to, if any. */
|
|
35
|
+
export function boundGameState(store) {
|
|
36
|
+
return bound.get(store);
|
|
37
|
+
}
|
|
38
|
+
/** The running game of a table, wherever it is kept. */
|
|
39
|
+
export function readGameState(store, table) {
|
|
40
|
+
const gameState = bound.get(store);
|
|
41
|
+
if (gameState)
|
|
42
|
+
return gameState.get(table.id) ?? null;
|
|
43
|
+
const { snapshot, turnStartedAt } = table.meta;
|
|
44
|
+
return snapshot ? { snapshot, turnStartedAt } : null;
|
|
45
|
+
}
|
|
46
|
+
/** Store (or with `null`, clear) a table's game. Returns the meta fields to write on the table. */
|
|
47
|
+
export function writeGameState(store, table, state) {
|
|
48
|
+
const gameState = bound.get(store);
|
|
49
|
+
if (gameState) {
|
|
50
|
+
gameState.set(table.id, state);
|
|
51
|
+
return { snapshot: null, turnStartedAt: null };
|
|
52
|
+
}
|
|
53
|
+
return { snapshot: state?.snapshot ?? null, turnStartedAt: state?.turnStartedAt ?? null };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=game-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-state.js","sourceRoot":"","sources":["../src/game-state.ts"],"names":[],"mappings":"AAiCA,iFAAiF;AACjF,MAAM,UAAU,kBAAkB,CAAC,GAAiB;IACnD,OAAO;QACN,GAAG,CAAC,OAAO;YACV,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAA+B,CAAC;YAC7D,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QACjF,CAAC;QACD,GAAG,CAAC,OAAO,EAAE,KAAK;YACjB,IAAI,KAAK;gBAAE,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;iBAC9B,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS;gBAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9D,CAAC;QACD,SAAS,CAAC,QAAQ;YACjB,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;YAClC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACtB,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC;KACD,CAAC;AACH,CAAC;AAED,MAAM,KAAK,GAAG,IAAI,OAAO,EAA8B,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAiB,EAAE,SAAyB;IACzE,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC5B,OAAO,GAAG,EAAE;QACX,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,SAAS;YAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC;AACH,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,cAAc,CAAC,KAAiB;IAC/C,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,KAAiB,EAAE,KAAiB;IACjE,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;IACtD,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC;IAC/C,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACtD,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,cAAc,CAC7B,KAAiB,EACjB,KAAiB,EACjB,KAA4B;IAE5B,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,SAAS,EAAE,CAAC;QACf,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC/B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;IAChD,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,IAAI,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,IAAI,IAAI,EAAE,CAAC;AAC3F,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { CROUPIER_AWARENESS_FIELD, createAwarenessPresence, type PresenceAwareness, } from "./awareness-presence.js";
|
|
2
|
+
export { type CreateCroupierApiParams, type CroupierApi, type CroupierApiHandle, type CroupierPresence, createCroupierApi, croupierService, } from "./croupier-service.js";
|
|
3
|
+
export { bindGameState, boundGameState, createMapGameState, type GameStateMap, type GameStateStore, readGameState, type TableGameState, } from "./game-state.js";
|
|
4
|
+
export { CROUPIER_TABLE_TOOL_ID, type CroupierPluginOptions, createCroupierPlugin, } from "./plugin.js";
|
|
5
|
+
export { type CreateTableParams, claimHost, DEFAULT_TABLE_SIZE, dispatchAction, endGame, type GameRegistry, getTable, getTableView, isSeat, isTable, joinTable, kickFromTable, layoutSeats, leaveTable, listSeats, listTables, makeTableShape, repairTables, resetTable, SEAT_SIZE, SNAPSHOT_LOG_LIMIT, seatIdFor, setSeatNote, setTableOptions, startGame, takeOverTurn, } from "./table-ops.js";
|
|
6
|
+
export { defaultRenderSeat, defaultRenderTable, type SeatRenderContext, type SeatRenderer, stopCanvas, TABLE_MIN_SIZE, type TableRenderContext, type TableRenderer, } from "./table-shape.js";
|
|
7
|
+
export { type ActionChoice, CROUPIER_SEAT_TYPE, CROUPIER_SHAPE_TYPES, CROUPIER_TABLE_TYPE, type CroupierGameDefinition, type CroupierPlayer, defineCroupierGame, type GameOptions, type OpResult, type SeatMeta, type SeatShape, type TableMeta, type TablePhase, type TableShape, type TableView, } from "./types.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,wBAAwB,EACxB,uBAAuB,EACvB,KAAK,iBAAiB,GACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,iBAAiB,EACjB,eAAe,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,aAAa,EACb,KAAK,cAAc,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,sBAAsB,EACtB,KAAK,qBAAqB,EAC1B,oBAAoB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,KAAK,iBAAiB,EACtB,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,OAAO,EACP,KAAK,YAAY,EACjB,QAAQ,EACR,YAAY,EACZ,MAAM,EACN,OAAO,EACP,SAAS,EACT,aAAa,EACb,WAAW,EACX,UAAU,EACV,SAAS,EACT,UAAU,EACV,cAAc,EACd,YAAY,EACZ,UAAU,EACV,SAAS,EACT,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,GACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACN,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,UAAU,EACV,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,aAAa,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACN,KAAK,YAAY,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,kBAAkB,EAClB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,SAAS,GACd,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { CROUPIER_AWARENESS_FIELD, createAwarenessPresence, } from "./awareness-presence.js";
|
|
2
|
+
export { createCroupierApi, croupierService, } from "./croupier-service.js";
|
|
3
|
+
export { bindGameState, boundGameState, createMapGameState, readGameState, } from "./game-state.js";
|
|
4
|
+
export { CROUPIER_TABLE_TOOL_ID, createCroupierPlugin, } from "./plugin.js";
|
|
5
|
+
export { claimHost, DEFAULT_TABLE_SIZE, dispatchAction, endGame, getTable, getTableView, isSeat, isTable, joinTable, kickFromTable, layoutSeats, leaveTable, listSeats, listTables, makeTableShape, repairTables, resetTable, SEAT_SIZE, SNAPSHOT_LOG_LIMIT, seatIdFor, setSeatNote, setTableOptions, startGame, takeOverTurn, } from "./table-ops.js";
|
|
6
|
+
export { defaultRenderSeat, defaultRenderTable, stopCanvas, TABLE_MIN_SIZE, } from "./table-shape.js";
|
|
7
|
+
export { CROUPIER_SEAT_TYPE, CROUPIER_SHAPE_TYPES, CROUPIER_TABLE_TYPE, defineCroupierGame, } from "./types.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,wBAAwB,EACxB,uBAAuB,GAEvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAKN,iBAAiB,EACjB,eAAe,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,aAAa,EACb,cAAc,EACd,kBAAkB,EAGlB,aAAa,GAEb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,sBAAsB,EAEtB,oBAAoB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAEN,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,OAAO,EAEP,QAAQ,EACR,YAAY,EACZ,MAAM,EACN,OAAO,EACP,SAAS,EACT,aAAa,EACb,WAAW,EACX,UAAU,EACV,SAAS,EACT,UAAU,EACV,cAAc,EACd,YAAY,EACZ,UAAU,EACV,SAAS,EACT,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,GACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACN,iBAAiB,EACjB,kBAAkB,EAGlB,UAAU,EACV,cAAc,GAGd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEN,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EAGnB,kBAAkB,GASlB,MAAM,YAAY,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { UsketchPlugin } from "@edv4h/usketch-shared";
|
|
2
|
+
import type { ReactElement } from "react";
|
|
3
|
+
import { type CroupierPresence } from "./croupier-service.js";
|
|
4
|
+
import { type GameStateStore } from "./game-state.js";
|
|
5
|
+
import { type SeatRenderer, type TableRenderer } from "./table-shape.js";
|
|
6
|
+
import { type CroupierGameDefinition, type CroupierPlayer } from "./types.js";
|
|
7
|
+
export declare const CROUPIER_TABLE_TOOL_ID = "croupier-table-draw";
|
|
8
|
+
export interface CroupierPluginOptions {
|
|
9
|
+
/**
|
|
10
|
+
* The local player. Pass a getter when the user is known only after the plugin is
|
|
11
|
+
* created (e.g. a session that loads after the board), so moves use the real id.
|
|
12
|
+
*/
|
|
13
|
+
player: CroupierPlayer | (() => CroupierPlayer);
|
|
14
|
+
/** Games that can be played on this board. The first one is the default. */
|
|
15
|
+
games: CroupierGameDefinition[];
|
|
16
|
+
/**
|
|
17
|
+
* Who is connected, e.g. from the board's awareness. Enables playing for absent
|
|
18
|
+
* players and handing the host role over. Without it everyone counts as online.
|
|
19
|
+
*/
|
|
20
|
+
presence?: CroupierPresence;
|
|
21
|
+
/**
|
|
22
|
+
* Where running games are kept. Default: the table shape's meta, which the board
|
|
23
|
+
* rewrites as a whole on every change (each drag frame included), so long games
|
|
24
|
+
* bloat the board's history. Pass `createMapGameState(doc.getMap("croupier"))`
|
|
25
|
+
* to keep them in a document map beside the shapes.
|
|
26
|
+
*/
|
|
27
|
+
gameState?: GameStateStore;
|
|
28
|
+
/** Clock for turn timing. Use a shared server clock when the board has one. Default `Date.now`. */
|
|
29
|
+
now?: () => number;
|
|
30
|
+
/** Host look for the table. Defaults to {@link defaultRenderTable}. */
|
|
31
|
+
renderTable?: TableRenderer;
|
|
32
|
+
/** Host look for a seat. Defaults to {@link defaultRenderSeat}. */
|
|
33
|
+
renderSeat?: SeatRenderer;
|
|
34
|
+
/** Size of a new table. Default 720×480. */
|
|
35
|
+
tableSize?: {
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Absent-player handling, run by the host's client: after `awayGraceMs` offline
|
|
41
|
+
* (or the game's own turn timeout) the host plays that turn with the game's bot,
|
|
42
|
+
* and an offline host is replaced by the first connected player. Default 20 s.
|
|
43
|
+
* Set `false` to disable.
|
|
44
|
+
*/
|
|
45
|
+
awayGraceMs?: number | false;
|
|
46
|
+
/** Register the table placement tool. Default `true`. */
|
|
47
|
+
tool?: boolean;
|
|
48
|
+
/** Icon of the placement tool. */
|
|
49
|
+
toolIcon?: () => ReactElement;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Plays croupier (`@edv4h/croupier-core`) games on the board. A `croupier-table`
|
|
53
|
+
* shape holds the game and its engine snapshot; each joined player gets a
|
|
54
|
+
* `croupier-seat` child shape. The plugin knows no rules — games come from
|
|
55
|
+
* {@link CroupierPluginOptions.games}. Operations are on the
|
|
56
|
+
* {@link croupierService} (and the HUD actions call the same functions).
|
|
57
|
+
*/
|
|
58
|
+
export declare function createCroupierPlugin(options: CroupierPluginOptions): UsketchPlugin;
|
|
59
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA4B,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAErF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAEN,KAAK,gBAAgB,EAGrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAiB,KAAK,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AAQpF,OAAO,EACN,KAAK,YAAY,EAEjB,KAAK,aAAa,EAElB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAGN,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EAEnB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,sBAAsB,wBAAwB,CAAC;AAE5D,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,MAAM,EAAE,cAAc,GAAG,CAAC,MAAM,cAAc,CAAC,CAAC;IAChD,4EAA4E;IAC5E,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,mGAAmG;IACnG,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,uEAAuE;IACvE,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,mEAAmE;IACnE,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,4CAA4C;IAC5C,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B,yDAAyD;IACzD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,YAAY,CAAC;CAC9B;AAsBD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,aAAa,CAwOlF"}
|