@flayerlabs/gamemode-client 0.1.0

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 ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Flayer Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @flayerlabs/gamemode-client
2
+
3
+ Use the same `Room` interface while building offline and playing through a live Game Mode gate.
4
+
5
+ ## Requirements
6
+
7
+ Install the package with Node.js 20 or later:
8
+
9
+ ```bash
10
+ pnpm add @flayerlabs/gamemode-client @flayerlabs/gamemode-spec
11
+ ```
12
+
13
+ The package runs in the browser. It has no React dependency.
14
+
15
+ ## Build against the offline room
16
+
17
+ Pass your real rules and config to `createMockRoom`. This example uses the number game created by
18
+ the Game Mode CLI:
19
+
20
+ ```ts
21
+ import { createMockRoom } from '@flayerlabs/gamemode-client'
22
+ import { rules, type Action, type Config } from './game/rules.js'
23
+
24
+ const config: Config = {
25
+ rounds: 3,
26
+ ceiling: 10,
27
+ points: 500,
28
+ roundMs: 10_000,
29
+ }
30
+
31
+ const room = createMockRoom(rules, { config })
32
+
33
+ const unsubscribe = room.subscribe(({ publicView, playerView }) => {
34
+ console.log({ publicView, playerView })
35
+ })
36
+
37
+ await room.send({ guess: 7 } satisfies Action)
38
+ ```
39
+
40
+ The mock drives the same rules module as a live gate. It replaces only the network, wallet, chain
41
+ and real spending. Call `unsubscribe()`, then `room.dispose()` when the game closes.
42
+
43
+ ## Join from an embedded game
44
+
45
+ An embedded game first receives its context and restricted host services from the trusted parent:
46
+
47
+ ```ts
48
+ import { connectEmbeddedGame, joinRoom } from '@flayerlabs/gamemode-client'
49
+
50
+ const embedded = await connectEmbeddedGame({
51
+ parentOrigin: 'https://flaunch.gg',
52
+ })
53
+
54
+ const room = await joinRoom({
55
+ gateUrl: embedded.context.gateUrl,
56
+ roundId: embedded.context.roundId,
57
+ launch: embedded.context.launch,
58
+ host: embedded.host,
59
+ platform: embedded.platform,
60
+ })
61
+ ```
62
+
63
+ Creator code sends game actions and reads room state. It cannot send a wallet transaction or create
64
+ Flaunch calldata. The trusted parent uses `serveEmbeddedGame` to provide the permitted wallet,
65
+ market and identity operations to one known iframe.
66
+
67
+ Call `room.dispose()` and `embedded.dispose()` when the iframe closes.
68
+
69
+ ## Use room capabilities
70
+
71
+ `Room` provides:
72
+
73
+ - `subscribe` for public and player-specific game views
74
+ - `send` for proposed game actions
75
+ - `now` for the server-corrected clock
76
+ - `launch` for fixed launch details
77
+ - `economy` for earned, held, spent and available allowance
78
+ - `market` and `identity` for data supplied by the trusted parent
79
+ - `presence` and `social` for connected-player counts and cosmetic reactions
80
+
81
+ Use [`replayMarket`](https://github.com/flayerlabs/gamemode-sdk/blob/main/packages/client/src/replay-market.ts)
82
+ to build market presentation without a live chain.
83
+
84
+ ## Integrate the trusted parent
85
+
86
+ Read the [trusted platform integration guide](https://github.com/flayerlabs/gamemode-sdk/blob/main/docs/guides/integrate-the-platform.md)
87
+ before implementing `serveEmbeddedGame`, `Host` or a production `joinRoom` call.
88
+
89
+ The [quiz example](https://github.com/flayerlabs/gamemode-sdk/tree/main/examples/quiz) shows a live
90
+ room with a stand-in wallet.
91
+
92
+ The [generated game source](https://github.com/flayerlabs/gamemode-sdk/blob/main/packages/cli/src/scaffold.ts)
93
+ shows the offline room.
@@ -0,0 +1,45 @@
1
+ import { type EmbeddedContext } from '@flayerlabs/gamemode-spec/embed';
2
+ import type { ClientPlatform } from './index.js';
3
+ import { type Authorisation, type Host } from './live.js';
4
+ export interface EmbedEventWindow {
5
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject | null): void;
6
+ removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null): void;
7
+ }
8
+ export interface EmbedTargetWindow {
9
+ postMessage(message: unknown, targetOrigin: string): void;
10
+ }
11
+ export interface EmbeddedGameConnection {
12
+ context: EmbeddedContext;
13
+ host: Host;
14
+ platform: ClientPlatform;
15
+ dispose(): void;
16
+ }
17
+ export interface ConnectEmbeddedGameOptions {
18
+ /** Fixed in trusted game code, never taken from a query string supplied by the parent. */
19
+ parentOrigin: string;
20
+ timeoutMs?: number;
21
+ currentWindow?: EmbedEventWindow;
22
+ parentWindow?: EmbedTargetWindow;
23
+ }
24
+ /** Connect an untrusted game frame to the semantic services of its trusted parent. */
25
+ export declare function connectEmbeddedGame(options: ConnectEmbeddedGameOptions): Promise<EmbeddedGameConnection>;
26
+ export interface ServeEmbeddedGameOptions {
27
+ currentWindow?: EmbedEventWindow;
28
+ frameWindow: EmbedTargetWindow;
29
+ gameOrigin: string;
30
+ context: EmbeddedContext;
31
+ host: Host;
32
+ /**
33
+ * Verify the gate signature and signer for the canonical spend fields.
34
+ *
35
+ * The bridge itself checks the complete ABI encoding, including the required zero referrer,
36
+ * before calling this seam. A false result fails closed before any wallet UI can open.
37
+ */
38
+ validateAuthorisation(authorisation: Authorisation, context: EmbeddedContext, signal?: AbortSignal): boolean | Promise<boolean>;
39
+ platform?: ClientPlatform;
40
+ /** Display domain used by the gate's canonical wallet challenge. */
41
+ signInDomain?: string;
42
+ }
43
+ /** Serve one known iframe. Both its origin and WindowProxy are required for every request. */
44
+ export declare function serveEmbeddedGame(options: ServeEmbeddedGameOptions): () => void;
45
+ //# sourceMappingURL=embed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"AACA,OAAO,EAUL,KAAK,eAAe,EAGrB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,EAAE,cAAc,EAA8B,MAAM,YAAY,CAAC;AAC7E,OAAO,EAA+B,KAAK,aAAa,EAAE,KAAK,IAAI,EAAwB,MAAM,WAAW,CAAC;AAO7G,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,kCAAkC,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1F,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,kCAAkC,GAAG,IAAI,GAAG,IAAI,CAAC;CAC9F;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3D;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,IAAI,IAAI,CAAC;CACjB;AAUD,MAAM,WAAW,0BAA0B;IACzC,0FAA0F;IAC1F,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,gBAAgB,CAAC;IACjC,YAAY,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAED,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAkIxG;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,CAAC,EAAE,gBAAgB,CAAC;IACjC,WAAW,EAAE,iBAAiB,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;IACX;;;;;OAKG;IACH,qBAAqB,CACnB,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE,eAAe,EACxB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAYD,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,IAAI,CA4K/E"}
package/dist/embed.js ADDED
@@ -0,0 +1,329 @@
1
+ import { EMBED_CHANNEL, EMBED_PROTOCOL_VERSION, isCanonicalSpendHookData, isPlayerIdentities, isSessionChallenge, parseGameToHostMessage, parseHostToGameMessage, } from '@flayerlabs/gamemode-spec/embed';
2
+ import { immutableLaunch, marketFrom } from './live.js';
3
+ import { Signal } from './signal.js';
4
+ const DEFAULT_TIMEOUT_MS = 120_000;
5
+ /** A room uses far fewer; refusing after the cap keeps lifetime replay memory strictly bounded. */
6
+ const MAX_ACCEPTED_REQUEST_IDS = 1_024;
7
+ /** Connect an untrusted game frame to the semantic services of its trusted parent. */
8
+ export function connectEmbeddedGame(options) {
9
+ const current = options.currentWindow ?? window;
10
+ const parent = options.parentWindow ?? window.parent;
11
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
12
+ const market = new Signal({ status: 'unavailable', prices: [], trades: [], marketCapUsd: null });
13
+ const pending = new Map();
14
+ let disposed = false;
15
+ let context = null;
16
+ let resolveContext;
17
+ let rejectContext;
18
+ const ready = new Promise((resolve, reject) => {
19
+ resolveContext = resolve;
20
+ rejectContext = reject;
21
+ });
22
+ const post = (message) => parent.postMessage(message, options.parentOrigin);
23
+ const request = (value, progress) => {
24
+ if (disposed)
25
+ return Promise.reject(new Error('the embedded connection is closed'));
26
+ const id = globalThis.crypto.randomUUID();
27
+ return new Promise((resolve, reject) => {
28
+ const timer = setTimeout(() => {
29
+ pending.delete(id);
30
+ reject(new Error('the trusted parent did not answer in time'));
31
+ }, timeoutMs);
32
+ pending.set(id, { method: value.method, resolve, reject, timer, ...(progress ? { progress } : {}) });
33
+ post({ channel: EMBED_CHANNEL, v: EMBED_PROTOCOL_VERSION, type: 'request', id, request: value });
34
+ });
35
+ };
36
+ const onMessage = (rawEvent) => {
37
+ const event = rawEvent;
38
+ if (event.origin !== options.parentOrigin || event.source !== parent)
39
+ return;
40
+ const message = parseHostToGameMessage(event.data);
41
+ if (!message)
42
+ return;
43
+ if (message.type === 'context') {
44
+ if (context === null) {
45
+ context = Object.freeze({ ...message.context, launch: immutableLaunch(message.context.launch) });
46
+ resolveContext(context);
47
+ }
48
+ return;
49
+ }
50
+ if (message.type === 'market') {
51
+ const parsed = marketFrom(message.market);
52
+ if (parsed)
53
+ market.set(parsed);
54
+ return;
55
+ }
56
+ const waiting = pending.get(message.id);
57
+ if (!waiting)
58
+ return;
59
+ if (message.type === 'buy-progress') {
60
+ waiting.progress?.(message.transactionHash
61
+ ? { state: 'pending', transactionHash: message.transactionHash }
62
+ : { state: 'pending' });
63
+ return;
64
+ }
65
+ pending.delete(message.id);
66
+ clearTimeout(waiting.timer);
67
+ if (!message.ok) {
68
+ if (waiting.method === 'buy') {
69
+ const reason = message.error === 'no-wallet' ? 'try-again' : message.error;
70
+ waiting.resolve({ failed: { bought: false, reason } });
71
+ }
72
+ else {
73
+ waiting.reject(new Error(message.error));
74
+ }
75
+ return;
76
+ }
77
+ waiting.resolve(message.value);
78
+ };
79
+ current.addEventListener('message', onMessage);
80
+ const contextTimer = setTimeout(() => {
81
+ disposed = true;
82
+ current.removeEventListener('message', onMessage);
83
+ rejectContext(new Error('the trusted parent did not provide game context'));
84
+ }, timeoutMs);
85
+ post({ channel: EMBED_CHANNEL, v: EMBED_PROTOCOL_VERSION, type: 'ready' });
86
+ return ready.then((trustedContext) => {
87
+ clearTimeout(contextTimer);
88
+ const host = {
89
+ address: async () => {
90
+ const value = await request({ method: 'address' });
91
+ return typeof value === 'string' || value === null ? value : null;
92
+ },
93
+ signIn: async (message) => {
94
+ const value = await request({ method: 'sign-in', message });
95
+ if (typeof value !== 'string' || !/^0x[0-9a-fA-F]+$/.test(value))
96
+ throw new Error('invalid signature response');
97
+ return value;
98
+ },
99
+ sessionEvidence: () => request({ method: 'session-evidence' }),
100
+ buy: async (authorisation, progress) => {
101
+ const value = await request({ method: 'buy', authorisation }, progress);
102
+ if (typeof value !== 'object' || value === null)
103
+ return { failed: { bought: false, reason: 'try-again' } };
104
+ const spent = value.spentWei;
105
+ try {
106
+ const spentWei = BigInt(String(spent));
107
+ return spentWei >= 0n ? { spentWei } : { failed: { bought: false, reason: 'try-again' } };
108
+ }
109
+ catch {
110
+ return { failed: { bought: false, reason: 'try-again' } };
111
+ }
112
+ },
113
+ };
114
+ const identity = {
115
+ resolve: async (players) => {
116
+ const value = await request({ method: 'identity', players });
117
+ return isPlayerIdentities(value) ? value : [];
118
+ },
119
+ };
120
+ const marketReadable = {
121
+ current: () => market.current(),
122
+ subscribe: (listener) => market.subscribe(listener),
123
+ };
124
+ return {
125
+ context: trustedContext,
126
+ host,
127
+ platform: { identity, market: marketReadable },
128
+ dispose: () => {
129
+ if (disposed)
130
+ return;
131
+ disposed = true;
132
+ current.removeEventListener('message', onMessage);
133
+ for (const waiting of pending.values()) {
134
+ clearTimeout(waiting.timer);
135
+ waiting.reject(new Error('the embedded connection is closed'));
136
+ }
137
+ pending.clear();
138
+ market.clear();
139
+ },
140
+ };
141
+ });
142
+ }
143
+ function wireMarket(state) {
144
+ return { ...state, trades: state.trades.map((trade) => ({ ...trade, spendWei: trade.spendWei.toString() })) };
145
+ }
146
+ function failure(reason) {
147
+ return ['nothing-to-spend', 'declined', 'not-enough-for-fees', 'window-closed'].includes(reason)
148
+ ? reason
149
+ : 'try-again';
150
+ }
151
+ /** Serve one known iframe. Both its origin and WindowProxy are required for every request. */
152
+ export function serveEmbeddedGame(options) {
153
+ const current = options.currentWindow ?? window;
154
+ const active = new Set();
155
+ const acceptedRequestIds = new Set();
156
+ const usedAuthorisations = new Set();
157
+ const requestTimes = [];
158
+ let buyActive = false;
159
+ let signInActive = false;
160
+ let evidenceActive = false;
161
+ let childReady = false;
162
+ let stopped = false;
163
+ const lifetime = new AbortController();
164
+ let latestMarket = options.platform?.market?.current() ?? null;
165
+ const post = (message) => {
166
+ if (!stopped)
167
+ options.frameWindow.postMessage(message, options.gameOrigin);
168
+ };
169
+ const answer = (id, value) => post({ channel: EMBED_CHANNEL, v: EMBED_PROTOCOL_VERSION, type: 'response', id, ok: true, value });
170
+ const refuse = (id, error) => post({ channel: EMBED_CHANNEL, v: EMBED_PROTOCOL_VERSION, type: 'response', id, ok: false, error });
171
+ const run = async (id, request) => {
172
+ if (stopped || acceptedRequestIds.has(id) || acceptedRequestIds.size >= MAX_ACCEPTED_REQUEST_IDS)
173
+ return;
174
+ const now = Date.now();
175
+ while (requestTimes[0] !== undefined && requestTimes[0] <= now - 1_000)
176
+ requestTimes.shift();
177
+ if (active.size >= 16 || requestTimes.length >= 64)
178
+ return;
179
+ requestTimes.push(now);
180
+ acceptedRequestIds.add(id);
181
+ active.add(id);
182
+ try {
183
+ switch (request.method) {
184
+ case 'address':
185
+ answer(id, await options.host.address(lifetime.signal));
186
+ break;
187
+ case 'sign-in':
188
+ if (signInActive) {
189
+ refuse(id, 'try-again');
190
+ break;
191
+ }
192
+ signInActive = true;
193
+ try {
194
+ const address = await options.host.address(lifetime.signal);
195
+ if (stopped)
196
+ break;
197
+ if (!address ||
198
+ !isSessionChallenge(request.message, address, options.context.gateUrl, options.signInDomain)) {
199
+ refuse(id, address ? 'try-again' : 'no-wallet');
200
+ break;
201
+ }
202
+ const signature = await options.host.signIn(request.message, lifetime.signal);
203
+ if (stopped)
204
+ break;
205
+ answer(id, signature);
206
+ }
207
+ finally {
208
+ signInActive = false;
209
+ }
210
+ break;
211
+ case 'session-evidence':
212
+ if (evidenceActive) {
213
+ refuse(id, 'try-again');
214
+ break;
215
+ }
216
+ evidenceActive = true;
217
+ try {
218
+ const evidence = await options.host.sessionEvidence?.(lifetime.signal);
219
+ if (!stopped)
220
+ answer(id, evidence);
221
+ }
222
+ finally {
223
+ evidenceActive = false;
224
+ }
225
+ break;
226
+ case 'identity':
227
+ answer(id, (await options.platform?.identity?.resolve(request.players)) ?? []);
228
+ break;
229
+ case 'buy': {
230
+ if (buyActive) {
231
+ refuse(id, 'try-again');
232
+ break;
233
+ }
234
+ buyActive = true;
235
+ try {
236
+ const address = await options.host.address(lifetime.signal);
237
+ if (stopped)
238
+ break;
239
+ if (!address ||
240
+ address.toLowerCase() !== request.authorisation.buyer.toLowerCase() ||
241
+ request.authorisation.poolId.toLowerCase() !== options.context.launch.poolId.toLowerCase()) {
242
+ refuse(id, address ? 'try-again' : 'no-wallet');
243
+ break;
244
+ }
245
+ if (!isCanonicalSpendHookData(request.authorisation) ||
246
+ !(await options.validateAuthorisation(request.authorisation, options.context, lifetime.signal))) {
247
+ refuse(id, 'try-again');
248
+ break;
249
+ }
250
+ if (stopped)
251
+ break;
252
+ const authorisationId = [
253
+ request.authorisation.buyer.toLowerCase(),
254
+ request.authorisation.poolId.toLowerCase(),
255
+ request.authorisation.nonce,
256
+ ].join(':');
257
+ if (usedAuthorisations.has(authorisationId) || usedAuthorisations.size >= 256) {
258
+ refuse(id, 'try-again');
259
+ break;
260
+ }
261
+ usedAuthorisations.add(authorisationId);
262
+ const result = await options.host.buy(request.authorisation, (event) => {
263
+ post({
264
+ channel: EMBED_CHANNEL,
265
+ v: EMBED_PROTOCOL_VERSION,
266
+ type: 'buy-progress',
267
+ id,
268
+ ...(event.transactionHash ? { transactionHash: event.transactionHash } : {}),
269
+ });
270
+ }, lifetime.signal);
271
+ if (stopped)
272
+ break;
273
+ if ('failed' in result) {
274
+ refuse(id, result.failed.bought ? 'try-again' : failure(result.failed.reason));
275
+ }
276
+ else {
277
+ answer(id, { spentWei: result.spentWei.toString() });
278
+ }
279
+ }
280
+ finally {
281
+ buyActive = false;
282
+ }
283
+ break;
284
+ }
285
+ }
286
+ }
287
+ catch {
288
+ refuse(id, 'try-again');
289
+ }
290
+ finally {
291
+ active.delete(id);
292
+ }
293
+ };
294
+ const onMessage = (rawEvent) => {
295
+ const event = rawEvent;
296
+ if (event.origin !== options.gameOrigin || event.source !== options.frameWindow)
297
+ return;
298
+ const message = parseGameToHostMessage(event.data);
299
+ if (!message)
300
+ return;
301
+ if (message.type === 'ready') {
302
+ if (childReady)
303
+ return;
304
+ childReady = true;
305
+ post({ channel: EMBED_CHANNEL, v: EMBED_PROTOCOL_VERSION, type: 'context', context: options.context });
306
+ if (latestMarket) {
307
+ post({ channel: EMBED_CHANNEL, v: EMBED_PROTOCOL_VERSION, type: 'market', market: wireMarket(latestMarket) });
308
+ }
309
+ return;
310
+ }
311
+ void run(message.id, message.request);
312
+ };
313
+ current.addEventListener('message', onMessage);
314
+ const stopMarket = options.platform?.market?.subscribe((state) => {
315
+ latestMarket = state;
316
+ if (childReady) {
317
+ post({ channel: EMBED_CHANNEL, v: EMBED_PROTOCOL_VERSION, type: 'market', market: wireMarket(state) });
318
+ }
319
+ }) ?? (() => { });
320
+ return () => {
321
+ if (stopped)
322
+ return;
323
+ stopped = true;
324
+ lifetime.abort();
325
+ current.removeEventListener('message', onMessage);
326
+ stopMarket();
327
+ };
328
+ }
329
+ //# sourceMappingURL=embed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed.js","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { PlayerId } from '@flayerlabs/gamemode-spec';
2
+ import type { PlayerIdentity } from '@flayerlabs/gamemode-spec/live';
3
+ import type { IdentityResolver } from './index.js';
4
+ /** Batches misses through the platform adapter and gives every requested player a stable answer. */
5
+ export declare class CachedIdentityResolver implements IdentityResolver {
6
+ private readonly adapter?;
7
+ private readonly cache;
8
+ private readonly inFlight;
9
+ constructor(adapter?: IdentityResolver | undefined);
10
+ resolve(players: readonly PlayerId[]): Promise<readonly PlayerIdentity[]>;
11
+ private resolveMissing;
12
+ }
13
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,oGAAoG;AACpG,qBAAa,sBAAuB,YAAW,gBAAgB;IAIjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IAHrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAC7D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;gBAElC,OAAO,CAAC,EAAE,gBAAgB,YAAA;IAEjD,OAAO,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAAC,SAAS,cAAc,EAAE,CAAC;YAqBjE,cAAc;CAkB7B"}
@@ -0,0 +1,49 @@
1
+ /** Batches misses through the platform adapter and gives every requested player a stable answer. */
2
+ export class CachedIdentityResolver {
3
+ adapter;
4
+ cache = new Map();
5
+ inFlight = new Map();
6
+ constructor(adapter) {
7
+ this.adapter = adapter;
8
+ }
9
+ async resolve(players) {
10
+ if (players.length > 1_000)
11
+ throw new RangeError('at most 1,000 identities may be resolved at once');
12
+ const unique = [...new Set(players)];
13
+ const missing = unique.filter((player) => !this.cache.has(player));
14
+ const unclaimed = missing.filter((player) => !this.inFlight.has(player));
15
+ if (unclaimed.length > 0) {
16
+ let loading;
17
+ loading = this.resolveMissing(unclaimed).finally(() => {
18
+ for (const player of unclaimed) {
19
+ if (this.inFlight.get(player) === loading)
20
+ this.inFlight.delete(player);
21
+ }
22
+ });
23
+ for (const player of unclaimed)
24
+ this.inFlight.set(player, loading);
25
+ }
26
+ await Promise.all(missing.map((player) => this.inFlight.get(player)));
27
+ return unique.map((player) => this.cache.get(player));
28
+ }
29
+ async resolveMissing(players) {
30
+ const asked = new Set(players);
31
+ const batches = [];
32
+ for (let offset = 0; offset < players.length; offset += 100) {
33
+ batches.push(players.slice(offset, offset + 100));
34
+ }
35
+ const results = await Promise.all(batches.map((batch) => this.adapter?.resolve(batch).catch(() => []) ?? []));
36
+ for (const resolved of results) {
37
+ for (const identity of resolved) {
38
+ if (asked.has(identity.player))
39
+ this.cache.set(identity.player, identity);
40
+ }
41
+ }
42
+ for (const player of players) {
43
+ if (!this.cache.has(player)) {
44
+ this.cache.set(player, { player, displayName: null, avatarUrl: null });
45
+ }
46
+ }
47
+ }
48
+ }
49
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}