@indiegems/gem-web-sdk 20.0.0 → 20.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/CHANGELOG.md +14 -0
- package/dist/game.d.ts +51 -3
- package/dist/gem-web-sdk.bridge.js +2 -2
- package/dist/gem-web-sdk.conformance.js +3 -3
- package/dist/gem-web-sdk.esm.js +3 -3
- package/dist/gem-web-sdk.function.js +3 -3
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +3 -3
- package/dist/gem-web-sdk.sdk.js +3 -3
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +57 -6
- package/dist/sdk.d.ts +51 -3
- package/docs/api/api-reference-function.md +1 -1
- package/docs/api/api-reference-game.md +9 -8
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +4 -4
- package/docs/changelog.md +14 -0
- package/docs/dedicated-server-flow.md +30 -2
- package/docs/getting-started.md +3 -3
- package/docs/manifest.json +14 -14
- package/docs/match-session.md +9 -0
- package/docs/p2p-and-parties.md +79 -6
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +8 -5
- package/package.json +1 -1
- package/schema/bridge.v1.json +67 -2
- package/schema/capabilities.public.json +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,20 @@ Its entries are grouped under up to five headings:
|
|
|
16
16
|
- **Rebuild**: a type changed. Recompile against the new version; code that compiles today keeps compiling unless the entry says otherwise.
|
|
17
17
|
- **New capability**: something a game can now do. Nothing changes unless you use it.
|
|
18
18
|
|
|
19
|
+
## 20.1.0
|
|
20
|
+
|
|
21
|
+
Upgrading from 20.0.0. Impact: required.
|
|
22
|
+
|
|
23
|
+
### Rebuild
|
|
24
|
+
|
|
25
|
+
- The list of capability names a game can advertise gained one value, p2pRelayDrain. Recompile. If the game switches exhaustively over capability names, add a case for it. Otherwise nothing else changes.
|
|
26
|
+
- The list of messages the Arcade page sends to the game frame gained one entry, gem.relayDraining, and the parent message type gained the matching member. Recompile. If the game handles every parent message in an exhaustive switch, add a case for the new one. Otherwise nothing else changes.
|
|
27
|
+
|
|
28
|
+
### New capability
|
|
29
|
+
|
|
30
|
+
- A game can now be told when a relay carrying its peer-to-peer match is draining and by when. Pass onRelayDraining in the frame options, or implement it on the frame callbacks, to receive the notice. The SDK already moves the connection off the draining relay by itself, so use this only to inform the player or log it. The notice is sent only by an Arcade page that supports it.
|
|
31
|
+
- Telemetry samples can now carry labels, so a game that reads or forwards telemetry samples can see per-sample label values such as outcome and reason.
|
|
32
|
+
|
|
19
33
|
## 20.0.0
|
|
20
34
|
|
|
21
35
|
Upgrading from 19.0.2. Impact: required.
|
package/dist/game.d.ts
CHANGED
|
@@ -2244,6 +2244,15 @@ interface GemSignalEvent {
|
|
|
2244
2244
|
readonly matchId: string;
|
|
2245
2245
|
readonly data: unknown;
|
|
2246
2246
|
}
|
|
2247
|
+
interface GemRelayDraining {
|
|
2248
|
+
readonly gem: typeof PROTOCOL;
|
|
2249
|
+
readonly type: "gem.relayDraining";
|
|
2250
|
+
readonly matchId: string;
|
|
2251
|
+
readonly clusterTargetId: string;
|
|
2252
|
+
readonly relayIps: string[];
|
|
2253
|
+
readonly drainingSince: string;
|
|
2254
|
+
readonly deadline: string;
|
|
2255
|
+
}
|
|
2247
2256
|
interface GemDedicatedEvent {
|
|
2248
2257
|
readonly gem: typeof PROTOCOL;
|
|
2249
2258
|
readonly type: "gem.dedicatedEvent";
|
|
@@ -2560,6 +2569,7 @@ interface TelemetrySample {
|
|
|
2560
2569
|
readonly type: string;
|
|
2561
2570
|
readonly value?: number;
|
|
2562
2571
|
readonly transport?: TransportLabel;
|
|
2572
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
2563
2573
|
}
|
|
2564
2574
|
interface P2pApiOptions {
|
|
2565
2575
|
readonly client: GemClient;
|
|
@@ -2676,9 +2686,12 @@ interface IceCandidateLike {
|
|
|
2676
2686
|
readonly sdpMid?: string | null;
|
|
2677
2687
|
readonly sdpMLineIndex?: number | null;
|
|
2678
2688
|
}
|
|
2689
|
+
type EventListenerLike = {
|
|
2690
|
+
bivarianceHack(event: unknown): void;
|
|
2691
|
+
}["bivarianceHack"];
|
|
2679
2692
|
interface EventTargetLike {
|
|
2680
|
-
addEventListener(type: string, listener:
|
|
2681
|
-
removeEventListener(type: string, listener:
|
|
2693
|
+
addEventListener(type: string, listener: EventListenerLike): void;
|
|
2694
|
+
removeEventListener(type: string, listener: EventListenerLike): void;
|
|
2682
2695
|
}
|
|
2683
2696
|
interface DataChannelLike extends EventTargetLike {
|
|
2684
2697
|
readonly readyState: string;
|
|
@@ -2693,6 +2706,16 @@ interface DataChannelInitLike {
|
|
|
2693
2706
|
readonly negotiated?: boolean;
|
|
2694
2707
|
readonly id?: number;
|
|
2695
2708
|
}
|
|
2709
|
+
interface IceConfigurationLike {
|
|
2710
|
+
iceServers?: {
|
|
2711
|
+
urls: string | string[];
|
|
2712
|
+
username?: string;
|
|
2713
|
+
credential?: string;
|
|
2714
|
+
}[];
|
|
2715
|
+
}
|
|
2716
|
+
interface StatsReportLike {
|
|
2717
|
+
forEach(callback: (value: unknown, key: string) => void): void;
|
|
2718
|
+
}
|
|
2696
2719
|
interface PeerConnectionLike extends EventTargetLike {
|
|
2697
2720
|
readonly localDescription: SessionDescriptionLike | null;
|
|
2698
2721
|
readonly remoteDescription: SessionDescriptionLike | null;
|
|
@@ -2705,6 +2728,10 @@ interface PeerConnectionLike extends EventTargetLike {
|
|
|
2705
2728
|
setRemoteDescription(description: SessionDescriptionLike): Promise<void>;
|
|
2706
2729
|
addIceCandidate(candidate: IceCandidateLike): Promise<void>;
|
|
2707
2730
|
close(): void;
|
|
2731
|
+
setConfiguration?(configuration: IceConfigurationLike): void;
|
|
2732
|
+
getConfiguration?(): IceConfigurationLike;
|
|
2733
|
+
restartIce?(): void;
|
|
2734
|
+
getStats?(): Promise<StatsReportLike>;
|
|
2708
2735
|
}
|
|
2709
2736
|
interface PeerConnectionConfig {
|
|
2710
2737
|
readonly iceServers: readonly {
|
|
@@ -2739,6 +2766,7 @@ interface P2pSessionOptions {
|
|
|
2739
2766
|
readonly baseUrl: string;
|
|
2740
2767
|
readonly signaling: SignalingClient;
|
|
2741
2768
|
readonly clusterTargetId?: ClusterTargetLike;
|
|
2769
|
+
readonly clusterTargetIds?: readonly string[];
|
|
2742
2770
|
readonly peerConnectionFactory?: PeerConnectionFactory;
|
|
2743
2771
|
readonly certificate?: unknown;
|
|
2744
2772
|
readonly connectTimeoutMs?: number;
|
|
@@ -2755,7 +2783,11 @@ declare class P2pSession {
|
|
|
2755
2783
|
private readonly keys;
|
|
2756
2784
|
private localPlayerId;
|
|
2757
2785
|
private migration;
|
|
2758
|
-
private
|
|
2786
|
+
private lease;
|
|
2787
|
+
private clusterTargetIds;
|
|
2788
|
+
private leaseRenewal;
|
|
2789
|
+
private leaseAskedAt;
|
|
2790
|
+
private leaseRenewalWarned;
|
|
2759
2791
|
private credential;
|
|
2760
2792
|
private nextPeerId;
|
|
2761
2793
|
private ended;
|
|
@@ -2769,6 +2801,7 @@ declare class P2pSession {
|
|
|
2769
2801
|
private readonly answeredSessions;
|
|
2770
2802
|
private credentialGeneration;
|
|
2771
2803
|
private credentialDeadline;
|
|
2804
|
+
private readonly drainTimers;
|
|
2772
2805
|
constructor(options: P2pSessionOptions);
|
|
2773
2806
|
get matchId(): string;
|
|
2774
2807
|
get playerId(): string;
|
|
@@ -2794,6 +2827,7 @@ declare class P2pSession {
|
|
|
2794
2827
|
private fetchCredential;
|
|
2795
2828
|
private waitForCredential;
|
|
2796
2829
|
private onOffer;
|
|
2830
|
+
private answer;
|
|
2797
2831
|
private onAnswer;
|
|
2798
2832
|
private hostVerified;
|
|
2799
2833
|
private maybeAuthenticate;
|
|
@@ -2804,6 +2838,17 @@ declare class P2pSession {
|
|
|
2804
2838
|
private watchMigration;
|
|
2805
2839
|
private onMatchEnded;
|
|
2806
2840
|
private onSignalingError;
|
|
2841
|
+
private onRelayDraining;
|
|
2842
|
+
private onDrainDeadline;
|
|
2843
|
+
private withoutDrainingRelays;
|
|
2844
|
+
private restartIceToHost;
|
|
2845
|
+
private offerIceRestart;
|
|
2846
|
+
private progressRestart;
|
|
2847
|
+
private reportRestart;
|
|
2848
|
+
private takeLease;
|
|
2849
|
+
private refreshLease;
|
|
2850
|
+
private reconfigureLinks;
|
|
2851
|
+
private iceServers;
|
|
2807
2852
|
private openLink;
|
|
2808
2853
|
private flushCandidates;
|
|
2809
2854
|
private onReliableOpen;
|
|
@@ -2812,6 +2857,8 @@ declare class P2pSession {
|
|
|
2812
2857
|
private refuseLink;
|
|
2813
2858
|
private finishAdmission;
|
|
2814
2859
|
private onIceState;
|
|
2860
|
+
private recoverHostLink;
|
|
2861
|
+
private voteHostUnreachable;
|
|
2815
2862
|
private onConnectionState;
|
|
2816
2863
|
private expose;
|
|
2817
2864
|
private dropLink;
|
|
@@ -3070,6 +3117,7 @@ interface FrameCallbacks {
|
|
|
3070
3117
|
onTicketEvent?(msg: GemTicketEvent): void;
|
|
3071
3118
|
onSocketState?(msg: GemSocketState): void;
|
|
3072
3119
|
onSignalEvent?(msg: GemSignalEvent): void;
|
|
3120
|
+
onRelayDraining?(msg: GemRelayDraining): void;
|
|
3073
3121
|
onDedicatedEvent?(msg: GemDedicatedEvent): void;
|
|
3074
3122
|
onClusterTarget?(msg: GemClusterTarget): void;
|
|
3075
3123
|
onPartyResult?(msg: GemPartyResult): void;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var GemWebSdkBridgeBundle=(function(){'use strict';var L="GemWebSdkHandshake";function oe(){return typeof document>"u"?void 0:document.readyState}var U=()=>Date.now();function H(n={}){let e=n.win??window,t=e,r=t[L];if(r!==void 0)return r;let s=n.readyState??oe,i=n.now??U,d=[],a=[],u=y=>{let c=y.data;!c||c.type!=="gem.handshake"||(d.length>=4&&(d.shift(),a.shift(),p.dropped+=1),d.push(y),a.push(i()));};e.addEventListener("message",u);let p={events:d,times:a,dropped:0,late:s()==="complete",uninstall(){e.removeEventListener("message",u);}};return t[L]=p,p}function B(n,e,t={}){let r=n[L];if(r===void 0)return null;let s=t.ttlMs??9500,i=t.now??U,d=r.events.splice(0),a=r.times?.splice(0),u=0,p=0,y=false,c=i()-s;for(let f=d.length-1;f>=0;f--){let S=a?.[f];if(S!==void 0&&S<c){p+=1;continue}if(u+=1,e(d[f])===true){y=true;break}}return r.uninstall(),{capture:r,delivered:u,stale:p,accepted:y}}var l="gem-web-bridge",R={min:1,max:1},ae=65536,N=8,G=5e3,W=1024;var b=Object.create(null),O="__malformed";var j=Object.create(null),se=Object.freeze([["gem.asyncOpponents.","asyncOpponents"],["gem.vibration.","vibration"],["gem.viewport.","reservedAreas"],["gem.moments.","moments"],["gem.prefs.","audioChannels"]]);function x(n){let e=j[n];if(e!==void 0)return e;for(let[t,r]of se)if(n.startsWith(t))return r}function ie(n){let e=/^([a-z][a-z0-9+.-]*):\/\/([a-z0-9.-]+|\[[0-9a-f:.]+\])(:([0-9]{1,5}))?$/i.exec(n);return e===null?false:e[4]===void 0||Number(e[4])<=65535}function le(n){let e;try{e=new URL(n);}catch{return false}return e.username!==""||e.password!==""?false:e.protocol==="https:"?true:e.protocol==="http:"&&(e.hostname==="localhost"||e.hostname==="127.0.0.1")}function de(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function me(n){return /^[a-z_]+$/.test(n)}function ue(n){return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(n)||/^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$/.test(n)&&!n.includes("--")}function _(n,e,t){if(e>N)return `nesting deeper than ${N}`;if(++t.nodes>G)return `more than ${G} values`;if(t.bytes>t.cap)return `over the ${t.cap} byte limit`;if(n==null)return t.bytes+=4,null;let r=typeof n;if(r==="boolean")return t.bytes+=5,null;if(r==="number")return t.bytes+=8,null;if(r==="string")return t.bytes+=n.length*3+2,null;if(r!=="object")return `${r} is not plain data`;let s=n;if(t.seen.has(s))return "a repeated or cyclic object reference";if(t.seen.add(s),Array.isArray(n)){if(n.length>W)return `an array of ${n.length} entries, over the ${W} limit`;t.bytes+=2;for(let a of n){let u=_(a,e+1,t);if(u)return u}return null}let i=Object.getPrototypeOf(n);if(i!==Object.prototype&&i!==null)return "a value that is not a plain object";t.bytes+=2;let d=n;for(let a in d){if(!Object.prototype.hasOwnProperty.call(d,a))continue;if(++t.nodes>G)return `more than ${G} values`;if(t.bytes+=a.length*3+4,t.bytes>t.cap)return `over the ${t.cap} byte limit`;let u=_(d[a],e+1,t);if(u)return u}return null}function ce(n,e=ae){let t={nodes:0,bytes:0,seen:new Set,cap:e},r=_(n,0,t);return r?`message contains ${r}`:t.bytes>e?`message is over the ${e} byte limit`:null}var ge={origin:[ie,"not a well-formed origin"],url:[le,"not a safe absolute http(s) URL"],bcp47:[de,"not a well-formed BCP 47 tag"],code:[me,"not a code of lower-case letters and underscores"],gameRef:[ue,"not a game id or web address"]};function V(n,e,t){for(let r of e){let s=t===""?r[0]:`${t}.${r[0]}`,i=n[r[0]];if(i==null){if(r[1]<"a")continue;return `${s}: required`}let d=M(i,r,s);if(d)return d}return null}function M(n,e,t){let r=e[1].toLowerCase();switch(r){case "u":return typeof n=="boolean"?null:M(n,typeof n=="string"?[e[0],"s",e[2]]:[e[0],"n",e[3],e[4]],t);case "s":{if(typeof n!="string")return `${t}: expected string`;if(n.length>e[2])return `${t}: exceeds ${e[2]} characters`;if(e[3]){let[s,i]=ge[e[3]];if(!s(n))return `${t}: ${i}`}return null}case "n":case "i":return typeof n!="number"||!(r=="i"?Number.isInteger:Number.isFinite)(n)?`${t}: expected ${r=="i"?"integer":"number"}`:n<e[2]||n>e[3]?`${t}: out of range ${e[2]}..${e[3]}`:null;case "b":return typeof n=="boolean"?null:`${t}: expected boolean`;case "e":{let s=e[2];return s.includes(n)?null:`${t}: not one of ${s.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:V(n,e[2],t);case "j":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected a plain object`:null;case "a":{if(!Array.isArray(n))return `${t}: expected array`;if(n.length>e[2])return `${t}: exceeds ${e[2]} items`;let s=e[3],i=`${t}[]`;for(let d of n){if(d==null){if(s[1]<"a")continue;return `${i}: required`}let a=M(d,s,i);if(a)return a}return null}default:return `${t}: unknown kind ${String(r)}`}}var z=Object.create(null);function o(n,e,t,r,s){return b[n]=t,r&&(j[n]=r),z[n]=i=>{let d=ce(i,s);if(d)return d;if(typeof i!="object"||i===null||Array.isArray(i))return "message: expected object";let a=i;return a.gem!==l?"gem: wrong protocol marker":a.type!==n?`type: expected ${n}`:V(a,e,"")}}var m=["requestId","s",64],h=["outcome","e",["ok","declined","unavailable"]],v=["reason","S",64,"code"];o("gem.handshake",[["versions","o",[["min","i",1,1e3],["max","i",1,1e3]]],["capabilities","A",64,["","s",64]],["embedder","s",2048,"origin"],["unbidden","B"]],30);o("gem.handshake.accept",[["v","i",1,1e3],["capabilities","A",64,["","s",64]]],5);o("gem.token",[["token","s",4096],["requestId","S",64],["generation","i",0,2147483647],["apiBaseUrl","S",512,"url"],["playerId","s",64],["gameId","s",64],["channel","e",["dev","test","release"]],["gameVersion","S",64],["expiresAt","i",0,4102444800],["chainEndsAt","i",0,4102444800],["allowDevTools","B"]],30);o("gem.token.denied",[m,["retryable","b"],["retryAfterMs","I",0,36e5],["reason","e",["chain_exhausted","unavailable"]],["correlationId","s",64]],30);o("gem.player",[["untrusted","o",[["displayName","s",64],["displayTag","S",80],["displayHandle","s",80]]],["playerId","S",64],["discriminator","S",8],["avatarIconUrl","S",2048,"url"],["avatarIconId","S",64],["avatar3dId","S",64],["avatar3dModelUrl","S",2048,"url"],["avatar3dIconUrl","S",2048,"url"],["avatars3d","A",8,["","o",[["family","s",32],["id","s",64],["modelUrl","s",256,"url"],["iconUrl","s",256,"url"]]]],["badges","A",2,["","o",[["itemId","s",64],["iconUrl","s",2048,"url"]]]],["displayTag","s",8],["avatarUrl","S",2048,"url"],["boost","O",[["multipliers","a",8,["","o",[["currencyId","s",64],["currencySlug","s",32],["multiplierBp","i",1e4,1e6]]]],["expiresInDays","I",1,7]]]],12);o("gem.pause",[["paused","b"]],120);o("gem.prefs",[["reducedMotion","b"],["muted","b"],["showFrameStats","B"],["music","B"],["sounds","B"],["locale","s",35,"bcp47"],["recommended","O",[["volume","i",0,100],["batterySaver","b"],["keyboardLayout","e",["wasd","arrows"]],["gamepadConfirm","e",["south","east"]],["vibration","b"],["lookSensitivity","I",20,400],["gamepadLookSensitivity","I",20,400],["invertLookY","B"],["touchMovement","S",16,"code"],["graphicsQuality","S",16,"code"],["graphicsQualityAuto","B"],["colorHelp","B"]]]],60);o("gem.cohorts",[["cohorts","a",8,["","o",[["experimentId","s",36],["variantKey","s",32]]]]],12,"cohorts");o("gem.earnAllowanceChanged",[],30,"earnAllowance");o("gem.viewport",[["insets","o",[["top","i",0,4096],["right","i",0,4096],["bottom","i",0,4096],["left","i",0,4096]]],["reserved","A",4,["","o",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384],["control","O",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384]]]]]]],60,"viewport");o("gem.deviceSettings",[["settings","J"]],12,"deviceSettings");o("gem.willTerminate",[["reason","e",["grant_expiring","session_ended","navigating","kill_switch"]],["graceMs","i",0,6e4]],10);o("gem.roomEvent",[["event","e",["updated","match_started","match_ready","match_failed","version_stale"]],["roomId","s",64],["matchId","S",64],["version","I",0,2147483647],["reason","S",64],["platform","E",["universal","mobile"]]],120,"rooms");o("gem.socketState",[["connected","b"],["reason","E",["connecting","dropped","watchdog","terminal"]]],12,"socketState");o("gem.seatedEvent",[["roomId","s",64]],60,"partySeating");o("gem.autoStartFailedEvent",[["roomId","s",64],["code","s",64]],30,"autoStartFailure");o("gem.ticketEvent",[["event","e",["queued","matched","expired","cancelled","failed"]],["ticketId","s",64],["partySize","I",1,64],["queuedAt","I",0,4102444800],["expiresAt","I",0,4102444800],["reason","S",64],["roomId","S",64]],60,"ticketEvents");o("gem.signalEvent",[["event","e",["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]],["matchId","s",64],["data","j"]],1800,"p2p");o("gem.dedicatedEvent",[["event","e",["answer","error","matchEnded"]],["matchId","s",64],["sdp","S",10240],["sessionId","S",64],["code","S",64],["message","S",256],["reason","S",64]],120,"dedicated");o("gem.partyResult",[m,["partyId","S",64]],30,"parties");o("gem.clusterTarget",[["clusterTargetId","s",64],["clusterTargetIds","A",3,["","s",64]]],12,"clusterTarget");o("gem.devtools",[["action","e",["open","close","toggle"]]],30,"devtools");o("gem.screenshot",[["maxWidth","i",1,7680],["maxHeight","i",1,4320]],6,"screenshot");o("gem.screenshot.denied",[["reason","e",["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]],["retryable","b"]],12,"screenshot");o("gem.requestContext",[],12,"feedbackContext");o("gem.gameContext",[["context","j"]],12,"feedbackContext");o("gem.signal",[["signal","e",["join","offer","answer","ice","migrationAck","hostUnreachable"]],["matchId","s",64],["data","j"]],1200,"p2p");o("gem.dedicatedOffer",[["matchId","s",64],["sdp","s",10240],["intent","e",["initial","ice_restart","full_reconnect"]]],60,"dedicated");o("gem.partyRequest",[m],12,"parties");o("gem.ready",[],5);o("gem.engaged",[],5,"engagement");o("gem.progress",[["loaded","i",0,1099511627776],["total","i",0,1099511627776]],600);o("gem.requestToken",[m,["reason","e",["initial","expiring","rejected"]]],12);o("gem.requestLocalServer",[m,["gameRoomId","s",64]],6,"localHost");o("gem.localServer",[m,["serverId","S",128]],30,"localHost");o("gem.localServer.denied",[m,["retryable","b"],["reason","e",["refused","unavailable"]]],30,"localHost");o("gem.exit",[["reason","S",64]],5);o("gem.openExternal",[["url","s",2048,"url"]],6);o("gem.requestFullscreen",[["enabled","b"]],30);o("gem.audioState",[["muted","b"]],120,"audioState");o("gem.rewardCue",[["currencySlug","s",32],["expected","I",1,1e9],["origin","O",[["x","i",0,16384],["y","i",0,16384]]]],60,"rewardCue");o("gem.walletDisplay",[["visible","b"],["currencies","A",8,["","s",64]],["score","B"]],60,"walletDisplay");o("gem.controlsDeclare",[["uses","o",[["lookSensitivity","S",16,"code"],["gamepadLookSensitivity","S",16,"code"],["invertLookY","S",16,"code"],["touchMovement","S",16,"code"]]]],60,"controls");o("gem.menu",[["settings","b"],["items","A",4,["","o",[["id","s",32],["label","s",40]]]]],60,"gameMenu");o("gem.menuSelect",[["entry","e",["settings","item"]],["id","S",32]],60,"gameMenu");o("gem.gemCorner",[["corner","e",["top-left","top-right","bottom-left","bottom-right"]]],12,"gemCorner");o("gem.menuStyle",[["ground","S",7],["accent","S",7]],12,"menuStyle");o("gem.settingsDeclare",[["settings","a",8,["","o",[["id","s",40],["label","s",40],["kind","e",["toggle","slider","choice"]],["value","u",40,-1e6,1e6],["quick","B"],["icon","S",16,"code"],["min","N",-1e6,1e6],["max","N",-1e6,1e6],["step","N",0,1e6],["options","A",4,["","o",[["id","s",40],["label","s",40]]]]]]]],120,"gameSettings");o("gem.settingChange",[["id","s",40],["value","u",40,-1e6,1e6]],120,"gameSettings");o("gem.requestShare",[["params","a",16,["","o",[["key","s",64],["value","s",512]]]]],6,"share");o("gem.requestAvatarPicker",[["tab","E",["2d","3d"]]],6,"avatarPicker");o("gem.vibrate",[["pattern","a",99,["","i",0,1e4]]],600,"vibration");o("gem.frameStats",[["fps","i",0,1e3],["frameTimeP95Ms","i",0,1e5],["counts","E",["pageFrames","gameDraws"]]],120,"frameStats");o("gem.portraitBorders",[["name","S",40]],60,"portraitBorders");o("gem.error",[["code","e",["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]],["message","S",256]],60);o("gem.requestPurchase",[m,["purchaseKind","e",["item","recipe"]],["targetId","s",64],["currencySlug","s",32],["quantity","I",1,10],["catalogId","S",64]],12,"purchases");o("gem.requestAvatar3dPurchase",[m,["itemId","s",64],["currencySlug","s",32]],12,"avatar3dPurchases");o("gem.purchaseResult",[m,h,v,["purchaseId","S",64]],30,"purchases");o("gem.deviceSettings.put",[m,["settings","j"]],30,"deviceSettings");o("gem.deviceSettings.result",[m,["outcome","e",["stored","too_large","unavailable"]]],30,"deviceSettings");o("gem.requestAd",[m,["placement","s",32],["format","e",["interstitial","rewarded"]]],6,"ads");o("gem.adResult",[m,["outcome","e",["shown","no_ad","unavailable"]]],12,"ads");o("gem.signalMoment",[m,["kind","e",["level_end","run_end","match_end","menu_return","other_break"]],["placement","S",32],["wall","B"]],6,"moments");o("gem.momentResult",[m,["shown","b"]],12,"moments");o("gem.requestSignIn",[m],6,"signIn");o("gem.signInResult",[m,["outcome","e",["signed_in","declined","unavailable"]]],12,"signIn");o("gem.requestFriendChallenge",[m,["playerId","s",64],["template","s",64],["params","J"]],12,"friendChallenges");o("gem.friendChallengeResult",[m,["outcome","e",["accepted","cancelled","refused","unavailable","rate_limited","invalid"]],["code","S",64],["param","S",64],["channel","S",16],["configVersion","I",0,9007199254740991],["retryAfterMs","I",0,864e5],["detail","S",300]],30,"friendChallenges");o("gem.requestAppLinks",[m],12,"appLinks");o("gem.appLinksResult",[m,h,v,["arcade","O",[["store","e",["app_store","google_play"]]]],["game","O",[["store","e",["app_store","google_play"]]]]],30,"appLinks");o("gem.requestOpenApp",[m,["target","e",["arcade","game"]]],6,"appLinks");o("gem.openAppResult",[m,h,v],30,"appLinks");o("gem.requestOpenGame",[m,["ref","s",40,"gameRef"]],6,"openGame");o("gem.openGameResult",[m,h,v],30,"openGame");o("gem.requestSimilarGames",[m,["limit","I",1,24]],12,"openGame");o("gem.similarGamesResult",[m,h,v,["games","a",24,["","o",[["gameId","s",36,"gameRef"],["slug","S",40,"gameRef"],["iconUrl","s",1024,"url"],["capsuleLandscapeUrl","s",1024,"url"],["capsulePortraitUrl","s",1024,"url"],["previewVideoUrl","S",1024,"url"],["previewPosterUrl","S",1024,"url"],["untrusted","o",[["name","s",64],["tagline","s",64],["shortDescription","s",320],["tags","a",10,["","s",32]]]]]]]],30,"openGame",524288);o("gem.requestStoreOffers",[m],12,"store");o("gem.storeOffersResult",[m,h,v,["offers","a",32,["","o",[["skuId","s",64],["kind","e",["gem_pack","boost","pass","bundle"]],["name","s",80],["description","s",400],["imageUrl","S",1024,"url"],["priceDisplay","s",32],["grant","O",[["currency","s",32],["amount","i",1,1e9]]]]]]],30,"store",262144);o("gem.requestStorePurchase",[m,["skuId","s",64]],12,"store");o("gem.storePurchaseResult",[m,h,v],30,"store");o("gem.storePurchaseSettled",[["skuId","s",64]],30,"store");o("gem.requestStoreOfferSheet",[m,["skuId","s",64]],12,"storeOfferSheet");o("gem.storeOfferSheetResult",[m,h,v],30,"storeOfferSheet");var P=Object.freeze(z);b.__malformed=60;Object.freeze(b);Object.freeze(j);var pe=/^[a-z0-9][a-z0-9_-]{0,39}$/;function ye(n){return typeof n=="string"&&pe.test(n)}function K(n){if(n===null||ye(n))return n;let e=typeof n=="string"?JSON.stringify(n.length>48?`${n.slice(0,48)}\u2026`:n):`a value of type ${typeof n}`;console.warn(`[gem] showPortraitBorders: ${e} is not a border name, so nothing was sent. A name is 1 to 40 of a-z, 0-9, _ and -, starting with a letter or digit, exactly as your store page declares it under media.portrait_borders. Pass null for no art.`);}var D={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)},w=class{constructor(e=D){this.clock=e;}clock;tokens=new Map;lastRefill=new Map;allow(e){let t=b[e];if(t===void 0)return false;let r=this.clock.now(),s=this.lastRefill.get(e)??r,i=this.tokens.get(e)??t,d=Math.max(0,r-s),a=Math.min(t,i+d*t/6e4);return a<1?(this.tokens.set(e,a),this.lastRefill.set(e,r),false):(this.tokens.set(e,a-1),this.lastRefill.set(e,r),true)}};function T(n,e){return n.toLowerCase()===e.toLowerCase()}function C(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}function Y(n,e){if(typeof e!="string"||e==="")return false;let t,r;try{t=new URL(n),r=new URL(e);}catch{return false}return t.protocol!=="http:"&&t.protocol!=="https:"||r.protocol!=="http:"&&r.protocol!=="https:"||t.username!==""||t.password!==""||t.pathname!=="/"||t.search!==""||t.hash!==""||t.port===""?false:C(t.hostname)&&C(r.hostname)}function J(n,e){if(n.min>n.max||e.min>e.max)return null;let t=Math.min(n.max,e.max),r=Math.max(n.min,e.min);return t>=r?t:null}var Z="[gem: use gem.text.toPlain()]",Q=new Set,X;function fe(n){try{if(Q.has(n))return;let e=globalThis.location;if(X??=!!e?.port&&C(e.hostname??""),!X)return;Q.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${Z}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
|
|
2
|
-
${new Error().stack}`);}catch{}}var F=class{#e;#t;constructor(e,t="player text"){this.#e=e,this.#t=t,Object.freeze(this);}toString(){return fe(this.#t),Z}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function $(n,e){return new F(n,e)}function ee(n){return Math.min(16384,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function he(n,e){if(n!==true)return {visible:false};let t=[],r=false;for(let s of Array.isArray(e)?e:[])s==="score"?r=true:typeof s=="string"&&s.length>0&&s.length<=64&&!t.includes(s)&&t.push(s);return !r&&t.length===0?{visible:true}:{visible:true,currencies:t.slice(0,8),score:r}}function ve(n){let e=[];for(let t of Array.isArray(n)?n:[]){if(e.length===4)break;if(typeof t!="object"||t===null)continue;let{id:r,label:s}=t;typeof r!="string"||r.length===0||r.length>32||typeof s!="string"||s.trim().length===0||e.some(i=>i.id===r)||e.push({id:r,label:s.trim().slice(0,40)});}return e.length===0?{settings:false}:{settings:false,items:e}}function te(n,e){return Math.min(e,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function Se(n){let e=n.untrusted,t=$(e.displayName,"gem.player.displayName"),r=e.displayTag||e.displayHandle,s=r?$(r,"gem.player.displayTag"):void 0,i=n.discriminator||n.displayTag,d=i&&/^[0-9]{1,8}$/.test(i)?i:void 0,a=n.avatarIconUrl||n.avatarUrl,u=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,p=n.avatarIconId,y=n.avatar3dId,c={displayName:t,displayHandle:s??t,badges:(n.badges??[]).map(g=>({itemId:g.itemId,iconUrl:g.iconUrl}))};n.playerId&&(c.playerId=n.playerId),d&&(c.discriminator=d),s&&(c.displayTag=s),a&&(c.avatarIconUrl=c.avatarUrl=a),p&&u.test(p)&&(c.avatarIconId=p),y&&u.test(y)&&(c.avatar3dId=y),n.avatar3dModelUrl&&(c.avatar3dModelUrl=n.avatar3dModelUrl),n.avatar3dIconUrl&&(c.avatar3dIconUrl=n.avatar3dIconUrl),n.avatars3d&&(c.avatars3d=n.avatars3d.filter(g=>u.test(g.id)&&/^[a-z][a-z0-9_]*$/.test(g.family)).filter((g,q,I)=>I.findIndex(A=>A.family==g.family)==q).map(({family:g,id:q,modelUrl:I,iconUrl:A})=>({family:g,id:q,modelUrl:I,iconUrl:A})));let f=n.boost,S=(f?.multipliers??[]).map(g=>({currencyId:g.currencyId,currencySlug:g.currencySlug,multiplier:g.multiplierBp/1e4})),E=S.some(g=>g.multiplier>1);return c.boost=E&&f?.expiresInDays?{active:E,multipliers:S,expiresInDays:f.expiresInDays}:{active:E,multipliers:S},c}var Re=1e4,be=["appLinks","openGame","store","storeOfferSheet"],we=new WeakMap;var k=class{constructor(e){this.options=e;if(e.expectedSource===void 0||e.expectedSource===null)throw new Error("GemFrame: expectedSource is required and must not be null \u2014 pass window.parent. The handshake transfers the MessagePort, and origin alone does not say WHICH document on that origin sent it.");this.expectedSource=e.expectedSource,this.clock=e.clock??D,this.inbound=new w(this.clock),this.outbound=new w(this.clock);}options;state="waiting";port=null;version=null;peerCapabilities=[];unbidden=false;engagedSent=false;readyPending=false;readySent=false;settleWaiters=[];clock;inbound;outbound;expectedSource;get currentState(){return this.state}get parentCapabilities(){return this.peerCapabilities}parentSupports(e){return this.peerCapabilities.includes(e)}get protocolVersion(){return this.version}get unbiddenLaunch(){return this.unbidden}handleWindowMessage(e){if(e.source!==this.expectedSource)return this.refuse(e,"handshake did not come from the expected embedder window"),false;if(!(this.options.allowedEmbedders.some(a=>T(a,e.origin))||Y(e.origin,this.options.selfOrigin)||(we.get(this)??[]).some(a=>T(a,e.origin))))return this.refuse(e,`embedder '${e.origin}' is not in allowedEmbedders`),this.state==="waiting"&&this.transition("refused"),false;if(this.state==="connected")return this.fail("handshake_duplicate","this document already has a port"),false;if(this.state!=="waiting")return false;let r=P["gem.handshake"]?.(e.data);if(r)return this.refuse(e,`malformed handshake: ${r}`),false;let s=e.data;if(!T(s.embedder,e.origin))return this.refuse(e,`handshake claims embedder '${s.embedder}' but arrived from '${e.origin}'`),false;let i=e.ports?.[0];if(!i)return this.refuse(e,"handshake transferred no MessagePort"),false;let d=J(R,s.versions);return d===null?(i.onmessage=null,i.postMessage({gem:l,type:"gem.error",code:"version_mismatch",message:`frame supports v${R.min}..v${R.max}`}),i.close(),this.transition("refused"),this.options.onError?.("version_mismatch"),false):(this.port=i,this.version=d,i.onmessage=a=>this.receive(a.data,a.ports??[]),i.start?.(),this.peerCapabilities=s.capabilities??[],this.unbidden=s.unbidden===true,i.postMessage({gem:l,type:"gem.handshake.accept",v:d,...this.options.capabilities===void 0?{}:{capabilities:[...this.options.capabilities]}}),this.transition("connected"),this.readyPending&&(this.readyPending=false,this.ready()),true)}ready(){if(this.state!=="connected"||!this.port){this.readyPending=true;return}if(this.readySent){this.options.onDropped?.("internal","gem.ready was already sent; duplicate suppressed");return}this.readySent=true,this.send({gem:l,type:"gem.ready"});}settled(e=Re){return this.state!=="waiting"?Promise.resolve(this.state):new Promise(t=>{let r=i=>{this.clock.clearTimeout(s),t(i);},s=this.clock.setTimeout(()=>{let i=this.settleWaiters.indexOf(r);i>=0&&this.settleWaiters.splice(i,1),t(this.state);},e);this.settleWaiters.push(r);})}progress(e,t){this.send({gem:l,type:"gem.progress",loaded:e,total:t});}requestToken(e,t){this.send({gem:l,type:"gem.requestToken",requestId:e,reason:t});}requestLocalServer(e,t){return this.parentSupports("localHost")?(this.send({gem:l,type:"gem.requestLocalServer",requestId:e,gameRoomId:t}),true):false}exit(e){this.send(e===void 0?{gem:l,type:"gem.exit"}:{gem:l,type:"gem.exit",reason:e});}openExternal(e){this.send({gem:l,type:"gem.openExternal",url:e});}requestFullscreen(e){this.send({gem:l,type:"gem.requestFullscreen",enabled:e});}reportMuted(e){return this.gated("audioState")?(this.send({gem:l,type:"gem.audioState",muted:e}),true):false}reportEngaged(){return this.engagedSent||!this.gated("engagement")||!this.send({gem:l,type:"gem.engaged"})?false:(this.engagedSent=true,true)}cueReward(e){if(!this.gated("rewardCue"))return false;let t={gem:l,type:"gem.rewardCue",currencySlug:String(e.currencySlug).slice(0,32)};return e.expected!==void 0&&Number.isFinite(e.expected)&&e.expected>=1&&(t.expected=Math.min(1e9,Math.round(e.expected))),e.origin&&(t.origin={x:ee(e.origin.x),y:ee(e.origin.y)}),this.send(t),true}showPortraitBorders(e){let t=K(e);return t===void 0||!this.gated("portraitBorders")?false:this.send(t===null?{gem:l,type:"gem.portraitBorders"}:{gem:l,type:"gem.portraitBorders",name:t})}setWalletVisible(e,t){return this.gated("walletDisplay")?this.send({gem:l,type:"gem.walletDisplay",...he(e,t?.only)}):false}setMenu(e){return this.gated("gameMenu")?this.send({gem:l,type:"gem.menu",...ve(e)}):false}declareSettings(e){return this.gated("gameSettings")?this.send({gem:l,type:"gem.settingsDeclare",settings:e}):false}setGemCorner(e){return this.gated("gemCorner")?this.send({gem:l,type:"gem.gemCorner",corner:e}):false}setMenuStyle(e){return this.gated("menuStyle")?this.send(e===null?{gem:l,type:"gem.menuStyle"}:{gem:l,type:"gem.menuStyle",ground:e.ground,accent:e.accent}):false}requestShare(e){return this.gated("share")?this.send({gem:l,type:"gem.requestShare",params:e.map(({key:t,value:r})=>({key:t,value:r}))}):false}requestAvatarPicker(e){return this.gated("avatarPicker")?this.send({gem:l,type:"gem.requestAvatarPicker",...e==="2d"||e==="3d"?{tab:e}:{}}):false}requestVibrate(e){return this.gated("vibration")?this.send({gem:l,type:"gem.vibrate",pattern:[...e]}):false}reportFrameStats(e){return this.gated("frameStats")?(this.send({gem:l,type:"gem.frameStats",fps:te(e.fps,1e3),frameTimeP95Ms:te(e.frameTimeP95Ms,1e5),counts:e.counts==="gameDraws"?"gameDraws":"pageFrames"}),true):false}sendGameContext(e){return this.state!=="connected"?false:(this.send({gem:l,type:"gem.gameContext",context:e}),true)}signal(e,t,r){this.send({gem:l,type:"gem.signal",signal:e,matchId:t,data:r});}dedicatedOffer(e,t,r){this.send({gem:l,type:"gem.dedicatedOffer",matchId:e,sdp:t,intent:r});}requestParty(e){return this.send({gem:l,type:"gem.partyRequest",requestId:e})}requestPurchase(e,t,r,s,i,d){return this.send({gem:l,type:"gem.requestPurchase",requestId:e,purchaseKind:t,targetId:r,currencySlug:s,...i===void 0?{}:{quantity:i},...d===void 0?{}:{catalogId:d}})}requestAvatar3dPurchase(e,t,r){return this.parentSupports("avatar3dPurchases")?this.send({gem:l,type:"gem.requestAvatar3dPurchase",requestId:e,itemId:t,currencySlug:r}):false}putDeviceSettings(e,t){return this.send({gem:l,type:"gem.deviceSettings.put",requestId:e,settings:t})}requestAd(e,t,r){return this.send({gem:l,type:"gem.requestAd",requestId:e,placement:t,format:r})}signalMoment(e,t,r,s){return this.send({gem:l,type:"gem.signalMoment",requestId:e,kind:t,...r===void 0?{}:{placement:r},...s===true?{wall:true}:{}})}readyCalled(){return this.readySent||this.readyPending}requestSignIn(e){return this.send({gem:l,type:"gem.requestSignIn",requestId:e})}requestFriendChallenge(e,t,r,s){return this.send({gem:l,type:"gem.requestFriendChallenge",requestId:e,playerId:t,template:r,...s===void 0?{}:{params:s}})}requestArcade(e){return this.gated(x(e.type))&&this.send({gem:l,...e})}close(){this.port&&(this.port.onmessage=null,this.port.close(),this.port=null),this.state!=="refused"&&this.transition("closed");}transition(e){if(this.state!==e){this.state=e;for(let t of this.settleWaiters.splice(0))t(e);this.options.onStateChange?.(e);}}refuseMalformed(e,t){if(!this.inbound.allow(O)){this.options.onDropped?.(e,t);return}this.fail(e,t);}dropUnknownType(e){this.inbound.allow(O)&&this.options.onDropped?.("unsupported",`ignored unknown message type '${e}'`);}refuse(e,t){e.ports?.[0]?.close?.(),this.inbound.allow(O)&&this.options.onRefused?.(t);}fail(e,t){this.options.onError?.(e,t),this.state==="connected"&&this.port&&this.send({gem:l,type:"gem.error",code:e,...t===void 0?{}:{message:t}});}gated(e){return this.state==="connected"&&this.parentSupports(e)}send(e){if(this.state!=="connected"||!this.port)return this.options.onDropped?.("internal",`dropped ${String(e.type)} before connect`),false;let t=e.type;if(!this.outbound.allow(t))return this.options.onError?.("rate_limited",`frame exceeded its own limit for ${t}`),false;let r=P[t]?.(e);return r?(this.options.onError?.("internal",`refusing to send malformed ${t}: ${r}`),false):(this.port.postMessage(this.version===null?e:{...e,v:this.version}),true)}receive(e,t=[]){let r=false;try{r=this.dispatchInbound(e,t);}finally{r||ne(t);}}dispatchInbound(e,t){if(this.state!=="connected")return false;let r=typeof e=="object"&&e!==null?e.type:void 0;if(typeof r!="string")return this.refuseMalformed("invalid_message","message has no type"),false;let s=P[r];if(!s)return this.dropUnknownType(r),false;if(!this.inbound.allow(r))return this.fail("rate_limited",`parent exceeded the limit for ${r}`),false;let i=x(r);if(i!==void 0&&!this.options.capabilities?.includes(i))return this.fail("unsupported_capability",`${r} requires the '${i}' capability, which this frame did not advertise`),false;let d=s(e);if(d)return this.fail("invalid_message",`${r}: ${d}`),false;let a=e;if(a.v!==this.version)return this.fail("invalid_message",`${r}: expected v ${String(this.version)}`),false;switch(r){case "gem.token":this.options.onToken?.(a);break;case "gem.pause":this.options.onPause?.(a);break;case "gem.menuSelect":this.options.onMenuSelect?.(a);break;case "gem.settingChange":this.options.onSettingChange?.(a);break;case "gem.prefs":this.options.onPrefs?.(a);break;case "gem.cohorts":this.options.onCohorts?.(a);break;case "gem.earnAllowanceChanged":this.options.onEarnAllowanceChanged?.();break;case "gem.viewport":this.options.onViewport?.(a);break;case "gem.willTerminate":this.options.onWillTerminate?.(a);break;case "gem.devtools":this.options.onDevtools?.(a.action);break;case "gem.requestContext":this.options.onRequestContext?.();break;case "gem.screenshot":{let u=t[0];if(!u){this.options.onError?.("invalid_message","gem.screenshot transferred no MessagePort");break}let p={maxWidth:a.maxWidth,maxHeight:a.maxHeight};if(!this.options.onScreenshot){u.close(),this.screenshotDenied("not_wired",false);break}return ne(t.slice(1)),this.options.onScreenshot(p,u,(y,c)=>this.screenshotDenied(y,c)),true}case "gem.token.denied":this.options.onTokenDenied?.(a);break;case "gem.localServer":this.options.onLocalServer?.(a);break;case "gem.localServer.denied":this.options.onLocalServerDenied?.(a);break;case "gem.player":{this.options.onPlayer?.(Se(a));break}case "gem.roomEvent":this.options.onRoomEvent?.(a);break;case "gem.seatedEvent":this.options.onSeatedEvent?.(a);break;case "gem.autoStartFailedEvent":this.options.onAutoStartFailedEvent?.(a);break;case "gem.ticketEvent":this.options.onTicketEvent?.(a);break;case "gem.socketState":this.options.onSocketState?.(a);break;case "gem.signalEvent":this.options.onSignalEvent?.(a);break;case "gem.dedicatedEvent":this.options.onDedicatedEvent?.(a);break;case "gem.clusterTarget":this.options.onClusterTarget?.(a);break;case "gem.partyResult":this.options.onPartyResult?.(a);break;case "gem.purchaseResult":this.options.onPurchaseResult?.(a);break;case "gem.deviceSettings":this.options.onDeviceSettings?.(a);break;case "gem.deviceSettings.result":this.options.onDeviceSettingsResult?.(a);break;case "gem.adResult":this.options.onAdResult?.(a);break;case "gem.momentResult":this.options.onMomentResult?.(a);break;case "gem.signInResult":this.options.onSignInResult?.(a);break;case "gem.friendChallengeResult":this.options.onFriendChallengeResult?.(a);break;case "gem.error":this.options.onError?.(a.code,a.message);break;default:if(be.includes(x(r))){this.options.onArcadeAnswer?.(a);break}this.fail("unsupported",`no frame handler for '${r}'`);}return false}screenshotDenied(e,t){this.send({gem:l,type:"gem.screenshot.denied",reason:e,retryable:t});}};function ne(n){for(let e of n)try{e?.close?.();}catch{}}H();function ke(n){if((n.expectedSource??window.parent)===window)throw new Error("GemWebSdkBridge.connect: this document is not framed (window.parent === window), so there is no embedder to hand it a port. A game runs inside the Gem Arcade iframe; opening it directly will never complete a handshake.");let e=new k({...n,expectedSource:n.expectedSource??window.parent}),t=r=>{let s=e.handleWindowMessage({data:r.data,origin:r.origin,source:r.source,ports:r.ports});return s&&window.removeEventListener("message",t),s};return window.addEventListener("message",t),B(window,t),e}var re={connect:ke,GemFrame:k,PROTOCOL:l,SUPPORTED_VERSIONS:R};window.GemWebSdkBridge=re;var yn=re;return yn;})();
|
|
1
|
+
var GemWebSdkBridgeBundle=(function(){'use strict';var D="GemWebSdkHandshake";function se(){return typeof document>"u"?void 0:document.readyState}var B=()=>Date.now();function N(n={}){let e=n.win??window,t=e,r=t[D];if(r!==void 0)return r;let s=n.readyState??se,i=n.now??B,d=[],a=[],u=y=>{let c=y.data;!c||c.type!=="gem.handshake"||(d.length>=4&&(d.shift(),a.shift(),p.dropped+=1),d.push(y),a.push(i()));};e.addEventListener("message",u);let p={events:d,times:a,dropped:0,late:s()==="complete",uninstall(){e.removeEventListener("message",u);}};return t[D]=p,p}function W(n,e,t={}){let r=n[D];if(r===void 0)return null;let s=t.ttlMs??9500,i=t.now??B,d=r.events.splice(0),a=r.times?.splice(0),u=0,p=0,y=false,c=i()-s;for(let f=d.length-1;f>=0;f--){let R=a?.[f];if(R!==void 0&&R<c){p+=1;continue}if(u+=1,e(d[f])===true){y=true;break}}return r.uninstall(),{capture:r,delivered:u,stale:p,accepted:y}}var l="gem-web-bridge",S={min:1,max:1},ie=65536,V=8,x=5e3,z=1024;var b=Object.create(null),P="__malformed";var F=Object.create(null),le=Object.freeze([["gem.asyncOpponents.","asyncOpponents"],["gem.vibration.","vibration"],["gem.viewport.","reservedAreas"],["gem.moments.","moments"],["gem.prefs.","audioChannels"]]);function C(n){let e=F[n];if(e!==void 0)return e;for(let[t,r]of le)if(n.startsWith(t))return r}function de(n){let e=/^([a-z][a-z0-9+.-]*):\/\/([a-z0-9.-]+|\[[0-9a-f:.]+\])(:([0-9]{1,5}))?$/i.exec(n);return e===null?false:e[4]===void 0||Number(e[4])<=65535}function me(n){let e;try{e=new URL(n);}catch{return false}return e.username!==""||e.password!==""?false:e.protocol==="https:"?true:e.protocol==="http:"&&(e.hostname==="localhost"||e.hostname==="127.0.0.1")}function ue(n){return /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i.test(n)}function ce(n){return /^[a-z_]+$/.test(n)}function ge(n){return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(n)||/^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$/.test(n)&&!n.includes("--")}function M(n,e,t){if(e>V)return `nesting deeper than ${V}`;if(++t.nodes>x)return `more than ${x} values`;if(t.bytes>t.cap)return `over the ${t.cap} byte limit`;if(n==null)return t.bytes+=4,null;let r=typeof n;if(r==="boolean")return t.bytes+=5,null;if(r==="number")return t.bytes+=8,null;if(r==="string")return t.bytes+=n.length*3+2,null;if(r!=="object")return `${r} is not plain data`;let s=n;if(t.seen.has(s))return "a repeated or cyclic object reference";if(t.seen.add(s),Array.isArray(n)){if(n.length>z)return `an array of ${n.length} entries, over the ${z} limit`;t.bytes+=2;for(let a of n){let u=M(a,e+1,t);if(u)return u}return null}let i=Object.getPrototypeOf(n);if(i!==Object.prototype&&i!==null)return "a value that is not a plain object";t.bytes+=2;let d=n;for(let a in d){if(!Object.prototype.hasOwnProperty.call(d,a))continue;if(++t.nodes>x)return `more than ${x} values`;if(t.bytes+=a.length*3+4,t.bytes>t.cap)return `over the ${t.cap} byte limit`;let u=M(d[a],e+1,t);if(u)return u}return null}function pe(n,e=ie){let t={nodes:0,bytes:0,seen:new Set,cap:e},r=M(n,0,t);return r?`message contains ${r}`:t.bytes>e?`message is over the ${e} byte limit`:null}var ye={origin:[de,"not a well-formed origin"],url:[me,"not a safe absolute http(s) URL"],bcp47:[ue,"not a well-formed BCP 47 tag"],code:[ce,"not a code of lower-case letters and underscores"],gameRef:[ge,"not a game id or web address"]};function K(n,e,t){for(let r of e){let s=t===""?r[0]:`${t}.${r[0]}`,i=n[r[0]];if(i==null){if(r[1]<"a")continue;return `${s}: required`}let d=j(i,r,s);if(d)return d}return null}function j(n,e,t){let r=e[1].toLowerCase();switch(r){case "u":return typeof n=="boolean"?null:j(n,typeof n=="string"?[e[0],"s",e[2]]:[e[0],"n",e[3],e[4]],t);case "s":{if(typeof n!="string")return `${t}: expected string`;if(n.length>e[2])return `${t}: exceeds ${e[2]} characters`;if(e[3]){let[s,i]=ye[e[3]];if(!s(n))return `${t}: ${i}`}return null}case "n":case "i":return typeof n!="number"||!(r=="i"?Number.isInteger:Number.isFinite)(n)?`${t}: expected ${r=="i"?"integer":"number"}`:n<e[2]||n>e[3]?`${t}: out of range ${e[2]}..${e[3]}`:null;case "b":return typeof n=="boolean"?null:`${t}: expected boolean`;case "e":{let s=e[2];return s.includes(n)?null:`${t}: not one of ${s.join("|")}`}case "o":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected object`:K(n,e[2],t);case "j":return typeof n!="object"||n===null||Array.isArray(n)?`${t}: expected a plain object`:null;case "a":{if(!Array.isArray(n))return `${t}: expected array`;if(n.length>e[2])return `${t}: exceeds ${e[2]} items`;let s=e[3],i=`${t}[]`;for(let d of n){if(d==null){if(s[1]<"a")continue;return `${i}: required`}let a=j(d,s,i);if(a)return a}return null}default:return `${t}: unknown kind ${String(r)}`}}var Y=Object.create(null);function o(n,e,t,r,s){return b[n]=t,r&&(F[n]=r),Y[n]=i=>{let d=pe(i,s);if(d)return d;if(typeof i!="object"||i===null||Array.isArray(i))return "message: expected object";let a=i;return a.gem!==l?"gem: wrong protocol marker":a.type!==n?`type: expected ${n}`:K(a,e,"")}}var w=["","s",64],m=["requestId","s",64],k=["matchId","s",64],h=["outcome","e",["ok","declined","unavailable"]],v=["reason","S",64,"code"];o("gem.handshake",[["versions","o",[["min","i",1,1e3],["max","i",1,1e3]]],["capabilities","A",64,w],["embedder","s",2048,"origin"],["unbidden","B"]],30);o("gem.handshake.accept",[["v","i",1,1e3],["capabilities","A",64,w]],5);o("gem.token",[["token","s",4096],["requestId","S",64],["generation","i",0,2147483647],["apiBaseUrl","S",512,"url"],["playerId","s",64],["gameId","s",64],["channel","e",["dev","test","release"]],["gameVersion","S",64],["expiresAt","i",0,4102444800],["chainEndsAt","i",0,4102444800],["allowDevTools","B"]],30);o("gem.token.denied",[m,["retryable","b"],["retryAfterMs","I",0,36e5],["reason","e",["chain_exhausted","unavailable"]],["correlationId","s",64]],30);o("gem.player",[["untrusted","o",[["displayName","s",64],["displayTag","S",80],["displayHandle","s",80]]],["playerId","S",64],["discriminator","S",8],["avatarIconUrl","S",2048,"url"],["avatarIconId","S",64],["avatar3dId","S",64],["avatar3dModelUrl","S",2048,"url"],["avatar3dIconUrl","S",2048,"url"],["avatars3d","A",8,["","o",[["family","s",32],["id","s",64],["modelUrl","s",256,"url"],["iconUrl","s",256,"url"]]]],["badges","A",2,["","o",[["itemId","s",64],["iconUrl","s",2048,"url"]]]],["displayTag","s",8],["avatarUrl","S",2048,"url"],["boost","O",[["multipliers","a",8,["","o",[["currencyId","s",64],["currencySlug","s",32],["multiplierBp","i",1e4,1e6]]]],["expiresInDays","I",1,7]]]],12);o("gem.pause",[["paused","b"]],120);o("gem.prefs",[["reducedMotion","b"],["muted","b"],["showFrameStats","B"],["music","B"],["sounds","B"],["locale","s",35,"bcp47"],["recommended","O",[["volume","i",0,100],["batterySaver","b"],["keyboardLayout","e",["wasd","arrows"]],["gamepadConfirm","e",["south","east"]],["vibration","b"],["lookSensitivity","I",20,400],["gamepadLookSensitivity","I",20,400],["invertLookY","B"],["touchMovement","S",16,"code"],["graphicsQuality","S",16,"code"],["graphicsQualityAuto","B"],["colorHelp","B"]]]],60);o("gem.cohorts",[["cohorts","a",8,["","o",[["experimentId","s",36],["variantKey","s",32]]]]],12,"cohorts");o("gem.earnAllowanceChanged",[],30,"earnAllowance");o("gem.viewport",[["insets","o",[["top","i",0,4096],["right","i",0,4096],["bottom","i",0,4096],["left","i",0,4096]]],["reserved","A",4,["","o",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384],["control","O",[["x","i",0,16384],["y","i",0,16384],["width","i",0,16384],["height","i",0,16384]]]]]]],60,"viewport");o("gem.deviceSettings",[["settings","J"]],12,"deviceSettings");o("gem.willTerminate",[["reason","e",["grant_expiring","session_ended","navigating","kill_switch"]],["graceMs","i",0,6e4]],10);o("gem.roomEvent",[["event","e",["updated","match_started","match_ready","match_failed","version_stale"]],["roomId","s",64],["matchId","S",64],["version","I",0,2147483647],["reason","S",64],["platform","E",["universal","mobile"]]],120,"rooms");o("gem.socketState",[["connected","b"],["reason","E",["connecting","dropped","watchdog","terminal"]]],12,"socketState");o("gem.seatedEvent",[["roomId","s",64]],60,"partySeating");o("gem.autoStartFailedEvent",[["roomId","s",64],["code","s",64]],30,"autoStartFailure");o("gem.ticketEvent",[["event","e",["queued","matched","expired","cancelled","failed"]],["ticketId","s",64],["partySize","I",1,64],["queuedAt","I",0,4102444800],["expiresAt","I",0,4102444800],["reason","S",64],["roomId","S",64]],60,"ticketEvents");o("gem.signalEvent",[["event","e",["hello","offer","answer","ice","peerJoined","peerLeft","migration","matchEnded","error"]],k,["data","j"]],1800,"p2p");o("gem.relayDraining",[k,["clusterTargetId","s",64],["relayIps","a",16,w],["drainingSince","s",40],["deadline","s",40]],60,"p2pRelayDrain");o("gem.dedicatedEvent",[["event","e",["answer","error","matchEnded"]],k,["sdp","S",10240],["sessionId","S",64],["code","S",64],["message","S",256],["reason","S",64]],120,"dedicated");o("gem.partyResult",[m,["partyId","S",64]],30,"parties");o("gem.clusterTarget",[["clusterTargetId","s",64],["clusterTargetIds","A",3,w]],12,"clusterTarget");o("gem.devtools",[["action","e",["open","close","toggle"]]],30,"devtools");o("gem.screenshot",[["maxWidth","i",1,7680],["maxHeight","i",1,4320]],6,"screenshot");o("gem.screenshot.denied",[["reason","e",["not_wired","no_source","unsupported","unreadable","tainted","too_large","busy","declined","failed"]],["retryable","b"]],12,"screenshot");o("gem.requestContext",[],12,"feedbackContext");o("gem.gameContext",[["context","j"]],12,"feedbackContext");o("gem.signal",[["signal","e",["join","offer","answer","ice","migrationAck","hostUnreachable"]],k,["data","j"]],1200,"p2p");o("gem.dedicatedOffer",[k,["sdp","s",10240],["intent","e",["initial","ice_restart","full_reconnect"]]],60,"dedicated");o("gem.partyRequest",[m],12,"parties");o("gem.ready",[],5);o("gem.engaged",[],5,"engagement");o("gem.progress",[["loaded","i",0,1099511627776],["total","i",0,1099511627776]],600);o("gem.requestToken",[m,["reason","e",["initial","expiring","rejected"]]],12);o("gem.requestLocalServer",[m,["gameRoomId","s",64]],6,"localHost");o("gem.localServer",[m,["serverId","S",128]],30,"localHost");o("gem.localServer.denied",[m,["retryable","b"],["reason","e",["refused","unavailable"]]],30,"localHost");o("gem.exit",[["reason","S",64]],5);o("gem.openExternal",[["url","s",2048,"url"]],6);o("gem.requestFullscreen",[["enabled","b"]],30);o("gem.audioState",[["muted","b"]],120,"audioState");o("gem.rewardCue",[["currencySlug","s",32],["expected","I",1,1e9],["origin","O",[["x","i",0,16384],["y","i",0,16384]]]],60,"rewardCue");o("gem.walletDisplay",[["visible","b"],["currencies","A",8,w],["score","B"]],60,"walletDisplay");o("gem.controlsDeclare",[["uses","o",[["lookSensitivity","S",16,"code"],["gamepadLookSensitivity","S",16,"code"],["invertLookY","S",16,"code"],["touchMovement","S",16,"code"]]]],60,"controls");o("gem.menu",[["settings","b"],["items","A",4,["","o",[["id","s",32],["label","s",40]]]]],60,"gameMenu");o("gem.menuSelect",[["entry","e",["settings","item"]],["id","S",32]],60,"gameMenu");o("gem.gemCorner",[["corner","e",["top-left","top-right","bottom-left","bottom-right"]]],12,"gemCorner");o("gem.menuStyle",[["ground","S",7],["accent","S",7]],12,"menuStyle");o("gem.settingsDeclare",[["settings","a",8,["","o",[["id","s",40],["label","s",40],["kind","e",["toggle","slider","choice"]],["value","u",40,-1e6,1e6],["quick","B"],["icon","S",16,"code"],["min","N",-1e6,1e6],["max","N",-1e6,1e6],["step","N",0,1e6],["options","A",4,["","o",[["id","s",40],["label","s",40]]]]]]]],120,"gameSettings");o("gem.settingChange",[["id","s",40],["value","u",40,-1e6,1e6]],120,"gameSettings");o("gem.requestShare",[["params","a",16,["","o",[["key","s",64],["value","s",512]]]]],6,"share");o("gem.requestAvatarPicker",[["tab","E",["2d","3d"]]],6,"avatarPicker");o("gem.vibrate",[["pattern","a",99,["","i",0,1e4]]],600,"vibration");o("gem.frameStats",[["fps","i",0,1e3],["frameTimeP95Ms","i",0,1e5],["counts","E",["pageFrames","gameDraws"]]],120,"frameStats");o("gem.portraitBorders",[["name","S",40]],60,"portraitBorders");o("gem.error",[["code","e",["version_mismatch","handshake_timeout","handshake_duplicate","invalid_message","rate_limited","token_unavailable","unsupported","unsupported_capability","internal"]],["message","S",256]],60);o("gem.requestPurchase",[m,["purchaseKind","e",["item","recipe"]],["targetId","s",64],["currencySlug","s",32],["quantity","I",1,10],["catalogId","S",64]],12,"purchases");o("gem.requestAvatar3dPurchase",[m,["itemId","s",64],["currencySlug","s",32]],12,"avatar3dPurchases");o("gem.purchaseResult",[m,h,v,["purchaseId","S",64]],30,"purchases");o("gem.deviceSettings.put",[m,["settings","j"]],30,"deviceSettings");o("gem.deviceSettings.result",[m,["outcome","e",["stored","too_large","unavailable"]]],30,"deviceSettings");o("gem.requestAd",[m,["placement","s",32],["format","e",["interstitial","rewarded"]]],6,"ads");o("gem.adResult",[m,["outcome","e",["shown","no_ad","unavailable"]]],12,"ads");o("gem.signalMoment",[m,["kind","e",["level_end","run_end","match_end","menu_return","other_break"]],["placement","S",32],["wall","B"]],6,"moments");o("gem.momentResult",[m,["shown","b"]],12,"moments");o("gem.requestSignIn",[m],6,"signIn");o("gem.signInResult",[m,["outcome","e",["signed_in","declined","unavailable"]]],12,"signIn");o("gem.requestFriendChallenge",[m,["playerId","s",64],["template","s",64],["params","J"]],12,"friendChallenges");o("gem.friendChallengeResult",[m,["outcome","e",["accepted","cancelled","refused","unavailable","rate_limited","invalid"]],["code","S",64],["param","S",64],["channel","S",16],["configVersion","I",0,9007199254740991],["retryAfterMs","I",0,864e5],["detail","S",300]],30,"friendChallenges");o("gem.requestAppLinks",[m],12,"appLinks");o("gem.appLinksResult",[m,h,v,["arcade","O",[["store","e",["app_store","google_play"]]]],["game","O",[["store","e",["app_store","google_play"]]]]],30,"appLinks");o("gem.requestOpenApp",[m,["target","e",["arcade","game"]]],6,"appLinks");o("gem.openAppResult",[m,h,v],30,"appLinks");o("gem.requestOpenGame",[m,["ref","s",40,"gameRef"]],6,"openGame");o("gem.openGameResult",[m,h,v],30,"openGame");o("gem.requestSimilarGames",[m,["limit","I",1,24]],12,"openGame");o("gem.similarGamesResult",[m,h,v,["games","a",24,["","o",[["gameId","s",36,"gameRef"],["slug","S",40,"gameRef"],["iconUrl","s",1024,"url"],["capsuleLandscapeUrl","s",1024,"url"],["capsulePortraitUrl","s",1024,"url"],["previewVideoUrl","S",1024,"url"],["previewPosterUrl","S",1024,"url"],["untrusted","o",[["name","s",64],["tagline","s",64],["shortDescription","s",320],["tags","a",10,["","s",32]]]]]]]],30,"openGame",524288);o("gem.requestStoreOffers",[m],12,"store");o("gem.storeOffersResult",[m,h,v,["offers","a",32,["","o",[["skuId","s",64],["kind","e",["gem_pack","boost","pass","bundle"]],["name","s",80],["description","s",400],["imageUrl","S",1024,"url"],["priceDisplay","s",32],["grant","O",[["currency","s",32],["amount","i",1,1e9]]]]]]],30,"store",262144);o("gem.requestStorePurchase",[m,["skuId","s",64]],12,"store");o("gem.storePurchaseResult",[m,h,v],30,"store");o("gem.storePurchaseSettled",[["skuId","s",64]],30,"store");o("gem.requestStoreOfferSheet",[m,["skuId","s",64]],12,"storeOfferSheet");o("gem.storeOfferSheetResult",[m,h,v],30,"storeOfferSheet");var T=Object.freeze(Y);b.__malformed=60;Object.freeze(b);Object.freeze(F);var fe=/^[a-z0-9][a-z0-9_-]{0,39}$/;function he(n){return typeof n=="string"&&fe.test(n)}function J(n){if(n===null||he(n))return n;let e=typeof n=="string"?JSON.stringify(n.length>48?`${n.slice(0,48)}\u2026`:n):`a value of type ${typeof n}`;console.warn(`[gem] showPortraitBorders: ${e} is not a border name, so nothing was sent. A name is 1 to 40 of a-z, 0-9, _ and -, starting with a letter or digit, exactly as your store page declares it under media.portrait_borders. Pass null for no art.`);}var $={now:()=>Date.now(),setTimeout:(n,e)=>setTimeout(n,e),clearTimeout:n=>clearTimeout(n)},G=class{constructor(e=$){this.clock=e;}clock;tokens=new Map;lastRefill=new Map;allow(e){let t=b[e];if(t===void 0)return false;let r=this.clock.now(),s=this.lastRefill.get(e)??r,i=this.tokens.get(e)??t,d=Math.max(0,r-s),a=Math.min(t,i+d*t/6e4);return a<1?(this.tokens.set(e,a),this.lastRefill.set(e,r),false):(this.tokens.set(e,a-1),this.lastRefill.set(e,r),true)}};function q(n,e){return n.toLowerCase()===e.toLowerCase()}function E(n){let e=n.toLowerCase();return e==="localhost"||e.endsWith(".localhost")||e==="[::1]"||e==="::1"?true:/^127(\.\d{1,3}){3}$/.test(e)}function Q(n,e){if(typeof e!="string"||e==="")return false;let t,r;try{t=new URL(n),r=new URL(e);}catch{return false}return t.protocol!=="http:"&&t.protocol!=="https:"||r.protocol!=="http:"&&r.protocol!=="https:"||t.username!==""||t.password!==""||t.pathname!=="/"||t.search!==""||t.hash!==""||t.port===""?false:E(t.hostname)&&E(r.hostname)}function X(n,e){if(n.min>n.max||e.min>e.max)return null;let t=Math.min(n.max,e.max),r=Math.max(n.min,e.min);return t>=r?t:null}var te="[gem: use gem.text.toPlain()]",Z=new Set,ee;function ve(n){try{if(Z.has(n))return;let e=globalThis.location;if(ee??=!!e?.port&&E(e.hostname??""),!ee)return;Z.add(n),console.warn(`[gem] ${n} is PlayerText, turned into a string: players see ${te}. Render text.toPlain(value) (text is in @indiegems/gem-web-sdk/sdk).
|
|
2
|
+
${new Error().stack}`);}catch{}}var U=class{#e;#t;constructor(e,t="player text"){this.#e=e,this.#t=t,Object.freeze(this);}toString(){return ve(this.#t),te}toJSON(){return this.toString()}get[Symbol.toStringTag](){return "PlayerText"}static unwrap(e){return e.#e}};function H(n,e){return new U(n,e)}function ne(n){return Math.min(16384,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function Re(n,e){if(n!==true)return {visible:false};let t=[],r=false;for(let s of Array.isArray(e)?e:[])s==="score"?r=true:typeof s=="string"&&s.length>0&&s.length<=64&&!t.includes(s)&&t.push(s);return !r&&t.length===0?{visible:true}:{visible:true,currencies:t.slice(0,8),score:r}}function Se(n){let e=[];for(let t of Array.isArray(n)?n:[]){if(e.length===4)break;if(typeof t!="object"||t===null)continue;let{id:r,label:s}=t;typeof r!="string"||r.length===0||r.length>32||typeof s!="string"||s.trim().length===0||e.some(i=>i.id===r)||e.push({id:r,label:s.trim().slice(0,40)});}return e.length===0?{settings:false}:{settings:false,items:e}}function re(n,e){return Math.min(e,Math.max(0,Math.round(Number.isFinite(n)?n:0)))}function be(n){let e=n.untrusted,t=H(e.displayName,"gem.player.displayName"),r=e.displayTag||e.displayHandle,s=r?H(r,"gem.player.displayTag"):void 0,i=n.discriminator||n.displayTag,d=i&&/^[0-9]{1,8}$/.test(i)?i:void 0,a=n.avatarIconUrl||n.avatarUrl,u=/^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/i,p=n.avatarIconId,y=n.avatar3dId,c={displayName:t,displayHandle:s??t,badges:(n.badges??[]).map(g=>({itemId:g.itemId,iconUrl:g.iconUrl}))};n.playerId&&(c.playerId=n.playerId),d&&(c.discriminator=d),s&&(c.displayTag=s),a&&(c.avatarIconUrl=c.avatarUrl=a),p&&u.test(p)&&(c.avatarIconId=p),y&&u.test(y)&&(c.avatar3dId=y),n.avatar3dModelUrl&&(c.avatar3dModelUrl=n.avatar3dModelUrl),n.avatar3dIconUrl&&(c.avatar3dIconUrl=n.avatar3dIconUrl),n.avatars3d&&(c.avatars3d=n.avatars3d.filter(g=>u.test(g.id)&&/^[a-z][a-z0-9_]*$/.test(g.family)).filter((g,A,L)=>L.findIndex(_=>_.family==g.family)==A).map(({family:g,id:A,modelUrl:L,iconUrl:_})=>({family:g,id:A,modelUrl:L,iconUrl:_})));let f=n.boost,R=(f?.multipliers??[]).map(g=>({currencyId:g.currencyId,currencySlug:g.currencySlug,multiplier:g.multiplierBp/1e4})),I=R.some(g=>g.multiplier>1);return c.boost=I&&f?.expiresInDays?{active:I,multipliers:R,expiresInDays:f.expiresInDays}:{active:I,multipliers:R},c}var we=1e4,ke=["appLinks","openGame","store","storeOfferSheet"],Ge=new WeakMap;var O=class{constructor(e){this.options=e;if(e.expectedSource===void 0||e.expectedSource===null)throw new Error("GemFrame: expectedSource is required and must not be null \u2014 pass window.parent. The handshake transfers the MessagePort, and origin alone does not say WHICH document on that origin sent it.");this.expectedSource=e.expectedSource,this.clock=e.clock??$,this.inbound=new G(this.clock),this.outbound=new G(this.clock);}options;state="waiting";port=null;version=null;peerCapabilities=[];unbidden=false;engagedSent=false;readyPending=false;readySent=false;settleWaiters=[];clock;inbound;outbound;expectedSource;get currentState(){return this.state}get parentCapabilities(){return this.peerCapabilities}parentSupports(e){return this.peerCapabilities.includes(e)}get protocolVersion(){return this.version}get unbiddenLaunch(){return this.unbidden}handleWindowMessage(e){if(e.source!==this.expectedSource)return this.refuse(e,"handshake did not come from the expected embedder window"),false;if(!(this.options.allowedEmbedders.some(a=>q(a,e.origin))||Q(e.origin,this.options.selfOrigin)||(Ge.get(this)??[]).some(a=>q(a,e.origin))))return this.refuse(e,`embedder '${e.origin}' is not in allowedEmbedders`),this.state==="waiting"&&this.transition("refused"),false;if(this.state==="connected")return this.fail("handshake_duplicate","this document already has a port"),false;if(this.state!=="waiting")return false;let r=T["gem.handshake"]?.(e.data);if(r)return this.refuse(e,`malformed handshake: ${r}`),false;let s=e.data;if(!q(s.embedder,e.origin))return this.refuse(e,`handshake claims embedder '${s.embedder}' but arrived from '${e.origin}'`),false;let i=e.ports?.[0];if(!i)return this.refuse(e,"handshake transferred no MessagePort"),false;let d=X(S,s.versions);return d===null?(i.onmessage=null,i.postMessage({gem:l,type:"gem.error",code:"version_mismatch",message:`frame supports v${S.min}..v${S.max}`}),i.close(),this.transition("refused"),this.options.onError?.("version_mismatch"),false):(this.port=i,this.version=d,i.onmessage=a=>this.receive(a.data,a.ports??[]),i.start?.(),this.peerCapabilities=s.capabilities??[],this.unbidden=s.unbidden===true,i.postMessage({gem:l,type:"gem.handshake.accept",v:d,...this.options.capabilities===void 0?{}:{capabilities:[...this.options.capabilities]}}),this.transition("connected"),this.readyPending&&(this.readyPending=false,this.ready()),true)}ready(){if(this.state!=="connected"||!this.port){this.readyPending=true;return}if(this.readySent){this.options.onDropped?.("internal","gem.ready was already sent; duplicate suppressed");return}this.readySent=true,this.send({gem:l,type:"gem.ready"});}settled(e=we){return this.state!=="waiting"?Promise.resolve(this.state):new Promise(t=>{let r=i=>{this.clock.clearTimeout(s),t(i);},s=this.clock.setTimeout(()=>{let i=this.settleWaiters.indexOf(r);i>=0&&this.settleWaiters.splice(i,1),t(this.state);},e);this.settleWaiters.push(r);})}progress(e,t){this.send({gem:l,type:"gem.progress",loaded:e,total:t});}requestToken(e,t){this.send({gem:l,type:"gem.requestToken",requestId:e,reason:t});}requestLocalServer(e,t){return this.parentSupports("localHost")?(this.send({gem:l,type:"gem.requestLocalServer",requestId:e,gameRoomId:t}),true):false}exit(e){this.send(e===void 0?{gem:l,type:"gem.exit"}:{gem:l,type:"gem.exit",reason:e});}openExternal(e){this.send({gem:l,type:"gem.openExternal",url:e});}requestFullscreen(e){this.send({gem:l,type:"gem.requestFullscreen",enabled:e});}reportMuted(e){return this.gated("audioState")?(this.send({gem:l,type:"gem.audioState",muted:e}),true):false}reportEngaged(){return this.engagedSent||!this.gated("engagement")||!this.send({gem:l,type:"gem.engaged"})?false:(this.engagedSent=true,true)}cueReward(e){if(!this.gated("rewardCue"))return false;let t={gem:l,type:"gem.rewardCue",currencySlug:String(e.currencySlug).slice(0,32)};return e.expected!==void 0&&Number.isFinite(e.expected)&&e.expected>=1&&(t.expected=Math.min(1e9,Math.round(e.expected))),e.origin&&(t.origin={x:ne(e.origin.x),y:ne(e.origin.y)}),this.send(t),true}showPortraitBorders(e){let t=J(e);return t===void 0||!this.gated("portraitBorders")?false:this.send(t===null?{gem:l,type:"gem.portraitBorders"}:{gem:l,type:"gem.portraitBorders",name:t})}setWalletVisible(e,t){return this.gated("walletDisplay")?this.send({gem:l,type:"gem.walletDisplay",...Re(e,t?.only)}):false}setMenu(e){return this.gated("gameMenu")?this.send({gem:l,type:"gem.menu",...Se(e)}):false}declareSettings(e){return this.gated("gameSettings")?this.send({gem:l,type:"gem.settingsDeclare",settings:e}):false}setGemCorner(e){return this.gated("gemCorner")?this.send({gem:l,type:"gem.gemCorner",corner:e}):false}setMenuStyle(e){return this.gated("menuStyle")?this.send(e===null?{gem:l,type:"gem.menuStyle"}:{gem:l,type:"gem.menuStyle",ground:e.ground,accent:e.accent}):false}requestShare(e){return this.gated("share")?this.send({gem:l,type:"gem.requestShare",params:e.map(({key:t,value:r})=>({key:t,value:r}))}):false}requestAvatarPicker(e){return this.gated("avatarPicker")?this.send({gem:l,type:"gem.requestAvatarPicker",...e==="2d"||e==="3d"?{tab:e}:{}}):false}requestVibrate(e){return this.gated("vibration")?this.send({gem:l,type:"gem.vibrate",pattern:[...e]}):false}reportFrameStats(e){return this.gated("frameStats")?(this.send({gem:l,type:"gem.frameStats",fps:re(e.fps,1e3),frameTimeP95Ms:re(e.frameTimeP95Ms,1e5),counts:e.counts==="gameDraws"?"gameDraws":"pageFrames"}),true):false}sendGameContext(e){return this.state!=="connected"?false:(this.send({gem:l,type:"gem.gameContext",context:e}),true)}signal(e,t,r){this.send({gem:l,type:"gem.signal",signal:e,matchId:t,data:r});}dedicatedOffer(e,t,r){this.send({gem:l,type:"gem.dedicatedOffer",matchId:e,sdp:t,intent:r});}requestParty(e){return this.send({gem:l,type:"gem.partyRequest",requestId:e})}requestPurchase(e,t,r,s,i,d){return this.send({gem:l,type:"gem.requestPurchase",requestId:e,purchaseKind:t,targetId:r,currencySlug:s,...i===void 0?{}:{quantity:i},...d===void 0?{}:{catalogId:d}})}requestAvatar3dPurchase(e,t,r){return this.parentSupports("avatar3dPurchases")?this.send({gem:l,type:"gem.requestAvatar3dPurchase",requestId:e,itemId:t,currencySlug:r}):false}putDeviceSettings(e,t){return this.send({gem:l,type:"gem.deviceSettings.put",requestId:e,settings:t})}requestAd(e,t,r){return this.send({gem:l,type:"gem.requestAd",requestId:e,placement:t,format:r})}signalMoment(e,t,r,s){return this.send({gem:l,type:"gem.signalMoment",requestId:e,kind:t,...r===void 0?{}:{placement:r},...s===true?{wall:true}:{}})}readyCalled(){return this.readySent||this.readyPending}requestSignIn(e){return this.send({gem:l,type:"gem.requestSignIn",requestId:e})}requestFriendChallenge(e,t,r,s){return this.send({gem:l,type:"gem.requestFriendChallenge",requestId:e,playerId:t,template:r,...s===void 0?{}:{params:s}})}requestArcade(e){return this.gated(C(e.type))&&this.send({gem:l,...e})}close(){this.port&&(this.port.onmessage=null,this.port.close(),this.port=null),this.state!=="refused"&&this.transition("closed");}transition(e){if(this.state!==e){this.state=e;for(let t of this.settleWaiters.splice(0))t(e);this.options.onStateChange?.(e);}}refuseMalformed(e,t){if(!this.inbound.allow(P)){this.options.onDropped?.(e,t);return}this.fail(e,t);}dropUnknownType(e){this.inbound.allow(P)&&this.options.onDropped?.("unsupported",`ignored unknown message type '${e}'`);}refuse(e,t){e.ports?.[0]?.close?.(),this.inbound.allow(P)&&this.options.onRefused?.(t);}fail(e,t){this.options.onError?.(e,t),this.state==="connected"&&this.port&&this.send({gem:l,type:"gem.error",code:e,...t===void 0?{}:{message:t}});}gated(e){return this.state==="connected"&&this.parentSupports(e)}send(e){if(this.state!=="connected"||!this.port)return this.options.onDropped?.("internal",`dropped ${String(e.type)} before connect`),false;let t=e.type;if(!this.outbound.allow(t))return this.options.onError?.("rate_limited",`frame exceeded its own limit for ${t}`),false;let r=T[t]?.(e);return r?(this.options.onError?.("internal",`refusing to send malformed ${t}: ${r}`),false):(this.port.postMessage(this.version===null?e:{...e,v:this.version}),true)}receive(e,t=[]){let r=false;try{r=this.dispatchInbound(e,t);}finally{r||oe(t);}}dispatchInbound(e,t){if(this.state!=="connected")return false;let r=typeof e=="object"&&e!==null?e.type:void 0;if(typeof r!="string")return this.refuseMalformed("invalid_message","message has no type"),false;let s=T[r];if(!s)return this.dropUnknownType(r),false;if(!this.inbound.allow(r))return this.fail("rate_limited",`parent exceeded the limit for ${r}`),false;let i=C(r);if(i!==void 0&&!this.options.capabilities?.includes(i))return this.fail("unsupported_capability",`${r} requires the '${i}' capability, which this frame did not advertise`),false;let d=s(e);if(d)return this.fail("invalid_message",`${r}: ${d}`),false;let a=e;if(a.v!==this.version)return this.fail("invalid_message",`${r}: expected v ${String(this.version)}`),false;switch(r){case "gem.token":this.options.onToken?.(a);break;case "gem.pause":this.options.onPause?.(a);break;case "gem.menuSelect":this.options.onMenuSelect?.(a);break;case "gem.settingChange":this.options.onSettingChange?.(a);break;case "gem.prefs":this.options.onPrefs?.(a);break;case "gem.cohorts":this.options.onCohorts?.(a);break;case "gem.earnAllowanceChanged":this.options.onEarnAllowanceChanged?.();break;case "gem.viewport":this.options.onViewport?.(a);break;case "gem.willTerminate":this.options.onWillTerminate?.(a);break;case "gem.devtools":this.options.onDevtools?.(a.action);break;case "gem.requestContext":this.options.onRequestContext?.();break;case "gem.screenshot":{let u=t[0];if(!u){this.options.onError?.("invalid_message","gem.screenshot transferred no MessagePort");break}let p={maxWidth:a.maxWidth,maxHeight:a.maxHeight};if(!this.options.onScreenshot){u.close(),this.screenshotDenied("not_wired",false);break}return oe(t.slice(1)),this.options.onScreenshot(p,u,(y,c)=>this.screenshotDenied(y,c)),true}case "gem.token.denied":this.options.onTokenDenied?.(a);break;case "gem.localServer":this.options.onLocalServer?.(a);break;case "gem.localServer.denied":this.options.onLocalServerDenied?.(a);break;case "gem.player":{this.options.onPlayer?.(be(a));break}case "gem.roomEvent":this.options.onRoomEvent?.(a);break;case "gem.seatedEvent":this.options.onSeatedEvent?.(a);break;case "gem.autoStartFailedEvent":this.options.onAutoStartFailedEvent?.(a);break;case "gem.ticketEvent":this.options.onTicketEvent?.(a);break;case "gem.socketState":this.options.onSocketState?.(a);break;case "gem.signalEvent":this.options.onSignalEvent?.(a);break;case "gem.relayDraining":this.options.onRelayDraining?.(a);break;case "gem.dedicatedEvent":this.options.onDedicatedEvent?.(a);break;case "gem.clusterTarget":this.options.onClusterTarget?.(a);break;case "gem.partyResult":this.options.onPartyResult?.(a);break;case "gem.purchaseResult":this.options.onPurchaseResult?.(a);break;case "gem.deviceSettings":this.options.onDeviceSettings?.(a);break;case "gem.deviceSettings.result":this.options.onDeviceSettingsResult?.(a);break;case "gem.adResult":this.options.onAdResult?.(a);break;case "gem.momentResult":this.options.onMomentResult?.(a);break;case "gem.signInResult":this.options.onSignInResult?.(a);break;case "gem.friendChallengeResult":this.options.onFriendChallengeResult?.(a);break;case "gem.error":this.options.onError?.(a.code,a.message);break;default:if(ke.includes(C(r))){this.options.onArcadeAnswer?.(a);break}this.fail("unsupported",`no frame handler for '${r}'`);}return false}screenshotDenied(e,t){this.send({gem:l,type:"gem.screenshot.denied",reason:e,retryable:t});}};function oe(n){for(let e of n)try{e?.close?.();}catch{}}N();function Oe(n){if((n.expectedSource??window.parent)===window)throw new Error("GemWebSdkBridge.connect: this document is not framed (window.parent === window), so there is no embedder to hand it a port. A game runs inside the Gem Arcade iframe; opening it directly will never complete a handshake.");let e=new O({...n,expectedSource:n.expectedSource??window.parent}),t=r=>{let s=e.handleWindowMessage({data:r.data,origin:r.origin,source:r.source,ports:r.ports});return s&&window.removeEventListener("message",t),s};return window.addEventListener("message",t),W(window,t),e}var ae={connect:Oe,GemFrame:O,PROTOCOL:l,SUPPORTED_VERSIONS:S};window.GemWebSdkBridge=ae;var vn=ae;return vn;})();
|