castle-web-sdk 0.4.44 → 0.4.46
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/README.md +487 -53
- package/dist/agent.js +4 -4
- package/dist/avatar.d.ts +12 -0
- package/dist/avatar.js +23 -0
- package/dist/castle.d.ts +10 -1
- package/dist/castle.js +5 -0
- package/dist/commands.d.ts +229 -0
- package/dist/commands.js +16 -0
- package/dist/context.d.ts +6 -0
- package/dist/experiments.d.ts +15 -0
- package/dist/experiments.js +83 -0
- package/dist/hub.d.ts +85 -0
- package/dist/hub.js +107 -0
- package/dist/multiplayer.d.ts +88 -6
- package/dist/multiplayer.js +219 -8
- package/dist/save.d.ts +197 -0
- package/dist/save.js +525 -0
- package/dist/saveCoordinator.d.ts +39 -0
- package/dist/saveCoordinator.js +252 -0
- package/dist/server/hub.d.ts +169 -0
- package/dist/server/hub.js +125 -0
- package/dist/server/platformHandle.d.ts +51 -2
- package/dist/server/storage.d.ts +58 -4
- package/dist/server/storage.js +25 -5
- package/dist/server/wrapper.d.ts +147 -6
- package/dist/server/wrapper.js +741 -34
- package/dist/store.d.ts +3 -2
- package/dist/store.js +5 -0
- package/dist/transport.d.ts +2 -1
- package/dist/transport.js +13 -1
- package/package.json +1 -1
package/dist/server/storage.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Json } from "../types";
|
|
2
2
|
import type { CharacterHistory, CharacterOperation } from "../commands";
|
|
3
3
|
import type { PlatformHandle } from "./platformHandle";
|
|
4
|
+
import { type SaveApi, type SaveBucketApi } from "../save";
|
|
4
5
|
export interface ServerStorageListOptions {
|
|
5
6
|
prefix?: string;
|
|
6
7
|
limit?: number;
|
|
@@ -15,7 +16,7 @@ export interface ServerStorageListPage<T extends Json = Json> {
|
|
|
15
16
|
/** Null means there is nothing left. A short page alone does not mean that. */
|
|
16
17
|
cursor: string | null;
|
|
17
18
|
}
|
|
18
|
-
export interface ServerStorageScope {
|
|
19
|
+
export interface ServerStorageScope extends SaveBucketApi {
|
|
19
20
|
get<T extends Json = Json>(keys: string[]): Promise<Record<string, T>>;
|
|
20
21
|
set(values: Record<string, Json>): Promise<void>;
|
|
21
22
|
remove(keys: string[]): Promise<void>;
|
|
@@ -95,6 +96,59 @@ export interface DeliverCharacterKnowledgeInput {
|
|
|
95
96
|
claimIds: string[];
|
|
96
97
|
source: CharacterKnowledgeSource;
|
|
97
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* A character acting on its own after a command woke it (autonomous character
|
|
101
|
+
* actions, bundles with `characterActions`). `declined` covers choosing
|
|
102
|
+
* nothing, too little confidence for its temperament, and an action no longer
|
|
103
|
+
* eligible when it committed; `failed` is a decision-model error.
|
|
104
|
+
*/
|
|
105
|
+
export type CharacterActionNotice = {
|
|
106
|
+
characterId: string;
|
|
107
|
+
/** `id` is the event occurrence, routine, or promise id. */
|
|
108
|
+
trigger: {
|
|
109
|
+
type: "witnessed" | "routine" | "promise_due";
|
|
110
|
+
id: string;
|
|
111
|
+
};
|
|
112
|
+
outcome: "acted" | "declined" | "failed";
|
|
113
|
+
/** When acted. */
|
|
114
|
+
actionId?: string;
|
|
115
|
+
/** When acted with a parameterized action's variant. */
|
|
116
|
+
variantId?: string;
|
|
117
|
+
/** The action's world event, when it has one. */
|
|
118
|
+
eventOccurrenceId?: string;
|
|
119
|
+
/** The decision model's probability for its choice. */
|
|
120
|
+
probability?: number;
|
|
121
|
+
};
|
|
122
|
+
export type CharacterGameStateResult = {
|
|
123
|
+
revision: number;
|
|
124
|
+
state: {
|
|
125
|
+
[key: string]: string | number | boolean;
|
|
126
|
+
};
|
|
127
|
+
promiseOutcomes: Array<{
|
|
128
|
+
id: string;
|
|
129
|
+
status: "fulfilled" | "broken";
|
|
130
|
+
due: number;
|
|
131
|
+
resolvedAtGameTime: number;
|
|
132
|
+
}>;
|
|
133
|
+
/** At most 8, omitted when no character acted, declined, or failed. */
|
|
134
|
+
characterActions?: CharacterActionNotice[];
|
|
135
|
+
};
|
|
136
|
+
export type CharacterGameStateOperation = Omit<CharacterOperation, "result"> & {
|
|
137
|
+
result: CharacterGameStateResult | null;
|
|
138
|
+
};
|
|
139
|
+
export type CharacterKnowledgeDeliveryResult = {
|
|
140
|
+
deliveryId: string;
|
|
141
|
+
recipientCharacterId: string;
|
|
142
|
+
eventOccurrenceId: string;
|
|
143
|
+
claimIds: string[];
|
|
144
|
+
source: CharacterKnowledgeSource;
|
|
145
|
+
deliveredAt: string;
|
|
146
|
+
/** At most 8, omitted when no character acted, declined, or failed. */
|
|
147
|
+
characterActions?: CharacterActionNotice[];
|
|
148
|
+
};
|
|
149
|
+
export type CharacterKnowledgeDeliveryOperation = Omit<CharacterOperation, "result"> & {
|
|
150
|
+
result: CharacterKnowledgeDeliveryResult | null;
|
|
151
|
+
};
|
|
98
152
|
export interface CompleteCharacterBeatInput {
|
|
99
153
|
operationId: string;
|
|
100
154
|
characterId: string;
|
|
@@ -189,10 +243,10 @@ export interface ServerPlaythroughCharacters {
|
|
|
189
243
|
/** Queues a funded turn whose admitted listeners are fixed at enqueue time. */
|
|
190
244
|
send(input: SendCharacterInput): Promise<CharacterOperation>;
|
|
191
245
|
recordEvent(input: RecordCharacterEventInput): Promise<CharacterOperation>;
|
|
192
|
-
setGameState(input: SetCharacterGameStateInput): Promise<
|
|
246
|
+
setGameState(input: SetCharacterGameStateInput): Promise<CharacterGameStateOperation>;
|
|
193
247
|
shareClaim(input: ShareCharacterClaimInput): Promise<CharacterOperation>;
|
|
194
248
|
inspect(): Promise<Json>;
|
|
195
|
-
deliverKnowledge(input: DeliverCharacterKnowledgeInput): Promise<
|
|
249
|
+
deliverKnowledge(input: DeliverCharacterKnowledgeInput): Promise<CharacterKnowledgeDeliveryOperation>;
|
|
196
250
|
completeBeat(input: CompleteCharacterBeatInput): Promise<CharacterOperation>;
|
|
197
251
|
evaluateInformation(input: EvaluateCharacterInformationInput): Promise<CharacterInformationEvaluationOperation>;
|
|
198
252
|
evaluateBeats(input: EvaluateCharacterBeatsInput): Promise<CharacterBeatEvaluationOperation>;
|
|
@@ -204,7 +258,7 @@ export interface ServerCharacterHistoryOptions {
|
|
|
204
258
|
limit?: number;
|
|
205
259
|
cursor?: string | null;
|
|
206
260
|
}
|
|
207
|
-
export interface ServerStorage {
|
|
261
|
+
export interface ServerStorage extends SaveApi {
|
|
208
262
|
/** Shared by everyone playing the deck, and owned by this server. */
|
|
209
263
|
readonly deck: ServerStorageScope;
|
|
210
264
|
/** One player's public data. Readable by anyone; writable by them or by this server. */
|
package/dist/server/storage.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { chunked } from "../chunk";
|
|
2
2
|
import { decodeStorageValue, encodeStorageValue, storageError, } from "../storageJson";
|
|
3
|
+
import { createSaveApi, createSaveBucket, preflightSaveRequest, SaveError, } from "../save";
|
|
3
4
|
// Deck storage for an author's session server.
|
|
4
5
|
//
|
|
5
6
|
// The same store the deck's clients use, reached over the platform channel instead of
|
|
@@ -56,8 +57,26 @@ export function createServerStorage(platformHandle) {
|
|
|
56
57
|
const result = await platformHandle.storage(op, JSON.stringify(args));
|
|
57
58
|
return JSON.parse(result);
|
|
58
59
|
};
|
|
60
|
+
// The host forwards these args to the platform's storage route unchanged,
|
|
61
|
+
// which takes a save's request as `input` and a lookup as `operationId`.
|
|
62
|
+
const saveApi = createSaveApi(async (verb, input) => {
|
|
63
|
+
if (typeof platformHandle.storage !== "function" ||
|
|
64
|
+
platformHandle.storageEnabled === false) {
|
|
65
|
+
throw new SaveError({
|
|
66
|
+
code: "CAULDRON_STORAGE_UNAVAILABLE",
|
|
67
|
+
message: "Storage is not available to this session.",
|
|
68
|
+
operationId: input.operationId,
|
|
69
|
+
retryable: false,
|
|
70
|
+
committed: false,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const args = verb === "operation" ? input : { input };
|
|
74
|
+
preflightSaveRequest(verb, args, MAX_REQUEST_BYTES);
|
|
75
|
+
return call(verb, args);
|
|
76
|
+
}, true);
|
|
59
77
|
return {
|
|
60
|
-
|
|
78
|
+
...saveApi,
|
|
79
|
+
deck: createScope(call, "DECK", null, saveApi),
|
|
61
80
|
user(userId) {
|
|
62
81
|
// A bad id poisons the scope rather than throwing here, so the failure arrives
|
|
63
82
|
// the same way every other failure does -- as a rejection the author can await.
|
|
@@ -65,12 +84,12 @@ export function createServerStorage(platformHandle) {
|
|
|
65
84
|
// resolved by the platform to the deck's creator, quietly writing the wrong
|
|
66
85
|
// player's bucket, so nothing is sent either.
|
|
67
86
|
if (typeof userId !== "string" || userId.length === 0) {
|
|
68
|
-
return createScope(rejectMissingSubject, "USER", null);
|
|
87
|
+
return createScope(rejectMissingSubject, "USER", null, saveApi);
|
|
69
88
|
}
|
|
70
89
|
if (utf8Length(userId) > MAX_SUBJECT_BYTES) {
|
|
71
|
-
return createScope(rejectOversizeSubject, "USER",
|
|
90
|
+
return createScope(rejectOversizeSubject, "USER", userId, saveApi);
|
|
72
91
|
}
|
|
73
|
-
return createScope(call, "USER", userId);
|
|
92
|
+
return createScope(call, "USER", userId, saveApi);
|
|
74
93
|
},
|
|
75
94
|
board(name) {
|
|
76
95
|
return createBoard(call, name);
|
|
@@ -224,8 +243,9 @@ function createPlaythrough(call, subject, generation) {
|
|
|
224
243
|
},
|
|
225
244
|
});
|
|
226
245
|
}
|
|
227
|
-
function createScope(call, scope, subject) {
|
|
246
|
+
function createScope(call, scope, subject, saveApi) {
|
|
228
247
|
return {
|
|
248
|
+
...createSaveBucket(saveApi, scope === "DECK" ? "deck" : "user", subject ?? undefined),
|
|
229
249
|
async get(keys) {
|
|
230
250
|
// A bare array, not a wrapper: the platform hands back exactly what the
|
|
231
251
|
// storage verb returns, and `get` returns its entries directly.
|
package/dist/server/wrapper.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import type { MultiplayerSessionMode, PlatformHandle, 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
|
-
|
|
5
|
-
export type {
|
|
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, 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, } from "./storage";
|
|
6
13
|
export interface MultiplayerServerPlayer extends PlayerIdentity {
|
|
7
14
|
playerId: string;
|
|
15
|
+
readonly participantId: string;
|
|
8
16
|
}
|
|
9
17
|
export interface MultiplayerBroadcastOptions {
|
|
10
18
|
except?: string;
|
|
@@ -14,6 +22,12 @@ export interface MultiplayerServerSession {
|
|
|
14
22
|
readonly sessionId: string;
|
|
15
23
|
readonly deckId: string;
|
|
16
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;
|
|
17
31
|
/**
|
|
18
32
|
* Deck storage that outlives the session. Nothing else here does: a session ends
|
|
19
33
|
* when its last player leaves and its memory goes with it.
|
|
@@ -21,6 +35,27 @@ export interface MultiplayerServerSession {
|
|
|
21
35
|
readonly storage: ServerStorage;
|
|
22
36
|
/** A durable checkpoint for an opted-in persistent destination. */
|
|
23
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>;
|
|
24
59
|
/** Sends bytes as a binary message and all other payloads as JSON. */
|
|
25
60
|
send(playerId: string, data: unknown): void;
|
|
26
61
|
/** Broadcasts bytes as a binary message and all other payloads as JSON. */
|
|
@@ -52,8 +87,106 @@ export interface MultiplayerServerWorld {
|
|
|
52
87
|
read(): Promise<MultiplayerWorldSnapshot>;
|
|
53
88
|
/** Commits exactly against expectedRevision; conflicts never partially write. */
|
|
54
89
|
commit(input: MultiplayerWorldCommitInput): Promise<MultiplayerWorldCommitReceipt>;
|
|
90
|
+
catchUp(options: MultiplayerWorldCatchUpOptions): Promise<MultiplayerWorldCatchUpResult>;
|
|
91
|
+
readonly journal: MultiplayerServerWorldJournal;
|
|
92
|
+
readonly participants: MultiplayerServerWorldParticipants;
|
|
93
|
+
readonly notifications: MultiplayerServerWorldNotifications;
|
|
55
94
|
readonly characters: MultiplayerServerWorldCharacters;
|
|
56
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
|
+
}
|
|
110
|
+
export interface MultiplayerWorldJournalAppendInput {
|
|
111
|
+
operationId: string;
|
|
112
|
+
entries: readonly MultiplayerWorldJournalAppendEntry[];
|
|
113
|
+
}
|
|
114
|
+
export interface MultiplayerWorldJournalAppendEntry {
|
|
115
|
+
kind: string;
|
|
116
|
+
disclosure: "world" | "participants";
|
|
117
|
+
participantIds?: readonly string[];
|
|
118
|
+
data?: Json;
|
|
119
|
+
}
|
|
120
|
+
export interface MultiplayerWorldJournalAppendReceipt {
|
|
121
|
+
operationId: string;
|
|
122
|
+
replayed: boolean;
|
|
123
|
+
firstSeq: string;
|
|
124
|
+
lastSeq: string;
|
|
125
|
+
}
|
|
126
|
+
export interface MultiplayerWorldJournalReadOptions {
|
|
127
|
+
afterSeq?: string;
|
|
128
|
+
limit?: number;
|
|
129
|
+
participantId?: string;
|
|
130
|
+
}
|
|
131
|
+
export interface MultiplayerWorldJournalEntry {
|
|
132
|
+
seq: string;
|
|
133
|
+
kind: string;
|
|
134
|
+
disclosure: "world" | "participants";
|
|
135
|
+
participantIds: readonly string[];
|
|
136
|
+
data: Json;
|
|
137
|
+
createdAt: string;
|
|
138
|
+
}
|
|
139
|
+
export interface MultiplayerWorldJournalReadResult {
|
|
140
|
+
entries: readonly MultiplayerWorldJournalEntry[];
|
|
141
|
+
nextSeq: string;
|
|
142
|
+
oldestRetainedSeq: string | null;
|
|
143
|
+
}
|
|
144
|
+
export interface MultiplayerServerWorldJournal {
|
|
145
|
+
append(input: MultiplayerWorldJournalAppendInput): Promise<MultiplayerWorldJournalAppendReceipt>;
|
|
146
|
+
read(options?: MultiplayerWorldJournalReadOptions): Promise<MultiplayerWorldJournalReadResult>;
|
|
147
|
+
}
|
|
148
|
+
export interface MultiplayerWorldParticipantsReadOptions {
|
|
149
|
+
participantIds?: readonly string[];
|
|
150
|
+
limit?: number;
|
|
151
|
+
}
|
|
152
|
+
export interface MultiplayerWorldParticipant {
|
|
153
|
+
participantId: string;
|
|
154
|
+
firstSeenAt: string;
|
|
155
|
+
lastSeenAt: string;
|
|
156
|
+
lastLeftAt: string | null;
|
|
157
|
+
visitCount: number;
|
|
158
|
+
}
|
|
159
|
+
export interface MultiplayerWorldParticipantsReadResult {
|
|
160
|
+
participants: readonly MultiplayerWorldParticipant[];
|
|
161
|
+
}
|
|
162
|
+
export interface MultiplayerServerWorldParticipants {
|
|
163
|
+
read(options?: MultiplayerWorldParticipantsReadOptions): Promise<MultiplayerWorldParticipantsReadResult>;
|
|
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
|
+
}
|
|
57
190
|
export interface MultiplayerServerWorldCharacters {
|
|
58
191
|
create(input: MultiplayerWorldRuntimeCharacterCreateInput): Promise<MultiplayerWorldRuntimeCharacterCreateReceipt>;
|
|
59
192
|
list(): Promise<MultiplayerWorldCharacterList>;
|
|
@@ -97,14 +230,21 @@ export interface MultiplayerWorldRuntimeCharacterCreateReceipt {
|
|
|
97
230
|
export interface MultiplayerWorldCharacterList {
|
|
98
231
|
characters: readonly MultiplayerWorldCharacterSummary[];
|
|
99
232
|
}
|
|
100
|
-
|
|
233
|
+
interface MultiplayerWorldCharacterCommandRequestFor<Command extends MultiplayerWorldCharacterCommand> {
|
|
234
|
+
/** Command discriminator mirrored at the request level for TypeScript narrowing. */
|
|
235
|
+
readonly type: Command["type"];
|
|
101
236
|
readonly requestId: string;
|
|
102
237
|
readonly operationId: string;
|
|
103
238
|
readonly speaker: MultiplayerServerPlayer;
|
|
104
|
-
readonly command: Readonly<
|
|
105
|
-
apply(): Promise<
|
|
239
|
+
readonly command: Readonly<Command>;
|
|
240
|
+
apply(): Promise<MultiplayerWorldCharacterCommandResult<MultiplayerWorldCharacterCommandResultValue<Command>>>;
|
|
106
241
|
reject(code?: string): void;
|
|
107
242
|
}
|
|
243
|
+
export type MultiplayerWorldCharacterCommandRequest = {
|
|
244
|
+
[Type in MultiplayerWorldCharacterCommand["type"]]: MultiplayerWorldCharacterCommandRequestFor<Extract<MultiplayerWorldCharacterCommand, {
|
|
245
|
+
type: Type;
|
|
246
|
+
}>>;
|
|
247
|
+
}[MultiplayerWorldCharacterCommand["type"]];
|
|
108
248
|
export interface MultiplayerWorldCharacterReadRequest {
|
|
109
249
|
readonly requestId: string;
|
|
110
250
|
readonly speaker: MultiplayerServerPlayer;
|
|
@@ -136,7 +276,8 @@ export interface MultiplayerWorldCharacterTurn {
|
|
|
136
276
|
type CallbackResult = unknown;
|
|
137
277
|
export interface MultiplayerServerCallbacks {
|
|
138
278
|
onStart?: (session: MultiplayerServerSession) => CallbackResult;
|
|
139
|
-
|
|
279
|
+
/** `arrival` is set when ghost placed the player through a hub door. */
|
|
280
|
+
onPlayerJoin?: (session: MultiplayerServerSession, player: MultiplayerServerPlayer, arrival: MultiplayerHubArrival | null) => CallbackResult;
|
|
140
281
|
onPlayerLeave?: (session: MultiplayerServerSession, player: MultiplayerServerPlayer) => CallbackResult;
|
|
141
282
|
/** Receives binary messages as Uint8Array values and JSON messages otherwise. */
|
|
142
283
|
onMessage?: (session: MultiplayerServerSession, player: MultiplayerServerPlayer, data: unknown) => CallbackResult;
|