castle-web-sdk 0.4.45 → 0.4.47

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.
@@ -1,8 +1,15 @@
1
- import type { MultiplayerSessionMode, PlatformHandle, PlayerIdentity } from "./platformHandle";
1
+ import type { MultiplayerSessionMode, PlatformHandle, PlatformPlaceTool, PlayerIdentity } from "./platformHandle";
2
2
  import type { Json } from "../types";
3
+ import type { MultiplayerWorldCharacterCommand, MultiplayerWorldCharacterCommandResult, MultiplayerWorldCharacterCommandResultValue } from "../multiplayer";
3
4
  import { type ServerStorage } from "./storage";
4
- export type { MultiplayerSessionMode, PlatformHandle, PlatformSessionConfig, PlatformWorldCheckpoint, PlatformWorldSnapshot, PlayerIdentity, } from "./platformHandle";
5
- export type { CharacterKnowledgeSource, CompleteCharacterBeatInput, DeliverCharacterKnowledgeInput, RecordCharacterEventInput, SetCharacterGameStateInput, ShareCharacterClaimInput, ServerStorage, ServerStorageBoard, ServerStorageBoardScore, ServerStorageListEntry, ServerStorageListOptions, ServerStorageListPage, ServerPlaythrough, ServerPlaythroughCharacters, ServerCharacterHistoryOptions, ServerStorageScope, } from "./storage";
5
+ import { type MultiplayerHubArrival, type MultiplayerServerHub } from "./hub";
6
+ export type { MultiplayerSessionMode, PlatformHandle, PlatformSessionConfig, PlatformWorldCheckpoint, PlatformWorldSnapshot, PlatformWorldTimeConfig, PlayerIdentity, } from "./platformHandle";
7
+ export { createSaveCoordinator } from "../saveCoordinator";
8
+ export type { CoordinatedChange, CoordinatedSave, SaveCoordinator, SaveStatus, } from "../saveCoordinator";
9
+ export { SaveError } from "../save";
10
+ export type { SaveApi, SaveBucketApi, SaveBucketRead, SaveChange, SaveCheck, SaveClaimed, SaveClaimInput, SaveErrorEnvelope, SaveInitial, SaveInitializeInput, SaveOpened, SaveOperation, SaveReadTarget, SaveReceipt, SaveRevision, SaveRows, SaveScope, SaveSelection, SaveSnapshot, SaveTarget, SaveTransactionInput, SaveWriter, } from "../save";
11
+ export type { MultiplayerHubArrival, MultiplayerHubBoard, MultiplayerHubOutcome, MultiplayerHubPadUpdateInput, MultiplayerHubPadUpdateResult, MultiplayerHubPlayerResults, MultiplayerHubRecord, MultiplayerHubReportReceipt, MultiplayerHubResultInput, MultiplayerHubResultRow, MultiplayerHubResultShape, MultiplayerHubResultsRead, MultiplayerHubResultsReadInput, MultiplayerHubWearable, MultiplayerHubWearReceipt, MultiplayerHubEarnedClass, MultiplayerHubEarnedItem, MultiplayerHubHomeClass, MultiplayerHubHomeItem, MultiplayerHubUnlockReceipt, MultiplayerHubUnlocksRead, MultiplayerHubWearEarnedReceipt, MultiplayerServerHub, } from "./hub";
12
+ export type { CharacterActionNotice, CharacterGameStateOperation, CharacterGameStateResult, CharacterKnowledgeDeliveryOperation, CharacterKnowledgeDeliveryResult, CharacterKnowledgeSource, CompleteCharacterBeatInput, DeliverCharacterKnowledgeInput, RecordCharacterEventInput, SetCharacterGameStateInput, ShareCharacterClaimInput, ServerStorage, ServerStorageBoard, ServerStorageBoardScore, ServerStorageListEntry, ServerStorageListOptions, ServerStorageListPage, ServerPlaythrough, ServerPlaythroughCharacters, ServerCharacterHistoryOptions, ServerStorageScope, ServerTownStorage, } from "./storage";
6
13
  export interface MultiplayerServerPlayer extends PlayerIdentity {
7
14
  playerId: string;
8
15
  readonly participantId: string;
@@ -15,6 +22,12 @@ export interface MultiplayerServerSession {
15
22
  readonly sessionId: string;
16
23
  readonly deckId: string;
17
24
  readonly mode: MultiplayerSessionMode;
25
+ /**
26
+ * The place the host started this session for (a hub door's destination, or
27
+ * the hub place ghost pinned the session to), or null when the host stamped
28
+ * none. Never set from a client.
29
+ */
30
+ readonly place: string | null;
18
31
  /**
19
32
  * Deck storage that outlives the session. Nothing else here does: a session ends
20
33
  * when its last player leaves and its memory goes with it.
@@ -22,6 +35,27 @@ export interface MultiplayerServerSession {
22
35
  readonly storage: ServerStorage;
23
36
  /** A durable checkpoint for an opted-in persistent destination. */
24
37
  readonly world: MultiplayerServerWorld | null;
38
+ /**
39
+ * The shared characters this session can address: `session.world.characters`
40
+ * in a shared-character world (the same object), or a hub place's characters
41
+ * on one of its layers, where `session.world` stays null. Null everywhere else.
42
+ */
43
+ readonly characters: MultiplayerServerWorldCharacters | null;
44
+ /**
45
+ * The hub place this session runs (castle.json `hub`, as ghost pinned it):
46
+ * results, pads and arrivals. Null for a deck without a hub block and on hosts
47
+ * without hub support.
48
+ */
49
+ readonly hub: MultiplayerServerHub | null;
50
+ /**
51
+ * The variant a connected player has in the A/B experiment `name`, or null
52
+ * when they are in no such experiment (or have left). Castle's server
53
+ * resolved it and the host delivered it with the join, so it is the same arm
54
+ * the player's page reads and nothing a client sent.
55
+ */
56
+ experiment(player: MultiplayerServerPlayer | string, name: string): string | null;
57
+ /** Every experiment a connected player is in, `{ name: variant }`. */
58
+ experiments(player: MultiplayerServerPlayer | string): Record<string, string>;
25
59
  /** Sends bytes as a binary message and all other payloads as JSON. */
26
60
  send(playerId: string, data: unknown): void;
27
61
  /** Broadcasts bytes as a binary message and all other payloads as JSON. */
@@ -53,10 +87,26 @@ export interface MultiplayerServerWorld {
53
87
  read(): Promise<MultiplayerWorldSnapshot>;
54
88
  /** Commits exactly against expectedRevision; conflicts never partially write. */
55
89
  commit(input: MultiplayerWorldCommitInput): Promise<MultiplayerWorldCommitReceipt>;
90
+ catchUp(options: MultiplayerWorldCatchUpOptions): Promise<MultiplayerWorldCatchUpResult>;
56
91
  readonly journal: MultiplayerServerWorldJournal;
57
92
  readonly participants: MultiplayerServerWorldParticipants;
93
+ readonly notifications: MultiplayerServerWorldNotifications;
58
94
  readonly characters: MultiplayerServerWorldCharacters;
59
95
  }
96
+ export interface MultiplayerWorldCatchUpOptions {
97
+ key?: string;
98
+ maxElapsedMs?: number;
99
+ apply(input: {
100
+ elapsedMs: number;
101
+ now: number;
102
+ checkpoint: MultiplayerWorldCheckpoint | null;
103
+ }): MultiplayerWorldCheckpoint | Promise<MultiplayerWorldCheckpoint>;
104
+ }
105
+ export interface MultiplayerWorldCatchUpResult {
106
+ applied: boolean;
107
+ elapsedMs: number;
108
+ revision: string;
109
+ }
60
110
  export interface MultiplayerWorldJournalAppendInput {
61
111
  operationId: string;
62
112
  entries: readonly MultiplayerWorldJournalAppendEntry[];
@@ -112,6 +162,31 @@ export interface MultiplayerWorldParticipantsReadResult {
112
162
  export interface MultiplayerServerWorldParticipants {
113
163
  read(options?: MultiplayerWorldParticipantsReadOptions): Promise<MultiplayerWorldParticipantsReadResult>;
114
164
  }
165
+ export interface MultiplayerWorldNotificationNotifyInput {
166
+ key: string;
167
+ participantIds: readonly string[];
168
+ templateId: string;
169
+ slots?: Readonly<Record<string, string>>;
170
+ at?: string;
171
+ expiresAt?: string;
172
+ }
173
+ export interface MultiplayerWorldNotificationNotifyResult {
174
+ key: string;
175
+ accepted: number;
176
+ skipped: number;
177
+ limited: number;
178
+ }
179
+ export interface MultiplayerWorldNotificationCancelInput {
180
+ key: string;
181
+ participantIds?: readonly string[];
182
+ }
183
+ export interface MultiplayerWorldNotificationCancelResult {
184
+ cancelled: number;
185
+ }
186
+ export interface MultiplayerServerWorldNotifications {
187
+ notify(input: MultiplayerWorldNotificationNotifyInput): Promise<MultiplayerWorldNotificationNotifyResult>;
188
+ cancel(input: MultiplayerWorldNotificationCancelInput): Promise<MultiplayerWorldNotificationCancelResult>;
189
+ }
115
190
  export interface MultiplayerServerWorldCharacters {
116
191
  create(input: MultiplayerWorldRuntimeCharacterCreateInput): Promise<MultiplayerWorldRuntimeCharacterCreateReceipt>;
117
192
  list(): Promise<MultiplayerWorldCharacterList>;
@@ -155,14 +230,21 @@ export interface MultiplayerWorldRuntimeCharacterCreateReceipt {
155
230
  export interface MultiplayerWorldCharacterList {
156
231
  characters: readonly MultiplayerWorldCharacterSummary[];
157
232
  }
158
- export interface MultiplayerWorldCharacterCommandRequest {
233
+ interface MultiplayerWorldCharacterCommandRequestFor<Command extends MultiplayerWorldCharacterCommand> {
234
+ /** Command discriminator mirrored at the request level for TypeScript narrowing. */
235
+ readonly type: Command["type"];
159
236
  readonly requestId: string;
160
237
  readonly operationId: string;
161
238
  readonly speaker: MultiplayerServerPlayer;
162
- readonly command: Readonly<Record<string, unknown>>;
163
- apply(): Promise<unknown>;
239
+ readonly command: Readonly<Command>;
240
+ apply(): Promise<MultiplayerWorldCharacterCommandResult<MultiplayerWorldCharacterCommandResultValue<Command>>>;
164
241
  reject(code?: string): void;
165
242
  }
243
+ export type MultiplayerWorldCharacterCommandRequest = {
244
+ [Type in MultiplayerWorldCharacterCommand["type"]]: MultiplayerWorldCharacterCommandRequestFor<Extract<MultiplayerWorldCharacterCommand, {
245
+ type: Type;
246
+ }>>;
247
+ }[MultiplayerWorldCharacterCommand["type"]];
166
248
  export interface MultiplayerWorldCharacterReadRequest {
167
249
  readonly requestId: string;
168
250
  readonly speaker: MultiplayerServerPlayer;
@@ -186,21 +268,53 @@ export interface MultiplayerWorldCharacterTurn {
186
268
  readonly players: readonly MultiplayerServerPlayer[];
187
269
  readonly characterIds: readonly string[];
188
270
  };
189
- /** Durably admits the exact host-authorized request. No arguments can broaden it. */
190
- send(): Promise<MultiplayerWorldCharacterAdmission>;
271
+ /**
272
+ * Durably admits the exact host-authorized request. No arguments can broaden it:
273
+ * `tools` only declares lookups the character may make of this place during the
274
+ * turn (answered by `onWorldCharacterTool`), and is dropped on a host that does
275
+ * not serve place tools.
276
+ */
277
+ send(options?: MultiplayerWorldCharacterSendOptions): Promise<MultiplayerWorldCharacterAdmission>;
191
278
  /** Explicitly rejects the request before admission. */
192
279
  reject(code?: string): void;
193
280
  }
281
+ /**
282
+ * A lookup a place offers its character for one turn. The host drops an invalid
283
+ * list whole (the turn proceeds without tools): at most 8, a unique `name` matching
284
+ * `^[A-Za-z][A-Za-z0-9_]{0,31}$` other than `perform_action` or `capture_claim`,
285
+ * a 1..300 character `description`, and a JSON-object `input_schema` of at most
286
+ * 1 KiB, with the whole list at most 6 KiB.
287
+ */
288
+ export type MultiplayerPlaceTool = PlatformPlaceTool;
289
+ export interface MultiplayerWorldCharacterSendOptions {
290
+ tools?: readonly MultiplayerPlaceTool[];
291
+ }
292
+ /** One place-tool use by a character during a turn this place admitted. */
293
+ export interface MultiplayerWorldCharacterToolRequest {
294
+ readonly operationId: string;
295
+ readonly characterId: string;
296
+ readonly playerId: string;
297
+ readonly name: string;
298
+ readonly input: Readonly<Record<string, unknown>>;
299
+ }
194
300
  type CallbackResult = unknown;
195
301
  export interface MultiplayerServerCallbacks {
196
302
  onStart?: (session: MultiplayerServerSession) => CallbackResult;
197
- onPlayerJoin?: (session: MultiplayerServerSession, player: MultiplayerServerPlayer) => CallbackResult;
303
+ /** `arrival` is set when ghost placed the player through a hub door. */
304
+ onPlayerJoin?: (session: MultiplayerServerSession, player: MultiplayerServerPlayer, arrival: MultiplayerHubArrival | null) => CallbackResult;
198
305
  onPlayerLeave?: (session: MultiplayerServerSession, player: MultiplayerServerPlayer) => CallbackResult;
199
306
  /** Receives binary messages as Uint8Array values and JSON messages otherwise. */
200
307
  onMessage?: (session: MultiplayerServerSession, player: MultiplayerServerPlayer, data: unknown) => CallbackResult;
201
308
  onWorldCharacterTurn?: (session: MultiplayerServerSession, turn: MultiplayerWorldCharacterTurn) => CallbackResult;
202
309
  onWorldCharacterCommand?: (session: MultiplayerServerSession, request: MultiplayerWorldCharacterCommandRequest) => CallbackResult;
203
310
  onWorldCharacterRead?: (session: MultiplayerServerSession, request: MultiplayerWorldCharacterReadRequest) => CallbackResult;
311
+ /**
312
+ * Answers a place-tool use declared through `turn.send({ tools })`. Return a
313
+ * string or a JSON value (stringified); a result over 1 KiB becomes
314
+ * `{"error":"result too large"}` and a throw `{"error":"tool failed"}`. The host
315
+ * waits 1.5 s, then the character answers without it.
316
+ */
317
+ onWorldCharacterTool?: (request: MultiplayerWorldCharacterToolRequest, session: MultiplayerServerSession) => unknown;
204
318
  onTick?: (session: MultiplayerServerSession) => CallbackResult;
205
319
  onReplaced?: (session: MultiplayerServerSession) => CallbackResult;
206
320
  /**